当前位置: 首页 > news >正文

C++代码优化(二): 区分接口继承和实现继承

目录

1.引言

2.接口继承

3.实现继承

4.如何选择接口继承与实现继承

5.完整实例

6.总结


1.引言

        在C++中,区分接口继承和实现继承是一种良好的编程实践,有助于提高代码的可维护性、可读性和可扩展性。接口继承通常指的是从基类继承纯虚函数(pure virtual functions),而实现继承则是从基类继承具体的实现。接口继承和实现继承之间的区别在于,它们分别用于不同的目的:前者用于定义行为,后者用于共享实现。

2.接口继承

        接口继承通常用于定义一个抽象基类,其中只包含纯虚函数。这个基类不能被实例化,只能作为其他类的基类使用。

        这种方式通常用于定义一组行为约定,保证所有派生类都实现相同的行为,但实现的细节可以各自不同。

      特点:

  • 基类只定义接口,不提供任何实现。

  • 派生类必须实现基类中声明的所有纯虚函数。

  • 主要目的是为了多态性,通过基类指针或引用调用派生类的方法。

// 纯虚类,用作接口
class Shape {
public:virtual ~Shape() = default;virtual void draw() const = 0;  // 纯虚函数,派生类必须实现
};class Circle : public Shape {
public:void draw() const override {// Circle 特有的实现std::cout << "Drawing Circle" << std::endl;}
};class Rectangle : public Shape {
public:void draw() const override {// Rectangle 特有的实现std::cout << "Drawing Rectangle" << std::endl;}
};

在这个例子中,Shape 类定义了一个纯虚函数 draw(),用于描述绘制形状的行为,但没有提供实现。Circle 和 Rectangle 是具体实现类,负责实现接口中的 draw() 函数。客户端代码可以通过基类指针来调用具体类的实现,利用多态性实现灵活的代码设计。

优点:

  • 派生类必须提供实现,保证了不同派生类实现同样的行为。

  • 可以在接口层面定义抽象概念,解耦具体实现。

缺点:

  • 如果没有具体实现,可能会产生重复代码,导致冗余实现。

3.实现继承

        实现继承是指基类不仅定义接口,还提供某些功能的默认实现。派生类可以直接继承这些功能,或者根据需要选择覆盖(override)它们。

        这种方式通常用于减少代码重复,提供通用的功能实现,派生类可以选择复用基类的实现,也可以根据具体情况进行覆盖。

     特点:

  • 基类既定义接口,也提供某些函数的实现。

  • 派生类可以复用基类中的实现,也可以选择覆盖(override)基类的实现。

  • 主要目的是代码复用,减少重复实现。

class Shape {
public:virtual ~Shape() = default;// 提供默认实现virtual void draw() const {std::cout << "Drawing a generic shape" << std::endl;}// 需要派生类实现的纯虚函数virtual double area() const = 0;
};class Circle : public Shape {
public:void draw() const override {// 调用基类实现,避免重复代码Shape::draw();std::cout << "Specifically drawing a circle" << std::endl;}double area() const override {return 3.14 * radius * radius;}private:double radius = 1.0;
};

在这个例子中,Shape 类不仅定义了接口,还提供了一个默认的 draw() 实现,派生类 Circle 可以调用基类的实现,同时扩展自己特有的行为。Circle 也可以选择覆盖 draw() 函数,改变绘制行为。

优点:

  • 提供了代码复用机制,减少重复代码。

  • 派生类可以在需要时覆盖基类中的实现,灵活性较高。

缺点:

  • 基类的实现可能与派生类不完全匹配,派生类可能需要额外工作去适应。

  • 如果不当使用,可能导致派生类对基类实现的过度依赖。

4.如何选择接口继承与实现继承

        类设计时,接口继承与实现继承相互独立,代表着一定的设计意义,在二者之间进行选择时,我们需要考虑的因素:

        1) 对于无法提供默认版本的函数接口选择函数接口继承,对于能够提供默认版本的函数接口,选择函数实现继承。

        2) 如果你需要强制派生类实现某些行为,而基类不关心实现细节,使用接口继承;如果基类中有可以复用的代码实现,且派生类可能会依赖它,使用实现继承。

5.完整实例

#include <iostream>
#include <vector>
#include <memory>// 接口继承
class IShape {
public:virtual ~IShape() = default;virtual void draw() const = 0;virtual double area() const = 0;
};// 实现继承
class Shape : public IShape {
public:void draw() const override {std::cout << "Drawing a generic shape" << std::endl;}// 具体形状必须提供自身的面积计算方式virtual double area() const = 0;
};class Circle : public Shape {
public:Circle(double r) : radius(r) {}// 覆盖并调用基类方法void draw() const override {Shape::draw();  // 调用基类的默认行为std::cout << "Drawing a circle with radius " << radius << std::endl;}double area() const override {return 3.14 * radius * radius;}private:double radius;
};class Rectangle : public Shape {
public:Rectangle(double w, double h) : width(w), height(h) {}void draw() const override {Shape::draw();std::cout << "Drawing a rectangle with width " << width << " and height " << height << std::endl;}double area() const override {return width * height;}private:double width, height;
};int main() {std::vector<std::shared_ptr<IShape>> shapes;shapes.push_back(std::make_shared<Circle>(5.0));shapes.push_back(std::make_shared<Rectangle>(3.0, 4.0));for (const auto& shape : shapes) {shape->draw();std::cout << "Area: " << shape->area() << std::endl;}return 0;
}

6.总结

        在面向对象编程中,接口继承和实现继承是两种不同的继承方式,它们的区别在于继承的成员的特性不同,分别对应了不同的编程需求。

        接口继承是指派生类只继承了基类的接口(也就是纯虚函数),而没有继承基类的实现。这种方式使得派生类必须实现基类中的所有纯虚函数,从而使得派生类和基类的实现是分离的,实现了接口和实现的分离。这种继承方式常常用于实现抽象类和接口,强制要求派生类实现接口中的所有函数。

        实现继承是指派生类继承了基类的接口和实现,包括数据成员和函数实现。这种方式使得派生类可以复用基类的代码,从而减少了代码的重复编写,同时也保证了派生类和基类的一致性。但是,这也意味着派生类和基类的实现是紧密耦合的,基类的修改可能会影响到派生类的行为。

        因此,接口继承和实现继承各有其优缺点,需要根据具体的编程需求来选择合适的继承方式。如果需要实现接口或抽象类,或者需要避免实现的紧密耦合,那么应该选择接口继承;如果需要复用代码,并且基类的实现不会被修改,那么可以考虑使用实现继承。


http://www.mrgr.cn/news/68821.html

相关文章:

  • 【Django】创建应用
  • 显存占用 显存测试
  • 前端零基础学习Day-Seven
  • OpenCV相机标定与3D重建(1)概述
  • 国内 ChatGPT中文版镜像网站整理合集(2024/11/08)
  • GIF图片格式详解(三)
  • 小白docker入门简介
  • 【计网不挂科】计算机网络期末考试(综合)——【选择题&填空题&判断题&简述题】完整试卷
  • AI 大模型重塑软件开发:从代码自动生成到智能测试
  • 深度学习:bert模型
  • C#-里氏替换原则
  • 2.Python解释器
  • 抗辐照CANFD芯片工艺解析:如何保障芯片的可靠性
  • Python操作Excel
  • 1.python介绍、安装
  • _处理匿名命名空间里的变量时进入硬件中断错误
  • Scaffold-ETH 2:颠覆传统开发的区块链神器,快速构建你的去中心化应用!
  • 基于毫米波雷达和TinyML的车内检测、定位与分类
  • Excel快捷键大全
  • LeetCode 二分算法 范围内整数的最大得分
  • JavaScript 网页设计详解教程
  • Liunx:文件fd、重定向、管道
  • UE4.27打包为Html5
  • 我谈维纳(Wiener)去噪滤波器
  • ReactPress:构建高效、灵活、可扩展的开源发布平台
  • C++11新特性