成员初始化列表中能否引用未列入列表的成员变量?代码合法性存疑
在C++成员初始化列表中,能否引用未列入列表的成员变量?
你的代码虽然能编译甚至偶尔能“正常运行”,但绝对是不合法、不符合C++标准的,它属于标准中明确定义的未定义行为——现在的运行结果只是巧合,换个编译器、优化等级或者操作系统,大概率会直接崩溃或者输出乱码。
核心原因:成员初始化顺序规则
C++对类成员的初始化顺序有严格且不可违背的规定:成员变量的初始化顺序完全按照它们在类中的声明顺序,和初始化列表里的书写顺序毫无关系。
看你的Foo类成员声明顺序:
class Foo { public: std::string const & x; // 先声明的x private: std::string y; // 后声明的y };
所以创建Foo对象时,编译器会先初始化x,再初始化y。你的初始化列表写x(y)时,y还没有被构造——它只是一块未初始化的内存,x引用的是一个完全无效的std::string对象。后续的y = msg赋值操作,本质是给一个从未被正确构造的对象赋值,完全违反了C++的对象生命周期规则。
为什么代码看起来能运行?
这纯粹是运气问题:编译器可能把y放在了x相邻的内存位置,后续的赋值操作刚好覆盖了这块内存,让x引用的区域看起来有了合法数据。但这种行为没有任何标准保障,只要环境稍有变化,结果就会彻底失控。
正确的写法
要让x合法引用y,必须保证y在x之前完成初始化。调整成员的声明顺序即可:
#include <string> #include <iostream> class Foo { public: Foo( char const * msg ) : y( msg ), x( y ) {} // 初始化列表顺序可同步调整,但核心是声明顺序 std::string const & x; private: std::string y; // 将y的声明放在x前面,确保先初始化y }; int main( int argc, char * argv[] ) { if ( argc >= 2 ) { Foo f( argv[1] ); std::cout << f.x << std::endl; } }
现在y会先被构造(用msg完成初始化),然后x引用的是一个完全合法、已构造完成的std::string对象,完全符合C++标准的要求。
内容的提问来源于stack exchange,提问作者DevSolar
相关产品推荐
相关产品推荐

