在PIMPL习语中使用引用的利弊

时间:2013-04-11 09:50:12

标签: c++ design-patterns

如上所述herePIMPL idiom可以使用引用( d-reference )而不是指针( d-pointer )。

我试图了解此实施是否存在任何严重问题以及利弊是什么。

优点:

  • 由于使用“。”而缩短语法。而不是“ - >”。
  • ...

缺点:

  • 如果 new ObjectPivate()失败并且 new 没有抛出(例如: new(std :: nothrow)或自定义,该怎么办? new )并返回 nullptr ?您需要实现其他内容来检查referance是否有效。如果是指针,你只需使用:

if (m_Private)
  m_Private->Foo();

  • 在具有复杂初始化逻辑的 Object 的多个构造函数的极少数情况下,该解决方案可能不适用。 [©JamesKanze]
  • 使用指针进行内存管理更自然。 [©JamesKanze]
  • 需要考虑一些额外的实现细节(使用 swap())以确保异常安全(例如赋值运算符的实现)[©Matt Yang]
  • ...

此处为示例代码示例:

// Header file

class ObjectPrivate;

class Object
{
public:
 Object();
 virtual ~Object();

 virtual void Foo();

 private:
   ObjectPrivate&  m_Private;
};

// Cpp file

class ObjectPrivate
{
public:
  void Boo() { std::cout << "boo" << std::endl; }
};

Object::Object() :
m_Private(* new ObjectPrivate())
{
}

Object::~Object()
{
  delete &m_Private;
}

void Object::Foo()
{
  m_Private.Boo();
}

3 个答案:

答案 0 :(得分:6)

这真的只是风格问题。我倾向于不使用 类中的引用开头,所以在中使用指针 编译防火墙似乎更自然。但是有 通常没有真正的优势:new可以 通过异常失败。

你可能喜欢指针的一种情况是 对象有很多不同的构造函数,其中一些需要 在致电new之前进行初步计算。在这 例如,您可以使用NULL初始化指针,然后调用 常见的初始化例程。我认为这种情况很少见, 然而。 (我已经遇到过一次,我记得。)

编辑:

只是另一种风格考虑因素:很多人不喜欢像delete &something;这样的东西,如果你使用引用而不是指针则需要它。再一次,管理内存的对象使用指针似乎更自然(至少对我而言)。

答案 1 :(得分:1)

我认为编写异常安全的代码并不方便。

Object::operator=(Object const&)的第一个版本可能是:

Object& operator=(Object const& other)
{
    ObjectPrivate *p = &m_Private;
    m_Private = other.m_Private;        // Dangerous sometimes
    delete *p;
}

如果ObjectPrivate::operator=(ObjectPrivate const&)抛出异常,那就太危险了。那么使用临时变量呢?啊哈,没办法。如果您想要更改m_Private,则必须调用operator=()

所以,void ObjectPrivate::swap(ObjectPrivate&) noexcept可以充当我们的救世主。

Object& operator=(Object const& other)
{
    ObjectPrivate *tmp = new ObjectPrivate(other.m_Private);
    m_Private.swap(*tmp);                // Well, no exception.
    delete tmp;
}

然后考虑void ObjectPrivate::swap(ObjectPrivate&) noexcept的实施。我们假设ObjectPrivate可能包含没有swap() noexceptoperator=() noexcept的类实例。我认为这很难。

好吧,这个假设太严格了,有时也不正确。即便如此,在大多数情况下,ObjectPrivate也没有必要提供swap() noexcept,因为它通常是一个集中数据的辅助结构。

相比之下,指针可以节省大量的脑细胞。

Object& operator=(Object const& other)
{
    ObjectPrivate *tmp = new ObjectPrivate(*other.p_Private);
    delete p_Private;
    p_Private = tmp;        // noexcept ensured
}

如果使用智能指针,它会更加优雅。

Object& operator=(Object const& other)
{
    p_Private.reset(new ObjectPrivate(*other.p_Private));
}

答案 2 :(得分:1)

一些快速而明显的补充:

<强>临

  • 参考不得为0
  • 可能不会为参考分配另一个实例。
  • 由于变量较少,类职责/实施更简单。
  • 编译器可以进行一些优化。

<强> N

  • 可能不会为参考分配另一个实例。
  • 对于某些情况,参考文件限制性太强。
相关问题