mirror of
https://github.com/CnTransGroup/EffectiveModernCppChinese.git
synced 2024-12-29 14:30:46 +08:00
deploy: 7559edafc3
This commit is contained in:
parent
098fceafe5
commit
e58f62ff21
@ -167,8 +167,8 @@ private:
|
||||
};
|
||||
</code></pre>
|
||||
<p>因为类<code>Widget</code>不再提到类型<code>std::string</code>,<code>std::vector</code>以及<code>Gadget</code>,<code>Widget</code>的使用者不再需要为了这些类型而引入头文件。 这可以加速编译,并且意味着,如果这些头文件中有所变动,<code>Widget</code>的使用者不会受到影响。</p>
|
||||
<p>一个已经被声明,却还未被实现的类型,被称为<strong>未完成类型</strong>(<em>incomplete type</em>)。 <code>Widget::Impl</code>就是这种类型。 你能对一个未完成类型做的事很少,但是声明一个指向它的指针是可以的。Pimpl惯用法利用了这一点。</p>
|
||||
<p>Pimpl惯用法的第一步,是声明一个数据成员,它是个指针,指向一个未完成类型。 第二步是动态分配和回收一个对象,该对象包含那些以前在原来的类中的数据成员。 内存分配和回收的代码都写在实现文件里,比如,对于类<code>Widget</code>而言,写在<code>Widget.cpp</code>里:</p>
|
||||
<p>一个已经被声明,却还未被实现的类型,被称为<strong>不完整类型</strong>(<em>incomplete type</em>)。 <code>Widget::Impl</code>就是这种类型。 你能对一个不完整类型做的事很少,但是声明一个指向它的指针是可以的。Pimpl惯用法利用了这一点。</p>
|
||||
<p>Pimpl惯用法的第一步,是声明一个数据成员,它是个指针,指向一个不完整类型。 第二步是动态分配和回收一个对象,该对象包含那些以前在原来的类中的数据成员。 内存分配和回收的代码都写在实现文件里,比如,对于类<code>Widget</code>而言,写在<code>Widget.cpp</code>里:</p>
|
||||
<pre><code class="language-cpp">#include "widget.h" //以下代码均在实现文件“widget.cpp”里
|
||||
#include "gadget.h"
|
||||
#include <string>
|
||||
@ -221,10 +221,10 @@ Widget::Widget() //根据条款21,通过std::make_unique
|
||||
|
||||
Widget w; //错误!
|
||||
</code></pre>
|
||||
<p>你所看到的错误信息根据编译器不同会有所不同,但是其文本一般会提到一些有关于“把<code>sizeof</code>或<code>delete</code>应用到未完成类型上”的信息。对于未完成类型,使用以上操作是禁止的。</p>
|
||||
<p>在Pimpl惯用法中使用<code>std::unique_ptr</code>会抛出错误,有点惊悚,因为第一<code>std::unique_ptr</code>宣称它支持未完成类型,第二Pimpl惯用法是<code>std::unique_ptr</code>的最常见的使用情况之一。 幸运的是,让这段代码能正常运行很简单。 只需要对上面出现的问题的原因有一个基础的认识就可以了。</p>
|
||||
<p>在对象<code>w</code>被析构时(例如离开了作用域),问题出现了。在这个时候,它的析构函数被调用。我们在类的定义里使用了<code>std::unique_ptr</code>,所以我们没有声明一个析构函数,因为我们并没有任何代码需要写在里面。根据编译器自动生成的特殊成员函数的规则(见 <a href="../3.MovingToModernCpp/item17.html">Item17</a>),编译器会自动为我们生成一个析构函数。 在这个析构函数里,编译器会插入一些代码来调用类<code>Widget</code>的数据成员<code>pImpl</code>的析构函数。 <code>pImpl</code>是一个<code>std::unique_ptr<Widget::Impl></code>,也就是说,一个使用默认删除器的<code>std::unique_ptr</code>。 默认删除器是一个函数,它使用<code>delete</code>来销毁内置于<code>std::unique_ptr</code>的原始指针。然而,在使用<code>delete</code>之前,通常会使默认删除器使用C++11的特性<code>static_assert</code>来确保原始指针指向的类型不是一个未完成类型。 当编译器为<code>Widget w</code>的析构生成代码时,它会遇到<code>static_assert</code>检查并且失败,这通常是错误信息的来源。 这些错误信息只在对象<code>w</code>销毁的地方出现,因为类<code>Widget</code>的析构函数,正如其他的编译器生成的特殊成员函数一样,是暗含<code>inline</code>属性的。 错误信息自身往往指向对象<code>w</code>被创建的那行,因为这行代码明确地构造了这个对象,导致了后面潜在的析构。</p>
|
||||
<p>为了解决这个问题,你只需要确保在编译器生成销毁<code>std::unique_ptr<Widget::Impl></code>的代码之前, <code>Widget::Impl</code>已经是一个完成类型(<em>complete type</em>)。 当编译器“看到”它的定义的时候,该类型就成为完成类型了。 但是 <code>Widget::Impl</code>的定义在<code>widget.cpp</code>里。成功编译的关键,就是在<code>widget.cpp</code>文件内,让编译器在“看到” <code>Widget</code>的析构函数实现之前(也即编译器插入的,用来销毁<code>std::unique_ptr</code>这个数据成员的代码的,那个位置),先定义<code>Widget::Impl</code>。</p>
|
||||
<p>你所看到的错误信息根据编译器不同会有所不同,但是其文本一般会提到一些有关于“把<code>sizeof</code>或<code>delete</code>应用到不完整类型上”的信息。对于不完整类型,使用以上操作是禁止的。</p>
|
||||
<p>在Pimpl惯用法中使用<code>std::unique_ptr</code>会抛出错误,有点惊悚,因为第一<code>std::unique_ptr</code>宣称它支持不完整类型,第二Pimpl惯用法是<code>std::unique_ptr</code>的最常见的使用情况之一。 幸运的是,让这段代码能正常运行很简单。 只需要对上面出现的问题的原因有一个基础的认识就可以了。</p>
|
||||
<p>在对象<code>w</code>被析构时(例如离开了作用域),问题出现了。在这个时候,它的析构函数被调用。我们在类的定义里使用了<code>std::unique_ptr</code>,所以我们没有声明一个析构函数,因为我们并没有任何代码需要写在里面。根据编译器自动生成的特殊成员函数的规则(见 <a href="../3.MovingToModernCpp/item17.html">Item17</a>),编译器会自动为我们生成一个析构函数。 在这个析构函数里,编译器会插入一些代码来调用类<code>Widget</code>的数据成员<code>pImpl</code>的析构函数。 <code>pImpl</code>是一个<code>std::unique_ptr<Widget::Impl></code>,也就是说,一个使用默认删除器的<code>std::unique_ptr</code>。 默认删除器是一个函数,它使用<code>delete</code>来销毁内置于<code>std::unique_ptr</code>的原始指针。然而,在使用<code>delete</code>之前,通常会使默认删除器使用C++11的特性<code>static_assert</code>来确保原始指针指向的类型不是一个不完整类型。 当编译器为<code>Widget w</code>的析构生成代码时,它会遇到<code>static_assert</code>检查并且失败,这通常是错误信息的来源。 这些错误信息只在对象<code>w</code>销毁的地方出现,因为类<code>Widget</code>的析构函数,正如其他的编译器生成的特殊成员函数一样,是暗含<code>inline</code>属性的。 错误信息自身往往指向对象<code>w</code>被创建的那行,因为这行代码明确地构造了这个对象,导致了后面潜在的析构。</p>
|
||||
<p>为了解决这个问题,你只需要确保在编译器生成销毁<code>std::unique_ptr<Widget::Impl></code>的代码之前, <code>Widget::Impl</code>已经是一个完整类型(<em>complete type</em>)。 当编译器“看到”它的定义的时候,该类型就成为完整类型了。 但是 <code>Widget::Impl</code>的定义在<code>widget.cpp</code>里。成功编译的关键,就是在<code>widget.cpp</code>文件内,让编译器在“看到” <code>Widget</code>的析构函数实现之前(也即编译器插入的,用来销毁<code>std::unique_ptr</code>这个数据成员的代码的,那个位置),先定义<code>Widget::Impl</code>。</p>
|
||||
<p>做出这样的调整很容易。只需要先在<code>widget.h</code>里,只声明类<code>Widget</code>的析构函数,但不要在这里定义它:</p>
|
||||
<pre><code class="language-cpp">class Widget { //跟之前一样,在“widget.h”中
|
||||
public:
|
||||
@ -274,7 +274,7 @@ private: //跟之前一样
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
</code></pre>
|
||||
<p>这样的做法会导致同样的错误,和之前的声明一个不带析构函数的类的错误一样,并且是因为同样的原因。 编译器生成的移动赋值操作符,在重新赋值之前,需要先销毁指针<code>pImpl</code>指向的对象。然而在<code>Widget</code>的头文件里,<code>pImpl</code>指针指向的是一个未完成类型。移动构造函数的情况有所不同。 移动构造函数的问题是编译器自动生成的代码里,包含有抛出异常的事件,在这个事件里会生成销毁<code>pImpl</code>的代码。然而,销毁<code>pImpl</code>需要<code>Impl</code>是一个完成类型。</p>
|
||||
<p>这样的做法会导致同样的错误,和之前的声明一个不带析构函数的类的错误一样,并且是因为同样的原因。 编译器生成的移动赋值操作符,在重新赋值之前,需要先销毁指针<code>pImpl</code>指向的对象。然而在<code>Widget</code>的头文件里,<code>pImpl</code>指针指向的是一个不完整类型。移动构造函数的情况有所不同。 移动构造函数的问题是编译器自动生成的代码里,包含有抛出异常的事件,在这个事件里会生成销毁<code>pImpl</code>的代码。然而,销毁<code>pImpl</code>需要<code>Impl</code>是一个完整类型。</p>
|
||||
<p>因为这个问题同上面一致,所以解决方案也一样——把移动操作的定义移动到实现文件里:</p>
|
||||
<pre><code class="language-cpp">class Widget { //仍然在“widget.h”中
|
||||
public:
|
||||
@ -353,7 +353,7 @@ auto w2(std::move(w1)); //移动构造w2
|
||||
w1 = std::move(w2); //移动赋值w1
|
||||
</code></pre>
|
||||
<p>这些都能编译,并且工作地如我们所望:<code>w1</code>将会被默认构造,它的值会被移动进<code>w2</code>,随后值将会被移动回<code>w1</code>,然后两者都会被销毁(因此导致指向的<code>Widget::Impl</code>对象一并也被销毁)。</p>
|
||||
<p><code>std::unique_ptr</code>和<code>std::shared_ptr</code>在<code>pImpl</code>指针上的表现上的区别的深层原因在于,他们支持自定义删除器的方式不同。 对<code>std::unique_ptr</code>而言,删除器的类型是这个智能指针的一部分,这让编译器有可能生成更小的运行时数据结构和更快的运行代码。 这种更高效率的后果之一就是<code>std::unique_ptr</code>指向的类型,在编译器的生成特殊成员函数(如析构函数,移动操作)被调用时,必须已经是一个完成类型。 而对<code>std::shared_ptr</code>而言,删除器的类型不是该智能指针的一部分,这让它会生成更大的运行时数据结构和稍微慢点的代码,但是当编译器生成的特殊成员函数被使用的时候,指向的对象不必是一个完成类型。(译者注:知道<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的实现,这一段才比较容易理解。)</p>
|
||||
<p><code>std::unique_ptr</code>和<code>std::shared_ptr</code>在<code>pImpl</code>指针上的表现上的区别的深层原因在于,他们支持自定义删除器的方式不同。 对<code>std::unique_ptr</code>而言,删除器的类型是这个智能指针的一部分,这让编译器有可能生成更小的运行时数据结构和更快的运行代码。 这种更高效率的后果之一就是<code>std::unique_ptr</code>指向的类型,在编译器的生成特殊成员函数(如析构函数,移动操作)被调用时,必须已经是一个完整类型。 而对<code>std::shared_ptr</code>而言,删除器的类型不是该智能指针的一部分,这让它会生成更大的运行时数据结构和稍微慢点的代码,但是当编译器生成的特殊成员函数被使用的时候,指向的对象不必是一个完整类型。(译者注:知道<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的实现,这一段才比较容易理解。)</p>
|
||||
<p>对于Pimpl惯用法而言,在<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的特性之间,没有一个比较好的折中。 因为对于像<code>Widget</code>的类以及像<code>Widget::Impl</code>的类之间的关系而言,他们是独享占有权关系,这让<code>std::unique_ptr</code>使用起来很合适。 然而,有必要知道,在其他情况中,当共享所有权存在时,<code>std::shared_ptr</code>是很适用的选择的时候,就没有<code>std::unique_ptr</code>所必需的声明——定义(function-definition)这样的麻烦事了。</p>
|
||||
<p><strong>请记住:</strong></p>
|
||||
<ul>
|
||||
|
16
print.html
16
print.html
@ -3027,8 +3027,8 @@ private:
|
||||
};
|
||||
</code></pre>
|
||||
<p>因为类<code>Widget</code>不再提到类型<code>std::string</code>,<code>std::vector</code>以及<code>Gadget</code>,<code>Widget</code>的使用者不再需要为了这些类型而引入头文件。 这可以加速编译,并且意味着,如果这些头文件中有所变动,<code>Widget</code>的使用者不会受到影响。</p>
|
||||
<p>一个已经被声明,却还未被实现的类型,被称为<strong>未完成类型</strong>(<em>incomplete type</em>)。 <code>Widget::Impl</code>就是这种类型。 你能对一个未完成类型做的事很少,但是声明一个指向它的指针是可以的。Pimpl惯用法利用了这一点。</p>
|
||||
<p>Pimpl惯用法的第一步,是声明一个数据成员,它是个指针,指向一个未完成类型。 第二步是动态分配和回收一个对象,该对象包含那些以前在原来的类中的数据成员。 内存分配和回收的代码都写在实现文件里,比如,对于类<code>Widget</code>而言,写在<code>Widget.cpp</code>里:</p>
|
||||
<p>一个已经被声明,却还未被实现的类型,被称为<strong>不完整类型</strong>(<em>incomplete type</em>)。 <code>Widget::Impl</code>就是这种类型。 你能对一个不完整类型做的事很少,但是声明一个指向它的指针是可以的。Pimpl惯用法利用了这一点。</p>
|
||||
<p>Pimpl惯用法的第一步,是声明一个数据成员,它是个指针,指向一个不完整类型。 第二步是动态分配和回收一个对象,该对象包含那些以前在原来的类中的数据成员。 内存分配和回收的代码都写在实现文件里,比如,对于类<code>Widget</code>而言,写在<code>Widget.cpp</code>里:</p>
|
||||
<pre><code class="language-cpp">#include "widget.h" //以下代码均在实现文件“widget.cpp”里
|
||||
#include "gadget.h"
|
||||
#include <string>
|
||||
@ -3081,10 +3081,10 @@ Widget::Widget() //根据条款21,通过std::make_unique
|
||||
|
||||
Widget w; //错误!
|
||||
</code></pre>
|
||||
<p>你所看到的错误信息根据编译器不同会有所不同,但是其文本一般会提到一些有关于“把<code>sizeof</code>或<code>delete</code>应用到未完成类型上”的信息。对于未完成类型,使用以上操作是禁止的。</p>
|
||||
<p>在Pimpl惯用法中使用<code>std::unique_ptr</code>会抛出错误,有点惊悚,因为第一<code>std::unique_ptr</code>宣称它支持未完成类型,第二Pimpl惯用法是<code>std::unique_ptr</code>的最常见的使用情况之一。 幸运的是,让这段代码能正常运行很简单。 只需要对上面出现的问题的原因有一个基础的认识就可以了。</p>
|
||||
<p>在对象<code>w</code>被析构时(例如离开了作用域),问题出现了。在这个时候,它的析构函数被调用。我们在类的定义里使用了<code>std::unique_ptr</code>,所以我们没有声明一个析构函数,因为我们并没有任何代码需要写在里面。根据编译器自动生成的特殊成员函数的规则(见 <a href="4.SmartPointers/../3.MovingToModernCpp/item17.html">Item17</a>),编译器会自动为我们生成一个析构函数。 在这个析构函数里,编译器会插入一些代码来调用类<code>Widget</code>的数据成员<code>pImpl</code>的析构函数。 <code>pImpl</code>是一个<code>std::unique_ptr<Widget::Impl></code>,也就是说,一个使用默认删除器的<code>std::unique_ptr</code>。 默认删除器是一个函数,它使用<code>delete</code>来销毁内置于<code>std::unique_ptr</code>的原始指针。然而,在使用<code>delete</code>之前,通常会使默认删除器使用C++11的特性<code>static_assert</code>来确保原始指针指向的类型不是一个未完成类型。 当编译器为<code>Widget w</code>的析构生成代码时,它会遇到<code>static_assert</code>检查并且失败,这通常是错误信息的来源。 这些错误信息只在对象<code>w</code>销毁的地方出现,因为类<code>Widget</code>的析构函数,正如其他的编译器生成的特殊成员函数一样,是暗含<code>inline</code>属性的。 错误信息自身往往指向对象<code>w</code>被创建的那行,因为这行代码明确地构造了这个对象,导致了后面潜在的析构。</p>
|
||||
<p>为了解决这个问题,你只需要确保在编译器生成销毁<code>std::unique_ptr<Widget::Impl></code>的代码之前, <code>Widget::Impl</code>已经是一个完成类型(<em>complete type</em>)。 当编译器“看到”它的定义的时候,该类型就成为完成类型了。 但是 <code>Widget::Impl</code>的定义在<code>widget.cpp</code>里。成功编译的关键,就是在<code>widget.cpp</code>文件内,让编译器在“看到” <code>Widget</code>的析构函数实现之前(也即编译器插入的,用来销毁<code>std::unique_ptr</code>这个数据成员的代码的,那个位置),先定义<code>Widget::Impl</code>。</p>
|
||||
<p>你所看到的错误信息根据编译器不同会有所不同,但是其文本一般会提到一些有关于“把<code>sizeof</code>或<code>delete</code>应用到不完整类型上”的信息。对于不完整类型,使用以上操作是禁止的。</p>
|
||||
<p>在Pimpl惯用法中使用<code>std::unique_ptr</code>会抛出错误,有点惊悚,因为第一<code>std::unique_ptr</code>宣称它支持不完整类型,第二Pimpl惯用法是<code>std::unique_ptr</code>的最常见的使用情况之一。 幸运的是,让这段代码能正常运行很简单。 只需要对上面出现的问题的原因有一个基础的认识就可以了。</p>
|
||||
<p>在对象<code>w</code>被析构时(例如离开了作用域),问题出现了。在这个时候,它的析构函数被调用。我们在类的定义里使用了<code>std::unique_ptr</code>,所以我们没有声明一个析构函数,因为我们并没有任何代码需要写在里面。根据编译器自动生成的特殊成员函数的规则(见 <a href="4.SmartPointers/../3.MovingToModernCpp/item17.html">Item17</a>),编译器会自动为我们生成一个析构函数。 在这个析构函数里,编译器会插入一些代码来调用类<code>Widget</code>的数据成员<code>pImpl</code>的析构函数。 <code>pImpl</code>是一个<code>std::unique_ptr<Widget::Impl></code>,也就是说,一个使用默认删除器的<code>std::unique_ptr</code>。 默认删除器是一个函数,它使用<code>delete</code>来销毁内置于<code>std::unique_ptr</code>的原始指针。然而,在使用<code>delete</code>之前,通常会使默认删除器使用C++11的特性<code>static_assert</code>来确保原始指针指向的类型不是一个不完整类型。 当编译器为<code>Widget w</code>的析构生成代码时,它会遇到<code>static_assert</code>检查并且失败,这通常是错误信息的来源。 这些错误信息只在对象<code>w</code>销毁的地方出现,因为类<code>Widget</code>的析构函数,正如其他的编译器生成的特殊成员函数一样,是暗含<code>inline</code>属性的。 错误信息自身往往指向对象<code>w</code>被创建的那行,因为这行代码明确地构造了这个对象,导致了后面潜在的析构。</p>
|
||||
<p>为了解决这个问题,你只需要确保在编译器生成销毁<code>std::unique_ptr<Widget::Impl></code>的代码之前, <code>Widget::Impl</code>已经是一个完整类型(<em>complete type</em>)。 当编译器“看到”它的定义的时候,该类型就成为完整类型了。 但是 <code>Widget::Impl</code>的定义在<code>widget.cpp</code>里。成功编译的关键,就是在<code>widget.cpp</code>文件内,让编译器在“看到” <code>Widget</code>的析构函数实现之前(也即编译器插入的,用来销毁<code>std::unique_ptr</code>这个数据成员的代码的,那个位置),先定义<code>Widget::Impl</code>。</p>
|
||||
<p>做出这样的调整很容易。只需要先在<code>widget.h</code>里,只声明类<code>Widget</code>的析构函数,但不要在这里定义它:</p>
|
||||
<pre><code class="language-cpp">class Widget { //跟之前一样,在“widget.h”中
|
||||
public:
|
||||
@ -3134,7 +3134,7 @@ private: //跟之前一样
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
</code></pre>
|
||||
<p>这样的做法会导致同样的错误,和之前的声明一个不带析构函数的类的错误一样,并且是因为同样的原因。 编译器生成的移动赋值操作符,在重新赋值之前,需要先销毁指针<code>pImpl</code>指向的对象。然而在<code>Widget</code>的头文件里,<code>pImpl</code>指针指向的是一个未完成类型。移动构造函数的情况有所不同。 移动构造函数的问题是编译器自动生成的代码里,包含有抛出异常的事件,在这个事件里会生成销毁<code>pImpl</code>的代码。然而,销毁<code>pImpl</code>需要<code>Impl</code>是一个完成类型。</p>
|
||||
<p>这样的做法会导致同样的错误,和之前的声明一个不带析构函数的类的错误一样,并且是因为同样的原因。 编译器生成的移动赋值操作符,在重新赋值之前,需要先销毁指针<code>pImpl</code>指向的对象。然而在<code>Widget</code>的头文件里,<code>pImpl</code>指针指向的是一个不完整类型。移动构造函数的情况有所不同。 移动构造函数的问题是编译器自动生成的代码里,包含有抛出异常的事件,在这个事件里会生成销毁<code>pImpl</code>的代码。然而,销毁<code>pImpl</code>需要<code>Impl</code>是一个完整类型。</p>
|
||||
<p>因为这个问题同上面一致,所以解决方案也一样——把移动操作的定义移动到实现文件里:</p>
|
||||
<pre><code class="language-cpp">class Widget { //仍然在“widget.h”中
|
||||
public:
|
||||
@ -3213,7 +3213,7 @@ auto w2(std::move(w1)); //移动构造w2
|
||||
w1 = std::move(w2); //移动赋值w1
|
||||
</code></pre>
|
||||
<p>这些都能编译,并且工作地如我们所望:<code>w1</code>将会被默认构造,它的值会被移动进<code>w2</code>,随后值将会被移动回<code>w1</code>,然后两者都会被销毁(因此导致指向的<code>Widget::Impl</code>对象一并也被销毁)。</p>
|
||||
<p><code>std::unique_ptr</code>和<code>std::shared_ptr</code>在<code>pImpl</code>指针上的表现上的区别的深层原因在于,他们支持自定义删除器的方式不同。 对<code>std::unique_ptr</code>而言,删除器的类型是这个智能指针的一部分,这让编译器有可能生成更小的运行时数据结构和更快的运行代码。 这种更高效率的后果之一就是<code>std::unique_ptr</code>指向的类型,在编译器的生成特殊成员函数(如析构函数,移动操作)被调用时,必须已经是一个完成类型。 而对<code>std::shared_ptr</code>而言,删除器的类型不是该智能指针的一部分,这让它会生成更大的运行时数据结构和稍微慢点的代码,但是当编译器生成的特殊成员函数被使用的时候,指向的对象不必是一个完成类型。(译者注:知道<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的实现,这一段才比较容易理解。)</p>
|
||||
<p><code>std::unique_ptr</code>和<code>std::shared_ptr</code>在<code>pImpl</code>指针上的表现上的区别的深层原因在于,他们支持自定义删除器的方式不同。 对<code>std::unique_ptr</code>而言,删除器的类型是这个智能指针的一部分,这让编译器有可能生成更小的运行时数据结构和更快的运行代码。 这种更高效率的后果之一就是<code>std::unique_ptr</code>指向的类型,在编译器的生成特殊成员函数(如析构函数,移动操作)被调用时,必须已经是一个完整类型。 而对<code>std::shared_ptr</code>而言,删除器的类型不是该智能指针的一部分,这让它会生成更大的运行时数据结构和稍微慢点的代码,但是当编译器生成的特殊成员函数被使用的时候,指向的对象不必是一个完整类型。(译者注:知道<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的实现,这一段才比较容易理解。)</p>
|
||||
<p>对于Pimpl惯用法而言,在<code>std::unique_ptr</code>和<code>std::shared_ptr</code>的特性之间,没有一个比较好的折中。 因为对于像<code>Widget</code>的类以及像<code>Widget::Impl</code>的类之间的关系而言,他们是独享占有权关系,这让<code>std::unique_ptr</code>使用起来很合适。 然而,有必要知道,在其他情况中,当共享所有权存在时,<code>std::shared_ptr</code>是很适用的选择的时候,就没有<code>std::unique_ptr</code>所必需的声明——定义(function-definition)这样的麻烦事了。</p>
|
||||
<p><strong>请记住:</strong></p>
|
||||
<ul>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user