编程C++ 开发者

虚拟函数和虚拟继承在C++中的机制是如何工作的?在设计具有抽象接口的类时有哪些特点?

用 Hintsage AI 助手通过面试

回答

虚拟函数允许实现 多态 — 通过指向基类的指针或引用处理派生类对象的能力。声明虚拟函数使用关键字 virtual

class Base { public: virtual void foo() { std::cout << "Base::foo"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo"; } };

调用 foo() 通过类型为 Base* 的指针时,如果指针指向一个 Derived 对象,将调用 Derived 中的实现。

抽象类 — 至少包含一个纯虚函数:

class Interface { public: virtual void process() = 0; // 纯虚函数 };

虚拟继承 解决了钻石继承(diamond problem)的问题:

class A { }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { };

这保证了在对象 D 中仅会有一个基类 A 的实例。

有陷阱的问题

虚拟析构函数和普通析构函数有什么区别?如果类用于继承,是否需要将析构函数声明为虚拟的?

回答: 虚拟析构函数保证在通过基类型指针删除对象时,会调用派生类本身的析构函数。这对正确释放资源非常重要。

class Base { public: virtual ~Base() {} };

如果析构函数不是虚拟的,则在 delete BasePtr; 时,只有 Base 的析构函数会被调用,从而不会释放继承字段的资源。

由于不知道主题细节而导致的实际错误示例


故事

在一个大型金融系统中,为各种工具设计了一个公共类接口,但没有声明虚拟析构函数。在一个子类中使用了动态资源。当通过指向基类型的指针删除对象时发生了内存泄漏,这个问题只是在工业负载阶段被发现。


故事

团队使用了多重继承,但没有使用虚拟继承。类 D 通过中间类两次继承了 A。这导致了状态的重复和在访问 A 的成员时出现错误。只有通过静态分析工具审计后才得以修复。


故事

在日志插件开发项目中使用了抽象类,但没有将其析构函数声明为虚拟。在通过接口指针删除插件时出现了明显的依赖关系和与未调用子类析构函数相关的错误。问题影响了资源池并导致了资源泄漏。