C++ Constructor

1 概述

2 Constructor

函数原型:Constructor();

3 Copy Constructor

函数原型:Constructor(const Constructor &);

3.1 调用场景

  • 对象作为函数的参数,以值传递的方式传给函数;
  • 对象作为函数的返回值,以值的形式从函数返回;
  • 使用一个对象给另一个对象初始化。
1
2
3
4
5
void example(ClassType class_name);
ClassType example(void);

ClassType c1();
ClassType c2(c1);
  • 对象不存在,且没用别的对象来初始化,就是调用了构造函数;
  • 对象不存在,且用别的对象来初始化,就是拷贝构造函数;
  • 对象存在,用别的对象来给它赋值,就是赋值函数。

3.2 与赋值运算符的区别

拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给另一个对象(已经存在的)。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。

编译器生成默认拷贝构造函数的场景:
如果用户没有自定义拷贝构造函数,并且在代码中使用到了拷贝构造函数,编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数,编译器就不在生成。
如果用户定义了一个构造函数,但不是拷贝构造函数,而此时代码中又用到了拷贝构造函数,那编译器也会生成默认的拷贝构造函数。

3.3 深拷贝与浅拷贝

4 Move Constructor

函数原型:Constructor(Constructor &&);

5 特性

  1. 构造函数不能为虚函数。
    因为构造一个对象的时候,需要知道对象的实际类型,而虚函数是在运行期间才确定实际类型。如果构造函数为虚函数,则在构造一个对象时,由于对象还未构造成功,编译器还无法知道对象的实际类型,也就无法确定是该类本身还是派生类。
    虚函数的执行依赖虚函数表,而虚函数表是在构造函数中初始化的,即初始化为vptr,让它指向虚函数表。如果构造函数为虚函数,则在构造对象期间,虚函数表还没有被初始化,将无法进行。
    在执行构造函数的函数体前,对象的内存已经分配了,函数的对应关系、虚表也已经创建了,执行构造函数的函数体时,此对象已经是一个完整的对象了。
    编译器会在构造函数的一开始插入一部分代码,如果类中有虚函数,这段代码会创建虚表指针成员,并初始化虚表,然后执行初始化成员列表,最后才执行原本的函数体,故在执行函数体时,对象已经实例化了。
  2. 构造函数中调用虚函数的情况。
    在基类的构造函数内调用纯虚方法会编译失败,在派生类的构造函数中调用虚函数是可行的。因为构造对象时,会先执行基类的构造函数,而此时虚表创建了,但是代表纯虚函数的函数指针还未初始化,直到执行派生类的构造函数时,虚表内的指针才指向派生类的方法,此时调用才是有效的。
    abstract class 源码

6 参考文献


C++ Constructor
https://laplac2.github.io/cpp/constructor/
作者
Laplace
发布于
2022年3月18日
许可协议