C++11 标准新增了两个重要的构造函数特性:委托构造继承构造。这两个特性极大地简化了代码,减少了重复代码的编写,提升了代码的可读性和维护性。


一、委托构造(Delegating Constructors)

1.1 什么是委托构造?

在 C++ 开发中,某个类可能会有多个构造函数用于不同的初始化需求。这些构造函数往往存在重复的代码,比如成员变量的初始化。如果每个构造函数都独立编写初始化代码,会导致代码臃肿、冗余且难以维护。

委托构造 允许我们在一个构造函数的初始化列表中调用另一个构造函数,从而简化代码,避免重复初始化的代码块。这使得类的构造逻辑更加清晰、简洁。

2. 委托构造的使用规则

  • 调用链:一个构造函数可以调用同一个类中的其他构造函数来初始化某些成员变量。
  • 避免循环调用:构造函数不能形成环状调用,否则会导致编译错误或运行时错误。
  • 初始化顺序:一旦使用了委托构造,委托的构造函数负责初始化被调用的成员变量,委托者不能再初始化其他成员。

2.3 委托构造的示例

 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
39
40
41
#include <iostream>
#include <string>
using namespace std;

class AA
{
private:
    int m_a;
    int m_b;
    double m_c;
public:
    // 一个参数的构造函数,初始化 m_c
    AA(double c) {
        m_c = c + 3;
        cout << "AA(double c)" << endl;
    }

    // 两个参数的构造函数,初始化 m_a 和 m_b
    AA(int a, int b) {
        m_a = a + 1;
        m_b = b + 2;
        cout << "AA(int a, int b)" << endl;
    }

    // 委托构造:调用 AA(int a, int b)
    AA(int a, int b, const string& str) : AA(a, b) {
        cout << "m_a=" << m_a << ", m_b=" << m_b << ", str=" << str << endl;
    }

    // 委托构造:调用 AA(double c)
    AA(double c, const string& str) : AA(c) {
        cout << "m_c=" << m_c << ", str=" << str << endl;
    }
};

int main()
{
    AA a1(10, 20, "我是一只傻傻鸟。");
    AA a2(3.8, "我有一只小小鸟。");
    return 0;
}
代码解析:
  • AA(int a, int b, const string& str) 调用了 AA(int a, int b),从而初始化了 m_am_b
  • AA(double c, const string& str) 调用了 AA(double c),从而初始化了 m_c
  • 这种方式可以避免在多个构造函数中重复编写相同的初始化代码。

二、继承构造(Inheriting Constructors)

2.1 什么是继承构造?

在 C++11 之前,派生类不能直接继承基类的构造函数。如果需要调用基类的构造函数,必须在派生类的构造函数中显式调用基类的构造函数。这种方式虽然灵活,但当基类有多个构造函数时,派生类需要逐个编写构造函数,显得繁琐。

继承构造 允许派生类通过 using 关键字继承基类的构造函数,从而自动继承基类的所有构造函数,无需显式定义派生类的构造函数。

2.2 继承构造的使用规则

  • 自动继承基类构造函数:在派生类中使用 using 关键字可以继承基类的所有构造函数。
  • 支持自定义构造函数:派生类仍然可以定义自己的构造函数,并与继承的构造函数共存。
  • 自动调用基类构造函数:继承的构造函数会自动调用基类的构造函数进行初始化。

2.3 继承构造的示例

 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
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
using namespace std;

class AA       // 基类
{
public:
    int m_a;
    int m_b;

    // 一个参数的构造函数
    AA(int a) : m_a(a) { 
        cout << "AA(int a)" << endl; 
    }

    // 两个参数的构造函数
    AA(int a, int b) : m_a(a), m_b(b) { 
        cout << "AA(int a, int b)" << endl; 
    }
};

class BB : public AA  // 派生类
{
public:
    double m_c;

    using AA::AA;   // 继承基类的构造函数

    // 派生类的自定义构造函数
    BB(int a, int b, double c) : AA(a, b), m_c(c) {
        cout << "BB(int a, int b, double c)" << endl;
    }

    void show() {
        cout << "m_a=" << m_a << ", m_b=" << m_b << ", m_c=" << m_c << endl;
    }
};

int main()
{
    BB b1(10);    // 调用继承的基类构造函数 AA(int a)
    b1.show();

    BB b2(10, 20);    // 调用继承的基类构造函数 AA(int a, int b)
    b2.show();

    BB b3(10, 20, 10.58);    // 调用派生类自己的构造函数
    b3.show();

    return 0;
}
代码解析:
  • BB(int a)BB(int a, int b) 是自动继承自基类 AA 的构造函数,通过 using AA::AA 实现。
  • BB(int a, int b, double c) 是派生类自定义的构造函数,用于初始化额外的成员 m_c

4. 继承构造的注意事项

  • using 只能继承构造函数using 关键字只能用于继承构造函数,不能用于其他成员函数的继承。
  • 派生类的自定义构造函数不会覆盖继承的构造函数:派生类的构造函数和继承的基类构造函数可以共存。

三、总结

C++11 引入的 委托构造继承构造 大大简化了类构造函数的编写,避免了冗余的代码。在复杂的类体系中,使用委托构造可以减少重复的初始化代码,提升代码的可维护性;使用继承构造则可以减少派生类对基类构造函数的重复实现,使代码更加简洁直观。

  • 委托构造:一个构造函数可以调用另一个构造函数来简化初始化逻辑,减少代码重复。
  • 继承构造:派生类可以通过 using 关键字继承基类的构造函数,避免手动编写重复的构造函数。