我是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 ++中模拟这种行为? 另外,这只是不好的设计吗?如果是这样,您会推荐哪种方法?
答案 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;
}