作为一名 C# 开发人员,我习惯于通过构造函数运行:
class Test {
public Test() {
DoSomething();
}
public Test(int count) : this() {
DoSomethingWithCount(count);
}
public Test(int count, string name) : this(count) {
DoSomethingWithName(name);
}
}
有没有办法在 C++ 中做到这一点?
我尝试调用类名并使用“this”关键字,但都失败了。
this
OR auto
将是未来重构目的的有趣关键字。
C++11:是的!
C++11 及更高版本具有相同的功能(称为 delegating constructors)。
语法与 C# 略有不同:
class Foo {
public:
Foo(char x, int y) {}
Foo(int y) : Foo('a', y) {}
};
C++03:没有
不幸的是,在 C++03 中没有办法做到这一点,但有两种模拟方式:
您可以通过默认参数组合两个(或更多)构造函数: class Foo { public: Foo(char x, int y=0); // 结合两个构造函数 (char) 和 (char, int) // ... };使用 init 方法共享公共代码:class Foo { public: Foo(char x); Foo(char x, int y); // ... private: void init(char x, int y); }; Foo::Foo(char x) { init(x, int(x) + 7); // ... } Foo::Foo(char x, int y) { init(x, y); // ... } void Foo::init(char x, int y) { // ... }
请参阅 the C++FAQ entry 以供参考。
是和否,取决于 C++ 的版本。
在 C++03 中,您不能从另一个构造函数调用一个构造函数(称为委托构造函数)。
这在 C++11(又名 C++0x)中发生了变化,增加了对以下语法的支持:
(示例取自 Wikipedia)
class SomeType
{
int number;
public:
SomeType(int newNumber) : number(newNumber) {}
SomeType() : SomeType(42) {}
};
SomeType(string const &s) { /*...*/ } SomeType(char const *pc) : SomeType(string(pc)) { /*...*/ }
我相信您可以从构造函数中调用构造函数。它将编译并运行。我最近看到有人这样做,它可以在 Windows 和 Linux 上运行。
它只是不做你想做的事。内部构造函数将构造一个临时本地对象,一旦外部构造函数返回,该对象就会被删除。它们也必须是不同的构造函数,否则您将创建递归调用。
参考:https://isocpp.org/wiki/faq/ctors#init-methods
new
语法。不过,这通常不是您想要的。 (并且它没有做任何事情来允许您显式提供模板参数。)
new
仍会创建一个 new 对象,尽管它位于相同的内存位置。但是仍然是一个不同的对象,并且可以将证明这一点的代码放在一起。
C++11:是的!
C++11 及更高版本具有相同的功能(称为 delegating constructors)。
语法与 C# 略有不同:
class Foo {
public:
Foo(char x, int y) {}
Foo(int y) : Foo('a', y) {}
};
C++03:没有
值得指出的是,您可以在构造函数中调用父类的构造函数,例如:
class A { /* ... */ };
class B : public A
{
B() : A()
{
// ...
}
};
但是,不,您不能在 C++03 之前调用同一类的另一个构造函数。
在 C++11 中,一个 constructor can call another constructor overload:
class Foo {
int d;
public:
Foo (int i) : d(i) {}
Foo () : Foo(42) {} //New to C++11
};
此外,成员也可以像这样初始化。
class Foo {
int d = 5;
public:
Foo (int i) : d(i) {}
};
这应该消除创建初始化辅助方法的需要。并且仍然建议不要在构造函数或析构函数中调用任何虚函数,以避免使用任何可能未初始化的成员。
如果你想作恶,你可以使用就地的“new”操作符:
class Foo() {
Foo() { /* default constructor deliciousness */ }
Foo(Bar myParam) {
new (this) Foo();
/* bar your param all night long */
}
};
似乎对我有用。
编辑
正如@ElvedinHamzagic 指出的那样,如果 Foo 包含一个分配内存的对象,则该对象可能不会被释放。这使事情进一步复杂化。
一个更一般的例子:
class Foo() {
private:
std::vector<int> Stuff;
public:
Foo()
: Stuff(42)
{
/* default constructor deliciousness */
}
Foo(Bar myParam)
{
this->~Foo();
new (this) Foo();
/* bar your param all night long */
}
};
当然,看起来有点不那么优雅。 @JohnIdol 的解决方案要好得多。
this->~Foo();
,在 new (this) Foo();
之前
简单地说,你不能在 C++11 之前。
C++11 引入了 delegating constructors:
委托构造函数如果类本身的名称在成员初始化器列表中显示为类或标识符,则列表必须仅包含一个成员初始化器;这样的构造函数称为委托构造函数,初始化器列表中唯一成员选择的构造函数就是目标构造函数。这种情况下,目标构造函数通过重载决议选择并首先执行,然后控制权返回委托构造函数,它的身体被执行。委托构造函数不能是递归的。 class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) 委托给 Foo(char,int) };
请注意,委托构造函数是一个全有或全无的提议;如果一个构造函数委托给另一个构造函数,则调用构造函数的初始化列表中不允许有任何其他成员。如果您考虑一次且仅初始化一次 const/reference 成员,这是有道理的。
不,在 C++ 中,您不能从构造函数调用构造函数。正如沃伦指出的那样,您可以做的是:
使用不同的签名重载构造函数
在参数上使用默认值,以提供“更简单”的版本
请注意,在第一种情况下,您不能通过从另一个构造函数调用一个构造函数来减少代码重复。您当然可以有一个单独的、私有/受保护的方法来完成所有初始化,并让构造函数主要处理参数处理。
尚未显示的另一个选项是将您的类分成两个,在原始类周围包装一个轻量级接口类,以实现您正在寻找的效果:
class Test_Base {
public Test_Base() {
DoSomething();
}
};
class Test : public Test_Base {
public Test() : Test_Base() {
}
public Test(int count) : Test_Base() {
DoSomethingWithCount(count);
}
};
如果您有许多必须调用其“下一级”对应物的构造函数,这可能会变得混乱,但对于少数构造函数,它应该是可行的。
在 Visual C++ 中,您还可以在构造函数中使用此表示法:this->Classname::Classname(parameters of another constructor)。请参见下面的示例:
class Vertex
{
private:
int x, y;
public:
Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
Vertex()
{
this->Vertex::Vertex(-1, -1);
}
};
我不知道它是否适用于其他地方,我只在 Visual C++ 2003 和 2008 中对其进行了测试。我想你也可以这样调用多个构造函数,就像在 Java 和 C# 中一样。
PS:坦率地说,我很惊讶之前没有提到这一点。
我建议使用实现构造函数的应用程序逻辑并由各种构造函数调用的 private friend
方法。这是一个例子:
假设我们有一个名为 StreamArrayReader
的类,其中包含一些私有字段:
private:
istream * in;
// More private fields
我们要定义两个构造函数:
public:
StreamArrayReader(istream * in_stream);
StreamArrayReader(char * filepath);
// More constructors...
第二个只是使用第一个(当然我们不想重复前者的实现)。理想情况下,一个人想做类似的事情:
StreamArrayReader::StreamArrayReader(istream * in_stream){
// Implementation
}
StreamArrayReader::StreamArrayReader(char * filepath) {
ifstream instream;
instream.open(filepath);
StreamArrayReader(&instream);
instream.close();
}
但是,这在 C++ 中是不允许的。出于这个原因,我们可以定义一个如下的私有友元方法,它实现了第一个构造函数应该做的事情:
private:
friend void init_stream_array_reader(StreamArrayReader *o, istream * is);
现在这个方法(因为它是一个朋友)可以访问 o
的私有字段。然后,第一个构造函数变为:
StreamArrayReader::StreamArrayReader(istream * is) {
init_stream_array_reader(this, is);
}
请注意,这不会为新创建的副本创建多个副本。第二个变成:
StreamArrayReader::StreamArrayReader(char * filepath) {
ifstream instream;
instream.open(filepath);
init_stream_array_reader(this, &instream);
instream.close();
}
也就是说,不是让一个构造函数调用另一个构造函数,而是调用一个私人朋友!
这种方法可能适用于某些类型的类(当赋值运算符表现得“好”时):
Foo::Foo()
{
// do what every Foo is needing
...
}
Foo::Foo(char x)
{
*this = Foo();
// do the special things for a Foo with char
...
}
如果我正确理解您的问题,您是在问是否可以在 C++ 中调用多个构造函数?
如果那是您正在寻找的东西,那么不-那是不可能的。
你当然可以有多个构造函数,每个都有唯一的参数签名,然后在你实例化一个新对象时调用你想要的那个。
您甚至可以在最后拥有一个带有默认参数的构造函数。
但是你可能没有多个构造函数,然后分别调用它们中的每一个。
当调用构造函数时,它实际上是从堆栈或堆中分配内存。因此,在另一个构造函数中调用构造函数会创建一个本地副本。所以我们正在修改另一个对象,而不是我们关注的对象。
比决定更容易测试:)试试这个:
#include <iostream>
class A {
public:
A( int a) : m_a(a) {
std::cout << "A::Ctor" << std::endl;
}
~A() {
std::cout << "A::dtor" << std::endl;
}
public:
int m_a;
};
class B : public A {
public:
B( int a, int b) : m_b(b), A(a) {}
public:
int m_b;
};
int main() {
B b(9, 6);
std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;
return 0;
}
并用 98 std: g++ main.cpp -std=c++98 -o test_1 编译它
你会看见:
A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor
所以 :)