C++默认初始化、值初始化与类内初始化行为异常问题咨询
你可能觉得代码很长我写了很多内容,但相信我它其实非常简单。
代码
#include <iostream> class A { public: int k1=3; float f1=34.20; char ch1='a'; std::string str1="bonaparte"; void display() { std::cout<<k1<<" "<<f1<<" "<<ch1<<" "<<str1<<" xx\n"; } }; class B { public: int k2; float f2; char ch2; std::string str2; void display() { std::cout<<k2<<" "<<f2<<" "<<ch2<<" "<<str2<<" xx\n"; } }; A a1; B b1; A *p1=new A; B *q1=new B; int main() { A a2, a3{}; B b2, b3{}; A *p2=new A; A *p3=new A{}; A *p4=new A(); B *q2=new B; B *q3=new B{}; B *q4=new B(); a1.display(); a2.display(); a3.display(); p1->display(); p2->display(); p3->display(); p4->display(); std::cout<<"\n\n"; b1.display(); b2.display(); b3.display(); q1->display(); q2->display(); q3->display(); q4->display(); }
运行输出
3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 3 34.2 a bonaparte xx 0 0 xx 13965136 0 ╠ xx 0 0 xx 38872208 0 p xx 38872208 0 p xx 0 0 xx 0 0 xx
类B相关疑问
b1是值初始化,准确来说是零初始化。b2是默认初始化,即未进行初始化。b3是花括号初始化,属于值初始化,最终触发零初始化。- 我没办法像写
q4那样写b4()(不是完全一致,你明白我的意思),因为这会被识别为函数定义而非对象声明,也就是Most vexing parse问题。 - 我原本认为
q1应该是值初始化,但输出结果和我的预期不符。 q2是默认初始化,即未进行初始化。q3是花括号初始化,属于值初始化,最终触发零初始化。q4会调用构造函数,但类没有用户定义的构造函数,所以应该是默认初始化,也就是未初始化。- 我知道
q1、q2、q3、q4都是指针,为了表述简便我直接用上述说法,比如称q3是花括号初始化,我想你能明白我的意思。 - 我对
b1、b2、b3的认知和运行结果一致,这部分可能只是巧合,如果我的理解有误也请纠正我。 - 我认为
b1的规则也应该适用于q1,但实际结果并非如此,请解释原因。 q2的运行结果符合我的预期,如果我的理解有误也请纠正。- 现在我对
q3和q4的行为存在困惑:我认为两者都会调用构造函数,但我们没有用户定义的构造函数,所以应该走默认初始化也就是不初始化,对吗?那为什么两者实际上都是值初始化,准确来说是零初始化呢?
类A相关疑问
我认为如果我们使用类内初始化,无论在哪里声明对象,都会按我们提供的值完成初始化,这个结论是否正确?也请补充说明类内初始化的相关规则。
解答
类B疑问解答
- 你对
b1、b2、b3的认知完全正确,不是巧合:b1是静态存储期的全局对象,程序启动阶段会先对所有静态存储期对象做零初始化,所以内置类型成员全为0,std::string成员默认初始化为空,符合输出结果。b2是栈上的局部对象,采用默认初始化规则:对于没有用户定义默认构造函数的类,隐式生成的默认构造函数不会初始化内置类型成员,所以这些成员的值是未定义的垃圾值,和输出结果一致。b3采用空花括号的列表初始化,属于值初始化范畴:对于没有用户定义默认构造函数的类,值初始化会先对整个对象做零初始化,再调用隐式默认构造函数,所以内置类型成员全为0,符合输出。
b1的规则不适用于q1:q1指向的是动态存储期对象,由new B生成,不带初始化器的new T属于默认初始化,和局部对象b2规则一致,内置类型成员不会被初始化,自然是垃圾值,和静态存储期的b1零初始化规则完全独立。- 你对
q2的认知正确,不带初始化器的new B是默认初始化,内置成员为垃圾值。 - 你对
q3、q4的规则理解有误:空括号()和空花括号{}的初始化都属于值初始化,不是默认初始化。对于没有用户定义默认构造函数的类,值初始化会先零初始化整个对象,再调用隐式默认构造函数,所以内置类型成员全为0,符合输出结果。只有不带任何括号/花括号的new T才是默认初始化。
类A疑问解答
你的结论基本正确。只要类内初始化(默认成员初始化器)没有被构造函数初始化列表覆盖,不管对象是全局、局部还是动态分配,不管是默认初始化还是值初始化,都会优先使用类内的初始化值。
补充类内初始化规则:
- 仅支持非静态成员变量,C++11及以上版本标准可用
- 构造函数初始化列表对同一成员的赋值优先级高于类内初始化
- 普通静态成员变量不能使用类内初始化(静态constexpr成员除外)
内容的提问来源于stack exchange,提问作者Abhishek Mane
相关产品推荐
相关产品推荐

