20200922 effective cpp C++ 【pimpl模式】

解决问题

写C++的同学可能遇到过有时候更改了一个头文件,结果编译时间变得巨长的情况。

这是由于这个头文件可能被很多地方直接或间接地包含了,形成了依赖关系。一旦这个头文件被修改,那么依赖它的所有其他obj文件都需要被重新编译链接。

解决思路之一是我们可以尽量避免修改到头文件。

换句话说,尽量少往头文件里面丢东西,这样一来修改到头文件的概率就少了。

pimpl模式

pimpl的核心思想就是把private变量和函数都抽离头文件。

那么如何抽离呢?做这么几步:

  1. 头文件的公开类中 声明 一个内置类,并持有一个指向该内置类实例的 指针 (由于只是声明,所以并不能使用类的内容,但是可以用指针引用)
  2. 这个内置类的定义放在实现文件里
  3. 公开类只提供一些public接口,实际上的函数实现全部放在所定义的内置类中

这就是pimpl模式。

Show Me The Code.

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Person.h

// 这个类可以看做是接口
class Person
{
public:
Person(uint InAge, string InName);
~Person();

GetInfo();

private:
// 这个内置类必须要是private的
class PersonImpl;
// pImpl也要是private的
PersonImpl* pImpl;
}

// Person.cpp
class Person::PersonImpl
{
public:
PersonImpl(uint InAge, string InName);
~PersonImpl();

string ToString();

private:
// 在ToString的时候做点见不得人的事儿
void ToStringPre();

private:
uint Age;
string Name;
}

Person::PersonImpl::PersonImpl(uint InAge, string InName):
Age(InAge),
Name(InName)
{

}

string Person::PersonImpl::ToString()
{
ToStringPre();
return sprintf("Name: %s, Age: %d", Name.cstr(), Age);
}

void Person::PersonImpl::ToStringPre()
{
// 做点见不得人的事情
}

Person::Person(uint InAge, string InName):
pImpl(new Person::PersonImpl(InAge, InName))
{}

// 实现Person接口
string Person::GetInfo()
{
return pImpl->ToString();
}

由于Person::PersonImpl是private且定义在实现文件中的,所以对于include了Person.h的人来说是不可见的。

想象一下如果有一天我们突然不想在ToString()里调用ToStringPre(),我们可以直接把ToStringPre()的声明和定义全都去掉,斩草除根。由于Person::PersonImpl没有暴露在头文件中,所以#include "Person.h"的文件不需要重新编译。

大快人心。

Buy Me A Coffee / 捐一杯咖啡的钱
分享这篇文章~
0%
//