简体   繁体   English

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

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

In the section on "Good Encapsulation" in Code Complete, it is recommended to hide private implementation details.在 Code Complete 的“良好封装”部分,建议隐藏私有实现细节。 An example is given in C++. C++ 中给出了一个示例。 The idea is basically to completely separate the interface from the implementation, even in the class level.这个想法基本上是将接口与实现完全分离,即使在 class 级别。

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

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

private:
    EmployeeImplementation *m_implementation;
};

Is this really a good use of time?这真的是一种很好的时间利用方式吗? Not only does this seem inefficient (what kind of performance penalties would this give?), but the whole motto of Code Complete ("managing complexity") seems to have been reversed- does this not add complexity?这不仅看起来效率低下(这会带来什么样的性能损失?),而且代码完成(“管理复杂性”)的整个座右铭似乎已经被颠倒了——这不会增加复杂性吗?

Another advantage of the PIMPL idiom may be in maintaining the ABI . PIMPL 习语的另一个优点可能是维护ABI See The Pimpl Idiom in practice .请参阅实践中的 The Pimpl Idiom

The size of the class remains constant. class 的大小保持不变。 This means that you may change the internal implementation, while keeping the interface intact.这意味着您可以更改内部实现,同时保持接口不变。

If the implementation is distributed in compiled form (lib, dll, so, etc.), then, under some conditions, you may be able to just replace the library without having to recompile the code that uses the class.如果实现以编译形式分发(lib、dll 等),那么,在某些情况下,您可以只替换库而无需重新编译使用 class 的代码。 Thus, you decouple the code as a completely stand-alone module, so long as the public interface doesn't change.因此,只要公共接口不变,您就可以将代码解耦为一个完全独立的模块。

As others have stated, it also reduces compilation time, which can be reason enough in some cases.正如其他人所说,它还减少了编译时间,这在某些情况下可能是足够的理由。

Well, it does increase encapsulation since your header file now contains only public members and a single pointer to a private implementation.好吧,它确实增加了封装,因为您的 header 文件现在只包含公共成员和指向私有实现的单个指针。

It also (slightly?) lowers performance due to the extra level of indirection.由于额外的间接级别,它还(稍微?)降低了性能。

The "reduces compilation time" is the key issue here. “减少编译时间”是这里的关键问题。

If you (your company) are the only users of the class then I don't think you have any reason to use this idiom.如果您(您的公司)是 class 的唯一用户,那么我认为您没有任何理由使用这个成语。 You get lower performance and you should have daily (or periodic) rebuilds of your source code anyway (which should be aware of dependencies between the classes).您会获得较低的性能,并且无论如何您都应该每天(或定期)重建源代码(这应该知道类之间的依赖关系)。

This means that compilation time should be largely irrelevant if you are the only consumer of the class.这意味着,如果您是 class 的唯一消费者,那么编译时间在很大程度上应该是无关紧要的。

If you are distributing the library then the story is completely different.如果您正在分发库,那么情况就完全不同了。 Changes in headers mean any clients you have will need to rebuild their applications to use your new version, even if what you did was change the private parts of the class.标头的更改意味着您拥有的任何客户端都需要重新构建他们的应用程序才能使用您的新版本,即使您所做的是更改 class 的私有部分。 Using the pimpl idiom here would mean the change is invisible to the users of your dynamic library.在这里使用 pimpl 成语意味着更改对您的动态库的用户是不可见的。

An extra level of indirection through pointer can cause extra cache misses and slow down your program.通过指针的额外间接级别可能会导致额外的缓存未命中并减慢程序的速度。 AFAIK, this idiom (PIMPL) is most often suggested to reduce compilation time. AFAIK,这个习语(PIMPL)最常被建议用来减少编译时间。 Say you have an employee.h header having with all fields in the class, instead of just one pointer.假设您有一个employee.h header 具有class 中的所有字段,而不仅仅是一个指针。 Now, whenever you change employee details (eg add or remove field), EVERY file including employee.h has to be recompiled.现在,每当您更改员工详细信息(例如添加或删除字段)时,包括employee.h在内的每个文件都必须重新编译。 If all you have is a single pointer to an implementation class defined in employee.cpp , then ONLY employee.cpp has to be recompiled when you change EmployeeImplementation .如果您只有一个指向定义在employee.cpp中的实现 class 的单个指针,那么当您更改EmployeeImplementation时,只需重新编译employee.cpp

Now, is the reduced compilation time worth the extra cost?现在,减少的编译时间值得付出额外的代价吗? Only you can decide that.只有你可以决定。

I think that the main advantage (or at least on of them) of the pimpl idiom is not saving compilation time, but allowing loose-coupling , ie breaking dependencies, between components.我认为 pimpl 习惯用法的主要优点(或至少其中一个)不是节省编译时间,而是允许松散耦合,即打破组件之间的依赖关系。

Suppose you provide an infrastructure library that many other components use.假设您提供了许多其他组件使用的基础架构库。 Then, as @zvrba indicated, every time you change your private implementation details all your clients must recompile.然后,正如@zvrba 所指出的,每次更改私有实现细节时,所有客户端都必须重新编译。 It could be not a big deal, but in large and complex projects an integration between components may be a complex task.这可能没什么大不了的,但在大型和复杂的项目中,组件之间的集成可能是一项复杂的任务。 With pimpl, if your library is dynamic (dll, .so) then no action is required by your clients.使用 pimpl,如果您的库是动态的(dll、.so),那么您的客户不需要任何操作。

This idiom is used for abstracting over poor headers, and not much else.这个习语用于对糟糕的标题进行抽象,仅此而已。 Only use it if the types required to define the class involve including headers which leak macros, take a long time to compile, etc. Apart from that, it's generally not considered the correct thing to do.仅当定义 class 所需的类型涉及包含泄漏宏的标头、编译时间长等时才使用它。除此之外,通常不认为这样做是正确的。 Since your implementation requires dynamic allocation and reference semantics anyway, you may as well just make it an interface and offer a CreateForMyPlatform() method that has a definition in a cpp file.由于您的实现无论如何都需要动态分配和引用语义,您不妨将其设为接口并提供在 cpp 文件中定义的CreateForMyPlatform()方法。 At least you can use smart pointers for that scenario.至少您可以在该场景中使用智能指针。

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

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