2
class MyClassPrivate
{
//My members.
};    

//and then
class MyClass {
private:
 MyClassPrivate* const d;
};

使用这种“模式”的原因是什么?它是如何正确调用的?

4

3 回答 3

4

这称为“指向实现”或“pimpl”。请参阅http://en.wikibooks.org/wiki/C++_Programming/Idioms#Pointer_To_Implementation_.28pImpl.29

当您使用此模式时,您将转发声明实现类,并在其他地方声明主体,即:

// header
class MyClassPrivate;
class MyClass {
public:
  MyClass();
  ~MyClass();
private:
  MyClassPrivate* const d;
};

// cpp
class MyClassPrivate {
};
MyClass::MyClass() : d(new MyClassPrivate) {}
MyClass::~MyClass() { delete d; }

这样做的好处MyClass是不会向MyClass. 如果实现发生变化,其他用户MyClass不需要重新编译。任何必须为成员包含的头文件也不需要公开,这提高了编译时间。

于 2013-01-15T11:08:07.497 回答
3

最常用的是Pimlp成语。

Pimpl 成语描述了一种使您的头文件不受更改的方法。您经常听到诸如“避免更改您的公共界面!”之类的建议。所以你可以修改你的私有接口,但是当你的头文件定义私有方法时,如何避免重新编译。这就是 Pimpl 所做的 - 当您的私有接口更改时减少编译损坏[ 3 ]。

这里

好处:

  1. 更改类的私有成员变量不需要重新编译依赖它的类,因此生成时间更快,并且 FragileBinaryInterfaceProblem 减少了。
  2. 头文件不需要#include 在私有成员变量中“按值”使用的类,因此编译时间更快。
  3. 这有点像 SmallTalk 自动处理类的方式......更纯粹的封装。

缺点:

  1. 为实施者做更多的工作。
  2. 不适用于需要通过子类访问的“受保护”成员。
  3. 阅读代码有点困难,因为某些信息不再在头文件中。
  4. 由于指针间接,运行时性能略有下降,特别是如果函数调用是虚拟的(间接分支的分支预测通常很差)。

怎么做:

  1. 将所有私有成员变量放入结构中。
  2. 将结构定义放在 .cpp 文件中。
  3. 在头文件中,仅放置结构的 ForwardDeclaration。
  4. 在类定义中,将指向结构的(智能)指针声明为唯一的私有成员变量。
  5. 类的构造函数需要创建结构。
  6. 类的析构函数需要销毁结构(可能是由于使用了智能指针而隐含地)。
  7. 赋值运算符和 CopyConstructor 需要适当地复制结构,否则将被禁用。
于 2013-01-15T11:10:26.960 回答
1

当您想将接口与实施分开时,您可以将其用于PIMPL idiom 。

许多设计模式也使用指向私有属性的“指针”,例如Strategy Pattern。此模式允许您在运行时选择不同的算法。

此外,如果您使数据的操作遵循相同的接口,则可以将数据封装在私有类中,使该类成为层次结构的一部分,并在运行时(或编译时)在不同的数据实现之间切换:))。

一个很好的例子是保存多边形数据的几何类。每个多边形都提供对点的访问,您还可以删除多边形边缘并进行各种其他拓扑操作。如果为 Polygon 类提供一个抽象基类,提供 deletePoint、addPoint、swapEdge 等方法,就可以测试不同的 Polygon 实现。

您可以直接将多边形定义为点类型的列表,并将点存储在不同的容器(列表或向量)中。Polygon 类可以通过间接寻址来定义,其中多边形实际上是点列表的 ID 列表(我说的是一般意义上的列表)。这样,您可以测试 PolygonGeometry 类的不同算法,并了解它们如何与不同的 Polygon 实现一起工作。

这背后有一个设计原则:Prefer Composition to Inheritance。每当您使用组合并且依赖于在运行时确定的类型时,您将拥有一个私有属性指针。

于 2013-01-15T11:08:38.423 回答