• VC++导入导出类


    一、导出类

    VC++中导出类很简单,下面列出了两个等价的方法:

    方法1:

    class __declspec(dllexport) CTest

    {

    public:

    int        m_nValue;

    CObj    m_obj;

    };

    方法2:

    class __declspec(dllexport) CTest;        //类声明,说明是一个导出类

    class CTest

    {

    public:

    int        m_nValue;

    CObj    m_obj;

    };

    注意:方法2的类声明必须放在 class CTest 的前面,最好放在预编译头文件 StdAfx.h 里。

    使用方法1比较麻烦,要导出一个类还得修改类定义;方法2就比较方便了,可以将要导出的类声明集中放在 StdAfx.h 里,方便维护。

    需要注意以下几个问题:

    1、成员对象所属类也需要被导出

    m_obj是CTest的成员对象,它所属的类CObj也需要被导出。否则编译的时候会产生警告,客户程序可能无法正常构造CTest类(Debug版正常,Release版分配内存但不调用构造函数)。如果导出CObj比较困难,如它是一个模板类,则应该将CObj m_obj更改为CObj*m_obj;

    2、内联成员函数

    内联函数相当于宏,编译的时候用来替换源代码,用以提高效率。一般它是不会被编译成目标代码的,但是一旦使用了__declspec(dllexport),编译程序将会为其生成一份目标代码,客户程序调用内联成员函数时,可能直接调用目标代码,此时函数将不再是内联的了。当然,导入类时也可以控制客户程序,使其使用内联的成员函数。

    3、友元函数

    需要注意的是,上述两个方法均不能导出类的友元函数。要导出友元函数,必须专门声明。方法1、2的改进方案为:

    方法1:

    class __declspec(dllexport) CTest

    {

    public:

    int m_nValue;

    public:

    //导出友元函数要专门声明

    friend __declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

    };

    方法2:

    class __declspec(dllexport) CTest;

    //导出友元函数要专门声明

    __declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

    class CTest

    {

    public:

    int m_nValue;

    public:

    friend CTest operator+(const CTest&a,const CTest&b);

    };

    4、嵌套类

    要导出嵌套类,方法1的改进方案为:

    class __declspec(dllexport) CTest

    {

    public:

    class __declspec(dllexport) CNest    //前面也要使用__declspec(dllexport)

    {

    }

    };

    方法2无法导出嵌套类,因为不能按下面的语法进行嵌套类声明:

    class __declspec(dllexport) CTest::CNest;    //这样声明是错误的

    此时,解决方案可能只有使用DEF文件了。

    二、使用DEF文件导出类

    按下图设置,使得编译时生成map文件。

    编译如下代码

    class __declspec(dllexport) CTest

    {

    public:

    void        SetValue(int v)        {m_Value = v;}

    int            GetValue();

    private:

    int            m_Value;

    public:

    static int    s_nValue;

    };

    int CTest::s_nValue = 1;

    int CTest::GetValue()                {return m_Value;}

    查看 map 文件,提取包含 CTest 的函数或变量:

    Address 

    Publics by Value 

    Rva+Base 

    Lib:Object 

    0001:00000030

    ?SetValue@CTest@@QAEXH@Z 

    10001030 f i 

    tDLL.obj 

    0001:00000070 

    ??4CTest@@QAEAAV0@ABV0@@Z 

    10001070 f i 

    tDLL.obj 

    0001:000000b0 

    ?GetValue@CTest@@QAEHXZ 

    100010b0 f 

    tDLL.obj 

    0003:00000a30 

    ?s_nValue@CTest@@2HA 

    1002ba30 

    tDLL.obj 

    注意上表的第三列,f表示函数,i表示内联。如果将 class __declspec(dllexport) CTest 中的 __declspec(dllexport) 去掉,重新编译,则上表第三列包含 f i 的在map文件中不会再出现。

    内联函数没有目标代码,所以无法通过 DEF 导出。只能通过DEF文件导出非内联的成员函数,如:下面的DEF文件内容导出了CTest::GetValue函数。

    EXPORTS

    ?GetValue@CTest@@QAEHXZ

    ?s_nValue@CTest@@2HA对应着CTest::s_nValue,它应该被导出,但是使用DEF导出变量需要注意:客户程序导入该变量时,只能导入该变量的地址。

    三、导入类

    导出类到动态链接库的同时,就生成了库文件(*.lib)。VC++很智能,客户程序包含这个库文件即可完成类的导入。要特别注意这种导入的内联成员函数。下面的代码中,客户程序调用内联成员函数GetValue时,将不会使用导出类的GetValue目标代码,而是使用类声明中的代码。此时,GetValue还是内联函数。

    class CTest

    {

    public:

    int m_nValue;

    public:

    int GetValue()

    {

    return m_nValue;

    }

    };

    如果不想客户程序使用类声明中的代码,有两种方法:

    1、删除内联函数GetValue的实现代码,仅仅保留函数声明;

    2、使用__declspec(dllimport),如下所示:

    class __declspec(dllimport) CTest

    {

    public:

    int m_nValue;

    public:

    int GetValue()

    {

    return m_nValue;

    }

    };

    或在 StdAfx.h 中,增加class __declspec(dllimport) CTest;这句类声明。使用__declspec(dllimport)声明类之后,友元函数就不再需要__declspec(dllimport)声明了。

    四、总结

    1、导出类有两种方法,一种是使用 __declspec(dllexport);另一种方法是使用DEF文件,该方法不能导出内联函数,导出类的静态成员变量也较为不便;

    2、友元函数不属于类,要导出的话必须使用 __declspec(dllexport) 声明,或在 DEF 文件中导出;

    3、导入类可以使用 __declspec(dllimport),也可以不使用。前者将取消内联成员函数的内联性质,后者不会;

    应用实例:

    下面是类CTest的定义部分,在 Test.h 里

    class CTest

    {

    public:

    class CNest

    {

    public:

    void SetValue(int v);

    int GetValue();

    friend CNest operator+(const CNest&a,const CNest&b);

    private:

    int m_nValue;

    };

    public:

    void SetValue(int v);

    int GetValue();

    friend CTest operator+(const CTest&a,const CTest&b);

    private:

    int m_nValue;

    public:

    static int s_nValue;

    };

    下面是类CTest的实现部分,在Test.cpp里

    //CTest-------------------------------------------------------------------------

    int CTest::s_nValue = 1;

    void CTest::SetValue(int v)

    {

    m_nValue = v;

    }

    int CTest::GetValue()

    {

    return m_nValue;

    }

    CTest operator+(const CTest&a,const CTest&b)

    {

    CTest c;

    c.m_nValue = a.m_nValue + b.m_nValue;

    return c;

    }

    //CTest::CNest------------------------------------------------------------------

    void CTest::CNest::SetValue(int v)

    {

    m_nValue = v;

    }

    int CTest::CNest::GetValue()

    {

    return m_nValue;

    }

    CTest::CNest operator+(const CTest::CNest&a,const CTest::CNest&b)

    {

    CTest::CNest c;

    c.m_nValue = a.m_nValue + b.m_nValue;

    return c;

    }

    导出 CTest 类及其友元函数,只需在 StdAfx.h 里增加如下代码:

    class __declspec(dllexport) CTest;

    __declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

    导出CTest::CNest的成员函数稍微麻烦些,需要在DEF文件中导出:

    EXPORTS

    ?SetValue@CNest@CTest@@QAEXH@Z

    ?GetValue@CNest@CTest@@QAEHXZ

    ??H@YA?AVCNest@CTest@@ABV01@0@Z

    客户程序导入CTest类,只需在 StdAfx.h 里增加如下代码:

    class __declspec(dllimport) CTest;

    //有了上面的声明,CTest的友元函数就不再需要特别声明了

    //下面这句话反而会引起警告

    __declspec(dllimport) CTest operator+(const CTest&a,const CTest&b);

    客户程序对 CTest::CNest 的导入无需特别声明,VC++编译器能够自动完成。但是,它对CNest是有限制的,即不能有静态成员变量。如果有,最好的办法就是不要使用嵌套了。

  • 相关阅读:
    背景样式、列表样式、变形样式、过渡动画
    边框样式、段落样式、背景样式
    属性选择符、字体样式和元素样式
    Targets选项下Other linker flags的设置
    OC金额转大写
    输入手机号码 和 金额有效性的判断
    iOS手势冲突问题
    解决iOS手势冲突问题
    iOS开发 字符串的转化 小技巧
    iOS开发添加pch文件
  • 原文地址:https://www.cnblogs.com/hanford/p/6177883.html
Copyright © 2020-2023  润新知