在不调用默认构造函数的情况下声明对象
Declare object without calling default constructor
我是 C++ 新手,习惯使用 Java。
在 Java 中,我可以选择在不实例化的情况下声明一个对象,并且想在 C++ 中做同样的事情。
假设有一些 class Foo
,在 Java 中我可以写 Foo bar;
来声明 Foo
的实例而不初始化 bar
.
但是,在 C++ 中,当我编写 Foo bar;
时,bar
是通过调用 class Foo
.
的默认构造函数来初始化的
如果我为 class Foo
编写了一个或多个构造函数,这尤其令人烦恼,每个构造函数至少有一个参数。在这种情况下,代码将无法编译并出现类似于 no matching function for call to 'Foo::Foo()'
的错误
例如,假设我在 Java 中有以下 class 定义:
public class Foo {
private boolean b;
Foo(boolean b) { this.b = b; }
}
和C++中对应的class定义:
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++ 中模拟这种行为?
或者,这只是糟糕的设计吗?如果是这样,您会推荐哪种方法?
您尝试做的事情称为工厂模式,通常以这种方式实现
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;
}
要使上述工作正常,您还需要包含 header。有关 unique_ptr 和 c++
中的内存管理的更多信息,请参阅 this
另请注意,我不建议在代码中使用这种级别的冗长代码,我写出来是为了演示目的。以下版本提供的代码行数更少,更优雅。
std::unique_ptr<Foo> makeFoo(int x) {
if (x > 10) {
return std::make_unique<Foo>(true);
}
return std::make_unique<Foo>(false);
}
在 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;
代替,把函数改成return a std::unique_ptr<Foo>
.
如果您不想像 Igor(和其他人)在对您的问题的第一条评论中所解释的那样使用指针来获取引用功能,那么您可以做一些事情。
首先,值类型而不是引用类型的哲学是在需要它们之前不要创建它们。您试图在使用对象之前声明引用以在函数的其余部分(可能是一些 post-create 通用初始化代码)中获得一种多态功能是合理的设计,但不能以您编写的方式表达因为这会涉及创造价值。
您可以提供一个默认构造函数并赋予它一些行为——但很明显,您和其他任何人都不想被迫这样做。
备选方案的实质是将变量移动到作用域内并 return 它。
Foo makeFoo(int x) {
if (x > 10) {
Foo result = Foo(true);
return result;
}
else {
Foo result = Foo(false);
return result;
}
}
显然,这会阻止您在 return 之前的 if 块之后编写通用 post-create 初始化代码.为此,您可以在自己的函数中编写 if 块并获得 return 结果,然后在初始化对象后编写后续代码。
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 逻辑。
我是 C++ 新手,习惯使用 Java。 在 Java 中,我可以选择在不实例化的情况下声明一个对象,并且想在 C++ 中做同样的事情。
假设有一些 class Foo
,在 Java 中我可以写 Foo bar;
来声明 Foo
的实例而不初始化 bar
.
但是,在 C++ 中,当我编写 Foo bar;
时,bar
是通过调用 class Foo
.
如果我为 class Foo
编写了一个或多个构造函数,这尤其令人烦恼,每个构造函数至少有一个参数。在这种情况下,代码将无法编译并出现类似于 no matching function for call to 'Foo::Foo()'
例如,假设我在 Java 中有以下 class 定义:
public class Foo {
private boolean b;
Foo(boolean b) { this.b = b; }
}
和C++中对应的class定义:
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++ 中模拟这种行为? 或者,这只是糟糕的设计吗?如果是这样,您会推荐哪种方法?
您尝试做的事情称为工厂模式,通常以这种方式实现
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;
}
要使上述工作正常,您还需要包含 header。有关 unique_ptr 和 c++
中的内存管理的更多信息,请参阅 this另请注意,我不建议在代码中使用这种级别的冗长代码,我写出来是为了演示目的。以下版本提供的代码行数更少,更优雅。
std::unique_ptr<Foo> makeFoo(int x) {
if (x > 10) {
return std::make_unique<Foo>(true);
}
return std::make_unique<Foo>(false);
}
在 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;
代替,把函数改成return a std::unique_ptr<Foo>
.
如果您不想像 Igor(和其他人)在对您的问题的第一条评论中所解释的那样使用指针来获取引用功能,那么您可以做一些事情。
首先,值类型而不是引用类型的哲学是在需要它们之前不要创建它们。您试图在使用对象之前声明引用以在函数的其余部分(可能是一些 post-create 通用初始化代码)中获得一种多态功能是合理的设计,但不能以您编写的方式表达因为这会涉及创造价值。
您可以提供一个默认构造函数并赋予它一些行为——但很明显,您和其他任何人都不想被迫这样做。
备选方案的实质是将变量移动到作用域内并 return 它。
Foo makeFoo(int x) {
if (x > 10) {
Foo result = Foo(true);
return result;
}
else {
Foo result = Foo(false);
return result;
}
}
显然,这会阻止您在 return 之前的 if 块之后编写通用 post-create 初始化代码.为此,您可以在自己的函数中编写 if 块并获得 return 结果,然后在初始化对象后编写后续代码。
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 逻辑。