1. 接口继承(Interface Inheritance)
接口继承指的是派生类从基类继承一组仅有声明没有实现的函数(纯虚函数),这些函数定义了接口(契约),而不包含具体的实现细节。接口继承的目的是为派生类提供一个统一的接口约定,所有派生类必须提供这些接口函数的具体实现。
特点:
- 纯虚函数:接口继承通常通过在基类中声明纯虚函数来实现。纯虚函数没有函数体,派生类必须提供具体的实现。
- 没有数据成员:接口类通常不包含任何成员变量,只定义接口方法。
- 无法实例化:接口类不能直接创建对象,因为它包含纯虚函数,只有通过派生类才能实例化。
- 派生类要求实现所有纯虚函数:派生类如果继承了接口类,必须实现接口类中所有的纯虚函数。
例子:
#include <iostream>// 接口类:只声明接口方法,没有实现
class IShape {
public:virtual void draw() = 0; // 纯虚函数virtual double area() = 0; // 纯虚函数virtual ~IShape() {} // 虚析构函数,防止派生类对象销毁时内存泄漏
};// 派生类:实现接口中的纯虚函数
class Circle : public IShape {
public:Circle(double radius) : radius_(radius) {}void draw() override {
std::cout << "Drawing Circle\n";}double area() override {return 3.14 * radius_ * radius_;}private:double radius_;
};int main() {
IShape* shape = new Circle(10);
shape->draw();
std::cout << "Area: " << shape->area() << std::endl;delete shape;return 0;
}
在上面的例子中,IShape 是一个接口类,它定义了两个纯虚函数 draw() 和 area()。Circle 类继承自 IShape,并实现了这两个函数。因此,Circle 类提供了具体的实现,而 IShape 类只是一个接口。
2. 实现继承(Implementation Inheritance)
实现继承指的是派生类从基类继承功能实现,基类提供了具体的成员函数实现,派生类可以直接使用这些实现,或者重写这些实现。实现继承的目的是通过继承基类的实现来复用代码。
特点:
- 成员函数实现:基类提供了具体的成员函数实现,派生类可以直接使用这些实现,也可以选择重写基类的成员函数。
- 可以包含数据成员:与接口类不同,实现继承的基类通常包含数据成员(变量)和成员函数的实现。
- 可以实例化:基类提供了实现,派生类可以实例化基类的对象,或者通过派生类实例化。
- 继承代码复用:实现继承允许派生类直接复用基类的实现,从而避免代码重复。
例子:
#include <iostream>// 基类:包含具体的实现
class Shape {
public:Shape(double width, double height) : width_(width), height_(height) {}// 成员函数实现void draw() {
std::cout << "Drawing Shape\n";}double area() {return width_ * height_;}private:double width_;double height_;
};// 派生类:可以继承基类的实现或重写基类的实现
class Rectangle : public Shape {
public:Rectangle(double width, double height) : Shape(width, height) {}// 可以选择不重写基类的函数,直接使用基类的实现
};int main() {
Shape* shape = new Rectangle(5, 10);
shape->draw();
std::cout << "Area: " << shape->area() << std::endl;delete shape;return 0;
}
在这个例子中,Shape 类提供了一个默认的实现,Rectangle 类继承自 Shape,并直接使用 Shape 类中定义的 draw() 和 area() 函数。Rectangle 类没有修改这些函数,因此它可以直接复用 Shape 类中的实现。
3. 接口继承与实现继承的区别
- 目的:
- 接口继承:定义了一个规范或合同,派生类必须提供具体实现。接口继承注重的是行为,即方法的定义而不是实现。
- 实现继承:基类提供具体实现,派生类可以直接继承并使用这些实现,或根据需要进行修改。实现继承注重的是复用代码,派生类可以继承基类的功能。
- 类的设计:
- 接口继承:通常用于定义统一的行为接口,派生类必须实现接口中的所有方法。接口类通常不含成员变量,只定义方法。
- 实现继承:用于复用代码,派生类继承了基类的具体实现和成员变量,能够直接使用或修改这些实现。
- 是否提供实现:
- 接口继承:接口类不提供实现,派生类必须提供自己的实现。
- 实现继承:基类提供具体的实现,派生类可以直接使用,或者修改这些实现。
- 多重继承:
- 接口继承:C++ 支持多重继承,多个接口可以被实现类同时继承,这通常用于混合多种行为。
- 实现继承:C++ 也支持多重继承,但如果不同的基类中有相同的方法实现,可能会出现方法冲突,导致“二义性”(ambiguity)。
4. C++ 中的接口继承和实现继承结合使用
在 C++ 中,接口继承和实现继承经常结合使用,以提供强大的灵活性。一个常见的模式是,基类提供接口(通过纯虚函数)并实现一些功能,而派生类则实现其他的功能或重写基类的某些方法。
例子:
#include <iostream>class IShape {
public:virtual void draw() = 0; // 纯虚函数virtual double area() = 0;virtual ~IShape() {}
};class Shape : public IShape {
public:Shape(double width, double height) : width_(width), height_(height) {}void draw() override {
std::cout << "Drawing Shape\n";}double area() override {return width_ * height_;}private:double width_;double height_;
};class Rectangle : public Shape {
public:Rectangle(double width, double height) : Shape(width, height) {}// 可以重写基类的方法void draw() override {
std::cout << "Drawing Rectangle\n";}
};int main() {
IShape* shape = new Rectangle(5, 10);
shape->draw();
std::cout << "Area: " << shape->area() << std::endl;delete shape;return 0;
}
在这个例子中,IShape 是接口类,Shape 是实现类,Rectangle 则是从 Shape 类继承并重写了 draw() 方法。通过这种方式,我们既可以使用接口定义统一的行为,也可以在实现类中提供默认行为,并允许派生类进一步修改。
总结
- 接口继承:注重行为的定义,没有实现,派生类必须提供实现。
- 实现继承:基类提供实现,派生类可以复用或修改这些实现。
- 应用场景:接口继承适用于需要保证一组类具有相同功能接口的场景,常用于多态设计;实现继承适用于需要共享代码或功能的场景,常用于避免代码重复。