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_ }
最新文章
- xcode国际化工具genstrings体验总结
- 慕课网Java高并发秒杀学习
- 小数5.2500四舍五入保留1位小数的java算法之一
- ICloud没有密码怎么注销?
- ADB常用的几个命令
- paper 20 :color moments
- [HDU 4787] GRE Words Revenge (AC自动机)
- struts2文件上传大小限制问题小结
- WebSphere常用设置
- Yii框架 多数据库、主从、读写分离
- DOM4J 解析 XML
- Delphi通过GetFileVersionInfo和VerQueryValue等API函数取得详细EXE信息
- Redis .Net 基本类型使用之南
- SpringMVC入门就这么简单
- ●BZOJ 4237 稻草人
- 前端入门24-响应式布局(BootStrap)
- C# 封装SDK 获取摄像头的水平角度和垂直角度
- Echarts tooltip 坐标值修改
- C# 创建多级文件夹示例
- Python “ValueError: incomplete format” print(“a%” % ”)