《C++ Primer 5th》类内union首成员默认初始化相关疑问
问题核心解答
你遇到的差异本质是union首成员的类型特性和C标准中union初始化规则的适配问题,针对你提到的《C Primer 第五版》的描述和测试代码的差异,具体解释如下:
1. 先明确书上Token示例的特殊前提
Token类中的匿名union定义如下:
union { // 匿名union char cval; // 首成员,基础平凡类型 int ival; double dval; std::string sval; };
这个union的首成员cval是char类型,属于平凡类型:没有自定义构造函数、析构函数,不需要额外的资源管理。
C++标准中规定:如果union的首成员是平凡类型,且包含类的构造函数没有在初始化列表中显式初始化union的任意成员,那么union会执行默认初始化,本质就是给union分配对应大小的内存,首成员被初始化为不确定值的平凡类型,整个过程没有任何构造函数调用,也不会产生任何需要释放的资源。
书上所说的“拷贝构造函数中union的首成员被默认初始化”就是指这个场景:没有实际的构造操作,也没有需要销毁的资源,后续不管是给int、double成员赋值,还是用placement new构造string,都是完全安全的,不需要提前清理原有内容。
2. 你的测试代码和书里场景的本质差异
你定义的union首成员是自定义类型A,A有非平凡的默认构造函数和析构函数,这种情况下规则完全不同:
- 如果union存在拥有非平凡构造函数的成员,那么union自身的默认构造函数会被隐式删除,除非你显式初始化union的某个成员。
- 你自己定义的
Foo拷贝构造函数没有在初始化列表中初始化union的任何成员,本质属于未定义行为,你观测到的“A构造函数只调用一次”只是未定义行为的一种表现,不代表通用规则。
3. 为什么Token的拷贝构造可以直接调用copyUnion
回到Token的场景,它的union首成员是平凡类型,默认初始化后没有需要清理的资源:
- 如果拷贝的源Token持有的是int/char/double,直接给对应成员赋值即可,覆盖掉原来的不确定值,完全合法。
- 如果拷贝的源Token持有的是string,直接用placement new在sval的地址构造新的string即可,原来的char类型成员是平凡类型,不需要调用析构函数,不会有资源泄漏。
内容的提问来源于stack exchange,提问作者Itachi Uchiwa
相关产品推荐
相关产品推荐

