繁体   English   中英

来自 Code Complete 关于封装的 C++ 建议?

[英]C++ advice from Code Complete on encapsulation?

在 Code Complete 的“良好封装”部分,建议隐藏私有实现细节。 C++ 中给出了一个示例。 这个想法基本上是将接口与实现完全分离,即使在 class 级别。

class Employee {
public:
    ...
    Employee( ... );
    ...

    FullName GetName() const;
    String GetAddress() const;

private:
    EmployeeImplementation *m_implementation;
};

这真的是一种很好的时间利用方式吗? 这不仅看起来效率低下(这会带来什么样的性能损失?),而且代码完成(“管理复杂性”)的整个座右铭似乎已经被颠倒了——这不会增加复杂性吗?

PIMPL 习语的另一个优点可能是维护ABI 请参阅实践中的 The Pimpl Idiom

class 的大小保持不变。 这意味着您可以更改内部实现,同时保持接口不变。

如果实现以编译形式分发(lib、dll 等),那么,在某些情况下,您可以只替换库而无需重新编译使用 class 的代码。 因此,只要公共接口不变,您就可以将代码解耦为一个完全独立的模块。

正如其他人所说,它还减少了编译时间,这在某些情况下可能是足够的理由。

好吧,它确实增加了封装,因为您的 header 文件现在只包含公共成员和指向私有实现的单个指针。

由于额外的间接级别,它还(稍微?)降低了性能。

“减少编译时间”是这里的关键问题。

如果您(您的公司)是 class 的唯一用户,那么我认为您没有任何理由使用这个成语。 您会获得较低的性能,并且无论如何您都应该每天(或定期)重建源代码(这应该知道类之间的依赖关系)。

这意味着,如果您是 class 的唯一消费者,那么编译时间在很大程度上应该是无关紧要的。

如果您正在分发库,那么情况就完全不同了。 标头的更改意味着您拥有的任何客户端都需要重新构建他们的应用程序才能使用您的新版本,即使您所做的是更改 class 的私有部分。 在这里使用 pimpl 成语意味着更改对您的动态库的用户是不可见的。

通过指针的额外间接级别可能会导致额外的缓存未命中并减慢程序的速度。 AFAIK,这个习语(PIMPL)最常被建议用来减少编译时间。 假设您有一个employee.h header 具有class 中的所有字段,而不仅仅是一个指针。 现在,每当您更改员工详细信息(例如添加或删除字段)时,包括employee.h在内的每个文件都必须重新编译。 如果您只有一个指向定义在employee.cpp中的实现 class 的单个指针,那么当您更改EmployeeImplementation时,只需重新编译employee.cpp

现在,减少的编译时间值得付出额外的代价吗? 只有你可以决定。

我认为 pimpl 习惯用法的主要优点(或至少其中一个)不是节省编译时间,而是允许松散耦合,即打破组件之间的依赖关系。

假设您提供了许多其他组件使用的基础架构库。 然后,正如@zvrba 所指出的,每次更改私有实现细节时,所有客户端都必须重新编译。 这可能没什么大不了的,但在大型和复杂的项目中,组件之间的集成可能是一项复杂的任务。 使用 pimpl,如果您的库是动态的(dll、.so),那么您的客户不需要任何操作。

这个习语用于对糟糕的标题进行抽象,仅此而已。 仅当定义 class 所需的类型涉及包含泄漏宏的标头、编译时间长等时才使用它。除此之外,通常不认为这样做是正确的。 由于您的实现无论如何都需要动态分配和引用语义,您不妨将其设为接口并提供在 cpp 文件中定义的CreateForMyPlatform()方法。 至少您可以在该场景中使用智能指针。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM