关于 pimpl 语法

About the pimpl syntax

我对 pimpl 语法中使用的 C++ 用法有疑问。

首先,为什么不用把pimpl( new impl )写成pimpl( new my_class::impl )

其次,为什么new impl的生命周期延长了,即使它是一个临时对象?

//my_class.h
class my_class {
   //  ... all public and protected stuff goes here ...
private:
   class impl; unique_ptr<impl> pimpl; // opaque type here
}
// my_class.cpp
class my_class::impl {  // defined privately here
  // ... all private data and functions: all of these
  //     can now change without recompiling callers ...
};
my_class::my_class(): pimpl( new impl )
{
  // ... set impl values ...
}

首先: 在您的构造函数中,表达式 new impl 确实是一个临时对象,但它会立即传递给 pimpl 成员,这不是一个暂时的。 std::unique_ptr 类型存储传递给它的指针并保留它直到它被销毁或移动。在 my_class 对象被销毁之前,成员不会被销毁。

此外,考虑使用 std::make_unique 而不是 new

其次:你不用写my_class::impl因为你是my_class的成员函数,所以名字解析看起来not only at namespace scope but also at class scope.所以,impl可以解决

First, why is it not necessary to write pimpl( new impl ) as pimpl( new my_class::impl )

范围。定义构造函数后,您就在 class 的副本中,因此 name lookup 可以毫无问题地找到它。

Second, why is the lifetime of new impl extended even though it is a temporary object?

您正在将临时指针作为 std::unique_ptr<impl> 的构造函数参数传递给 std::unique_ptr<impl>,因此值被字段 pimpl 使用。 impl 值不是临时的,因为它是在堆上创建的,其生命周期的控制权已传递给智能指针。

额外:
您在 header 中完成了 using namespace std;(唯一可以在 unique_ptr 之前跳过 std:: 的情况)!这是大错误,不要这样做。这将对包括此 header 在内的所有来源产生危险影响。因此,您可能会遇到符号歧义错误。

即使在 cpp 文件中有 using namespace std; 也是 considered a bad practice

new impl 不是临时变量,因为它不是变量。

是一个表达式,

  1. 在您的 my_class::pimpl 对象成员构造期间进行评估,
  2. 它的副作用是创建一个 impl 对象(它的生命周期是受控的,不是自动的,因为它不是局部自动变量),
  3. 其中 returns 指向新创建的 anonymous impl 对象的指针。

表达式用作正在构造的 my_class 对象的 pimpl 成员的初始化器,因此返回的指针作为初始化值传递给 pimpl 并被存储在里面。这允许您的 my_class 对象控制 impl 对象的生命周期。

pimpl 指针变量的生命周期显然与其拥有 my_class 对象的生命周期相同,但是指向 impl 对象的生命周期取决于您的 my_class逻辑:

  1. 它可能会保留它直到它自己结束并在它自己销毁时将其删除,
  2. 可以replace()随意搭配另一个
  3. release() pimpldelete impl 对象并在不再需要时无需任何对象即可工作...
  4. 它甚至可能释放对象而不是删除它,例如将所有权转移给另一个对象——或者只是放弃它,这会导致内存泄漏(这是通常是一个严重的错误!)