声明对象而不调用默认构造函数

时间:2019-06-21 14:41:05

标签: c++ object default-constructor

我是C ++的新手,并且习惯于使用Java。 在Java中,我可以选择声明一个对象而无需实例化它,并且希望在C ++中也可以做到这一点。

假设存在某个类Foo,在Java中,我可以编写Foo bar;来声明Foo的实例,而无需初始化bar。 但是,在C ++中,当我编写Foo bar;时,bar是通过调用类Foo的默认构造函数来初始化的。

如果我为类Foo编写了一个或多个构造函数,这尤其令人烦恼,每个构造函数都有至少一个参数。在这种情况下,代码将无法编译,并显示类似于no matching function for call to 'Foo::Foo()'

的错误

例如,假设我在Java中具有以下类定义:

public class Foo {
    private boolean b;

    Foo(boolean b) { this.b = b; }
}

以及C ++中相应的类定义:

class Foo {
        bool b_;
    public:
        Foo(bool b) : b_(b) {}
};

在Java中,我可以编写一些方法

public static Foo makeFoo(int x) {
    Foo result;
    if (x > 10) { result = new Foo(true); }
    else { result = new Foo(false); }
    return result;
}

但是,如果我用C ++编写类似的方法,则会出现编译错误:

Foo makeFoo(int x) {
    Foo result; // here, a call is made to Foo::Foo() which doesn't exist, and thus compilation fails
    if (x > 10) { 
        result = Foo(true); // this would probably also fail since I didn't specify a copy-constructor, but I'm not entirely sure
    }
    else {
        result = Foo(false); // as above, I think this would probably fail
    }
    return result;
}

尽管我给出的示例没有用,但是在编写Java代码时经常使用这种方法。 有没有办法在C ++中模拟这种行为? 另外,这只是不好的设计吗?如果是这样,您会推荐哪种方法?

4 个答案:

答案 0 :(得分:5)

如果您不想像Igor(和其他人)在对问题的第一条评论中所述,使用指针来获取参考功能,则可以做几件事。

首先,值类型而不是引用类型的理念是在需要它们之前不要创建它们。您倾向于在使用该对象以在该函数的其余部分中获得某种多态功能(例如,某些创建后的通用初始化代码)之前声明引用是合理的设计,但无法以您编写它的方式来表达,因为将涉及创造一个价值。

您可以提供一个默认的构造函数并赋予其某些行为-但很明显,您和其他任何人都不想被强迫这样做。

替代方法的本质是将变量移到范围内并返回。

Foo makeFoo(int x) {
    if (x > 10) { 
        Foo result = Foo(true);
        return result;
    }
    else {
        Foo result = Foo(false);
        return result;
    }
}

显然,这可以防止您在 return 之前的 if 块之后编写通用的创建后初始化代码。为此,您可以在自己的函数中编写 if 块,并使其返回结果,然后在初始化对象后编写后续代码。

Foo premakeFoo(int x) {
    if (x > 10) { 
        Foo result = Foo(true);
        return result;
    }
    else {
        Foo result = Foo(false);
        return result;
    }
}

Foo makeFoo(int x) {
    Foo result = premakeFoo(x);
    // common post init code can go here.
    return result;
}

如果您不希望在完全独立的函数中使用它,则可以执行lambda。

Foo makeFoo(int x) {
    Foo result = ([=]() {
    if (x > 10) { 
        Foo result = Foo(true);
        return result;
    }
    else {
        Foo result = Foo(false);
        return result;
    })();
    // common post init code can go here.
    return result;
}

或者在您的情况下,如果它很小,请使用内联三元表达式。

Foo makeFoo(int x) {
    Foo result = (x > 10) ? Foo(true) : Foo(false); // or just Foo(x>10)
    // common post init code can go here.
    return result;
}

还有其他涉及模板的聪明选择,但是它们都围绕着隔离重载构造函数的不同表达式,然后使用赋值来初始化给定一些更复杂表达式的变量的想法。而您想要做的就是获得以两种不同方式构造值的表达式,以 跨越作用域块 (跨越 if 块)。三元和函数是在单个表达式中执行 if 逻辑的选项。

答案 1 :(得分:3)

在Java中,当您Foo result;创建对Foo的引用时,实际上并没有创建对象。 C ++有所不同,因为它具有值语义,并且Foo result;实际上创建了一个对象。如果没有默认构造函数,则会引发错误。

要获得相同类型的行为,您需要在C ++中使用指针。您不能使用引用,因为与Java不同,引用在创建时需要绑定到对象。因此,如果使用std::unique_ptr<Foo>,则可以进行声明,然后稍后使用在创建逻辑中初始化的std::unique_ptr<Foo>对其进行初始化。使用您的示例,代码看起来像

Foo makeFoo(int x) {
    std::unique_ptr<Foo> result; // here result doesn't have an actual object yet
    if (x > 10) { 
        result = std::make_unique<Foo>(true); // now we pass the values we want to construct with and get a valid object
    }
    else {
        result = std::make_unique<Foo>(false); // same as above
    }
    return *result; // this lets you return a `Foo` which will be a copy, and the Foo created by make_unique will be destroyed
}

如果不想进行复制,则可以改用return result;,并更改函数以返回std::unique_ptr<Foo>

答案 2 :(得分:1)

您试图做的事情称为工厂模式,通常以这种方式实现

std::unique_ptr<Foo> makeFoo(int x) {
    std::unique_ptr<Foo> result = nullptr; 

    if (x > 10) { 
        result = std::make_unique<Foo>(true); 
    }
    else {
        result = std::make_unique<Foo>(false); 
    }
    return result;
}

要使上述功能正常工作,您还需要包含标题。请参阅this,以获取有关c ++中unique_ptr和内存管理的更多信息

另外,请注意,我不建议在代码中使用这种冗长的级别,我出于演示目的将其写出。以下版本提供的代码行更少,并且更美观。

std::unique_ptr<Foo> makeFoo(int x) {
    if (x > 10) { 
        return std::make_unique<Foo>(true); 
    }
    return std::make_unique<Foo>(false); 
}

答案 3 :(得分:-1)

首先,您可以以不需要默认构造结果的方式重新排列代码:

Foo makeFoo(int x) {
    return x > 10 ? Foo(true) : Foo(false);
}

如果这不可能,则可以使用指针,如其他建议的答案。

最后,如果允许此函数返回“ nothing”,则可以使用std::optional template

#include <optional>

std::optional<Foo> makeFoo(int x) {
    std::optional<Foo> result; 
    if (x > 10) { 
        result = Foo(true);
    }
    else {
        result = Foo(false);
    }
    return result;
}