什么是C++中的非平凡构造函数?

83
3个回答

103
简单来说,“trivial”特殊成员函数指的是以非常直接的方式执行其任务的成员函数。对于不同类型的特殊成员函数,“直接的方式”有不同的含义。
对于默认构造函数和析构函数,“trivial”字面上意味着“什么也不做”。对于复制构造函数和赋值运算符,“trivial”意味着“等同于简单的原始内存复制”(如使用memcpy进行复制)。
如果您自己定义一个构造函数,则被认为是非平凡的,即使它什么也不做,因此编译器必须隐式定义一个 trivial 构造函数。
为了满足上述要求,类必须具有非常简单的结构,当正在创建或销毁对象时,它不需要任何隐藏的初始化,或者在复制时需要任何隐藏的附加内部操作。
例如,如果类具有虚函数,则在创建该类的对象时将需要一些额外的隐藏初始化(初始化虚拟方法表等),因此该类的构造函数将不符合 trivial 的要求。
再比如,如果一个类具有虚基类,则该类的每个对象可能包含指向其它部分的隐藏指针。这样一个自引用的对象无法通过简单的原始内存复制例程(如memcpy)来复制。因此,该类的复制构造函数和赋值运算符将不符合 trivial 的要求。
显然,这个要求是递归的:类的所有子对象(基类和非静态成员)也必须具有 trivial 构造函数。

1
什么都不做...那么复制赋值运算符是什么呢? ;) - user34537
2
@acidzombie24:我对复制成员函数进行了一些更正。你的第二条评论对我来说不太清楚。任何用户定义的构造函数都是非平凡的,因此一旦定义它,该类就不再是POD。 - AnT stands with Russia
很棒的帖子。我在想如果我有一个结构体像 struct Rect1 { int l, r, w, h}; struct Rect2 { int l, t, r, b' Rect2(){} Rect2(Rect1 r) {...} }; 是否会被认为是微不足道的。但我猜不是,因为我不能定义任何构造函数。我本以为允许这样做,因为有一个微不足道的默认构造函数,而且这不是赋值或复制运算符。 - user34537
实际上,在C++0x的情况下,它说“平凡的默认构造函数”,所以我想要的在C++0x中是允许的(可能在旧版本中不行,但C++0x是有保证的)。 - user34537
3
当您声明任何构造函数时,都会抑制默认构造函数的隐式声明。因此,您的类将完全没有默认构造函数。这是在C++0x之前的语言中的情况。在C++0x中,据我所知,您可以强制编译器生成默认构造函数,这将需要使用关键字“default”的显式声明,如您提供链接中所示。 - AnT stands with Russia
1
@AndreyT:非常好的区分。 - user34537

38

如果以下条件都满足,类A的构造函数就是平凡的:

  • 它是隐式定义的(由编译器合成)
  • A没有虚函数和虚基类
  • A的所有直接基类都有平凡构造函数
  • A的所有非静态数据成员所属的类都有平凡构造函数

3
抱歉如果听起来很愚蠢,但它也适用于析构函数吗? - Zaid Khan

28
已经有正确的答案了,但这里是标准中的引用(当我遇到这篇帖子时我正在寻找它):
(§12.1/5) 如果默认构造函数未被用户提供并且满足以下条件,则认为是平凡的: - 其类没有虚函数(10.3)和虚基类(10.1); - 其类中没有任何非静态数据成员有大括号或等号初始化器; - 其类的所有直接基类都具有平凡的默认构造函数; - 对于其类的所有非静态数据成员,如果是类类型(或数组类型),则每个此类都有一个平凡的默认构造函数。
这是来自C++11的内容。C++03缺少第二条,并使用“隐含声明”代替“未由用户提供”。除此之外,两者相同。
请注意,此规范仅涵盖平凡的默认构造函数。单词属性“平凡”的定义也可以在不同的上下文中使用,例如复制构造函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接