首页 > 编程语言 >C++代码优化(二): 区分接口继承和实现继承

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

时间:2024-11-10 08:48:05浏览次数:3  
标签:draw 继承 基类 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.总结

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

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

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

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

标签:draw,继承,基类,C++,实现,代码优化,接口,派生类
From: https://blog.csdn.net/haokan123456789/article/details/143635107

相关文章

  • 华为OD机试2024年E卷-MVP争夺战[100分]( Java | Python3 | C++ | C语言 | JsNode | Go
    题目描述在星球争霸篮球赛对抗赛中,最大的宇宙战队希望每个人都能拿到MVP,MVP的条件是单场最高分得分获得者。可以并列所以宇宙战队决定在比赛中尽可能让更多队员上场,并且让所有得分的选手得分都相同,然而比赛过程中的每1分钟的得分都只能由某一个人包揽。输入描述输入第一行......
  • 华为OD机试2024年E卷-AI识别面板[100分]( Java | Python3 | C++ | C语言 | JsNode | Go
    题目描述AI识别到面板上有N(1≤N≤100)个指示灯,灯大小一样,任意两个之间无重叠。由于AI识别误差,每次别到的指示灯位置可能有差异,以4个坐标值描述AI识别的指示灯的大小和位置(左上角x1,y1,右下角x2,y2),请输出先行后列排序的指示灯的编号,排序规则:每次在尚未排序的灯中挑选最高的......
  • 【C++】详细介绍:priority_queue的使用、适配器、deque介绍、仿函数
    目录一、介绍二、使用三、函数模版和类模板的区别四、适配器1、适配器适配栈扩展:2、deque(双端队列)缺省模版五、仿函数一、介绍(1)、priority_queue称为优先级队列,是一种容器适配器,不是队列也不是容器。(2)、该结构的底层是堆结构,默认是大堆,用模版参数来区分是大堆......
  • c++中使用using namespace的命名空间污染问题
    如果 a.h 中使用了 usingnamespaceaa;,并且 b.cpp 包含了 a.h,那么在 b.cpp 中可以直接使用 aa 命名空间中的内容,因为 usingnamespaceaa; 会被一并包含进来。解释usingnamespace 会将指定命名空间中的所有符号引入当前作用域。当 b.cpp 包含 a.h 时,a.h 中......
  • C++和C中的结构体兼容问题Anonymous non-C-compatible type given name for linkage p
    这个编译错误消息指的是,在typedef声明中引入了一个匿名非C兼容类型,编译器为了链接处理给它了一个名字,但它建议添加一个标签名称来显式命名该类型,以便更好地支持C语言的兼容性和跨文件链接。问题原因在C++中,匿名结构体和匿名联合体是合法的,它们可以在没有显式名称的情况下......
  • C++基础学习3
    //3.常量和变量://生活中的一些数据//有些数据不能变:血型、性别、圆周率//有些数据可以变:年龄、身高、体重、工资//在C语言中的描述://变量:能被改变的量//常量:不能被改变的量//intmain()//{// //创建一个变量// //int=类型、age=变量的名字、=是赋值的意思、23是数值// ......
  • c++ 题目4 平方之和
    简单时间限制:1000MS内存限制:128MB分数:100OI排行榜得分:12(0.1*分数+2*难度)描述小杨有 n 个正整数 a1​,a2​,…,an​,他想知道对于所有的 i(1≤i≤n),是否存在两个正整数 x 和 y 满足 x×x+y×y=ai​。输入描述第一行包含一个正整数 n,代表正整数数量。之后 n ......
  • c++中final修饰的使用
    final可以用来修饰类和修饰类的虚函数final修饰类时final修饰的类就不能再被继承了,断绝了后代。classHuawei1{public: Huawei1(){};};classHuawei2final:publicHuawei1{public: Huawei2(){};};classHonor:publicHuawei2{public: Honor(){};};c......
  • C++基础学习2-数据类型
    ////数据类型:////计算机语言-写程序-解决生活中的问题////必须有能力来描述生活中的问题////购物商城-上架商品,价格-15.6元-小数////年龄50岁-整数////C语言-浮点数(小数点)////-整型//////a////'a'-字符a////intmain()//{// //char=字符类型// charch='a';......
  • C++高级编程(9)
    九、STL模板库1.C++函数模板函数模板是一个独立于类型的函数,可产生函数特定类型的版本。通过对参数类型进行参数化,获取有相同形式的函数体。它是一个通用函数,它可适应一定范围内的不同类型对象的操作。函数模板将代表着不同类型的一组函数,它们都使用相同的代码,这样可以......