跳到主要内容

C++ 11继承构造函数

引言

在C++11之前,当一个类继承自基类时,如果想在派生类中使用基类的构造函数,必须在派生类中显式地重新定义这些构造函数,即使这些构造函数只是简单地调用基类的构造函数。这导致了大量的重复代码,特别是当基类有多个构造函数时。

C++11引入了继承构造函数(Inherited Constructors)这一新特性,允许派生类直接继承基类的构造函数,从而简化代码并提高可维护性。

基本语法

继承构造函数使用using声明来实现:

cpp
class Base {
public:
Base(int x);
Base(double x, double y);
Base(std::string str, int z);
// ...其他成员
};

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

// 派生类特有的构造函数
Derived(char c);

// ...其他成员
};

通过using Base::Base;声明,Derived类继承了Base类的所有构造函数,就好像在Derived类中定义了如下构造函数:

cpp
Derived(int x) : Base(x) {}
Derived(double x, double y) : Base(x, y) {}
Derived(std::string str, int z) : Base(str, z) {}

继承构造函数的工作原理

让我们通过一个完整的例子来理解继承构造函数的工作原理:

cpp
#include <iostream>
#include <string>

class Base {
public:
Base() {
std::cout << "Base: 默认构造函数" << std::endl;
}

Base(int x) {
std::cout << "Base: 整数构造函数,x = " << x << std::endl;
}

Base(std::string s, double d) {
std::cout << "Base: 字符串和浮点数构造函数,s = " << s
<< ", d = " << d << std::endl;
}
};

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

// 派生类特有的构造函数
Derived(char c) {
std::cout << "Derived: 字符构造函数,c = " << c << std::endl;
}
};

int main() {
Derived d1; // 调用继承的默认构造函数
Derived d2(42); // 调用继承的整数构造函数
Derived d3("Hello", 3.14); // 调用继承的字符串和浮点数构造函数
Derived d4('A'); // 调用派生类自己的构造函数

return 0;
}

输出结果:

Base: 默认构造函数
Base: 整数构造函数,x = 42
Base: 字符串和浮点数构造函数,s = Hello, d = 3.14
Derived: 字符构造函数,c = A
备注

在上面的例子中,d1d2d3分别使用了继承自Base类的三个构造函数,而d4使用了Derived类特有的构造函数。

注意事项与限制

使用继承构造函数时,有一些重要的注意事项:

  1. 派生类成员不会被初始化:继承的构造函数只会初始化基类部分,派生类新增的成员变量不会被自动初始化(除了提供默认初始化的成员)。
cpp
class Base {
public:
Base(int x) : value(x) {}
int value;
};

class Derived : public Base {
public:
using Base::Base;
int derivedValue; // 在继承的构造函数中不会被初始化
};

int main() {
Derived d(10); // d.value = 10, 但d.derivedValue是未定义的
return 0;
}
  1. 构造函数的继承不是传递的:如果有多级继承,需要在每一级都使用using声明。

  2. 使用默认参数:基类构造函数中的默认参数会被继承。

cpp
class Base {
public:
Base(int x = 0, int y = 0) {
std::cout << "x = " << x << ", y = " << y << std::endl;
}
};

class Derived : public Base {
public:
using Base::Base;
};

int main() {
Derived d1; // x = 0, y = 0
Derived d2(5); // x = 5, y = 0
Derived d3(5, 7); // x = 5, y = 7
return 0;
}
  1. 冲突解决:如果派生类定义了与继承构造函数签名相同的构造函数,则派生类的构造函数会覆盖继承的构造函数。

实际应用场景

场景1:图形类层次结构

考虑一个图形库的设计,其中有各种不同的形状:

cpp
#include <iostream>
#include <string>

class Shape {
public:
Shape() : color("black"), lineWidth(1) {
std::cout << "创建默认形状" << std::endl;
}

Shape(std::string c) : color(c), lineWidth(1) {
std::cout << "创建" << color << "形状" << std::endl;
}

Shape(std::string c, int w) : color(c), lineWidth(w) {
std::cout << "创建" << color << "形状,线宽为" << lineWidth << std::endl;
}

protected:
std::string color;
int lineWidth;
};

class Circle : public Shape {
public:
using Shape::Shape; // 继承Shape的所有构造函数

Circle(double r) : Shape(), radius(r) {
std::cout << "创建半径为" << radius << "的圆" << std::endl;
}

Circle(std::string c, double r) : Shape(c), radius(r) {
std::cout << "创建" << color << "的圆,半径为" << radius << std::endl;
}

private:
double radius = 1.0; // 默认值
};

int main() {
Circle c1; // 使用继承的默认构造函数
Circle c2("red"); // 使用继承的单参数构造函数
Circle c3("blue", 2); // 使用继承的双参数构造函数
Circle c4(3.5); // 使用Circle特有的构造函数
Circle c5("green", 4.2); // 使用Circle特有的构造函数

return 0;
}

输出结果:

创建默认形状
创建red形状
创建blue形状,线宽为2
创建默认形状
创建半径为3.5的圆
创建green的圆,半径为4.2

场景2:异常类层次结构

在设计异常类层次结构时,继承构造函数非常有用:

cpp
#include <iostream>
#include <stdexcept>
#include <string>

class DatabaseError : public std::runtime_error {
public:
using std::runtime_error::runtime_error; // 继承构造函数
};

class ConnectionError : public DatabaseError {
public:
using DatabaseError::DatabaseError; // 继承构造函数
};

class QueryError : public DatabaseError {
public:
using DatabaseError::DatabaseError; // 继承构造函数

QueryError(const std::string& message, int errorCode)
: DatabaseError(message + " (Error code: " + std::to_string(errorCode) + ")"),
code(errorCode) {}

private:
int code;
};

int main() {
try {
throw ConnectionError("Failed to connect to database");
}
catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}

try {
throw QueryError("SQL syntax error", 1064);
}
catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}

return 0;
}

输出结果:

Caught exception: Failed to connect to database
Caught exception: SQL syntax error (Error code: 1064)

总结

C++11引入的继承构造函数是一个非常实用的特性,它可以:

  • 减少重复代码,提高代码可维护性
  • 简化派生类的设计,使得代码更加清晰
  • 在设计类层次结构时特别有用

然而,使用继承构造函数也有一些注意事项:

  • 继承的构造函数不会初始化派生类的新成员
  • 如果需要对派生类成员进行特殊初始化,可能仍然需要自定义构造函数
  • 构造函数的继承不是传递的,必须在每一级继承中使用using声明
提示

继承构造函数是C++11中的一个强大特性,但要根据具体情况判断是否适合使用。如果派生类有很多自己的成员需要初始化,可能显式定义构造函数更为清晰。

练习

  1. 创建一个Vehicle基类,包含不同的构造函数,然后创建CarTruck派生类,使用继承构造函数。
  2. 设计一个Person类层次结构,其中包括EmployeeStudentTeacher类,并使用继承构造函数。
  3. 修改上面的Circle类,添加额外的成员变量,观察继承构造函数对这些成员的影响。

延伸阅读