C++接口与实现分离

在C++中,存在这样一个令人烦恼的现象

假设

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Test.h
class Test
{
Test(){}
Method(){}
}
//main.cpp
#include "test.h"
int main()
{
Test t;
t.Method();
return 0;
}

编译完成后,如果我们修改了Test Class的内容,那么我们需要把所有文件再次编译,因为我们修改了Test.h的内容,而Test.h正好被main.cpp所包含

既然如此,那么有没有办法让我们就算改变了实现也只用编译单个文件然后重新链接即可的方法呢?

1.Pimpl

这个方法使用的是公有接口,私有实现,在私有成员中定义一个handle class,用来实现各种方法

例如下面的在T1 Class中声明一个T1Impl作为handle classT1所提供的是接口,T1Imple提供的实现

接下来,只需要在T1的接口中调用方法即可

1
2
3
4
5
6
7
8
#include <iostream>
#include "t1.h"
int main()
{
T1 t1;
t1.say();
return 0;
}

t1.h,t1.cpp

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
//t1.h
#ifndef T1_H_
#define T1_H_
class T1{
public:
T1();
void say();
~T1() {delete p;};
private:
class T1Impl;
T1Impl *p;
};
#endif


//t1.cpp
#include "t1.h"
#include "t1impl.h"
T1::T1()
{
p = new T1Impl();
}
void T1::say()
{
p->say();
}

t1impl.h,t1impl.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//t1impl.h
#ifndef T1IMPL_H_
#define T1IMPL_H_
#include "t1.h"
class T1::T1Impl {
public:
void say();
};
#endif


//t1impl.cpp
#include <iostream>
#include "t1impl.h"
void T1::T1Impl::say() {
std::cout << "Hello,world" << std::endl;
}

最后当然是makefile展示编译和连接步骤

1
2
3
4
5
6
7
8
all: main.o t1.o t1impl.o
g++ main.o t1.o t1impl.o
main.o:
g++ main.cpp -c
t1.o:
g++ t1.cpp -c
t1impl:
g++ t1impl.cpp -c

当实现改变的时候,只需重新编译t1impl和重新连接即可,当然如果是接口改变,自然要重新编译整个工程

2.Interface class

在C++中我们可以使用纯虚函数提供接口功能,其实解决的方法都差不多,都是使用中间层,只不过要提供一个工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class T1
{
public:
std::shared_ptr<T1> Create()
{
return...
}
virtual void say() = 0;
};

class T1Impl : public T1
{
public:
void say();
};