【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。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM