zoukankan      html  css  js  c++  java
  • DLL接口的实现(虚函数)

    DLL接口的实现(虚函数)

           我们在c++编程过程中往往要用到各种不同形式的程序库,这些库的发布方式有动态库和静态库。对于静态类库,设计良好的静态类库能实现功能上的隔离,无法避免类库实现必须重新编译、链接整个应用程序的问题。而调用各种DLL动态库成为我们程序员的家常便饭。

          以什么方式暴露库的接口?可选的做法有:以全局(含 namespace 级别)函数为接口、以 class 的 non-virtual 成员函数为接口、以 virtual 函数为接口(interface)。本文主要讲虚函数实现DLL接口和COM组件。

          虚函数实现接口的做法是定义一个头文件,将类实现的接口声明,可以将这个类的成员函数暴露给客户。而接口的实现部分是在派生类中,用户获得头文件中的接口,无需接口的实现就可以调用接口。往往我们会将这个接口类的成员定义为纯虚函数,这样更直观的体现派生类必须完成对接口的实现部分。

    以下是代码实例:

    接口头文件 IPerson.h

    #include<iostream>
    #include<string>
    using namespace std;
    
    #ifdef _EXPORTING
    #define CLASS_DECLSPEC __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC __declspec(dllimport)
    #endif
    
    class IPerson
    {
    public:
    	IPerson(){};
    
    	//接口
    	virtual void  SetName(const string &strName) = 0;
    	virtual const string GetName() = 0;
    	virtual void  Work() = 0;
    };
    

     declspec(dllexport)用于导出符号,也就是定义该函数的dll;__declspec(dllimport)用于导入,也就是使用该函数。因为这个头文件既要被定义该函数的dll包含,也要被使用该函数的程序包含,当被前者包含时我们希望使用__declspec(dllexport)定义函数,当被后者包含时我们希望使用dllimport。于是我们使用

             #ifdef _EXPORTING

             #define CLASS_DECLSPEC __declspec(dllexport)

             #else

             #define CLASS_DECLSPEC __declspec(dllimport)

             #endif

            这种技巧,在定义该函数的dll中,其编译选项定义了_EXPORTING而使用该函数的程序则没有定义。反正我们的头文件是要暴露给用户的,这样定义可以保持DLL库头文件和用户库头文件保持一致。

    接口实现部分:CTeacher.cpp

    #include "IPerson.h"
    
    //定义接口
    CLASS_DECLSPEC bool GetIPersonObject(void** _RtObject);
    
    class CTeacher:public IPerson
    {
    public:
        CTeacher(){}
        //接口实现
         void  SetName(const string &strName);
        const string GetName();
        void  Work();
    private:
        string m_strName;
    };
    
    void CTeacher::SetName(const string &strName)
    {
        m_strName = strName;
    } 
    const string CTeacher::GetName()
    {
        return m_strName;
    }
    void CTeacher::Work()
    {
        cout<<"I am teaching!"<<endl;
    }
    
    bool GetIPersonObject(void **_RtObject)
    {
        IPerson* pMan = NULL;
        pMan =  new CTeacher();
        *_RtObject = (void*)pMan;
        return true;
    }
    

     这样,我们在工程->属性->配置属性->常规->配置类型 中选择 动态库(.dll)运行就可以生成对应的.dll和.lib。我们在客户程序中就可以使用这个dll。具体做法如下:1.程序中包含接口的头文件,即 IPerson.h      2.包含lib,在 工程->属性->配置属性->VC++目录->库目录中加入.lib的文件目录,在工程->属性->配置属性->连接器->输入->附加依赖项中加入.lib  。

    #include "IPerson.h"
    
    #pragma comment(lib,"InterFace.lib")
    
    //导出接口
    bool CLASS_DECLSPEC GetIPersonObject(void** _RtObject);
    
    void main()
    {
    	IPerson * _IPersonObj = NULL;
    	void* pObj=NULL;
    	if (GetIPersonObject(&pObj))        
    	{
    		// 获取对象
    		_IPersonObj = (IPerson *)pObj;
    		// 调用接口,执行操作
    		_IPersonObj->SetName("Tom");
    			string strName = _IPersonObj->GetName();
    		_IPersonObj->Work();
    	}
    	if (_IPersonObj !=NULL)
    	{
    		delete _IPersonObj ;
    		_IPersonObj  = NULL;
    	}
    }
    

      到这里,一个简单的虚函数实现DLL接口就做完了。有一点必须要说,设计库的时候C++ 虚函数为接口是有弊端的。“一旦发布,不能修改”。a)函数重名问题:我们通过函数名来调用DLL的函数,在并行开发中 容易造成函数重名。b)依赖:如果采用常见的隐式连接,那DLL每发行了一个新版本都有 必要和应用程重新链接一次,因为DLL里面函数的地址可能已经发生了改变。

            COM组件的思想正好解决了上述问题,下一节讲COM组件思想的简单实现。

    https://blog.csdn.net/u011599942/article/details/11195667

  • 相关阅读:
    动态规划--数字三角形问题
    C#操作Office.word(一)
    【算法参考】动态规划
    数据结构--图的定义和存储结构
    重定向输入输出流--freopen
    C++内联函数、函数模板之于头文件
    C++静态局部对象
    C++函数模板
    条款05:了解C++默默编写并调用哪些函数
    设计模式 之 装饰器模式
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/4669029.html
Copyright © 2011-2022 走看看