你好,我是雨乐~
cpp的复杂性,相比接触编程的人都知道,稍不注意就进坑,这几天在看某论坛的时候,看到某个人在提问初始化相关的问题,自己当时也这样操作过,遂想把这块写出来,供大家乐呵乐呵~
好了,直接上源码:
#include <iostream>
#include <string>
classBase{
public:
Base(const std::string& value):value_(value){
std::cout <<"Base constructor: "<< value_ << std::endl;
}
private:
std::string value_;
};
classDerived:publicBase{
public:
Derived():Base(member_){
std::cout <<"Derived constructor: "<< member_ << std::endl;
}
private:
const std::string member_ ="test";
};
int main() {
Derived d;
return0;
}
预期结果
单看代码,相信有不少人会认为Base.value_的值为"test"。
但实际运行结果往往出乎意料,甚至产生未定义行为。示例输出是这样的:
Base constructor:
Derived constructor: test
原因解析
问题出在初始化顺序上,但凡有一点cpp基础的人都知道,派生类的构造函数初始化顺序是:先调用基类的构造函数,然后才是子类自己的成员初始化。
这是C++的基础规则之一,具体来说,在构造函数初始化列表中列出的顺序并不重要,实际的初始化顺序是:
1.基类构造函数先执行;2.然后是子类成员变量的初始化(按声明顺序,而不是初始化列表的书写顺序);3.最后执行子类的构造函数体。
也就是说,虽然 派生类中member_
是在Derived的构造函数体执行前就被初始化的,但在初始化列表中的 Base(member_)
表达式会率先执行,即先调用Base的构造函数。
当 Base
构造函数试图引用 member_
时,实际上它还未完成初始化,所以基类 Base
获得的是一个未初始化的值。这会导致 Base
构造函数中的 value_
不确定(编译器输出为test)。
最后,当 Derived
构造函数体执行时,member_
才会在另一块内存中完成初始化。
结语
•小心未初始化引用:在初始化列表中传递未初始化的引用可能会导致不可预期的对象构造和内存分布。•直接初始化不保证顺序:虽然直接初始化在构造函数体执行之前完成,但它不会改变初始化列表中的执行顺序。
以上~~
如果对本文有异议或者有其他技术问题,可以加微信交流: