前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Example】C++ 接口(抽象类)概念讲解及例子演示

【Example】C++ 接口(抽象类)概念讲解及例子演示

原创
作者头像
芯片烤电池
修改2022-04-28 10:07:06
1.1K0
修改2022-04-28 10:07:06
举报
文章被收录于专栏:C++教程

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

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

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

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

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

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

首先要讲两个概念:

一、纯虚函数 (Pure Virtual Function):

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

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

代码语言:javascript
复制
virtual void funtion() = 0;

3,纯虚函数只有声明,基类可以存在纯虚函数的实现(但无意义,因为子类都要重写)。

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

 ?

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

 ?

二、抽象类 (Abstract Class):

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

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

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

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

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

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

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

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

演示讲解部分

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

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

演示使用 Visual Studio 2022,其中会涉及智能指针、动态库类的导出、动态库链接等额外知识点,自行 Docs 补充。(一闪而过非常简单)

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

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

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

代码语言:javascript
复制
#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 属性官方解释:Microsoft 专用 dllexport 与 dllimport

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

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

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

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

“人力工具箱”动态库:

HumanToolBox.h 文件内容:

代码语言:javascript
复制
#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 文件内容:(手写了冒泡排序来实现)

代码语言:javascript
复制
#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 文件内容:

代码语言:javascript
复制
#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 文件内容:(调用标准库排序来实现)

代码语言:javascript
复制
#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;
}

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

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

再将 ComputerToolBox 和 HumanToolBox 头文件所在的路径添加到 “附加包含目录”。

【实际开发中建议使用 pIMPL 隐藏 DLL 实现】

CMD_Main.cpp 文件内容:

代码语言:javascript
复制
// 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!

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

芯片烤电池 C++ Example 2022-Spring Season Pass :

【Example】C++ 标准库常用容器全面概述

【Example】C++ 回调函数及 std::function 与 std::bind

【Example】C++ 运算符重载

【Example】C++ 标准库智能指针 unique_ptr 与 shared_ptr

【Example】C++ 接口(抽象类)概念讲解及例子演示

【Example】C++ 虚基类与虚继承 (菱形继承问题)

【Example】C++ Template (模板)概念讲解及编译避坑

【Example】C++ 标准库 std::thread 与 std::mutex

【Example】C++ 标准库多线程同步及数据共享 (std::future 与 std::promise)

【Example】C++ 标准库 std::condition_variable

【Example】C++ 用于编译时封装的 Pimpl 演示 (编译防火墙 Private-IMPL)

【Example】C++ 单例模式 演示代码 (被动模式、兼容VS2022编译)

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档