看zoom的win_sdk时,看到很多类在定义时,class和类名中间有一个DUILIB_API,形如:   class DUILIB_API CWindowWnd

好奇之后查资料,发现DUILIB_API被展开为:

#ifdef UILIB_STATIC
#   define DUILIB_API
#else
#   if defined(UILIB_EXPORTS)
#       if  defined(_MSC_VER)
#           define DUILIB_API __declspec(dllexport)
#       else
#           define DUILIB_API
#       endif
#   else
#       if defined(_MSC_VER)
#           define DUILIB_API __declspec(dllimport)
#       else
#           define DUILIB_API
#       endif
#   endif
#endif

然后去查了一下,找到微软的官方说明1/2:

————————————————————————————————————————————————————————

在 C++ 类中使用 dllimport 和 dllexport

可以使用 dllimport 或 dllexport 特性来声明 C++ 类。 这些形式表示已导入或导出整个类。 以这种方式导出的类称为可导出类。

dllexport 类

在声明类 dllexport 时,所有其成员函数和静态数据成员都将导出。 您必须在同一程序中提供所有此类成员的定义。 否则,将生成链接器错误。 此规则有一个例外情况,即对于纯虚函数,您无需为其提供显式定义。 但是,由于基类的析构函数始终在调用抽象类的析构函数,因此纯虚拟析构函数必须始终提供定义。 请注意,这些规则对不可导出的类是相同的。

如果导出类类型的数据或返回类的函数,请务必导出类。

dllimport 类

当声明类 dllimport 时,将导入所有其成员函数和静态数据成员。 与非类类型上的 dllimport 和 dllexport 的行为不同,静态数据成员无法在定义 dllimport 类的同一程序中指定定义。

继承和可导出类

可导出类的所有基类都必须是可导出的。 否则,会生成编译器警告。 此外,同样是类的所有可访问成员必须是可导出的。 此规则只允许 dllexport 类从 dllimport 类继承,dllimport 类从 dllexport 类继承(但不建议后一种方式)。 通常来说,对 DLL 客户端可访问的所有内容(根据 C++ 访问规则)都应该是可导出接口的一部分。 这包括在内联函数中引用的私有数据成员。

选择性成员导入/导出

由于类中的成员函数和静态数据隐式包含了外部链接,因此您可以使用 dllimport 或 dllexport 特性声明它们,除非整个类都已导出。 如果整个类都已导入或导出,则禁止将成员函数和数据显式声明为 dllimport 或 dllexport。 如果在类定义中将静态数据成员声明为 dllexport,定义一定会在同一程序中的某处出现(就像非类外部链接一样)。

同样,您可以使用 dllimport 或 dllexport 特性声明成员函数。 在这种情况下,您必须在同一程序中的某处提供 dllexport 定义。

有关选择性成员导入和导出的某些要点值得注意:

  • 选择性成员导入/导出最适合用于提供具有更强限制的导出类接口版本;即,您可以为该版本设计一个 DLL,该 DLL 公开的公用和专用功能比本应允许的语言公开的更少。 这对于优化可导出接口也很有用:当通过定义知道客户端无法访问某些私有数据时,您不需要导出整个类。

  • 如果导出了某个类中的一个虚函数,则必须导出其中的所有虚函数,或者至少必须提供客户端可直接使用的版本。

  • 如果有在其中将选择性成员导入/导出用于虚函数的类,则这些函数必须在可导出接口或已定义内联中(对客户端可见)。

  • 如果将某个成员定义为 dllexport,但不在类定义中包含它,则会产生编译器错误。 必须在类头中定义成员。

  • 尽管允许将类成员定义为 dllimport 或 dllexport,但无法重写在类定义中指定的接口。

  • 如果在声明成员函数的类定义的主体以外的地方定义成员函数,并且将函数定义为 dllexport 或 dllimport(如果此定义不同于类声明中指定的定义),则会生成警告。

————————————————————————————————————————————————————————

程序加载一个dll时,程序运行在两个独立空间的(dll的空间和程序空间),dll的对象模型其实相当严格,要访问dll空间的变量和函数,必须导出他们,否则这些对象是不可见的。这可以通过加入一个def文件,或者在声明中使用__declspec(dllimport)前缀,告诉编译器以下这些变量和函数是从dll导出的。同时定义这些变量的dll源文件必须加上__declspec(dllexport)前缀,告诉编译器这些函数需要被导出。 

对类对象来说,静态成员和函数必须加上这个前缀,因为这些对象都是在dll空间内的。在类的前面加上这些前缀就对整个类的成员进行了声明。

下面对之前的代码做解析:

#ifdef UILIB_STATIC //如果是静态UI库
#   define DUILIB_API //不需要动态链接,定义为空串
#else
#   if defined(UILIB_EXPORTS)//需要导出UI库
#       if  defined(_MSC_VER)//如果是微软的编译器(定义了版本号)
#           define DUILIB_API __declspec(dllexport)//定义为导出
#       else
#           define DUILIB_API //非微软编译器,空串
#       endif
#   else//不需要导出UI库
#       if defined(_MSC_VER)//如果是微软的编译器(定义了版本号)
#           define DUILIB_API __declspec(dllimport)//定义为导入
#       else
#           define DUILIB_API //非微软编译器,空串
#       endif
#   endif
#endif

对库作者而言,将类封装为动态库时,需要导出,会写在class _declspec(dllexport) A{};把这个类声明放在A.h文件中,库文件为A.dll。

对用户而言,为了在程序中使用A.dll动态库,需要导入,需要将A.h中关于类A的声明改为class _declspec(dllimport) A{};当库中有多个类时,替换非常麻烦,故用这种方式定义。

总结:定义动态库的时候是要考虑暴露接口(函数/类等)让外部用,所以用export,后续其他程序使用动态库时,是要导入接口,用import。

类定义中class+宏+类名的意义相关推荐

  1. #与##在宏定义中的--宏展开

    #与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int m ...

  2. python 类中定义列表_Python-从类定义中的列表理解访问类变量

    小编典典 类范围和列表,集合或字典的理解以及生成器表达式不混合. 为什么:或者,官方用词 在Python 3中,为列表理解赋予了它们自己的适当范围(本地名称空间),以防止其局部变量渗入周围的范围内(即 ...

  3. python类定义中__init__()_转:python学习——类中为什么要定义__init__()方法

    学习Python的类,一直不太理解为什么一定要定义init()方法,现在简要谈一下自己的理解吧. 1.不用init()方法定义类 定义一个矩形的类,目的是求周长和面积. 1 classRectangl ...

  4. 详解Python类定义中的各种方法

    首先应该明确,在面向对象程序设计中,函数和方法这两个概念是有本质区别的.方法一般指与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为第一个参数传递过去,普通函数并不具备这个特点. >&g ...

  5. python类定义中、对象字符串的特殊方法是_python中自定义类对象json字符串化的方法_python json转字符串、...

    python中自定义类对象json字符串化的方法 1. 用 json 或者simplejson 就可以 2.定义转换函数: def convert_to_builtin_type(obj): prin ...

  6. python类定义中__init__(),在__init__中定义一个成员以在python中的类体中定义它的区别?...

    What is the difference between doing class a: def __init__(self): self.val=1 to doing class a: val=1 ...

  7. VC宏定义 及常用宏定义说明

    1. 宏定义的格式 宏定义的一般格式是: #define  标识符  字符串 其中,标识符和字符串之间用空格隔开.标识符又称宏名,为了区别于一般变量,通常用英文大写字母表示:字符串又称宏体,可以是常 ...

  8. VC/MFC中常用宏的含义

    VC/MFC中常用宏的含义 Visual C++ MFC 中常用宏的含义(转载)     AND_CATCHAND_CATCH AND_CATCH(exception_class,exception ...

  9. C++ 笔记(16)— 类和对象(类定义、类实例对象定义、访问类成员、类成员函数、类 public/private/protected 成员、类对象引用和指针)

    1. 类的定义 类定义是以关键字 class 开头,后跟类的名称.并在它后面依次包含类名,一组放在 {} 内的成员属性和成员函数,以及结尾的分号. 类声明将类本身及其属性告诉编译器.类声明本身并不能改 ...

最新文章

  1. 分摊的意思_接了两单顺风车遇到几个有意思乘客,后来把顺风车软件删了
  2. python实习生面试题_大数据分析实习生面试题库
  3. mui请求php,PHP 怎样处理mui.ajax POST过来的数据?
  4. python正则匹配ip_[求助] 正则表达式匹配 IP 地址的问题求助
  5. tpl-spring-mybatis 模板工程
  6. Go WebSocket开发与测试实践【gorilla/websocket】
  7. 常用 MySQL 操作
  8. linux查看交换机配置命令大全,H3C交换机配置命令大全
  9. MAC IDEA 常用快捷键
  10. 手机代理上网_华为、荣耀手机安装GooglePlay的方法
  11. FontAwesome动态旋转图标类(fa-spinfa-pulse)
  12. Android Studio启动海马玩模拟器
  13. 基于二叉树的家谱系统
  14. 深圳的住房公积金的那些事儿~(缴纳标准,用处用法)
  15. putty linux上安装及使用
  16. MATLAB图像融合分割系统
  17. 【C语言】函数:实现一个函数,打印乘法口诀表
  18. 2020第八届“泰迪杯”特等奖(基于 BERT 深度语言模型的“智慧政务”文本挖掘应用)
  19. 动态规划----最长公共子序列问题
  20. MSVS2008和VS2010的痛苦加装

热门文章

  1. 建网站要云服务器么?网站服务器怎么选?
  2. ASP NET2 0雷霆之怒盗链者的祝福【月儿原创】
  3. 【PAT】PAT总结《语法篇》
  4. 【原创】《成都,今夜请将我遗忘》 —— 带着一颗沉重的心去感受陈重的故事...
  5. WIN10更新失败,win10安装组件失败解决方案
  6. 一台主机接两个显示器并独立使用_玩 PS4 时如果你需要一款画面出色的 HDR 显示器,我觉得就是它了...
  7. Android studio——图案解锁
  8. css6图层 解锁,javascript - 如何在Openlayers上更改WMS图层的样式(来自Javascript) - 堆栈内存溢出...
  9. OSChina 周日乱弹 ——不懂这么多妹子爆照
  10. 神隐模式云控信息 服务器,MIUI7神隐模式怎么设置?miui7设置神隐模式图文教程[多图]...