C++中省略构造函数与显式default构造函数的差异探究
C++三种构造函数写法的差异详解
一、三种写法的本质区别
先明确三个核心场景的不同:
- 完全不写构造函数:编译器会在类中没有任何用户声明的构造函数时,自动生成默认构造函数。生成的构造是否“平凡(trivial)”、是否属于用户提供的构造,完全取决于类成员的属性。
- 显式写
=default构造:主动要求编译器生成默认逻辑的构造函数,它的行为和编译器隐式生成的一致,但属于用户显式声明的构造函数。 - 手写平凡构造(比如
T() {}):这是用户自定义的构造函数,哪怕逻辑和默认生成的一模一样,也会让类的构造失去“平凡性”,同时属于用户提供的构造函数。
二、你的示例:example1和example2的区别
先看你给出的代码:
struct someint { int n; someint() : n(0) {} // 自定义构造,不是编译器生成的默认构造 }; class example1 { public: someint n; }; class example2 { public: someint n; example2() = default; };
在这个例子里,两者的默认构造行为完全一致——都会调用someint的自定义构造函数初始化成员n。但在类的属性上有两个关键细微区别:
- 是否属于“用户提供的构造函数”:
- example1的默认构造是编译器隐式生成的,不算用户提供的;
- example2的
=default构造是你显式声明的,属于用户提供的构造。
这个差异在判断类是否为**聚合类(aggregate)**时会体现:有用户提供的构造函数的类不能被当作聚合类。不过在你的例子里,因为someint有自定义构造,所以example1和example2都不是聚合类,这个区别暂时看不出来。
- 模板元编程/SFINAE场景:
某些模板代码会检查类是否有用户声明的构造函数,这时example2会被识别为有用户声明的构造,而example1不会。
除此之外,在日常的对象初始化、继承等场景下,两者没有任何功能差异,只是代码风格上的选择。
三、三种写法的完整对比
- 完全省略构造:
- 只有当类里没有任何用户写的构造函数时,编译器才会生成默认构造;
- 生成的构造是否平凡,取决于所有成员的构造是否平凡;
- 不属于用户提供的构造函数。
=default构造:- 哪怕类里有其他构造函数(比如带参数的),你也可以声明
=default的默认构造; - 构造的逻辑和编译器隐式生成的完全一致,但属于用户提供的构造;
- 构造的平凡性和隐式生成的一致(比如如果所有成员都是平凡构造,那这个
=default构造也是平凡的)。
- 哪怕类里有其他构造函数(比如带参数的),你也可以声明
- 手写平凡构造:
- 比如
T() {},哪怕什么都不做,也属于用户自定义构造,会让类的构造失去平凡性; - 一旦手写了这个构造,编译器就不会再隐式生成默认构造。
- 比如
内容的提问来源于stack exchange,提问作者Flecart
相关产品推荐
相关产品推荐

