一、导出类

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是有限制的,即不能有静态成员变量。如果有,最好的办法就是不要使用嵌套了。

VC++导入导出类的更多相关文章

  1. MinGW dll导入导出类

    dll不仅可以导入导出函数,还可以导入导出类.这篇文章就来介绍如何将类导入dll中并导出. 首先我们建立一个名为dll.cpp的文件(又是这种破名字),里面写上: #include <iostr ...

  2. 不依赖Excel是否安装的Excel导入导出类

    本文利用第三方开源库NPOI实现Excel97-2003,Excel2007+的数据导入导出操作. 不依赖Office是否安装.NPOI开源项目地址:http://npoi.codeplex.com/ ...

  3. .net core 基于NPOI 的excel导入导出类,支持自定义导出哪些字段,和判断导入是否有失败的记录

    #region 从Excel导入 //用法 //var cellHeader = new Dictionary<string, string>(); //cellHeader.Add(&q ...

  4. C++ DLL导出类 知识大全

    在公司使用C++ 做开发,公司的大拿搭了一个C++的跨平台开发框架.在C++开发领域我还是个新手,有很多知识要学,比如Dll库的开发. 参考了很多这方面的资料,对DLL有一个基本全面的了解.有一个问题 ...

  5. 导入导出Excel工具类ExcelUtil

    前言 前段时间做的分布式集成平台项目中,许多模块都用到了导入导出Excel的功能,于是决定封装一个ExcelUtil类,专门用来处理Excel的导入和导出 本项目的持久化层用的是JPA(底层用hibe ...

  6. NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中

    以下是NPOI导入导出EXCEL通用类,是在别人的代码上进行优化的,兼容xls与xlsx文件格式,供参考,可直接使用在WinForm项目中,由于XSSFWorkbook类型的Write方法限制,Wri ...

  7. 022医疗项目-模块二:药品目录的导入导出-对XSSF导出excel类进行封装

    资源全部来源于传智播客. 好的架构师写的程序,就算给刚入门的新手看,新手一看就知道怎么去用.所以我们要对XSSF导出excel类进行封装.这是架构师的工作,但我们也要知道. 我们写一个封装类: 这个类 ...

  8. 021医疗项目-模块二:药品目录的导入导出-介绍poi类

    我们使用的是.10版本 Apache POI - the Java API for Microsoft Documents,Apache POI 是用Java编写的免费开源的跨平台的 Java API ...

  9. ASP.NET 之 常用类、方法的超级总结,并包含动态的EXCEL导入导出功能,奉上类库源码

    实用类:UtilityClass 包含如下方法 判断对象是否为空或NULL,如果是空或NULL返回true,否则返回false 验证手机号是否正确 13,15,18 验证邮箱 验证网址 MD5加密,返 ...

随机推荐

  1. 关于mysql varchar 类型的最大长度限制

    Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This ...

  2. Luence学习笔记

    1.Luence的核心索引类 IndexWriter:建立索引的核心组件 Directory:代表一个lucene索引项的位置,是一个抽象类其子类有FSDirectory和RAMDirectory F ...

  3. .Net操作.exe文件

    Process proc = new Process(); proc.StartInfo.FileName = @"D:\Program Files\Foxmail\Foxmail.exe& ...

  4. 【leetcode❤python】119. Pascal's Triangle II

    #-*- coding: UTF-8 -*-#杨辉三角返回给定行#方法:自上而下考虑问题,从给定的一行计算出下一行,则给定行数之后,计算的最后一行就是求解的最后一行class Solution(obj ...

  5. 【转载】ODBC, OLEDB, ADO, ADO.Net的演化简史

    原文:ODBC, OLEDB, ADO, ADO.Net的演化简史 1.演变历史 它们是按照这个时间先后的顺序逐步出现的,史前->ODBC->OLEDB->ADO->ADO.N ...

  6. Ajax发送POST请求的心路历程

    好多年前就在项目中用ajax实现了页面部分数据的发送,当时用的是GET方法. 这次用POST方法实现同样的功能,竟然花费了不少的时间! ① 关于JQuery ajax的配置项说明 url : 请求的u ...

  7. 面向对象--类库、委托、is和as运算符、泛型集合

    类库: 其实就是一堆类文件,只是看不到这些类的源代码,保密性好. 优点:保密性好 缺点:如果这个方法不好用,使用者无法自己去更改它. 给的大多是dll文件.使用方法:引用右键,添加引用,浏览,选择到相 ...

  8. javascript 中函数eval()

    eval()函数可以把一个字符串当作一个JavaScript表达式一样去执行它. 我们常常在Javascript中间到Eval这个函数, 有些人觉得这个函数很奇怪,可以把一些字符串变的功能很强大 在我 ...

  9. JAVA排序--[选择排序]

    package com.array; public class Sort_Select { /** * 项目名称:选择排序 ; * 项目要求:用JAVA对数组进行排序,并运用选择排序算法; * 作者: ...

  10. ABAP RFC远程调用

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...