在编写C++动态库的过程中,我们常常会听到某个要求:请隐藏动态库头文件里类接口里的成员变量!或者自己在编写动态库时,突然意识到自己好像让调用者看到的信息太多了,而这些信息根本无需被调用者看到,往往调用者只需要接口函数而已,所以给他们接口函数就可以了。

  暴露动态库头文件类接口里的成员变量有很多坏处:
  1、增加头文件更新次数。如果成员变量不被隐藏,则每次修改成员变量都需要给调用者更新头文件。
  2、暴露给用户太多信息。编写库的目的一个是方便,另一个就是私密性,让类的实现部分在用户端不可见,如果过多地暴露成员变量,则很容易造成信息泄露。
  3、增加warning C4251。我们有时候会接到消除代码中warning的任务,这在要求很高的源码项目中很常见,而warning C4251便是很常见的一种警告,它警告的是在动态库头文件中接口类的成员函数中含有模板类,这在std标准库频繁使用的项目中很容易发生,如含有vector、string等成员变量,这时就需要隐藏这些成员变量,以消除此warning。

  入正题,如何隐藏动态库头文件类接口里的成员变量呢?使用C++ Implement。
  C++ Implement其实很简单,有两种实现方式:
  1、把接口类只当做一个壳子,实际的类实现,全部都放在另一个Impl类中。
  2、把所有成员变量放在另一个Impl类中,Impl相当于成员变量的外壳。
  以上两种方式都必须遵守一个关键点:Impl类的定义和实现都绝不能出现在开放给用户的动态库头文件里。你可以把定义和实现都放在源文件里,也可以定义放在不开放的头文件里,实现放在源文件里。

  代码示例:
  先看看不推荐的方式,即将成员变量暴露给用户:
  头文件:

#ifndef MY_DLL__H_
#define MY_DLL_H_

#include <vector>
using std::vector;

#ifndef MY_DLL_EXPORT
#define DLL_EXPORT __declspec(dllimport)
#else
#define DLL_EXPORT __declspec(dllexport)
#endif

class DLL_EXPORT MyClass
{
public:
	MyClass();
	~MyClass();

public:
	void DoSomething();			//给用户调用的接口函数
private:
	vector<int> vec_member_;		//暴露给用户的成员变量,用户实际并不关心
};
#endif // MY_DLL_H_
  源文件:

#include "stdafx.h"
#include "dll_class.h"

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

void MyClass::DoSomething()
{
	vec_member_.push_back(1);
}

  C++ Implement方式第一种:把接口类只当做一个壳子,实际的类实现,全部都放在另一个Impl类中:
  头文件:

#ifndef MY_DLL__H_
#define MY_DLL_H_

#include <vector>
using std::vector;

#ifndef MY_DLL_EXPORT
#define DLL_EXPORT __declspec(dllimport)
#else
#define DLL_EXPORT __declspec(dllexport)
#endif

class DLL_EXPORT MyClass
{
public:
	MyClass();
	~MyClass();

public:
	void DoSomething();			//给用户调用的接口函数

private:
	void* impl_;				//将所有实际功能实现放至类Impl中,定义一个void类型的类Impl指针,在构造函数中实例化
};

#endif // MY_DLL_H_

  源文件:

#include "stdafx.h"
#include "dll_class.h"

class Impl
{
public:
	Impl();
	~Impl();

public:
	void DoSomething();				//实际功能函数

private:
	vector<int> vec_member_;
};

Impl::Impl()
{
}

Impl::~Impl()
{
}

void Impl::DoSomething()
{
	vec_member_.push_back(1);		//实际功能实现,以前的MyClass类中实现改为在Impl类中实现
}

MyClass::MyClass()
	:impl_(nullptr)
{
	impl_ = new Impl();				//MyClass成员函数中实例化Impl类
}

MyClass::~MyClass()
{
	if (impl_ != nullptr)
		delete impl_;
	impl_ = nullptr;
}

void MyClass::DoSomething()
{
	Impl* impl = (Impl*)impl_;
	impl->DoSomething();				//通过Impl类实例调用功能函数
}

  C++ Implement方式第二种:把所有成员变量放在另一个Impl类中,Impl相当于成员变量的外壳。
  头文件:

#ifndef MY_DLL__H_
#define MY_DLL_H_

#include <vector>
using std::vector;

#ifndef MY_DLL_EXPORT
#define DLL_EXPORT __declspec(dllimport)
#else
#define DLL_EXPORT __declspec(dllexport)
#endif

class DLL_EXPORT MyClass
{
public:
	MyClass();
	~MyClass();

public:
	void DoSomething();			//给用户调用的接口函数

private:
	void* impl_;				//将成员变量移至类Impl中,定义一个void类型的类Impl指针,在构造函数中实例化
};

#endif // MY_DLL_H_

  源文件:

#include "stdafx.h"
#include "dll_class.h"

class Impl
{
public:
	Impl();
	~Impl();

public:
	vector<int> vec_member_;	//将之前暴露给用户的成员变量变为Impl类的成员变量,再通过Impl类的实例调用此成员
};

Impl::Impl()
{
}

Impl::~Impl()
{
}

MyClass::MyClass()
	:impl_(nullptr)
{
	impl_ = new Impl();			//构造函数中实例化Impl类
}

MyClass::~MyClass()
{
	if (impl_ != nullptr)
		delete impl_;
	impl_ = nullptr;
}

void MyClass::DoSomething()
{
	Impl* impl = (Impl*)impl_;
	impl->vec_member_.push_back(1);		//通过impl_成员调用vec_member_
}

C++学习 | C++ Implement的使用 | 消除 warning C4251 | 精简库接口的更多相关文章

  1. openresty 学习笔记番外篇:python的一些扩展库

    openresty 学习笔记番外篇:python的一些扩展库 要写一个可以使用的python程序还需要比如日志输出,读取配置文件,作为守护进程运行等 读取配置文件 使用自带的ConfigParser模 ...

  2. 解决:warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;找到 MSIL .netmodule 或使用 /GL 编译的模块;正在。。;LINK : warning LNK4075: 忽略“/INCREMENTAL”(由于“/LTCG”规范)

    参考资料: http://blog.csdn.net/laogaoav/article/details/8544880 http://stackoverflow.com/questions/18612 ...

  3. warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library

    最近在编译库文件后,使用它做APP,遇到如下问题: 1>LIBCMT.lib(invarg.obj) : error LNK2005: __pInvalidArgHandler 已经在 LIBC ...

  4. python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码

    python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码 淘宝IP地址库 http://ip.taobao.com/目前提供的服务包括:1. 根据用户提供的 ...

  5. (转)如何解决VC中的警告warning C4251 needs to have dll-interface

    这通常是由于以数据成员方式在DLL导出类中使用了模板类造成的.比如: #include <iostream> #include <vector> using namespace ...

  6. 解决:warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;找到 MSIL .netmodule 或使用 /GL 编译的模块;正在。。;LINK : warning LNK4075: 忽略“/INCREMENTAL”(由于“/LTCG”规范)

    原文链接地址:https://www.cnblogs.com/qrlozte/p/4844411.html 参考资料: http://blog.csdn.net/laogaoav/article/de ...

  7. LINK : warning LNK4098: 默认库“LIBCMTD”与其他库的使用冲突;请使用 /NODEFAULTLIB:library

    LINK : warning LNK4098: 默认库“LIBCMTD”与其他库的使用冲突:请使用 /NODEFAULTLIB:library 转自:http://blog.csdn.net/pgms ...

  8. C/C++ warning C4251: class ... 需要有 dll 接口由 class“..” 的客户端使用

    { 在DLL编程中, 如果调用模版类, 则可能出现类似以下的错误: 1>xclock.h(29): warning C4251: “XClock::m_FileName”: class“std: ...

  9. 消除Warning: Using a password on the command line interface can be insecure的提示

    最近在部署Zabbix时需要用脚本取得一些MySQL的返回参数,需要是numberic格式的,但是调用脚本时总是输出这一句: Warning: Using a password on the comm ...

随机推荐

  1. Docker 部署 nginx 前端项目

    docker pull nginx docker run -itd -p 82:80 -v /opt/soft/nginx/conf/nginx.conf:/etc/nginx/nginx.conf ...

  2. Tomcat启动报:The Server time zone value 'XXXXX' 乱码问题解决

    今天给自己项目打包到服务器发布时,运行时,发现报 java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecogniz ...

  3. BZOJ 1565: [NOI2009]植物大战僵尸(网络流+缩点)

    传送门 解题思路 最大权闭合子图.但是要注意一些细节,假如有一堆植物形成一个环,那么这些植物都是无敌的,并且他们保护的植物是无敌的,他们保护的保护的植物是无敌 的.所以要缩点,然后拓扑排序一次判无敌, ...

  4. rbd_rados命令拷屏

    mimic或者luminous rbd_rados sudo mount -t ceph 192.168.7.151:6789:/ /mnt -o name=admin,secret=AQBaPZNc ...

  5. 如何在Python中让两个print()函数的输出打印在一行内?

    1.两个连续的print()函数为什么在输出时内容会分行显示? 解:print()中有两个默认参数sep和end,其中sep是代替分隔符,end是代替末尾的换行符,默认使用‘,’代替空格,且默认末尾加 ...

  6. Java方法中形参能否改变实参

    前几天学习了java中的参数传递机制,总结了一些知识点: 1·参数类型为基本数据类型:整型:byte,short,int,long ,浮点型:float,double ,字符型:char ,布尔型:b ...

  7. ALSA 更改默认声卡

    用 aplay -l 看一下各声卡的配置信息 创建 /etc/asound.conf,根据 aplay -l  的输入,选择需要的声卡信息,按如下格式添入,即可更改默认声卡 defaults.pcm. ...

  8. 剑指offer——61平衡二叉树

    题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树.   题解: 方法一:使用深度遍历,判断每个节点是不是平衡二叉树,这种从上至下的方法会导致底层的节点重复判断多次 方法二:使用后序遍历判断,这种 ...

  9. linux 服务器修改密码

    登录服务器后直接输入命令行passwd root 然后输入两次新密码就行

  10. java读取pdf文本转换html

    补充:一下代码基于maven,现将依赖的jar包单独导出 地址:pdf jar 完整代码地址 也就两个文件 java读取pdf中的纯文字,这里使用的是pdfbox工具包 maven引入如下配置 < ...