13 多态
抽象类
使用 = 0 声明纯虚函数,包含纯虚函数的类是抽象类,不能实例化:
- 不能申明抽象类的对象。例如:
Animal a - 不能被显式转为抽象类对象。例如:
(Animal)dog - 不能作为函数参数类型或者返回的值。例如:
func(Animal) - 能申明为指针或引用,指代自己派生类对象
class Shape {
public:
virtual double area() const = 0; // 纯虚函数
};
class Circle : public Shape {
public:
double area() const override { return 3.14 * m_radius * m_radius; }
};
纯虚函数可以(不建议)提供定义(实现),但定义必须在类外提供。
如果基类析构函数是纯虚的,必须提供定义(哪怕空实现),因为当派生类对象被销毁时,编译器会自动隐式地调用所有基类的析构函数。
虚析构函数
虚析构函数就是在基类中声明为 virtual 的析构函数。它的作用是:确保通过基类指针删除派生类对象时,能正确调用完整的析构链(先派生类析构,再基类析构)。
class Base {
public:
~Base() { cout << "Base dtor\n"; } // 非虚析构
};
class Derived : public Base {
public:
int* data = new int[10];
~Derived() { delete[] data; cout << "Derived dtor\n"; }
};
int main() {
Base* ptr = new Derived();
delete ptr; // 仅输出 "Base dtor",Derived 的 data 发生内存泄漏!
}
如果基类析构不是虚函数:只会调用基类析构,子类析构不执行,子类资源泄漏!
class Base {
public:
virtual ~Base() { cout << "Base dtor\n"; } // 虚析构
};
// Derived 保持不变...
int main() {
Base* ptr = new Derived();
delete ptr; // 正确输出 "Derived dtor" -> "Base dtor",资源安全释放
}
如果基类析构不是虚函数,但是析构时用dynamic_cast转型成子类指针,再delete,也可以安全释放全部资源。
如果基类的析构函数是 protected 且非虚,这种设计通常用于表达一种明确的意图:"这个基类只能作为派生类的一部分存在,禁止通过基类指针从外部销毁对象。" 这在编译阶段就彻底阻断了外部代码通过基类指针 delete 派生类对象的可能,不需要靠 virtual 来"保护"。
RTTI
RTTI(Run-Time Type Identification),即运行时类型识别。它是 C++ 提供的一种机制,允许程序在运行期间动态地查询一个对象的实际派生类型。
RTTI 提供了两个非常有用的操作符:typeid 和 dynamic_cast。
typeid
- 在使用
typeid前,必须包含头文件<typeinfo> typeid返回std::type_info对象,它常用的有==、!=运算符 和name()成员typeid通常不对表达式求值,仅当操作数是多态类类型的左值时,才会在运行时对表达式求值以获取动态类型信息。
int a = 10;
typeid(a = 100); // a 不会变成 100
Base* p = new Derived;
typeid(*p); // 会对 *p 求值,查真实类型
typeid(p); // 只求指针类型,不求值
typeid(p->f()); // 不调用函数
dynamic_cast
沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用。
dynamic_cast < 新类型 > ( 表达式 )
若转型失败:
- 且 新类型 是指针类型,则它返回该类型的空指针。
- 且 新类型 是引用类型,则它抛出与类型
std::bad_cast的处理块匹配的异常。
final说明符 (C++11)
指定某个虚函数不能在子类中被覆盖,或者某个类不能被子类继承。
-
修饰类:禁止被继承
class Base final { ... }; // Base 类不能被继承 class Derived : public Base { ... }; // 编译错误! -
修饰虚函数:禁止被重写
class Base { virtual void show() final; // 此虚函数不可被重写 }; class Derived : public Base { void show() override; // 编译错误!(不写override也会编译错误) };