类定义中class+宏+类名的意义
看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 类从 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+宏+类名的意义相关推荐
- #与##在宏定义中的--宏展开
#与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int m ...
- python 类中定义列表_Python-从类定义中的列表理解访问类变量
小编典典 类范围和列表,集合或字典的理解以及生成器表达式不混合. 为什么:或者,官方用词 在Python 3中,为列表理解赋予了它们自己的适当范围(本地名称空间),以防止其局部变量渗入周围的范围内(即 ...
- python类定义中__init__()_转:python学习——类中为什么要定义__init__()方法
学习Python的类,一直不太理解为什么一定要定义init()方法,现在简要谈一下自己的理解吧. 1.不用init()方法定义类 定义一个矩形的类,目的是求周长和面积. 1 classRectangl ...
- 详解Python类定义中的各种方法
首先应该明确,在面向对象程序设计中,函数和方法这两个概念是有本质区别的.方法一般指与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为第一个参数传递过去,普通函数并不具备这个特点. >&g ...
- python类定义中、对象字符串的特殊方法是_python中自定义类对象json字符串化的方法_python json转字符串、...
python中自定义类对象json字符串化的方法 1. 用 json 或者simplejson 就可以 2.定义转换函数: def convert_to_builtin_type(obj): prin ...
- python类定义中__init__(),在__init__中定义一个成员以在python中的类体中定义它的区别?...
What is the difference between doing class a: def __init__(self): self.val=1 to doing class a: val=1 ...
- VC宏定义 及常用宏定义说明
1. 宏定义的格式 宏定义的一般格式是: #define 标识符 字符串 其中,标识符和字符串之间用空格隔开.标识符又称宏名,为了区别于一般变量,通常用英文大写字母表示:字符串又称宏体,可以是常 ...
- VC/MFC中常用宏的含义
VC/MFC中常用宏的含义 Visual C++ MFC 中常用宏的含义(转载) AND_CATCHAND_CATCH AND_CATCH(exception_class,exception ...
- C++ 笔记(16)— 类和对象(类定义、类实例对象定义、访问类成员、类成员函数、类 public/private/protected 成员、类对象引用和指针)
1. 类的定义 类定义是以关键字 class 开头,后跟类的名称.并在它后面依次包含类名,一组放在 {} 内的成员属性和成员函数,以及结尾的分号. 类声明将类本身及其属性告诉编译器.类声明本身并不能改 ...
最新文章
- 分摊的意思_接了两单顺风车遇到几个有意思乘客,后来把顺风车软件删了
- python实习生面试题_大数据分析实习生面试题库
- mui请求php,PHP 怎样处理mui.ajax POST过来的数据?
- python正则匹配ip_[求助] 正则表达式匹配 IP 地址的问题求助
- tpl-spring-mybatis 模板工程
- Go WebSocket开发与测试实践【gorilla/websocket】
- 常用 MySQL 操作
- linux查看交换机配置命令大全,H3C交换机配置命令大全
- MAC IDEA 常用快捷键
- 手机代理上网_华为、荣耀手机安装GooglePlay的方法
- FontAwesome动态旋转图标类(fa-spinfa-pulse)
- Android Studio启动海马玩模拟器
- 基于二叉树的家谱系统
- 深圳的住房公积金的那些事儿~(缴纳标准,用处用法)
- putty linux上安装及使用
- MATLAB图像融合分割系统
- 【C语言】函数:实现一个函数,打印乘法口诀表
- 2020第八届“泰迪杯”特等奖(基于 BERT 深度语言模型的“智慧政务”文本挖掘应用)
- 动态规划----最长公共子序列问题
- MSVS2008和VS2010的痛苦加装
热门文章
- 建网站要云服务器么?网站服务器怎么选?
- ASP NET2 0雷霆之怒盗链者的祝福【月儿原创】
- 【PAT】PAT总结《语法篇》
- 【原创】《成都,今夜请将我遗忘》 —— 带着一颗沉重的心去感受陈重的故事...
- WIN10更新失败,win10安装组件失败解决方案
- 一台主机接两个显示器并独立使用_玩 PS4 时如果你需要一款画面出色的 HDR 显示器,我觉得就是它了...
- Android studio——图案解锁
- css6图层 解锁,javascript - 如何在Openlayers上更改WMS图层的样式(来自Javascript) - 堆栈内存溢出...
- OSChina 周日乱弹 ——不懂这么多妹子爆照
- 神隐模式云控信息 服务器,MIUI7神隐模式怎么设置?miui7设置神隐模式图文教程[多图]...