C++抽象类与类继承相关注意事项 [学习笔记]
以下是关于 抽象类 与 类继承 在 C++ 编程中的注意事项总结:
抽象类的注意事项
-
定义抽象类:
- 抽象类中包含至少一个纯虚函数:
virtual 返回类型 函数名(参数列表) = 0;
- 抽象类不能直接实例化。
- 抽象类中包含至少一个纯虚函数:
-
虚析构函数:
- 抽象类通常需要定义虚析构函数,以便通过基类指针删除派生类对象时正确释放资源:
virtual ~Base() {}
- 抽象类通常需要定义虚析构函数,以便通过基类指针删除派生类对象时正确释放资源:
-
成员的种类:
- 抽象类可以包含:
- 成员变量
- 普通函数
- 纯虚函数
- 构造函数(用于初始化成员)
- 抽象类可以包含:
-
接口类:
- 如果抽象类中的所有成员函数都是纯虚函数,通常称之为 接口类,可用于定义行为规范。
类继承的注意事项
-
继承方式:
- 公有继承(
public
):基类的public
和protected
成员在派生类中保持原有访问权限。 - 私有继承(
private
):基类的public
和protected
成员在派生类中变为private
。 - 受保护继承(
protected
):基类的public
和protected
成员在派生类中变为protected
。
- 公有继承(
-
派生类的构造与析构:
- 派生类会调用基类的构造函数来初始化继承的成员。
- 析构时会按继承链逆序调用析构函数(先销毁派生类,再销毁基类)。
-
重写与隐藏:
- 如果派生类重写基类的虚函数,需要使用
override
关键字,避免误操作:virtual void func() override;
- 如果派生类定义了与基类同名的非虚函数,则基类函数会被隐藏,需显式调用:
Base::func();
- 如果派生类重写基类的虚函数,需要使用
-
虚函数与动态多态:
- 虚函数通过虚函数表实现动态绑定,基类指针或引用指向派生类对象时,可以正确调用派生类的重写函数。
-
纯虚函数的继承:
- 如果派生类未实现所有纯虚函数,则派生类本身仍是抽象类,不能实例化。
多继承的注意事项
-
菱形继承问题:
- 当多个基类继承自同一个父类,派生类再次继承这些基类时,可能会导致基类的成员被多次继承。
- 解决方法:使用虚继承:
class Base {}; class Derived1 : virtual public Base {}; class Derived2 : virtual public Base {}; class MostDerived : public Derived1, public Derived2 {};
-
继承冲突:
- 如果多继承中存在同名成员或函数,需通过作用域解析运算符明确指定调用哪个基类的成员:
class Base1 { void func(); }; class Base2 { void func(); }; class Derived : public Base1, public Base2 { void someFunc() { Base1::func(); } };
- 如果多继承中存在同名成员或函数,需通过作用域解析运算符明确指定调用哪个基类的成员:
类型转换的注意事项
-
向上转型(Upcasting):
- 基类指针或引用可以指向派生类对象。
- 安全并且不需要显式转换。
-
向下转型(Downcasting):
- 将基类指针或引用转换为派生类指针,需要显式转换并确保类型安全(可用
dynamic_cast
检查):Derived* d = dynamic_cast<Derived*>(basePtr); if (d) { d->func(); }
- 将基类指针或引用转换为派生类指针,需要显式转换并确保类型安全(可用
设计注意事项
-
基类的职责:
- 基类应设计为接口类或提供通用功能,不应过多关注具体实现。
-
派生类的职责:
- 派生类应负责实现基类定义的接口,避免改变基类的设计规范。
-
里氏替换原则:
- 任何基类可以出现的地方,都应该可以用派生类替换,而不会改变程序的行为。
-
避免多继承复杂性:
- 多继承的设计容易引入复杂性,尽量使用组合或接口类代替。
示例:抽象类与继承的综合使用
#include <iostream>
using namespace std;
// 抽象基类
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
virtual double area() = 0;
virtual ~Shape() {}
};
// 派生类 - 圆形
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override {
cout << "Drawing Circle" << endl;
}
double area() override {
return 3.14159 * radius * radius;
}
};
// 派生类 - 矩形
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
void draw() override {
cout << "Drawing Rectangle" << endl;
}
double area() override {
return width * height;
}
};
int main() {
Shape* shapes[] = {new Circle(5.0), new Rectangle(4.0, 6.0)};
for (Shape* shape : shapes) {
shape->draw();
cout << "Area: " << shape->area() << endl;
delete shape; // 动态删除
}
return 0;
}
输出
Drawing Circle
Area: 78.5397
Drawing Rectangle
Area: 24