• 【Example】C++ 接口概念讲解及例子演示


    C++ 和 Java 不同的是,C++ 没有 interface 关键字。对于很多新手来说,C++ 当中接口的概念不容易像 Java 当中那样被理解。

    然而接口是面向对象编程当中的重要组成部分,也是新手需要学习的重要思维,虽然 C++ 并不那么面向对象

    首先,要明确接口的概念:

    接口的存在意义是为不同的派生类提供统一的标准,继而实现面向对象编程当中的多态概念。

    对象是对客观事物的抽象,类是对对象的抽象。

    那么,C++ 当中既然不存在 interface 关键字,那么接口是通过什么方式来实现的呢?

    首先要讲两个概念:

    一、纯虚函数 (Pure Virtual Function)

    1,纯虚函数只有函数名、参数、返回值类型。

    2,纯虚函数的定义是在函数句首使用 virtual 关键字修饰,并且在句末增加 "= 0"。

    virtual void funtion() = 0;

    3,纯虚函数不能包含实现,只有声明,所以纯虚函数不能被调用。

    4,定义纯虚函数的目的在于,使每一个派生类都拥有相同的函数规范。

     

    承上启下:包含纯虚函数的类就是抽象类。

     

    二、抽象类 (Abstract Class)

    1,抽象类必须包含一个纯虚函数,存在纯虚函数的类就一定是一个抽象类。

    2,抽象类不能被实例化,只能被继承派生,因为纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。

    3,派生出来的子类必须实现所有抽象类当中的纯虚函数。

    4,抽象类默认存在隐式的构造函数,但是不能将构造函数定义为纯虚函数。(因为无意义)

    5,析构函数可以定义成纯虚函数,相应的派生类也要给出析构函数的实现。

    6,如果派生出来的子类没有实现父抽象类当中的所有纯虚函数,那子类仍然是抽象类。

    所以,特点就很显而易见了,有这么一个“类”,它不能被实例化,只能被继承,而继承它的派生类必须要重写它声明的所有函数。

    这就是接口的概念,为所有派生类提供了一个统一的规范可以实现多态。

    ====================================

    演示讲解部分

    这里的 Demo 不像高校老师或培训机构那样随便写几个类来演示了,而是选择一个更贴合实际开发的场景:

    定义一个接口(抽象类),两个动态库去继承并分别采用不同实现,最后在 main 函数中执行通过多态获得不同效果。

    演示使用 Visual Studio 2022,其中会涉及智能指针、动态库类的导出、动态库链接等额外知识点,自行 MSDN 补充。

    Visual Studio 是一个解决方案包含N个项目,这个 Demo 的结构就是接口作为一个项目、两个动态库两个项目、演示执行的EXE一个项目。

    ------------------------------------

    新建第一个项目,创建接口头文件:BrainToolBoxInterface.h

    文件中有一个叫“大脑工具箱”的抽象类,它拥有两个纯虚函数作为统一的接口。

    #pragma once
    
    #include <vector>
    using std::vector;
    
    // 定义统一的 DLL 导出宏 
    #define BrainToolBoxDLL_EXPORTS
    
    // 定义接口
    class BrainToolBoxInterface
    {
    public:
    
        // 对 Vector 进行排序的接口
        virtual void SortVector(std::vector<int>& vec) = 0;
    
        // 说自己是人脑还是电脑
        virtual void SelfIntroduction() = 0;
    
    };

    ------------------------------------

    新建第二和第三个项目:两个动态库

    分别叫 “人力工具箱” 和 “电脑工具箱”

    其中两个项目的 dllmain、phc、framework完全一致,并且VS会自动帮你创建。

    但是要注意,两个项目新建后,要将接口头文件所在的路径添加到 “附加包含目录”。

    补充 DLL 导出相关知识(重要):

    dllexport 与 dllimport 属性官方解释:https://docs.microsoft.com/zh-cn/cpp/cpp/dllexport-dllimport?view=msvc-170

    注意两个属性必须搭配 _declspec() 关键字使用。

    我们的两个动态库是给外部程序调用的,所以应使用 _declspec(dllexport),表明类可以被外部所使用。

    注意:[摘自MSDN] 不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

    “人力工具箱”动态库:

    HumanToolBox.h

    #pragma once
    
    #include "BrainToolBoxInterface.h"
    
    // 配置类的导出
    #ifdef BrainToolBoxDLL_EXPORTS
    #define BrainToolBoxDLL __declspec(dllexport)
    #else
    #define BrainToolBoxDLL __declspec(dllimport)
    #endif
    
    class BrainToolBoxDLL HumanToolBox : public BrainToolBoxInterface
    {
    public:
        // 构造
        HumanToolBox();
    
        // 析构
        ~HumanToolBox();
    
        // 对 Vector 进行排序
        void SortVector(std::vector<int>& vec);
    
        // 自我介绍
        void SelfIntroduction();
    
    };

    HumanToolBox.cpp

    #include "pch.h"
    #include "HumanToolBox.h"
    
    #include <iostream>
    
    HumanToolBox::HumanToolBox()
    {
    }
    
    HumanToolBox::~HumanToolBox()
    {
    }
    
    void HumanToolBox::SortVector(std::vector<int>& vec)
    {
        int vec_size = vec.size();
        for (size_t i = 0; i < vec_size; i++)
        {
            int trend_size = vec_size - i - 1;
            for (size_t j = 0; j < trend_size; j++)
            {
                if (vec[j] > vec[j + 1])
                {
                    int k = vec[j];
                    vec[j] = vec[j + 1];
                    vec[j + 1] = k;
                }
            }
        }
    
        return;
    }
    
    void HumanToolBox::SelfIntroduction()
    {
        std::cout << "I am super man!" << std::endl;
        return;
    }

    “电脑工具箱”动态库:

    ComputerToolBox.h

    #pragma once
    
    #include "BrainToolBoxInterface.h"
    
    // 配置类的导出
    #ifdef BrainToolBoxDLL_EXPORTS
    #define BrainToolBoxDLL __declspec(dllexport)
    #else
    #define BrainToolBoxDLL __declspec(dllimport)
    #endif
    
    class BrainToolBoxDLL ComputerToolBox : public BrainToolBoxInterface
    {
    public:
        // 构造
        ComputerToolBox();
    
        // 析构
        ~ComputerToolBox();
    
        // 对 Vector 进行排序
        void SortVector(std::vector<int>& vec);
    
        // 自我介绍
        void SelfIntroduction();
    
    };

    ComputerToolBox.cpp

    #include "pch.h"
    #include "ComputerToolBox.h"
    
    #include <iostream>
    #include <algorithm>
    
    ComputerToolBox::ComputerToolBox()
    {
    }
    
    ComputerToolBox::~ComputerToolBox()
    {
    }
    
    void ComputerToolBox::SortVector(std::vector<int>& vec)
    {
        std::sort(vec.begin(), vec.end());
        return;
    }
    
    void ComputerToolBox::SelfIntroduction()
    {
        std::cout << "I am byte!" << std::endl;
        return;
    }

    可以看到,“人力工具箱” 和 “电脑工具箱” 使用了共同的 BrainToolBoxInterface 接口,但是实现完全不同。

    ------------------------------------

    新建第四个项目:调用两个DLL的可执行文件

    在项目引用当中添加 ComputerToolBox 和 HumanToolBox。

    将 ComputerToolBox 和 HumanToolBox 头文件所在的路径添加到 “附加包含目录”。【实际开发中建议使用 pIMPL 隐藏 DLL 实现】

    CMD_Main.cpp

    // CMD_Main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include <iostream>
    #include <memory>
    using std::shared_ptr;
    using std::make_shared;
    
    #include "BrainToolBoxInterface.h"
    #include "ComputerToolBox.h"
    #include "HumanToolBox.h"
    
    int main()
    {
        shared_ptr<BrainToolBoxInterface> cr = make_shared<ComputerToolBox>();
        shared_ptr<BrainToolBoxInterface> hr = make_shared<HumanToolBox>();
    
        cr->SelfIntroduction();
        hr->SelfIntroduction();
    
        return EXIT_SUCCESS;
    }

    ------------------------------------

    最终运行效果:

    I am byte!
    I am super man!
    
    D:\Repos\AirChip\Demo\CPP_Interface\x64\Debug\CMD_Main.exe (进程 9516)已退出,代码为 0。

  • 相关阅读:
    Tomcat 7 的七大新特性 悟寰轩
    MapReduce提交作业常见问题 悟寰轩
    [MQ]关于ActiveMQ的配置 悟寰轩
    LinkedBlockingQueue应用实例 悟寰轩
    什么是java序列化,如何实现java序列化? 悟寰轩
    远程注入DLL的例子
    回调函数实例
    将网页保存为Stream
    删除文件或文件夹
    选择目录
  • 原文地址:https://www.cnblogs.com/airchip/p/15881339.html
Copyright © 2020-2023  润新知