C++学习 | C++ Implement的使用 | 消除 warning C4251 | 精简库接口
在编写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 | 精简库接口的更多相关文章
- openresty 学习笔记番外篇:python的一些扩展库
openresty 学习笔记番外篇:python的一些扩展库 要写一个可以使用的python程序还需要比如日志输出,读取配置文件,作为守护进程运行等 读取配置文件 使用自带的ConfigParser模 ...
- 解决:warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;找到 MSIL .netmodule 或使用 /GL 编译的模块;正在。。;LINK : warning LNK4075: 忽略“/INCREMENTAL”(由于“/LTCG”规范)
参考资料: http://blog.csdn.net/laogaoav/article/details/8544880 http://stackoverflow.com/questions/18612 ...
- warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
最近在编译库文件后,使用它做APP,遇到如下问题: 1>LIBCMT.lib(invarg.obj) : error LNK2005: __pInvalidArgHandler 已经在 LIBC ...
- python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码
python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码 淘宝IP地址库 http://ip.taobao.com/目前提供的服务包括:1. 根据用户提供的 ...
- (转)如何解决VC中的警告warning C4251 needs to have dll-interface
这通常是由于以数据成员方式在DLL导出类中使用了模板类造成的.比如: #include <iostream> #include <vector> using namespace ...
- 解决: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 ...
- LINK : warning LNK4098: 默认库“LIBCMTD”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 默认库“LIBCMTD”与其他库的使用冲突:请使用 /NODEFAULTLIB:library 转自:http://blog.csdn.net/pgms ...
- C/C++ warning C4251: class ... 需要有 dll 接口由 class“..” 的客户端使用
{ 在DLL编程中, 如果调用模版类, 则可能出现类似以下的错误: 1>xclock.h(29): warning C4251: “XClock::m_FileName”: class“std: ...
- 消除Warning: Using a password on the command line interface can be insecure的提示
最近在部署Zabbix时需要用脚本取得一些MySQL的返回参数,需要是numberic格式的,但是调用脚本时总是输出这一句: Warning: Using a password on the comm ...
随机推荐
- Java中Map集合的基本功能
Map基本方法: put方法: remove方法: isEmpty方法: . clear方法: containsKey方法: containsValue方法 size方法: get方法: keySet ...
- Centos剔除在线用户
CentOS踢除已登录用户的方法: 1.>先按下w查看用户终端号 2.>执行pkill -kill -t pts/1 (pts/1为w指令看到的用户终端号)命令
- CentOS 7.2 安装MySQL 5.7
CentOS 7之后的版本yum的默认源中使用MariaDB替代原先MySQL,因此安装方式较为以往有一些改变: 下载mysql的源 wget http://dev.mysql.com/get/mys ...
- 编码格式分类: 前后端传递数据的编码格式contentType
urlencoded:form表单和ajax提交数据的默认编码格式 form-data:传文件 application/json:json格式数据 >>> 前后端分离 urlenco ...
- kafka为什么快?
为什么Kafka那么快 原创 2016-07-18 fireflyc 写程序的康德 网上有很多Kafka的测试文章,测试结果通常都是"吊打"其他MQ.感慨它的牛B之余我觉得必要仔细 ...
- jmeter基本问题
jmetet加压的时候不用图像界面(GUI),直接在命令行加压(命令行生成一个report-命令行参数),不做断言,不加监听器--不然会很卡: 进入就meter命令行: 后置处理器可以从HTML页面拿 ...
- windows server2012r2 安装NET Framework 3.5
在Windows Server 2012上安装一些软件,比如Oracle 11g等,经常会出现下面这样的错误:“无法安装一下功能:.NET Framework 3.5(包括.NET 2.0和3.0)” ...
- (16)centos7 日志文件
常见日志文件 开机启动日志,只会记录本次信息 /var/log/boot.log 计划任务日志 /var/log/cron 开机内核检测信息 /var/log/dmesg 账号登录信息 /var/lo ...
- WIN7下怎么安装iis教程
点击开始→控制面板,然后再点击程序,勿点击卸载程序,否则到不了目标系统界面. 2 然后在程序和功能下面,点击打开和关闭windows功能. 3 进入Windows功能窗口,然后看到internet信息 ...
- 1044 Shopping in Mars (25 分)
Shopping in Mars is quite a different experience. The Mars people pay by chained diamonds. Each diam ...