设计类聚合 - 堆栈分配与动态内存分配

nab*_*lke 11 c++ class-design

请看下面设计类聚合的两个简化示例.

解决方案1

// need include, forward declaration is not enough
#include "door.h"

class CGarage
{
public:
    CGarage(const std::string &val);

private:
    CDoor m_door;
};
Run Code Online (Sandbox Code Playgroud)

资源

#include "garage.h"
CGarage::CGarage(const std::string &val)
        :m_door(val)
{
}
Run Code Online (Sandbox Code Playgroud)

解决方案2

#include "smart_ptr.hpp"

// forward declaration
class CDoor;

class CGarage
{
public:
    CGarage(const std::string &val);

private:
    scoped_ptr<CDoor> m_door;
};
Run Code Online (Sandbox Code Playgroud)

资源

#include "garage.h"
#include "door.h"

CGarage::CGarage(const std::string &val)
        :m_door(new CDoor(val))
{
}
Run Code Online (Sandbox Code Playgroud)

有关创建CDoor成员的问题

您在示例设计中看到了哪些优点/缺点(CDoor与自动分配的动态分配)?

这就是我想出的:

解决方案1:
+没有内存处理或生命周期的问题
+在运行时不需要昂贵的内存分配
- 在头文件中需要额外的包含(编译速度更慢?,更接近CDoor) - >头文件中的许多包含被认为是坏的...

解决方案2:
+标头中的CDoor松散耦合(仅需要前向声明)
- 内存需要由程序员处理

您通常喜欢哪种设计?

Mat*_* M. 8

我们很少得到问题设计(我的意思是,有趣的).

让我们暂时忘掉(显然)做作的例子并专注于这个概念.

我们有2个解决方案:

  • 遏制:拉入标题并直接构建对象
  • 包含:转发声明标头并使用指针

我会暂时放弃所有"表演"的论点.性能在97%的时间内无关紧要(Knuth说)所以除非我们测量明显的差异,因为功能相同,所以我们现在不必担心它.

因此,我们有两个正交的概念试图影响我们的决定:

  • 依赖性使我们倾向于遏制
  • 简单性使我们倾向于遏制

这里的一些答案正确地谈到了多态性,但确切的实现Door是一个Door关注的细节,而不是Garage.如果Door希望提供多种实现,那就没关系,只要客户不需要关注这些细节.

我自己也是KISS和YAGNI原则的粉丝.所以我会赞成遏制......有一点需要注意.

在设计一个将要公开的接口时,一个接口因此位于库的前沿,那么这个接口应该暴露出最少的依赖关系和内部.理想情况下,这应该是一个Facade或一个Proxy对象,其唯一目的是隐藏库的内部,并且此对象在其标头中应具有最小的依赖性并具有最大的布局兼容性,这意味着:

  • 没有虚拟方法
  • 一个简单的指针作为属性(Pimpl)

对于所有内部课程,简单性取胜.