struct C {
C() { cout << "constructor C called\n"; }
};
struct A {
A() { cout << "constructor A called\n"; }
};
struct B {
A A_;
C C_;
B() : C_(), A_() { cout << "constructor B is called\n"; }
};
int main()
{
B Michael;
}
初始化列表的目的是什么?
我知道在创建类的实例时,将调用成员变量的构造函数。我读过某个地方,初始化列表可以控制首先调用成员构造函数的顺序。在下面的代码中,输出为:
constructor A called
constructor C called
constructor B is called
此顺序是成员变量声明的顺序。
B() : C_(), A_()
的意义是什么?
答案 0 :(得分:1)
让我们稍微改变一下例子
struct C {
C(const std::string & name) :myName(name){ cout << "constructor C called\n"; }
private:
std::string myName;
};
现在,在构造string
时,您被迫提供C
,因为一旦指定了自定义构造函数,编译器生成的默认构造函数就会消失。构造C
的方法只有一种,必须给它一个string
。
在可以输入B
的构造函数的主体之前,必须完全构造基类。由于C
没有默认的构造函数,因此唯一的选择是使用初始化列表。
此外,有时对象的构造很昂贵。如果默认构造此昂贵的对象,然后必须在包含类的构造函数的主体中执行昂贵的分配(可能还要执行另一种构造来分配临时对象),则可能会做很多不必要的工作。
class X
{
dang_expensive ouch;
public:
X(Param param) // ouch constructed. big cost
{
ouch = dang_expensive(param); // temp constructed and assigned
// maybe two big costs
}
};
vs
class X
{
dang_expensive ouch;
public:
X(Param param) : ouch(param)// ouch constructed. big cost
{
}
};
这些天,编译器确实非常敏锐,并且将尽可能减少这种影响。如果没有明显的副作用,编译器很有可能将其完全消除。
旁注:
我读过某个地方,初始化列表可以控制首先调用成员构造函数的顺序。
您阅读的内容不正确。
struct B {
A A_;
C C_;
B() : C_(), A_() { cout << "constructor B is called\n"; }
};
仍将首先构造A_
,因为它首先被声明。这听起来很疯狂,但是它为编译器和程序整体可以利用的构造和最终破坏顺序提供了硬性保证。对于某些构造函数,某些成员可能不在初始化列表中。无论如何,它们将以相同的顺序初始化。绝对没有惊喜,而与电脑打交道时,没有惊喜是一件好事。如果您的警告级别足够高,现代编译器通常会在警告列表出现乱序时警告您,因为类似
B() : C_(), A_(C_) {}
会使您的程序陷入困境。永远不要忽略编译器警告。