符合以下规则:

  1. 默认构造函数(使用类内初始值和默认值,如果没有类内初始值,而且不是静态类型,一般是残值)
  2. 有定义构造函数,则不合成默认构造函数,除非使用=default
  3. 构造函数初始值列表进行初始化
  4. 构造函数内部,进入构造函数内部,所有成员已经完成初始化,对成员进行后续操作可以看成是一个函数在进行初始化。

下面的代码,初始化逻辑:

  1. 进入到构造函数内部之后,所有的成员变量都已经完成了初始化。此时,成员b采用默认构造函数进行初始化。打印第一个 default
  2. 使用默认构造函数创建一个临时对象B(),打印第二个 default。
  3. B() 是一个右值,所以调用右值赋值函数,但是由于B的移动构造函数是删除的,所以只能使用拷贝赋值函数 B& operator=(const B&) 来作为备选,本来一个const B& 也可以指向一个临时的右值 B()。
  4. 使用赋值拷贝函数,将临时对象B()拷贝给成员b。打印 B assign
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  
#include <iostream>

struct B {

    //default ctor
    B() {
        std::cout << "B default " <<std::endl;
    }
    //copy ctor
    B(const B&) {
        std::cout << "B copy " <<std::endl;
    }

    B& operator=(const B&) {
        std::cout <<"B assign "<< std::endl;
        return *this;
    }

    B(B &&) = delete;
    B& operator=(B &&) {
        std::cout <<"B move " << std::endl;
        return *this;
    }
};

struct A {
    B b;
    A() {
        b = B();
    }
};


int  main() {
    A a;
}

##三五法则

为了不用去考虑复杂的C++默认合成函数的细则,如果已经定义了任何一个构造函数,其余的构造函数最好都定义一遍。默认构造函数,拷贝构造,拷贝赋值,移动构造,移动赋值。其中移动赋值需要考虑赋值对象是自身的情况。