C++杂记——构造函数

文章目录

    • [default constructor](#default constructor)
    • [copy constructor](#copy constructor)
    • 参考

default constructor

当编译器需要构造函数的时候,而类本身没有定义构造函数时,会被合成出来,被合成的构造函数只会执行编译器所需的动作。

带有default constructor的成员对象

如果一个类没有任何构造函数,但它内含一个成员对象,而这个成员对象有默认构造函数,那么这个类的隐式默认构造函数就是"nontrival"的,编译器需要为该class合成出一个默认构造函数。不过这个合成操作只会在constructor真正需要被调用时才会发生。

如果一个类A 内含一个或一个以上的成员类对象,那么类A的每一个构造函数必须调用每个成员类的默认构造函数。编译器会扩张已存在的constructors,在其中安插一些代码,使得用户代码被执行前,先调用必要的默认构造函数。

如果是多个成员类对象,C++要求以成员在类中声明顺序来调用各个构造函数。这一点由编译器完成,它为每一个constructor安插程序代码,以成员声明顺序调用每一个成员所关联的默认构造函数。这些代码将安插在用户代码前面。

带有default constructor的基类

如果一个没有任何构造函数的类派生自一个"带有默认构造函数"的基类,那么这个派生类的默认构造函数会被是为nontrival,并因此需要被合成出来。它将调用上一层基类的默认构造函数(根据它们的声明顺序)。对一个后继派生的类而言,这个合成的构造函数和一个"被显式提供的默认构造函数"没有什么差异。

如果已经提供了多个构造函数,但其中都没有默认构造函数。编译期会扩张现有的每一个构造函数,将"用以调用所有必要之默认构造函数"的程序代码加进去。他不会合成一个新的默认构造函数,因为其他"由用户所提供的构造函数"存在的缘故。如果同时也存在着"带有默认构造函数"的成员类对象,那些默认构造函数也会被调用------在所有基类构造函数都被调用之后。

带有一个虚函数的类

另有两种情况,也需要合成出默认构造函数:

  • 类声明(或继承)一个虚函数
  • 类派生自一个继承串链,其中有一个或更多的虚基类(virutal base class)

不管哪一种情况,由于缺乏由用户声明的构造函数,编译期会详细记录合成一个默认构造函数的必要信息

下面两个扩张行为会在编译期间发生:

  • 一个virtual function table会被编译期产生出来,内放类的virtual functions地址
  • 在每一个类对象中,一个额外的pointer member(即vptr)会被编译期合成出来,内含相关的类vtbl的地址

带有一个虚基类的类

必须使vritual base class在其每一个derived class object中的位置,能够于执行期准备妥当。

总结

有4种情况,会造成编译期必须为未声明constructor的类合成一个default constructor.

C++标准把那些合成物称为implicit nontrivial default constructors.

被合成出来的constructor只能满足编译期(非程序)的需要。它之所以能够完成任务,是借着"调用member object或base class的default constructor"或是"为每一个object初始化其virtua fuction机制或virtual base class机制"而完成的。至于没有存在那4种情况而又没有声明任何constructor的class,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被合成出来。

在合成的defaullt constructor中,只有base class subobjects和member class objects会被初始化。所有其他的nonstatic data member(如整数、整数指针、整数数组等等)都不会被初始化。这些初始化操作对程序而言或许有必要,但对编译期而言则非必要。如果程序需要一个"把某指针设为0"的default constructor,那么提供它的人应该是程序员。

copy constructor

如果一个类没有提供一个explicit copy constructor, 此时以另外一个对象赋值时,其内部是将每个成员对象的值从一个对象拷贝到另一个对象。这种可以自动进行的拷贝过程称为默认拷贝构造。

一个对象可以用两种方式复制得到:

  1. 被初始化,即被拷贝构造
  2. 被指定(assignment),即拷贝赋值
    C++标准把copy constructor分为trivial和nontrivial的。只有nontrivial的实例才会被合成于程序中。决定一个copy constructor是否为trivial的标准在于class是否展现出所谓的"bitwise copy semantics(位逐次拷贝)"。

下面四种情况下,一个类不会展现出"bitwise copy semantics":

  1. 当类内含一个成员对象而该对象的类声明有一个copy constructor时(不论是被类设计者显式声明,或是被编译器合成)
  2. 当类继承自一个基类,而后者存在一个copy constructor时(不论是被显式声明或是被合成的)
  3. 当class声明一个或多个virtual functions时
  4. 当class派生自一个继承串链,其中有一个或多个virtual base classes时

前两种情况下,编译期必须将member或者base class的"copy constructors调用操作"安排到被合成的copy constructor中。

在上面四种情况下,类不再保持"bitwise copy semantics",而且default copy constructor如果未被声明的话,会被视为nontrivial。这种情况下,如果缺乏一个已声明的copy constructor,编译器为了正确处理"以一个class object作为另一个class object的初值",必须合成出一个copy constructor。

参考

C++杂记------返回值优化(RVO,Return Value Optimization)、具名返回值优化 (NRVO,Named Return Value Optimization)

相关推荐
淦。。。。2 小时前
题解:P14013 [POCamp 2023] 送钱 / The Generous Traveler
开发语言·c++·经验分享·学习·其他·娱乐·新浪微博
天赐学c语言2 小时前
1.18 - 滑动窗口最大值 && 子类的指针转换为父类的指针,指针的值是否会改变
数据结构·c++·算法·leecode
是娇娇公主~2 小时前
C++集群聊天服务器(3)—— 项目数据库以及表的设计
服务器·数据库·c++
zephyr053 小时前
C++ STL unordered_set 与 unordered_map 完全指南
开发语言·数据结构·c++
大锦终3 小时前
dfs解决FloodFill 算法
c++·算法·深度优先
一只小bit3 小时前
Qt 事件:覆盖介绍、处理、各种类型及运用全详解
前端·c++·qt·cpp
追烽少年x3 小时前
第三章 异常(一)
c++
苦藤新鸡3 小时前
14.合并区间(1,3)(2,5)=(1,5)
c++·算法·leetcode·动态规划
_OP_CHEN4 小时前
【算法基础篇】(四十八)突破 IO 与数值极限:快速读写 +__int128 实战指南
c++·算法·蓝桥杯·算法竞赛·快速读写·高精度算法·acm/icpc