我看到了两种使用成员初始化程序列表的方法。第一个是这样的:
class ClassName {
public:
arg_type_1 varName1;
arg_type_2 varName2;
// Constructor.
ClassName(arg_type_1 arg_name_1, arg_type_2 arg_name_2)
: varName1(arg_name_1), varName2(arg_name_2)
{
}
}
那里发生的事情很清楚。在构造函数中,我们有一个参数列表,并使用它们来初始化类的成员。例如,
arg_name_1
用于初始化该类的varName1
变量的值。在继承的情况下,会出现另一种使用成员初始化器的方法:
class ChildClass : public ParentClass
{
ChildClass(string name) : ParentClass( name )
{
// What can we put here and why we might need it.
}
};
这里发生的事情也很清楚。当我们使用一个字符串参数调用
ChildClass
的构造函数时,它将使用相同的字符串参数调用ParentClass
的构造函数。我不清楚的是编译器如何区分这两种情况(语法相同)。例如,在第二个示例中,编译器可能认为它需要获取
name
变量的值并将其分配给ParentClass
的ChildClass
变量,然后看到该变量未在ChildClass
。给我的第二个不清楚之处是为什么我们可能想在第二个示例中将一些内容放入构造函数的主体中。即使没有任何东西已经使用父类的构造函数创建并返回一个对象。
最佳答案
我不清楚的是编译器如何区分这两种情况(语法相同)。
在列表中看到一个初始化程序,编译器首先查找具有该名称的成员变量。如果找到一个,它将尝试使用给定的参数初始化该成员。如果不是,它将查找具有该名称的直接基类或虚拟基类,以使用给定的参数初始化基类子对象。当您在类的方法(包括构造函数)中命名某些内容时,将使用这些常用的名称查找规则。
在罕见且设计不佳的情况下,当您同时拥有一个成员变量和一个具有相同名称的直接基类时,您将必须限定基类类型,即添加名称空间:
struct B1 {
B1(char const*) {};
};
namespace Foo {
struct B2 {
B2(bool) {};
};
}
struct Weird : public B1, public Foo::B2 {
int B1;
double B2;
Weird()
: ::B1("meow")
, Foo::B2(false)
, B1(42)
, B2(3.14)
{}
};
给我的第二个不清楚之处是为什么我们可能想在第二个示例中将一些内容放入构造函数的主体中。即使没有任何东西已经使用父类的构造函数创建并返回一个对象。
它实际上并不返回任何对象,而只是创建该对象。但是,某些人可能想调用以下其他函数:
class ParentClass {
public:
ParentClass(string name);
void registerSomething(ParentClass const&);
}
class ChildClass : public ParentClass {
public:
ChildClass(string name) : ParentClass( name ) {
registerSomething(*this);
}
};
某人可能要这样做的原因可能有很多。通常,由于子类比父类“更多”,因此仅在其构造函数中要做的事情不仅仅是初始化基类子对象,这是自然的。
关于对象生存期的一些话:
在进入构造函数主体之前,您仅构造了新对象的子对象。当执行离开构造函数主体时,对象本身开始其生命周期。打个比方,可能是汽车及其零件:在对汽车的制造商车身进行娱乐之前,您已经给轮胎充气,组装了发动机并冲压出了车身零件。但是如果您的汽车尚未组装,就不会成为汽车,这将在构造函数主体中发生。这在析构函数中得到了反映,尤其是在存在异常的情况下可以看到:如果在对象的构造函数中发生异常,则不会调用其析构函数。将仅调用已经完全构造的子对象的析构函数。这是因为,如果构造函数尚未完成执行,则该对象将永远不存在,并且没有什么可以调用析构函数的。
关于c++ - C++中有两种类型的成员初始化器列表吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18248794/