pax*_*977 212 c++ oop object-construction
我偏向于使用我的构造函数的成员初始化列表......但我早就忘记了这背后的原因......
您是否在构造函数中使用成员初始化列表?如果是这样,为什么?如果没有,为什么不呢?
Ada*_*eld 263
对于POD类成员来说,它没有任何区别,只是风格问题.对于类的类成员,它避免了对默认构造函数的不必要的调用.考虑:
class A
{
public:
A() { x = 0; }
A(int x_) { x = x_; }
int x;
};
class B
{
public:
B()
{
a.x = 3;
}
private:
A a;
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,构造函数B
将调用默认构造函数A
,然后初始化a.x
为3.更好的方法是使B
构造函数直接调用A
初始化列表中的构造函数:
B()
: a(3)
{
}
Run Code Online (Sandbox Code Playgroud)
这只会调用A
的A(int)
构造函数,而不是它的默认构造函数.在这个例子中,差异可以忽略不计,但想象一下,如果你A
的默认构造函数做得更多,比如分配内存或打开文件.你不希望不必要地这样做.
此外,如果类没有默认构造函数,或者您有const
成员变量,则必须使用初始化列表:
class A
{
public:
A(int x_) { x = x_; }
int x;
};
class B
{
public:
B() : a(3), y(2) // 'a' and 'y' MUST be initialized in an initializer list;
{ // it is an error not to do so
}
private:
A a;
const int y;
};
Run Code Online (Sandbox Code Playgroud)
Nav*_*een 42
除了上面提到的性能原因,如果你的类存储对作为构造函数参数传递的对象的引用,或者你的类有const变量,那么除了使用初始化列表之外你没有任何选择.
yuv*_*uvi 18
使用构造函数初始化列表的一个重要原因是答案中未提及的是基类的初始化.
根据构造的顺序,基类应该在子类之前构造.如果没有构造函数初始化列表,如果您的基类具有默认构造函数,则可以在进入子类的构造函数之前调用它.
但是,如果您的基类只有参数化构造函数,那么您必须使用构造函数初始化列表来确保您的基类在子类之前初始化.
初始化仅具有参数化构造函数的子对象
效率
使用构造函数初始化列表,您可以将数据成员初始化为代码中所需的准确状态,而不是先将它们初始化为默认状态,然后将其状态更改为代码中所需的状态.
如果类中的非静态const数据成员具有默认构造函数并且您不使用构造函数初始值设定项列表,则无法将它们初始化为预期状态,因为它们将初始化为其默认状态.
当编译器进入构造函数时,必须初始化引用数据成员,因为引用不能仅在以后声明和初始化.这仅适用于构造函数初始化列表.
在性能问题旁边,还有一个非常重要的问题,我称之为代码可维护性和可扩展性.
如果T是POD并且您开始更喜欢初始化列表,那么如果一次T将更改为非POD类型,则不需要更改初始化周围的任何内容以避免不必要的构造函数调用,因为它已经过优化.
如果类型T确实具有默认构造函数和一个或多个用户定义的构造函数,并且有一次您决定删除或隐藏默认构造函数,那么如果使用了初始化列表,则如果您的用户定义的构造函数,则不需要更新代码,因为它们已经正确实施.
与const成员或引用成员相同,假设最初T定义如下:
struct T
{
T() { a = 5; }
private:
int a;
};
Run Code Online (Sandbox Code Playgroud)
接下来,如果您从头开始使用初始化列表,则决定限定为const,那么这是单行更改,但如上所述定义了T,它还需要挖掘构造函数定义以删除赋值:
struct T
{
T() : a(5) {} // 2. that requires changes here too
private:
const int a; // 1. one line change
};
Run Code Online (Sandbox Code Playgroud)
如果代码不是由"代码猴"编写,而是由工程师根据对他正在做的事情的深入考虑做出决策,那么维护更容易且不易出错也不是秘密.
在运行构造函数体之前,将调用其父类的所有构造函数,然后调用其字段.默认情况下,将调用无参数构造函数.初始化列表允许您选择调用哪个构造函数以及构造函数接收的参数.
如果您有引用或const字段,或者如果使用的其中一个类没有默认构造函数,则必须使用初始化列表.
小智 5
// Without Initializer List\nclass MyClass {\n Type variable;\npublic:\n MyClass(Type a) { // Assume that Type is an already\n // declared class and it has appropriate \n // constructors and operators\n variable = a;\n }\n};\n \n
Run Code Online (Sandbox Code Playgroud)\n这里编译器按照以下步骤创建类型的对象MyClass
:
Type
首先为 \xe2\x80\x9c \ a
xe2\x80\x9d 调用 \xe2\x80\x99s 构造函数。Type
在构造函数体内调用MyClass()
进行赋值。variable = a;\n
Run Code Online (Sandbox Code Playgroud)\nType
\xe2\x80\x9d 调用 \xe2\x80\x9c a
\xe2\x80\x9d 的析构函数,因为它超出了范围。MyClass()
现在考虑带有初始化列表的构造函数的相同代码:
// With Initializer List\n class MyClass {\n Type variable;\n public:\n MyClass(Type a):variable(a) { // Assume that Type is an already\n // declared class and it has appropriate\n // constructors and operators\n }\n };\n \n
Run Code Online (Sandbox Code Playgroud)\n对于初始值设定项列表,编译器将执行以下步骤:
\n调用 \xe2\x80\x9c Type
\xe2\x80\x9d 类的复制构造函数来初始化:variable(a)
。初始化列表中的参数用于variable
直接复制构造 \xe2\x80\x9c \xe2\x80\x9d。
\xe2\x80\x9c Type
\xe2\x80\x9d 的析构函数被调用为 \xe2\x80\x9c a
\xe2\x80\x9d,因为它超出了范围。