为什么分配器接口封装“列表”实现的嵌套类也可以从封装节点中受益?

时间:2013-03-23 20:09:27

标签: c++ list optimization

The "Empty Member" C++ Optimization中是以下代码段:

// Listing 4: A Better Way to Eliminate Bloat

template <class T, class Alloc = allocator<T> >
class list {
  . . .
  struct Node { . . . };
  struct P : public Alloc {
    P(Alloc const& a) : Alloc(a), p(0) { }
    Node* p; // Why also encapsulate a Node pointer?
  };
  P head_;

 public:
  explicit list(Alloc const& a = Alloc())
    : head_(a) { . . . }
  . . .
};

此实现的目的主要是在Alloc类没有数据成员(如默认的allocator类)中时避免对象膨胀,但这个特定实现的一个重要的显着优点是防止(任意)Alloc类与list类成员之间潜在的名称冲突。

代码段下方的评论通过声明以下内容对此进行了总结:

  

现在,列出&lt;&gt;成员通过说“head_.allocate()”得到存储,和   通过说“head_.p”提到第一个列表元素。这有效   完美的,没有任何不必要的开销和用户   列表&LT;&GT;分不清楚。

我理解此评论的第一部分,即P类中的界面封装,以便list<>成员必须调用head_.allocate()来获取存储空间。

但是,我不理解此评论的第二部分 - 为什么在课程Node中包含P指针是必要的(或有益的),以便list<>成员提及第一个列表元素是说head_.p(而不仅仅是让p成为list<>的成员)?这是否封装了Alloc类的接口?

为什么包含接口的嵌套类具有指向Node的指针,而不是顶级类?

2 个答案:

答案 0 :(得分:2)

在这种情况下,sizeof(list)将是sizeof(P),而sizeof(Node*)又是Alloc。如果class list { struct Node { . . . }; struct P : public Alloc { P(Alloc const& a) : Alloc(a), p(0) { } }; Node* head_pointer; P thing_with_allocator; } 类型为空,则其空间使用情况将被优化掉。

现在考虑以下替代方案。

sizeof(list)

在这种情况下,sizeof(Node*) + sizeof(P)将为P。空基类优化在这里确实没有用,因为即使{{1}}现在是一个空类,它也不会被用作基类,所以它不能受空基类优化的影响:它的大小至少是1。

答案 1 :(得分:1)

你的选择是什么?

class list {
    struct P : public Alloc {
        P(Alloc const& a) : Alloc(a), p(0) { }
    };
    P a;
    Node* h;
    ...
};

然后你就不会保存任何东西,因为你必须实例化一个可能为零的P对象。这将占用列表对象中至少一个字节。