类内用未声明成员变量初始化对象为何可编译?C++技术疑问
为什么这段C++代码能编译但输出不符合预期?
先看用户提供的代码:
#include <cstdio> class Test { public: Test(int param = 42) { printf("ctor %d\n", param); _data = param; } private: int _data = 0; }; class Test2 { // v在后面才声明,却能在这里使用? Test t{ v }; int v = 333; }; int main() { Test2 t2; // 这段代码却编译失败 // Test t{ v }; // int v = 333; printf("don't optimize 0x%X\n", &t2); return 0; }
用户的疑问:
- 类
Test2中先写Test t{v}再声明int v=333,为什么编译器不报错? - 为什么
Test的构造函数始终输出0,而不是333? - 为什么
main中同样的代码顺序会编译失败?
原因解析
1. 类内成员初始化的名字查找规则
C++标准规定,类的成员初始化器(类内直接初始化成员的语法)中,名字查找会遍历整个类的作用域,而不是只看当前代码行之前的内容。也就是说,哪怕v在Test t{v}之后声明,编译器依然能在整个Test2类的作用域中找到v的名字,因此不会抛出“未声明变量v”的错误。
而函数(比如main)内的局部变量遵循顺序名字查找:必须先声明变量,才能在后续代码中使用。所以main里先写Test t{v}再声明v,编译器找不到v,直接编译报错。
2. 为什么输出0(未定义行为的表现)
类成员的初始化顺序严格按照它们在类中的声明顺序,和初始化器的书写顺序无关。在Test2中,t的声明在v前面,所以初始化顺序是:
- 先初始化
Test t{v} - 再初始化
int v=333
当Test的构造函数读取v的值时,v的存储空间已经分配,但它的初始化器(=333)还没执行,此时v属于未初始化状态。读取未初始化的int值属于C++中的未定义行为——标准没有规定此时的值是什么,但很多编译器会将未初始化的栈内存(t2是main中的局部对象,成员存在栈上)填充为0,所以你看到构造函数输出0。哪怕把v声明为const,初始化顺序依然不变,结果还是一样。
如果调换Test2中成员的声明顺序:
int v = 333; Test t{ v };
此时v先被初始化为333,再用它初始化t,构造函数就会输出预期的333。
内容的提问来源于stack exchange,提问作者xterminal86
相关产品推荐
相关产品推荐

