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
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 vec_member_; //暴露给用户的成员变量,用户实际并不关心
};
#endif // MY_DLL_H_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

源文件:

#include “stdafx.h”
#include “dll_class.h”

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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

#ifndef MY_DLL__H_
#define MY_DLL_H_

#include
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_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

源文件:

#include “stdafx.h”
#include “dll_class.h”

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

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

private:
vector 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类实例调用功能函数
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

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

#ifndef MY_DLL__H_
#define MY_DLL_H_

#include
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_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

源文件:

#include “stdafx.h”
#include “dll_class.h”

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

public:
vector 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_
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值