C++ Implement使用| 消除 warning C4251 | 精简库接口

C++ Implement使用| 消除 warning C4251 | 精简库接口

  • 暴露成员变量的缺点
  • 解决办法
  • 代码示栗
    • 1. 不推荐的方式,即将成员变量暴露给用户:
    • 2. 推荐方式一
    • 3. 推荐方式二

Reference:

  1. 文章大多来源于: 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类的定义和实现都绝不能出现在开放给用户的动态库头文件里,实现放在源文件里。

代码示栗

1. 不推荐的方式,即将成员变量暴露给用户:

头文件:

#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<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);
}

2. 推荐方式一

把接口类只当做一个壳子,实际的类实现,全部都放在另一个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_

源文件:

#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()	// 这里使用的是Impl的DoSomething,而下面的推荐方式使用的直接是MyClass的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类实例调用功能函数
}

3. 推荐方式二

把所有成员变量放在另一个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_

源文件:

#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++,c++,接口)