构造函数语义学1
-
构造函数
-
拷贝构造函数
-
赋值操作运算符
构造函数
广泛存在的两个误解:

构造函数,构造类的对象,填充初始值。给对象的赋初始值应该是程序员的责任。

只要满足上面的四个条件,默认构造函数就不会被构建。
- 没有虚函数,没有虚基类,类中没有虚函数,没有虚表指针。
- 没有非静态的数据成员,在声明的地方未被初始话。(去设置初始值
- 所有直接继承的基类,他们有无用的默认构造函数。(需要生成构造函数去调用父类的构造函数完成初始话
- 如果所有非静态的数据成员都是类,每一个类都具有自己的trivial defualt constructor
什么情况会生成默认构造函数?四条会产生构造函数的情况

如上图,构造函数会设置虚表指针的值(却不会设置变量的值,这是程序员的责任

当我们用等号或大括号进行初始化 ,会合成默认构造函数。

上图中,3D会生成构造函数去调用2d的构造函数完成初始化。

需要生成构造函数来完成对有用的构造函数调用来完成初始化。
什么时候会产生构造函数呢,
- 需要进行初始化,而程序员又没办法进行,如虚表指针的生成。
- 我们使用大括号或等号给数据成员赋值,编译器会使用构造函数完成初始化赋值
- 继承的基类有默认构造函数,我们在使用基类成员时,自然要为他们完成初始化
- 也就是说,只要类成员存在有效的默认构造函数,那就要生成一个构造函数去调用它们
**构造函数语义学2 **
基类和数据成员的初始化时机
一旦由程序员声明了构造函数,那么编译器在任何情况下都不会合成默认构造函数。
所以编译器只能扩充每一个构造函数,去调用父类的默认构造函数。

**问题是,编译器会把扩充安排在什么地方呢?**之前,之后,还是我们自己的代码之间呢?
答案是,编译器会把扩充放在用户代码之前,因为用户代码可能立刻用到父类的成员,所以必须先初始化。
如上图中,3D的指针被强制转换成2d,用来调用默认构造函数。
基类和数据成员的初始化实际按照数据成员的声明顺序,调用其默认构造函数。

当我们采用参数列表来调用构造函数时,会直接调用拷贝构造函数,节省了一次成员构造函数的初始化,可能会造成性能的差异。当然,对于基本数据类型如:char,int由于没有默认构造函数,所以,无论采用哪种方式,效率都是一样的。当存在拷贝构造函数时,差异就会出现。

上图的本意是,把j的值给val,然后令i等于J。但是,由于含函数中i和j的声明顺序,编译器实际上不是这样作的。
拷贝构造函数
用到拷贝构造函数的三种情形:

- 对象被创建的时候赋初值(而不是两个已经存在的对象
- 函数用对象做参数,局部对象的拷贝,产生了新的副本
- 当函数的返回值是一个对象。也会产生局部对象的拷贝,也是通过拷贝构造函数来进行的(所以要返回指针?
当一个对象创建出来的时候,基于另外一个对象。
那么我们就有几个问题,
- 何时需要程序员提供拷贝构造函数?
- 编译器合成拷贝构造函数的条件?
- 编译器扩充拷贝构造函数的规则?(编译器 可能会对程序员提供的拷贝构造函数进行补充

如果可以直接按位拷贝,那么编译器不会生成拷贝构造函数。但是我们对指针进行考虑,我们不希望两个指针都指向这个字符串。所以我们需要提供拷贝构造函数。
看似好像都符合按位拷贝的要求,其实也有四种情况:
- data member有拷贝构造函数
- base class有拷贝构造函数
- 类有虚函数(需要设置正确的虚表指针
- 继承自虚基类
即:类对象的一部分需要被拷贝函数正确的设置
编译器何时会合成拷贝构造函数?

只有满足上述三个条件,编译器才不会合成拷贝构造函数
- 类函数没有虚函数,没有虚基类,并且直接继承的基类,没有拷贝构造函数
- 类X的非静态数据成员,对应的类没有拷贝构造函数
- 用户没有提供拷贝构造函数
只要一个不满足,就会合成构造函数
编译器扩充拷贝构造函数的规则?

目的都是:为了确保类对象的完整性。
小结
一切的目的都是 :一个类在声明的时候是完整的。

c++ 的核心是以对象模型为支撑的面向对象模型?
评论区