C++11 委托构造函数和继承构造函数

C++11 委托构造函数和继承构造函数

C++11 引入了两个重要的构造函数特性:委托构造函数(Delegating Constructors)和继承构造函数(Inheriting Constructors)。这两个特性都旨在减少代码重复,提高代码的可维护性和可读性。

委托构造函数(Delegating Constructors)

委托构造函数允许一个构造函数调用同一个类的另一个构造函数,从而避免在多个构造函数中重复相同的初始化代码。

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyClass {
private:
int x, y;
std::string name;

public:
// 主构造函数
MyClass(int x, int y, const std::string& name)
: x(x), y(y), name(name) {
std::cout << "主构造函数被调用" << std::endl;
// 其他复杂的初始化逻辑
}

// 委托构造函数 - 委托给主构造函数
MyClass(int x, int y) : MyClass(x, y, "default") {
std::cout << "委托构造函数1被调用" << std::endl;
}

// 委托构造函数 - 委托给主构造函数
MyClass() : MyClass(0, 0, "empty") {
std::cout << "委托构造函数2被调用" << std::endl;
}
};

委托构造函数的执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Example {
private:
int value;

public:
Example(int v) : value(v) {
std::cout << "1. 目标构造函数执行完成" << std::endl;
}

Example() : Example(42) { // 委托给 Example(int)
std::cout << "2. 委托构造函数体执行" << std::endl;
}
};

// 使用示例
Example obj; // 输出:
// 1. 目标构造函数执行完成
// 2. 委托构造函数体执行

注意事项

  1. 不能形成循环委托

    1
    2
    3
    4
    5
    class Bad {
    public:
    Bad(int x) : Bad() {} // 错误:形成循环委托
    Bad() : Bad(10) {} // 错误:形成循环委托
    };
  2. 委托构造函数不能同时使用成员初始化列表

    1
    2
    3
    4
    5
    6
    7
    class Invalid {
    private:
    int x, y;

    public:
    Invalid(int a) : x(a), Invalid() {} // 错误:不能同时委托和初始化成员
    };

实际应用场景

委托构造函数特别适用于需要多种初始化方式的类:

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
class Rectangle {
private:
double width, height;
std::string color;

public:
// 完整构造函数
Rectangle(double w, double h, const std::string& c)
: width(w), height(h), color(c) {
validateDimensions();
initializeDefaults();
}

// 正方形构造函数
Rectangle(double side) : Rectangle(side, side, "white") {}

// 默认构造函数
Rectangle() : Rectangle(1.0, 1.0, "white") {}

// 只指定颜色的构造函数
Rectangle(const std::string& c) : Rectangle(1.0, 1.0, c) {}

private:
void validateDimensions() {
if (width <= 0 || height <= 0) {
throw std::invalid_argument("尺寸必须为正数");
}
}

void initializeDefaults() {
// 其他初始化逻辑
}
};

继承构造函数(Inheriting Constructors)

继承构造函数允许派生类直接继承基类的构造函数,而不需要在派生类中重新声明和定义它们。

基本语法

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
class Base {
public:
Base(int x) {
std::cout << "Base(int): " << x << std::endl;
}

Base(int x, double y) {
std::cout << "Base(int, double): " << x << ", " << y << std::endl;
}

Base(const std::string& s) {
std::cout << "Base(string): " << s << std::endl;
}
};

class Derived : public Base {
public:
// 继承所有基类构造函数
using Base::Base;

// 也可以定义自己的构造函数
Derived(bool flag) : Base(flag ? 1 : 0) {
std::cout << "Derived(bool): " << flag << std::endl;
}
};

// 使用示例
int main() {
Derived d1(42); // 调用继承的 Base(int)
Derived d2(3, 3.14); // 调用继承的 Base(int, double)
Derived d3("hello"); // 调用继承的 Base(string)
Derived d4(true); // 调用 Derived(bool)

return 0;
}

继承构造函数的特点

  1. 选择性继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Base {
    public:
    Base(int x) {}
    Base(double y) {}
    Base(int x, int y) {}
    };

    class Derived : public Base {
    public:
    using Base::Base; // 继承所有构造函数

    // 如果定义了相同签名的构造函数,会覆盖继承的版本
    Derived(int x) : Base(x) {
    std::cout << "Derived 的 int 构造函数" << std::endl;
    }
    };
  2. 与默认构造函数的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Base {
    public:
    Base(int x) {}
    // 注意:没有默认构造函数
    };

    class Derived : public Base {
    public:
    using Base::Base;
    // Derived 也不会有默认构造函数
    };

    // Derived d; // 错误:没有默认构造函数
    Derived d(42); // 正确

实际应用场景

继承构造函数在扩展现有类时特别有用:

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
// 基础日志类
class Logger {
protected:
std::string prefix;
std::ofstream logFile;

public:
Logger(const std::string& filename) : logFile(filename) {}

Logger(const std::string& filename, const std::string& prefix)
: logFile(filename), prefix(prefix) {}

void log(const std::string& message) {
logFile << prefix << message << std::endl;
}
};

// 带时间戳的日志类
class TimestampLogger : public Logger {
public:
using Logger::Logger; // 继承所有构造函数

void log(const std::string& message) override {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);

logFile << "[" << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S")
<< "] " << prefix << message << std::endl;
}
};

// 使用示例
TimestampLogger logger1("app.log");
TimestampLogger logger2("debug.log", "[DEBUG] ");

组合使用委托构造函数和继承构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Base {
public:
Base(int x, int y) {
std::cout << "Base(" << x << ", " << y << ")" << std::endl;
}

Base(int x) : Base(x, 0) { // 委托构造函数
std::cout << "Base(int) 委托完成" << std::endl;
}
};

class Derived : public Base {
public:
using Base::Base; // 继承构造函数

// 自己的委托构造函数
Derived(const std::string& s) : Derived(std::stoi(s)) {
std::cout << "Derived(string) 委托完成" << std::endl;
}
};

// 使用示例
Derived d1(42); // 继承的构造函数 + 基类委托
Derived d2("123"); // 派生类委托 + 继承构造函数 + 基类委托

总结

  • 委托构造函数:减少同一类中多个构造函数的代码重复
  • 继承构造函数:简化派生类对基类构造函数的继承
  • 两者都提高了代码的可维护性和可读性
  • 在现代 C++ 开发中,应该充分利用这些特性来编写更清洁的代码

这些特性使得 C++11 在构造函数设计方面更加灵活和强大,是现代 C++ 编程的重要工具。