在编写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. java.lang.SecurityException: class "javax.servlet.ServletRegistration"'s signer information does not match signer information of other classes in the same package

    报错信息: 报错截图: 解决方案: 因为本人是sbt项目,所以添加一下依赖之后解决: 如果是maven项目的话,添加依赖到pom文件中然后在重新build,之后就可以了

  2. PHP水仙花数的实现

    php水仙花数是什么? 水仙花数是指一个 n 位数 ( n≥3 ),它的每个位上的数字的 n 次幂之和等于它本身.(例如:1^3 + 5^3 + 3^3 = 153)三位的水仙花数共有4个:153,3 ...

  3. Python删除文件夹

    import os os.rmdir('OS-Demo-2') os.removedirs('OS-Demo-3/sub-Dir-1') os.removedirs()会自动将上一级文件夹也删除,谨慎 ...

  4. 转载 如何理解API,API 是如何工作的

    本文转载于https://blog.csdn.net/cumtdeyurenjie/article/details/80211896 感谢作者 仁杰兄 大家可能最近经常听到 API 这个概念,那什么是 ...

  5. Mongodb导入本地数据(.dat)到仓库(window)

    借鉴文章,完成了数据文件导入到Mongodb, 尊重作者版权:https://blog.csdn.net/weixin_44198965/article/details/100022616 1.找到你 ...

  6. How To Use PostgreSQL with Your Ruby on Rails Application on Ubuntu 14.04

    How To Use PostgreSQL with Your Ruby on Rails Application on Ubuntu 14.04 链接来自于:https://www.digitalo ...

  7. J2EE学习篇之--Spring技术详解

    今天我们来看一下Spring的相关知识,我们知道Spring也是一个开源的框架,这个框架并不像是Struts一样,这个框架是可以用在Java的所有地方,所以,其实开发Android的时候我们也是可以使 ...

  8. Python每日一题 002

    做为 Apple Store App 独立开发者,你要搞限时促销,为你的应用生成激活码(或者优惠券),使用 Python 如何生成 200 个激活码(或者优惠券)? 在此生成由数字,字母组成的20位字 ...

  9. delphi 下载

    获取网络文件大小 //delphi 获取网络文件大小 function GetUrlFileSize(aURL: string): integer; var FileSize: integer; va ...

  10. VMware Network Adapter VMnet1/8详解

    转自:https://www.cnblogs.com/systemnet123/articles/2640883.html VMWare提供了三种工作模式,它们是bridged(桥接模式).NAT(网 ...