根据此article,它试图解释pimpl
解决的问题,但我发现他展示的示例没有问题。
它说: “以下设计存在问题(取决于Fridge拥有多少个客户端,这个问题是否严重。由于Fridge.h #includes Engine.h,所以Fridge类的任何客户端都将间接#includeEngine类。因此修改Engine类后,即使没有直接使用Engine,Fridge的所有客户端也必须重新编译。”
#include "Engine.h"
class Fridge
{
public:
void coolDown();
private:
Engine engine_;
};
#include "Fridge.h"
void Fridge::coolDown()
{
/* ... */
}
但是,我看到如果Engine
被修改,Fridge
应该被修改。而且由于Fridge
将被修改,所以使用Client
的{{1}}也将被修改。
换句话说,如果Fridge
被修改,那么Engine
应该被重新编译,并据此Fridge
也将被重新编译。在这种情况下,Client
被修改是因为修改了Client
,而不是因为Fridge
被修改了。
因此,在这种情况下不存在间接问题。
我是对的吗?如果是,那么Engine
解决的实际问题是什么?如果没有,您能给我一个间接例子来解释这个问题吗?
答案 0 :(得分:3)
换句话说,如果
Engine
被修改,那么Fridge
应该被重新编译。
这是正确的,但更准确地说:依赖于Engine
的功能需要重新编译。
并且据此,还将重新编译Client。
不。仅仅因为重新编译了实现Fridges成员功能的翻译单元,并不意味着就需要重新编译Fridges客户。仅当Fridge.h修改后,才需要重新编译包括该标头的翻译单元。
但是,我看到如果
Engine
被修改,Fridge
应该被修改。而且由于Fridge
将被修改,所以使用Client
的{{1}}也将被修改。
这是所示实现所存在的问题。如果改为使用PIMPL隐藏Fridge
,则对Engine
的修改并不意味着对Engine
类的修改。它仅意味着对Fridge
成员函数的实现进行修改,该函数依赖于Fridge
,而这些实现被PIMPL隐藏。
如果您查看Engine
的PIMPL版本,您会发现它不直接使用Fridge.h
类,因此不包含Engine
。因此,Engine.h
的更改不会引起Engine.h
的更改,因此不会引起包含Fridge.h
的翻译单元的更改:
Fridge.h
您能举个例子,修改Engine.h后不重新编译客户端翻译单元会导致问题吗?
如果一个翻译单元使用的class Fridge
{
public:
Fridge();
~Fridge();
void coolDown();
private:
class FridgeImpl;
FridgeImpl* impl_;
};
定义与另一个翻译单元不同,则程序违反了一个定义规则,并且行为将是未定义的。
使用PIMPL时,包含Engine
的翻译单元完全不使用ODR使用Fridge.h
类,因此不存在违反ODR的可能性。