C/C++基础知识复习(47)
1) 接口继承与实现继承的区别
接口继承
接口继承意味着定义一个类,它只声明一组方法(通常是纯虚函数),但是不提供任何实现。继承这个接口的子类必须实现这些方法。接口继承的主要目的是规范化行为。
C++ 例子:
在 C++ 中,通过定义一个纯虚类来模拟接口。比如,你定义了一个 Animal
类,这个类没有实现具体方法,只声明了方法的签名,像这样:
class Animal { public: virtual void speak() = 0; // 纯虚函数,子类必须实现 };
任何继承 Animal
类的子类(如 Dog
)都必须实现 speak
方法。接口继承的关键是:父类没有具体实现,子类必须提供实现。
实现继承
实现继承则是指一个类继承了另一个类,并且可以重用父类中已经实现的功能,或者在子类中重写父类的方法。
C++ 例子:
继承一个类 Animal
,并实现一个具体的 speak
方法:
class Animal { public: void speak() { cout << "Animal speaks" << endl; // 这是具体的实现 } };
在这种情况下,Dog
继承了 Animal
,并且可以使用父类已经实现的 speak
方法,或者重写它。
2) 如何平衡多态性与性能
在面向对象编程中,多态性是通过虚函数来实现的。当你通过基类指针或引用调用子类的方法时,程序会在运行时决定调用哪个版本的方法(这叫动态绑定)。
多态性带来了灵活性,但也有性能开销,因为每次调用虚函数时都需要查找虚函数表(vtable)。
如何优化多态性带来的性能开销?
-
避免不必要的虚函数调用
如果你不需要多态性,可以将方法声明为非虚函数。这样,方法的调用就不需要通过虚函数表,直接调用特定的实现。优化示例: 假设我们有
Animal
和Dog
类。如果我们知道Dog
类不需要重写父类的speak
方法,就可以直接将speak
声明为非虚函数。class Animal { public: void speak() { cout << "Animal speaks" << endl; } };
这样,
speak()
就会在编译时绑定,不再有虚函数表查找,性能会更好。 -
减少继承层级
深层次的继承结构会增加虚函数查找的复杂度。尽量保持简单的继承关系,以减少虚函数表的深度和查找成本。
例子:多态性带来的性能开销
假设我们有一个多态的设计,通过基类指针调用派生类的方法:
class Animal {
public: virtual void speak() {
// 虚函数 cout << "Animal speaks" << endl;
} };
class Dog : public Animal {
public: void speak() override {
cout << "Bark" << endl;
} };
这里每次调用 animal->speak()
时,都会通过虚函数表查找 Dog
类的 speak
方法,这带来了性能开销。如果你不需要多态性,可以避免虚函数,直接调用特定的方法。
总结
- 接口继承:用来定义一组必须实现的方法,父类只声明方法签名,不提供具体实现,子类必须实现这些方法。
- 实现继承:用来继承父类已实现的方法,子类可以直接使用父类的方法,或者重写它们。
- 多态与性能的平衡:避免不必要的虚函数调用(如果不需要多态),减少继承层级,这样可以提高程序的执行效率。