声明对象而不调用默认构造函数
Declare object without calling 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++中模拟这种行为? 或者,这只是糟糕的设计吗?如果是这样,您会推荐哪种方法?
如果您不想使用指针来获取 Igor (和其他人)在您的问题的第一条评论中解释的参考功能,那么您可以做几件事。
首先,值类型而不是引用类型的理念是,在需要它们之前不要创建它们。 在使用对象之前声明引用的诱惑是在函数的其余部分(也许是一些创建后的公共 init 代码)中获得一种多态功能,这是合理的设计,但不能用你编写它的方式表达,因为它会涉及创建一个值。
你可以提供一个默认的构造函数并给它一些行为——但很明显,你和其他人都不想被强迫这样做。
替代方法的本质是将变量移动到作用域内并返回它。
Foo makeFoo(int x) {
if (x > 10) {
Foo result = Foo(true);
return result;
}
else {
Foo result = Foo(false);
return result;
}
}
显然,这会阻止您在返回之前在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逻辑的选项。
在 Java 中,当你创建对 Foo 的引用Foo result;
时,你实际上并没有创建一个对象。 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>
。
您尝试执行的操作称为工厂模式,通常以这种方式实现
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;
}
要使上述内容正常工作,您还需要包含标头。 有关 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);
}
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么即使我调用参数化构造函数也会调用默认构造函数?
- 具有非默认构造函数的单例类
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 在C++中使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数的情况下创建的派生对象
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 创建类类型的动态分配数组,其中类不得具有默认构造函数