上传cppreference
This commit is contained in:
147
web/cppreference-zh/chmhelp/cpp-language-rule_of_three.html
Normal file
147
web/cppreference-zh/chmhelp/cpp-language-rule_of_three.html
Normal file
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
||||
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>三/五/零之法则</title>
|
||||
<link rel="stylesheet" href="ext.css" />
|
||||
<link rel="stylesheet" href="site_modules.css" />
|
||||
</head>
|
||||
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_rule_of_three skin-cppreference2 action-view cpp-navbar">
|
||||
<div id="cpp-content-base">
|
||||
<div id="content"><a id="top"></a>
|
||||
<h1 id="firstHeading" class="firstHeading">三/五/零之法则</h1>
|
||||
<div id="bodyContent">
|
||||
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
||||
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
||||
<h3><span class="mw-headline" id=".E4.B8.89.E4.B9.8B.E6.B3.95.E5.88.99">三之法则</span></h3>
|
||||
<p>若某个类需要用户定义的<a href="cpp-language-destructor.html">析构函数</a>、用户定义的<a href="cpp-language-copy_constructor.html">复制构造函数</a>或用户定义的<a href="cpp-language-copy_assignment.html">复制赋值运算符</a>,则它几乎肯定三者全部都需要。</p>
|
||||
<p>因为 C++ 在各种场合(按值传递/返回、操纵容器等)对对象进行复制和复制赋值,若可访问,这些特殊成员函数就会被调用,而且若用户不定义他们,则编译器会隐式定义。</p>
|
||||
<p>如果类对某种资源进行管理,而资源句柄是非类类型的对象(裸指针、POSIX 文件描述符等),则这些隐式定义的成员函数通常都不正确,其析构函数不做任何事,而复制构造函数/复制赋值运算符则进行“浅复制”(复制句柄的值,而不复制底层资源)。</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">class</span> rule_of_three
|
||||
<span class="br0">{</span>
|
||||
<span class="kw4">char</span><span class="sy2">*</span> cstring<span class="sy4">;</span> <span class="co1">// 以裸指针为动态分配内存的句柄</span>
|
||||
|
||||
<span class="kw4">void</span> init<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> s<span class="br0">)</span>
|
||||
<span class="br0">{</span>
|
||||
<a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> n <span class="sy1">=</span> <a href="cpp-string-byte-strlen.html"><span class="kw1145">std::<span class="me2">strlen</span></span></a><span class="br0">(</span>s<span class="br0">)</span> <span class="sy2">+</span> <span class="nu0">1</span><span class="sy4">;</span>
|
||||
cstring <span class="sy1">=</span> new <span class="kw4">char</span><span class="br0">[</span>n<span class="br0">]</span><span class="sy4">;</span>
|
||||
<a href="cpp-string-byte-memcpy.html"><span class="kw1159">std::<span class="me2">memcpy</span></span></a><span class="br0">(</span>cstring, s, n<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 填充</span>
|
||||
<span class="br0">}</span>
|
||||
<span class="kw1">public</span><span class="sy4">:</span>
|
||||
rule_of_three<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> s <span class="sy1">=</span> <span class="st0">""</span><span class="br0">)</span> <span class="br0">{</span> init<span class="br0">(</span>s<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
||||
|
||||
~rule_of_three<span class="br0">(</span><span class="br0">)</span>
|
||||
<span class="br0">{</span>
|
||||
delete<span class="br0">[</span><span class="br0">]</span> cstring<span class="sy4">;</span> <span class="co1">// 解分配</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
rule_of_three<span class="br0">(</span><span class="kw4">const</span> rule_of_three<span class="sy3">&</span> other<span class="br0">)</span> <span class="co1">// 复制构造函数</span>
|
||||
<span class="br0">{</span>
|
||||
init<span class="br0">(</span>other.<span class="me1">cstring</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
rule_of_three<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> rule_of_three<span class="sy3">&</span> other<span class="br0">)</span> <span class="co1">// 复制赋值</span>
|
||||
<span class="br0">{</span>
|
||||
<span class="kw1">if</span><span class="br0">(</span>this <span class="sy3">!</span><span class="sy1">=</span> <span class="sy3">&</span>other<span class="br0">)</span> <span class="br0">{</span>
|
||||
delete<span class="br0">[</span><span class="br0">]</span> cstring<span class="sy4">;</span> <span class="co1">// 解分配</span>
|
||||
init<span class="br0">(</span>other.<span class="me1">cstring</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="br0">}</span>
|
||||
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
||||
<span class="br0">}</span>
|
||||
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
||||
</div>
|
||||
<p>通过可复制句柄来管理不可复制资源的类,可能必须将其复制赋值和复制构造函数声明为私有的并且不提供其定义,或将它们定义为弃置的。这是三之法则的另一种应用:删除其中之一而遗留其他被隐式定义很可能会导致错误。</p>
|
||||
<h3><span class="mw-headline" id=".E4.BA.94.E4.B9.8B.E6.B3.95.E5.88.99">五之法则</span></h3>
|
||||
<p>因为用户定义析构函数、复制构造函数或复制赋值运算符的存在阻止<a href="cpp-language-move_constructor.html">移动构造函数</a>和<a href="cpp-language-move_assignment.html">移动赋值运算符</a>的隐式定义,所以任何想要移动语义的类必须声明全部五个特殊成员函数:</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">class</span> rule_of_five
|
||||
<span class="br0">{</span>
|
||||
<span class="kw4">char</span><span class="sy2">*</span> cstring<span class="sy4">;</span> <span class="co1">// 以裸指针为动态分配内存的句柄</span>
|
||||
<span class="kw1">public</span><span class="sy4">:</span>
|
||||
rule_of_five<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> s <span class="sy1">=</span> <span class="st0">""</span><span class="br0">)</span>
|
||||
<span class="sy4">:</span> cstring<span class="br0">(</span>nullptr<span class="br0">)</span>
|
||||
<span class="br0">{</span>
|
||||
<span class="kw1">if</span> <span class="br0">(</span>s<span class="br0">)</span> <span class="br0">{</span>
|
||||
<a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> n <span class="sy1">=</span> <a href="cpp-string-byte-strlen.html"><span class="kw1145">std::<span class="me2">strlen</span></span></a><span class="br0">(</span>s<span class="br0">)</span> <span class="sy2">+</span> <span class="nu0">1</span><span class="sy4">;</span>
|
||||
cstring <span class="sy1">=</span> new <span class="kw4">char</span><span class="br0">[</span>n<span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 分配</span>
|
||||
<a href="cpp-string-byte-memcpy.html"><span class="kw1159">std::<span class="me2">memcpy</span></span></a><span class="br0">(</span>cstring, s, n<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 填充</span>
|
||||
<span class="br0">}</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
~rule_of_five<span class="br0">(</span><span class="br0">)</span>
|
||||
<span class="br0">{</span>
|
||||
delete<span class="br0">[</span><span class="br0">]</span> cstring<span class="sy4">;</span> <span class="co1">// 解分配</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
rule_of_five<span class="br0">(</span><span class="kw4">const</span> rule_of_five<span class="sy3">&</span> other<span class="br0">)</span> <span class="co1">// 复制构造函数</span>
|
||||
<span class="sy4">:</span> rule_of_five<span class="br0">(</span>other.<span class="me1">cstring</span><span class="br0">)</span>
|
||||
<span class="br0">{</span><span class="br0">}</span>
|
||||
|
||||
rule_of_five<span class="br0">(</span>rule_of_five<span class="sy3">&&</span> other<span class="br0">)</span> <span class="kw1">noexcept</span> <span class="co1">// 移动构造函数</span>
|
||||
<span class="sy4">:</span> cstring<span class="br0">(</span><a href="cpp-utility-exchange.html"><span class="kw1118">std::<span class="me2">exchange</span></span></a><span class="br0">(</span>other.<span class="me1">cstring</span>, nullptr<span class="br0">)</span><span class="br0">)</span>
|
||||
<span class="br0">{</span><span class="br0">}</span>
|
||||
|
||||
rule_of_five<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> rule_of_five<span class="sy3">&</span> other<span class="br0">)</span> <span class="co1">// 复制赋值</span>
|
||||
<span class="br0">{</span>
|
||||
<span class="kw1">return</span> <span class="sy2">*</span>this <span class="sy1">=</span> rule_of_five<span class="br0">(</span>other<span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
rule_of_five<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span>rule_of_five<span class="sy3">&&</span> other<span class="br0">)</span> <span class="kw1">noexcept</span> <span class="co1">// 移动赋值</span>
|
||||
<span class="br0">{</span>
|
||||
<a href="cpp-algorithm-swap.html"><span class="kw1599">std::<span class="me2">swap</span></span></a><span class="br0">(</span>cstring, other.<span class="me1">cstring</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
||||
<span class="br0">}</span>
|
||||
|
||||
<span class="co1">// 另一种方法是用以下函数替代两个赋值运算符</span>
|
||||
<span class="co1">// rule_of_five& operator=(rule_of_five other) noexcept</span>
|
||||
<span class="co1">// {</span>
|
||||
<span class="co1">// std::swap(cstring, other.cstring);</span>
|
||||
<span class="co1">// return *this;</span>
|
||||
<span class="co1">// }</span>
|
||||
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
||||
</div>
|
||||
<p>与三之法则不同的是,不提供移动构造函数和移动赋值运算符通常不是错误,但会导致失去优化机会。</p>
|
||||
<h3><span class="mw-headline" id=".E9.9B.B6.E4.B9.8B.E6.B3.95.E5.88.99">零之法则</span></h3>
|
||||
<p>有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符的类应该专门处理所有权(这遵循<a href="https://en.wikipedia.org/wiki/Single_responsibility_principle" class="extiw" title="enwiki:Single responsibility principle">单一责任原则</a>)。其他类都不应该拥有自定义的析构函数、复制/移动构造函数或复制/移动赋值运算符<sup id="cite_ref-1" class="reference"><a href="cpp-language-rule_of_three.html#cite_note-1">[1]</a></sup></p>
|
||||
<p>这条法则也在 C++ 核心指南(C++ Core Guidelines)中出现,为 <a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-zero">C.20:一旦可以避免定义默认操作就应当施行</a>。</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">class</span> rule_of_zero
|
||||
<span class="br0">{</span>
|
||||
<a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> cppstring<span class="sy4">;</span>
|
||||
<span class="kw1">public</span><span class="sy4">:</span>
|
||||
rule_of_zero<span class="br0">(</span><span class="kw4">const</span> <a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a><span class="sy3">&</span> arg<span class="br0">)</span> <span class="sy4">:</span> cppstring<span class="br0">(</span>arg<span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
|
||||
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
||||
</div>
|
||||
<p>当有意将某个基类用于多态用途时,可能必须将其析构函数声明为公开的虚函数。由于这会阻拦隐式移动(并弃用隐式复制)的生成,因而必须将各特殊成员函数声明为预置的<sup id="cite_ref-2" class="reference"><a href="cpp-language-rule_of_three.html#cite_note-2">[2]</a></sup></p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">class</span> base_of_five_defaults
|
||||
<span class="br0">{</span>
|
||||
<span class="kw1">public</span><span class="sy4">:</span>
|
||||
base_of_five_defaults<span class="br0">(</span><span class="kw4">const</span> base_of_five_defaults<span class="sy3">&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
||||
base_of_five_defaults<span class="br0">(</span>base_of_five_defaults<span class="sy3">&&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
||||
base_of_five_defaults<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> base_of_five_defaults<span class="sy3">&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
||||
base_of_five_defaults<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span>base_of_five_defaults<span class="sy3">&&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
||||
<span class="kw1">virtual</span> ~base_of_five_defaults<span class="br0">(</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
||||
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
||||
</div>
|
||||
<p>然而这使得类有可能被切片,这是多态类经常把复制定义为弃置的原因(见 C++ 核心指南中的 <a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual">C.67:多态类应该抑制复制操作</a>),这带来了下列的五之法则的通用说法:</p>
|
||||
<dl>
|
||||
<dd><a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-five">C.21:若有任何默认操作被定义或 =delete,则应当对它们全部进行定义或 =delete</a></dd>
|
||||
</dl>
|
||||
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
||||
<ol class="references">
|
||||
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="cpp-language-rule_of_three.html#cite_ref-1">↑</a></span> <span class="reference-text"><a rel="nofollow" class="external text" href="https://rmf.io/cxx11/rule-of-zero">“零之法则”,R. Martinho Fernandes 8/15/2012</a></span></li>
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="cpp-language-rule_of_three.html#cite_ref-2">↑</a></span> <span class="reference-text"><a rel="nofollow" class="external text" href="http://scottmeyers.blogspot.fr/2014/03/a-concern-about-rule-of-zero.html">“关于零之法则的考虑”,Scott Meyers, 3/13/2014</a></span></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="visualClear"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user