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
备注
在上面的例子中,d1
、d2
和d3
分别使用了继承自Base
类的三个构造函数,而d4
使用了Derived
类特有的构造函数。
注意事项与限制
使用继承构造函数时,有一些重要的注意事项:
- 派生类成员不会被初始化:继承的构造函数只会初始化基类部分,派生类新增的成员变量不会被自动初始化(除了提供默认初始化的成员)。
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;
}
-
构造函数的继承不是传递的:如果有多级继承,需要在每一级都使用
using
声明。 -
使用默认参数:基类构造函数中的默认参数会被继承。
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:图形类层次结构
考虑一个图形库的设计,其中有各种不同的形状:
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中的一个强大特性,但要根据具体情况判断是否适合使用。如果派生类有很多自己的成员需要初始化,可能显式定义构造函数更为清晰。
练习
- 创建一个
Vehicle
基类,包含不同的构造函数,然后创建Car
和Truck
派生类,使用继承构造函数。 - 设计一个
Person
类层次结构,其中包括Employee
、Student
和Teacher
类,并使用继承构造函数。 - 修改上面的
Circle
类,添加额外的成员变量,观察继承构造函数对这些成员的影响。