83 lines
12 KiB
HTML
83 lines
12 KiB
HTML
<!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>RAII</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_raii skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">RAII</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">
|
|
<p><i>资源获取即初始化</i>(<i>Resource Acquisition Is Initialization</i>),或称 RAII,是一种 C++ 编程技术<sup id="cite_ref-1" class="reference"><a href="cpp-language-raii.html#cite_note-1">[1]</a></sup><sup id="cite_ref-2" class="reference"><a href="cpp-language-raii.html#cite_note-2">[2]</a></sup>,它将必须在使用前请求的资源(分配的堆内存、执行线程、打开的套接字、打开的文件、锁定的互斥体、磁盘空间、数据库连接等——任何存在受限供给中的事物)的生命周期绑定与一个对象的<a href="cpp-language-lifetime.html">生存期</a>相绑定。</p>
|
|
<p>RAII 保证资源可用于任何会访问该对象的函数(资源可用性是一种<a href="https://en.wikipedia.org/wiki/Class_invariant" class="extiw" title="enwiki:Class invariant">类不变式</a>,这会消除冗余的运行时测试)。它亦保证所有资源在其控制对象的生存期结束时,以获取顺序的逆序释放。类似地,若资源获取失败(构造函数以异常退出),则为已构造完成的对象和基类子对象所获取的所有资源,会以初始化顺序的逆序释放。这有效地利用了语言特性(<a href="cpp-language-lifetime.html">对象生存期</a>、<a href="cpp-language-statements.html">退出作用域</a>、<a href="cpp-language-initializer_list.html#.E5.88.9D.E5.A7.8B.E5.8C.96.E9.A1.BA.E5.BA.8F" class="mw-redirect">初始化顺序</a>以及<a href="cpp-language-throw.html#.E6.A0.88.E5.9B.9E.E6.BA.AF">栈回溯</a>)以消除内存泄漏并保证异常安全。根据 RAII 对象的生存期在退出作用域时结束这一基本状况,此技术的另一名称是<i>作用域界定的资源管理</i>( <i>Scope-Bound Resource Management</i>,SBRM)。</p>
|
|
<p>RAII 可总结如下:</p>
|
|
<ul>
|
|
<li>将每个资源封装入一个类,其中</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>构造函数请求资源,并建立所有类不变式,或在它无法完成时抛出异常,</li>
|
|
<li>析构函数释放资源并决不抛出异常;</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li>始终经由 RAII 类的实例使用满足要求的资源,该资源</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>自身拥有自动存储期或临时生存期,或</li>
|
|
<li>具有与自动或临时对象的生存期绑定的生存期</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<p>移动语义使得在对象间,跨作用域,以及在线程内外安全地移动所有权,而同时维护资源安全成为可能。</p>
|
|
<p>拥有 open()/close()、lock()/unlock(),或 init()/copyFrom()/destroy() 成员函数的类是非 RAII 类的典型的例子:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-thread-mutex.html"><span class="kw2154">std::<span class="me2">mutex</span></span></a> m<span class="sy4">;</span>
|
|
|
|
<span class="kw4">void</span> bad<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
m.<span class="me1">lock</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 请求互斥体</span>
|
|
f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 若 f() 抛异常,则互斥体永远不被释放</span>
|
|
<span class="kw1">if</span><span class="br0">(</span><span class="sy3">!</span>everything_ok<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span> <span class="kw1">return</span><span class="sy4">;</span> <span class="co1">// 提早返回,互斥体永远不被释放</span>
|
|
m.<span class="me1">unlock</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 若 bad() 抵达此语句,互斥才被释放</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">void</span> good<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-thread-lock_guard.html"><span class="kw2166">std::<span class="me2">lock_guard</span></span></a><span class="sy1"><</span><a href="cpp-thread-mutex.html"><span class="kw2154">std::<span class="me2">mutex</span></span></a><span class="sy1">></span> lk<span class="br0">(</span>m<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// RAII类:互斥体的请求即是初始化</span>
|
|
f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 若 f() 抛异常,则释放互斥体</span>
|
|
<span class="kw1">if</span><span class="br0">(</span><span class="sy3">!</span>everything_ok<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span> <span class="kw1">return</span><span class="sy4">;</span> <span class="co1">// 提早返回,互斥体被释放</span>
|
|
<span class="br0">}</span> <span class="co1">// 若 good() 正常返回,则释放互斥体</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E6.A0.87.E5.87.86.E5.BA.93">标准库</span></h3>
|
|
<p>C++ 标准库遵循 RAII 管理其自身的资源:<span class="t-lc"><a href="cpp-string-basic_string.html">std::string</a></span>、<span class="t-lc"><a href="cpp-container-vector.html">std::vector</a></span>、<span class="t-lc"><a href="cpp-thread-thread.html">std::thread</a></span>,以及多数其他类在构造函数中获取其资源(错误时抛出异常),并在其析构函数中释放之(决不抛出),而不要求显式清理。</p>
|
|
<p>另外,标准库提供几种 RAII 包装器以管理用户提供的资源:</p>
|
|
<ul>
|
|
<li><span class="t-lc"><a href="cpp-memory-unique_ptr.html">std::unique_ptr</a></span> 及 <span class="t-lc"><a href="cpp-memory-shared_ptr.html">std::shared_ptr</a></span> 用于管理动态分配的内存,或以用户提供的删除器管理任何以普通指针表示的资源;</li>
|
|
<li><span class="t-lc"><a href="cpp-thread-lock_guard.html">std::lock_guard</a></span>、<span class="t-lc"><a href="cpp-thread-unique_lock.html">std::unique_lock</a></span>、<span class="t-lc"><a href="cpp-thread-shared_lock.html">std::shared_lock</a></span> 用于管理互斥体。</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>RAII 不适用于并非在使用前请求的资源:CPU 时间、核心,以及缓存容量、熵池容量、网络带宽、电力消费、栈内存等。</p>
|
|
<h2><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h2>
|
|
<ol class="references">
|
|
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="cpp-language-raii.html#cite_ref-1">↑</a></span> <span class="reference-text"><a rel="nofollow" class="external text" href="http://www.stroustrup.com/bs_faq2.html#finally">Stroustrup's C++ FAQ 中的 RAII</a></span></li>
|
|
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="cpp-language-raii.html#cite_ref-2">↑</a></span> <span class="reference-text"><a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#e6-use-raii-to-prevent-leaks">C++ 核心方针 E.6 “用 RAII 避免泄露”</a></span></li>
|
|
</ol>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|