Skip to content

Instantly share code, notes, and snippets.

@lixingcong
Created August 18, 2018 06:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lixingcong/65a5cfe09340cd49d43ca9f5a7556910 to your computer and use it in GitHub Desktop.
Save lixingcong/65a5cfe09340cd49d43ca9f5a7556910 to your computer and use it in GitHub Desktop.
C++的pimpl机制demo
/*
* cpp14_pointers.h
*
* Note: std::make_unique was introduced in C++14.
* For C++11, this header will roll the implementation.
*
* https://stackoverflow.com/questions/17902405/how-to-implement-make-unique-function-in-c11/17902439#17902439
*
*/
#ifndef CPP14_POINTERS_H_
#define CPP14_POINTERS_H_
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#if __cplusplus <= 201103L
namespace std {
template<class T> struct _Unique_if {
typedef unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]> {
typedef unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
template<class T, class ... Args>
typename _Unique_if<T>::_Single_object make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound make_unique(size_t n)
{
typedef typename remove_extent<T>::type U;
return unique_ptr<T>(new U[n]());
}
template<class T, class ... Args>
typename _Unique_if<T>::_Known_bound make_unique(Args&&...) = delete;
}
#endif // __cplusplus
#endif /* CPP14_POINTERS_H_ */
#include <iostream>
using namespace std;
#include "Person.h"
int main()
{
Person p("Tony");
cout<<"Name="<<p.getName()<<endl;
return 0;
}
#include "Person.h"
#include "PersonPrivate.h"
#include "cpp14_pointers.h"
Person::Person(std::string name):
pimpl(std::make_unique<PersonPrivate>(name))
{
}
Person::~Person()
{
}
void Person::setName(std::string name)
{
pimpl->name=name;
}
std::string Person::getName()
{
return pimpl->name;
}
#ifndef PERSON_H_
#define PERSON_H_
#include <string>
#include <memory>
class PersonPrivate;
class Person {
public:
Person(std::string name);
~Person(); // Deconstructor need to be declared for unique_ptr
void setName(std::string name);
std::string getName();
private:
// Tips: unique_ptr requires C++11
friend class PersonPrivate;
std::unique_ptr<PersonPrivate> pimpl;
};
#endif /* PERSON_H_ */
#ifndef PERSONPRIVATE_H_
#define PERSONPRIVATE_H_
#include <string>
class PersonPrivate {
public:
PersonPrivate(std::string name):name(name){/* ... */}
std::string name;
};
#endif /* PERSONPRIVATE_H_ */
@lixingcong
Copy link
Author

lixingcong commented Aug 18, 2018

PImpl是 Pointer to implementation的缩写, 是C++ 在构建导出库接口时特有的技术手段。 即是将类Class中所有私有变量以及私有方法,封装在一单独的实现类ClassImpl中。

我们在Class中通过一指向ClassImpl的私有指针,访问这些私有数据。而ClassImpl类的具体定义和实现,我们放入cpp中。Qt中的D-Pointer技术,便是PImpl机制的一种实现方式。

优点:

  • 使得程序接口有着稳定的ABI(应用程序二进制接口),即不会打破二进制兼容。
  • 降低了程序编译依赖,从而缩短编译时间。
  • 数据隐藏,使得头文件很干净,不包含实现细节,可以直接作为 API 参考。

缺点:

  • 实现者需要做更多工作。
  • 由于子类需要访问,此机制对protected方法不奏效。
  • 由于数据的隐藏,多少造成了代码可读性下降。
  • 运行时性能被轻微的连累,尤其调用的函数为虚函数时。
  • 关于二进制兼容的问题,在C++的二进制兼容一文中有详细解释

摘自《PImpl机制以及Qt的D-Pointer实现》

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment