4

正如这里提到的,在PIMPL idiom的情况下,您可以使用引用 ( d-reference ) 而不是指针 ( d-pointer ) 。

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

优点:

  • 由于使用了“。”,因此语法更短。而不是“->”。
  • ...

缺点:

  • 如果new ObjectPivate()失败并且new没有抛出(例如:new(std::nothrow)或自定义new)并返回nullptr怎么办?您需要实施额外的东西来检查引用是否有效。如果是指针,您只需使用:

if (m_Private)
  m_Private->Foo();

  • 在具有复杂初始化逻辑的对象的多个构造函数的极少数情况下,该解决方案可能不适用。[©詹姆斯坎泽]
  • 使用指针进行内存管理更自然。[©詹姆斯坎泽]
  • 需要考虑一些额外的实现细节(使用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();
}
4

3 回答 3

6

这真的只是风格问题。我一开始倾向于不在类中使用引用,因此在编译防火墙中使用指针似乎更自然。但是通常没有任何一种方式真正的优势:new只能 通过异常来失败

您可能喜欢指针的一种情况是当对象有很多不同的构造函数时,其中一些需要在调用new. 在这种情况下,您可以使用 初始化指针NULL,然后调用通用初始化例程。但是,我认为这种情况很少见。(我遇到过一次,我记得。)

编辑:

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

于 2013-04-11T10:03:08.333 回答
1

一些快速而明显的补充:

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

骗局

  • 不能为引用分配另一个实例。
  • 对于某些情况,该参考将过于严格。
于 2013-04-25T23:57:48.953 回答
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&)抛出异常是危险的。那么使用临时变量呢?啊哈,没办法。operator=()如果要更改 m_Private,则必须调用。

所以,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() noexceptor的类实例operator=() 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));
}
于 2013-04-13T15:21:30.170 回答