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_
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章