C++的接口

c++ 的接口类到底是干什么用的?什么叫接口?

打一个比方如抽象一个类"人":class people。人有一些必须要做的事,比如:吃饭、呼吸。
但是每个人吃饭又不一样,比如:用筷子吃(中国人)、用刀叉(欧美人)、用手抓(印度人)。
那么你如果不把“人”(people)这个类做为一个接口(抽象类)。吃就至少要写3个“吃”的重载方法。但一个中国人可能永远也用不到用刀叉吃饭、用手抓饭这样的方法。这就造成写“人”(people)这个类的时候写了两个多余的方法。
所以C++提供了一个叫接口(也就是抽象类)的东西,让你在声明一个“人”这个类的时候,就告诉大家:你们如果想继承我写的这个接口类(抽象类),就必须为“人”(people)这个类的子类写吃饭、呼吸的方法。这样,欧美的朋友继承这个类的时候,他们写“吃”的方法就会用刀叉。而印度的朋友继承这个类的时候,就会手抓。
也就是说c++没有所谓的接口,通过类实现面向对象的编程,而在基类中只给出纯虚函数的声明,然后通过在派生类中实现纯虚函数的具体定义的方式实现接口,不同派生类实现接口的方式也不尽相同,从而实现多态;其实无论c#还是Java,里面的interface的本质都是类。
现通过一个简单的实例讲解具体实现步骤。

定义、实现接口并将其导出为可用的动态库

首先创建动态链接库(DLL),文件->新建->项目->选择Visual C++ ->Windows桌面->动态链接库

先来区分一下静态库和动态库:
静态库 window下是 .lib格式 linux下是 .a格式 编译期引入到程序代码
动态库 window下是 .dll格式 linux下是 .so格式 运行时候进行链接

DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件
DLL有以下几个优点:

  1. 节省内存。同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中。如果使用dll,则只在内存中加载一次,所有使用该dll的进程会共享此块内存(当然,像dll中的全局变量这种东西是会被每个进程复制一份的)。
  2. 不需编译的软件系统升级,若一个软件系统使用了dll,则该dll被改变(函数名不变)时,系统升级只需要更换此dll即可,不需要重新编译整个系统。事实上,很多软件都是以这种方式升级的。例如我们经常玩的星际、魔兽等游戏也是这样进行版本升级的。
  3. Dll库可以供多种编程语言使用,例如用c编写的dll可以在vb中调用。这一点上DLL还做得很不够,因此在dll的基础上发明了COM技术,更好的解决了一系列问题。
类接口的定义(通常在头文件中完成类接口的定义) /InterfaceDefineAndRealize.h/
#ifndef INTERFACE_DEFINE_AND_REALIZE  
#define INTERFACE_DEFINE_AND_REALIZE  
#include <string>  
using namespace std; 
//define interface  
class Person  
{  
public:  
    Person():m_StrName("###") {}; //成员列表初始化参数  
    virtual ~Person(){};        //析构函数
    virtual void Eat()=0;//人需要吃东西  
    virtual void Sleep()=0;//人需要睡觉  
    virtual void SetName(const string strName)=0;//人都有名字  
    virtual string GetName()=0;//获取名字  
    virtual void Work()=0;//人可能要有工作  
private:  
    string m_StrName;  
};  
//实现接口  
//实现接口是通过派生类实现的,每个派生类依据自身特点,可以获取同一接口的不同实现  
//也就是所谓的多态  
class Chinese:public Person  
{  
public:  
    Chinese():m_strName("***")  
    {};  
    ~Chinese()  
    {};  
    void Eat();  
    void Sleep();  
    void SetName(const string strName);  
    string GetName();  
    void Work();  
private:  
    string m_strName;  
};  
#endif  

#ifndef(if not define)的作用防止头文件的重复包含和编译
如定义
  #ifndef x
  #define x
  …
  #endif
它是宏定义的一种,可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等。实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的一种条件编译

接口的实现 (通常在源文件中完成接口的实现)/InterfaceDefineAndRealize.cpp/
#include "InterfaceDefineAndRealize.h"  
#include <iostream>  
#include <string>  
using namespace std;
  
//接口的外部实现  
void Chinese::Sleep()  
{  
    cout<<"Chinese sleep."<<endl;  
}  
void Chinese::Eat()  
{  
    cout<<"Chinese eat."<<endl;  
}  
void Chinese::SetName(const string strName)  
{  
    m_strName=strName;  
}  
void Chinese::Work()  
{  
    cout<<"Chinese work."<<endl;  
}  
string Chinese::GetName()  
{  
    return m_strName;  
}  
//需要导出的函数,即用户在外部可以调用的接口  
_declspec(dllexport)bool GetPersonObject(void** _RtObject)  
{  
    Person* pMan=NULL;  
    pMan=new Chinese();  
    *_RtObject=(void*)pMan;  
    return true;  
}  
接口的导出 通常在模块定义文件中完成 /InterfaceDefineAndRealize.def/

源文件下添加.def文件

LIBRARY InterfaceDefineAndRealize  
EXPORTS  
GetPersonObject  
生成解决方案

可以在项目文件夹的Debug目录下看到dll和lib文件。lib是我们写在程序中添加的文件,dll是我们在生成程序之后要使用的文件。

测试DLL

新建一个控制台应用程序,加载上述三个文件,设置项目属性—>配置属性——>常规——>配置类型 ,选择"动态库.dlll",生成可用的动态库,假如项目名称为InterfaceDefineAndRealize(注意:项目名称必须与模块定义文件中 LIBRARY 后面定义的名字相同,否则将导致出现无法找到动态库的错误。),则在该项目的当前工作目录下位生成动态库和它的导入库。

接口的调用

为了与常规的调用动态库的方式保持一致,这里做一些额外工作。新建“include”文件夹,并将InterfaceDefineAndRealize.h放到此文件夹下,新建“lib”文件夹并将InterfaceDefineAndRealize.lib文件放到此文件夹下。新建项目UsingInterface,添加源文件实现调用接口的功能。

1)为项目添加附加包含目录

方法1:项目属性——>配置属性——>C/C++——>常规——>附加包含目录 将include文件夹的全路径添加进来。
方法2:项目属性——>配置属性——>VC++目录——>包含目录 中将include文件夹的全路径添加进来。

2)为项目添加附加库

方法1:项目属性——>配置属性——>链接器——>常规——>附加库目录 将lib文件夹的全路径添加进来。
方法2:项目属性——>配置属性——>VC++目录——>库目录 将lib文件夹的全路径添加进来。
注意:1)中的方法1与2)中的方法1对应,1)中的方法2与2)中的方法2对应,不能交换使用。

3)为项目添加导入库

项目属性——>配置属性——>链接器——>输入——>附加依赖项 中添加InterfaceDefineAndRealize.lib

4)为项目提供动态库

将生成的.dll动态库放到项目的当前目录下的Debug目录下,防止出现缺少动态库的错误。(没有这个目录就先生成解决方案,有了Debug目录后再放文件进来,一切准备好以后再重新生成解决方案)

5)编写代码,实现接口的调用
#include <iostream>  
#include "InterfaceDefineAndRealize.h"  
using namespace std;
bool _declspec(dllimport) GetPersonObject(void** _RtObject);  
int main()  
{  
    Person* person=NULL;  
    void* pObj=NULL;  
    if(GetPersonObject(&pObj))//调用接口  
    {  
        person=(Person*)pObj;  
        person->Eat();  
        person->Sleep();  
        person->SetName("yuan mw");  
        cout<<person->GetName()<<endl;  
        person->Work();  
        if(person!=NULL)  
        {  
            delete person;  
            person=NULL;  
        }  
    }  
    system("pause");  
    return 0;  
}  
运行结果

在这里插入图片描述
在此特别感谢CSDN的众位大侠:chuanshaoke、shimadear、无力吐槽的典哥等人

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值