一 疑问
最近看到一段代码,如下,
#include <iostream>
class Little
{
public:
void func()
{
std::cout << "hello 123\n";
}
};
class Big
{
public:
Big(Little& one, int data) : m_li(one), m_data(data)
{
}
void func()
{
m_li.func();
}
int getData()
{
return m_data;
}
private:
Little& m_li;
const int m_data;
};
int main(void)
{
Little li;
Big big(li, 100);
big.func();
std::cout << "data: " << big.getData() << "\n";
return 0;
}
类Little定义了一个方法func()
最关键的是类Big,它有2个private成员变量,m_li和m_data,比较疑惑的是m_li类型是引用,m_data的类型是const int,根据C++的规定,引用和常量类型在定义时必须显式初始化,否则会报错。但是这段代码执行起来没问题。
这是怎么回事?
二 解释
类Big使用了构造函数初始值列表,根据《C++ primer 5th》的7.5.1节介绍,
就对象的数据成员而言,初始化和赋值也有类似的区别。如果没有在构造函数的初始值列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。
也就是说对象的数据成员是在构造函数之前定义并初始化,使用构造函数初始值列表可以显式初始化,如果没有使用,则会使用默认初始化。再强调一点,不管哪种初始化方式,对象的数据成员是在构造函数之前定义并初始化。
关于显式初始化和默认初始化,有以下代码可以解释,
string foo = "Hello World!"; // 定义并初始化,显式初始化
string bar; // 默认初始化成空string对象
bar = "Hello World!"; // 为bar赋一个新值
这里再回到之前的问题,由于Big使用了构造函数初始值列表,对m_li和m_data进行了显式初始化,
相当于在构造函数执行之前执行了下面语句,
Little& m_li = one;
const int m_data = data;
所以就不会有问题。
如果Big的构造函数写成如下这样,就会出错,
Big(Little& one, int data)
{
m_li = one;
m_data = data;
}
因为在构造函数执行之前,相当于已经执行了如下语句,但是没有显式初始化,就会出错。
Little& m_li;
const int m_data;
成员初始化顺序
成员的初始化顺序与它们在类定义中的出现顺序一致。构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。