365 lines
51 KiB
HTML
365 lines
51 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>new 表达式</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_new skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">new 表达式</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>创建并初始化拥有动态<a href="cpp-language-storage_duration.html">存储期</a>的对象,这些对象的生存期不受它们创建时所在的作用域限制。</p>
|
|
<h3><span class="mw-headline" id=".E8.AF.AD.E6.B3.95">语法</span></h3>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>::</b></code><span class="t-mark">(可选)</span> <code><b>new</b></code> <code><b>(</b></code> <span class="t-spar">类型</span> <code><b>)</b></code> <span class="t-spar">初始化器</span><span class="t-mark">(可选)</span></td>
|
|
<td>(1)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>::</b></code><span class="t-mark">(可选)</span> <code><b>new</b></code> <span class="t-spar">new类型</span> <span class="t-spar">初始化器</span><span class="t-mark">(可选)</span></td>
|
|
<td>(2)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>::</b></code><span class="t-mark">(可选)</span> <code><b>new</b></code> <code><b>(</b></code><span class="t-spar">布置参数</span><code><b>)</b></code> <code><b>(</b></code> <span class="t-spar">类型</span> <code><b>)</b></code> <span class="t-spar">初始化器</span><span class="t-mark">(可选)</span></td>
|
|
<td>(3)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>::</b></code><span class="t-mark">(可选)</span> <code><b>new</b></code> <code><b>(</b></code><span class="t-spar">布置参数</span><code><b>)</b></code> <span class="t-spar">new类型</span> <span class="t-spar">初始化器</span><span class="t-mark">(可选)</span></td>
|
|
<td>(4)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 尝试创建<a href="cpp-language-type.html#.E7.B1.BB.E5.9E.8B.E7.9A.84.E5.91.BD.E5.90.8D">类型标识</a> <span class="t-spar">类型</span> 所指代的类型的一个对象,它可以是数组类型<span class="t-rev-inl t-since-cxx11"><span>,可以包含<a href="cpp-language-auto.html">占位符类型说明符</a></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span><span class="t-rev-inl t-since-cxx17"><span>,或包含将由<a href="cpp-language-class_template_argument_deduction.html">类模板实参推导</a>推出的类模板名</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 同(1),但 <span class="t-spar">new类型</span> 不能包含括号:</div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1">new <span class="kw4">int</span><span class="br0">(</span><span class="sy2">*</span><span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">)</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:分析成 (new int) (*[10]) ()</span>
|
|
new <span class="br0">(</span><span class="kw4">int</span> <span class="br0">(</span><span class="sy2">*</span><span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">)</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:分配 10 个函数指针的数组</span></pre></div>
|
|
</div>
|
|
<dl>
|
|
<dd>另外,<span class="t-spar">new类型</span> 是贪心的:它将包含可以成为声明符一部分的所有记号:</dd>
|
|
</dl>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1">new <span class="kw4">int</span> <span class="sy2">+</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// OK:分析成 (new int) + 1,增加 new int 所返回的指针</span>
|
|
new <span class="kw4">int</span> <span class="sy2">*</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// 错误:分析成 (new int*) (1)</span></pre></div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 同(1),但会向<a href="cpp-memory-new-operator_new.html">分配函数</a>提供额外实参,见<a href="cpp-language-new.html#.E5.B8.83.E7.BD.AE_new">布置 new</a></div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 同(2),但会向分配函数提供额外实参</div>
|
|
<p><br />
|
|
符合下列条件之一时,必须要有 <span class="t-spar">初始化器</span>:</p>
|
|
<ul>
|
|
<li><span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是<a href="cpp-language-array.html#.E6.9C.AA.E7.9F.A5.E8.BE.B9.E7.95.8C.E6.95.B0.E7.BB.84">未知边界数组</a></li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 中使用了<a href="cpp-language-auto.html">占位符</a>,即 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span></span></span> <span class="t-rev-inl t-since-cxx14"><span>或 <span class="t-c"><span class="mw-geshi cpp source-cpp">decltype<span class="br0">(</span><span class="kw4">auto</span><span class="br0">)</span></span></span></span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span><span class="t-rev-inl t-since-cxx20"><span>,可能与<a href="cpp-language-constraints.html#.E6.A6.82.E5.BF.B5">类型约束</a>结合</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li><span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 中使用了类模板,且该模板有实参需要被<a href="cpp-language-class_template_argument_deduction.html">推导</a></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">double</span><span class="sy2">*</span> p <span class="sy1">=</span> new <span class="kw4">double</span><span class="br0">[</span><span class="br0">]</span><span class="br0">{</span><span class="nu0">1</span>,<span class="nu0">2</span>,<span class="nu0">3</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 创建 double[3] 类型的数组</span>
|
|
<span class="kw4">auto</span> p <span class="sy1">=</span> new <span class="kw4">auto</span><span class="br0">(</span><span class="st0">'c'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 创建单个 char 类型的对象。p 是一个 char*</span>
|
|
|
|
<span class="kw4">auto</span> q <span class="sy1">=</span> new std<span class="sy4">::</span><span class="me2">integral</span> <span class="kw4">auto</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK: q 是一个 int*</span>
|
|
<span class="kw4">auto</span> q <span class="sy1">=</span> new std<span class="sy4">::</span><span class="me2">floating_point</span> <span class="kw4">auto</span><span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span> <span class="co1">// 错误:不满足类型约束</span>
|
|
|
|
<span class="kw4">auto</span> r <span class="sy1">=</span> new <a href="cpp-utility-pair.html"><span class="kw1096">std::<span class="me2">pair</span></span></a><span class="br0">(</span><span class="nu0">1</span>, <span class="kw2">true</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK: r 是一个 std::pair<int, bool>*</span>
|
|
<span class="kw4">auto</span> r <span class="sy1">=</span> new <a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy4">;</span> <span class="co1">// 错误:无法推导元素类型</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p><code>new</code> 表达式尝试申请存储空间,并在已申请的存储空间上,尝试构造并初始化一个无名的对象或对象数组。<code>new</code> 表达式返回一个指向它构造的对象或者对象数组的纯右值指针。</p>
|
|
<p>如果 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是数组类型,那么它第一维之外的所有维都必须指定为正的<span class="t-rev-inl t-until-cxx14"><span><a href="cpp-language-constant_expression.html">整数常量表达式</a></span> <span><span class="t-mark-rev t-until-cxx14">(C++14 前)</span></span></span><span class="t-rev-inl t-since-cxx14"><span>类型是 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span> 的<a href="cpp-language-constant_expression.html">经转换的常量表达式</a></span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>,但(仅当使用无括号语法 <span class="t-v">(2)</span> 时)第一维可以是<span class="t-rev-inl t-until-cxx14"><span>整数类型、枚举类型或拥有单个到整数或枚举类型的非 explicit 转换函数的类类型</span> <span><span class="t-mark-rev t-until-cxx14">(C++14 前)</span></span></span><span class="t-rev-inl t-since-cxx14"><span>任何能转换成 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span> 的表达式</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>。这是唯一直接创建大小在运行时定义的数组的方法,这种数组常被称作<i>动态数组</i>:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span> n <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> a<span class="br0">[</span>n<span class="br0">]</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 错误</span>
|
|
<span class="kw4">auto</span> p1 <span class="sy1">=</span> new <span class="kw4">double</span><span class="br0">[</span>n<span class="br0">]</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="kw4">auto</span> p2 <span class="sy1">=</span> new <span class="kw4">double</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="br0">[</span>n<span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 错误:只有第一维可以不是常量</span>
|
|
<span class="kw4">auto</span> p3 <span class="sy1">=</span> new <span class="br0">(</span><span class="kw4">double</span><span class="br0">[</span>n<span class="br0">]</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:语法 (1) 不能用于动态数组</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx11">
|
|
<td>
|
|
<p>如果第一维中的值(如果需要则转换到整数或枚举类型)为负,那么行为未定义。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>下列情况中指定第一维的表达式是错误的:</p>
|
|
<ul>
|
|
<li>表达式是非类类型,且其值在转换到 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span> 前是负的;</li>
|
|
<li>表达式拥有类类型,且其值在用户定义转换函数后,在<a href="cpp-language-implicit_conversion.html">第二次标准转换</a>前是负的;</li>
|
|
<li>表达式的值大于某个实现定义极限;</li>
|
|
<li>其值小于<a href="cpp-language-aggregate_initialization.html">花括号初始化器</a>中提供的数组元素数量(也包含<a href="cpp-language-string_literal.html">字符串字面量</a>中的终止 <code>'\0'</code>)。</li>
|
|
</ul>
|
|
<p>如果第一维值因以上任何原因而错误,</p>
|
|
<ul>
|
|
<li>如果在转换到 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span> 后,第一维是<a href="cpp-language-constant_expression.html">核心常量表达式</a>,那么程序非良构(引发编译时错误);</li>
|
|
<li>否则,如果要调用的分配函数是不抛出的,那么 new 表达式返回请求结果类型的空指针</li>
|
|
<li>否则,new 表达式不调用分配函数,而代之以抛出 <span class="t-lc"><a href="cpp-memory-new-bad_array_new_length.html">std::bad_array_new_length</a></span> 类型或它的派生类的异常</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>第一维为零是可接受的,分配函数也会被调用。</p>
|
|
<p>注意:<span class="t-lc"><a href="cpp-container-vector.html">std::vector</a></span> 提供了与一维的动态数组类似的功能。</p>
|
|
<h4><span class="mw-headline" id=".E5.88.86.E9.85.8D">分配</span></h4>
|
|
<p>new 表达式通过调用适当的<a href="cpp-memory-new-operator_new.html">分配函数</a>分配存储。如果 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是非数组类型,那么函数名是 <code>operator new</code>。如果 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是数组类型,那么函数名是 <code>operator new[]</code>。</p>
|
|
<p>如<a href="cpp-memory-new-operator_new.html">分配函数</a>中所描述,C++ 程序可提供这些函数的全局和类特有替换函数。如果 new 表达式以 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy4">::</span></span></span> 运算符开始,如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy4">::</span><span class="me2">new</span> T</span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy4">::</span><span class="me2">new</span> T<span class="br0">[</span>n<span class="br0">]</span></span></span>,那么忽略类特有替换函数(在全局<a href="cpp-language-scope.html">作用域</a>中<a href="cpp-language-lookup.html">查找</a>函数)。否则,如果 <code>T</code> 是类类型,那么就会从 <code>T</code> 的类作用域中开始查找。</p>
|
|
<p>在调用分配函数时,new 表达式将请求的字节数作为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a></span></span> 类型的第一参数传递给它,该参数对于非数组 <code>T</code> 恰好是 <span class="t-c"><span class="mw-geshi cpp source-cpp">sizeof<span class="br0">(</span>T<span class="br0">)</span></span></span>。</p>
|
|
<p>数组的分配中可能带有一个未指明的开销(overhead),且每次调用 new 的这个开销可能不同,除非选择的分配函数是标准非分配形式。new 表达式所返回的指针等于分配函数所返回的指针加上该值。许多实现使用数组开销存储数组中的对象数量,<a href="cpp-language-delete.html"><span class="t-c"><span class="mw-geshi cpp source-cpp">delete<span class="br0">[</span><span class="br0">]</span></span></span></a> 表达式会用它进行正确数量的析构函数调用。另外,如果用 new 分配 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> <span class="t-rev-inl t-since-cxx17"><span>或 <a href="cpp-types-byte.html"><tt>std::byte</tt></a></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>的数组,那么它可能从分配函数请求额外内存,以此保证所有不大于请求数组大小的类型的对象在放入所分配的数组中时能够正确对齐。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<p>允许将各个 new 表达式通过可替换分配函数所进行的分配予以省略或合并。在省略的情况下,存储可以由编译器提供,而无需调用分配函数(这也允许优化掉不使用的 new 表达式)。在合并的情况下且满足以下所有条件时,new 表达式 <code>E1</code> 所做的分配可以被扩展,以提供另一个 new 表达式 <code>E2</code> 的额外存储:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> <code>E1</code> 所分配对象的生存期严格包含 <code>E2</code> 所分配对象的生存期,</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> <code>E1</code> 与 <code>E2</code> 将调用同一可替换全局分配函数</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 对于抛出异常的分配函数,<code>E1</code> 与 <code>E2</code> 中的异常将首先被同一处理块捕捉。</div>
|
|
注意此优化仅在使用 new 表达式,而非调用可替换分配函数的任何其他方法时允许:<span class="t-c"><span class="mw-geshi cpp source-cpp">delete <span class="br0">[</span><span class="br0">]</span> new <span class="kw4">int</span><span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="sy4">;</span></span></span> 能被优化掉,但 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-memory-new-operator_delete.html"><span class="kw689">operator delete</span></a><span class="br0">(</span><a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span><span class="nu0">10</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span></span></span> 不能。</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>在<a href="cpp-language-constant_expression.html">常量表达式</a>求值期间,始终省略对分配函数的调用。只有在其他情况下调用可替换全局分配函数的 new 表达式能在常量表达式中求值。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h5><span class="mw-headline" id=".E5.B8.83.E7.BD.AE_new">布置 new</span></h5>
|
|
<p>如果提供了 <span class="t-spar">布置参数</span>,那么它们会作为额外实参传递给分配函数。这些分配函数被称作“布置 new”,这来源于标准分配函数 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="sy2">*</span> <a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a>, <span class="kw4">void</span><span class="sy2">*</span><span class="br0">)</span></span></span>,它直接返回未更改的第二实参。它被用于在已分配的存储中构造对象:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 在任何块作用域内……</span>
|
|
<span class="br0">{</span>
|
|
alignas<span class="br0">(</span>T<span class="br0">)</span> <span class="kw4">unsigned</span> <span class="kw4">char</span> buf<span class="br0">[</span>sizeof<span class="br0">(</span>T<span class="br0">)</span><span class="br0">]</span><span class="sy4">;</span>
|
|
<span class="co1">// 静态分配拥有自动存储期的存储,对任何对象类型 `T` 足够大。</span>
|
|
T<span class="sy2">*</span> tptr <span class="sy1">=</span> new<span class="br0">(</span>buf<span class="br0">)</span> T<span class="sy4">;</span> <span class="co1">// 构造一个 `T` 对象,将它直接置于</span>
|
|
<span class="co1">// 你预分配的位于内存地址 `buf` 的存储。</span>
|
|
tptr<span class="sy2">-</span><span class="sy1">></span>~T<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 如果程序依赖对象的析构函数的副作用,你必须**手动**调用它。</span>
|
|
<span class="br0">}</span> <span class="co1">// 离开此块作用域自动解分配 `buf` 。</span></pre></div>
|
|
</div>
|
|
<p>注意:<a href="cpp-named_req-Allocator.html"><span style="font-style:italic">分配器</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(Allocator)</span></a> 类的各成员函数封装了此功能。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>在分配具有超出 <code>__STDCPP_DEFAULT_NEW_ALIGNMENT__</code> 的对齐要求的对象或这种对象的数组时,new 表达式将对齐要求作为第二实参(以 <span class="t-lc"><a href="cpp-memory-new-align_val_t.html">std::align_val_t</a></span> 包装)传递给分配函数(对于布置形式,<span class="t-spar">布置参数</span> 被放在对齐之后,作为第三、第四个实参等)。如果重载决议失败(当定义了具有不同签名的类特有分配函数时,会发生此种情况,因为它隐藏了全局版本),那么会以实参列表中不带对齐再次尝试进行重载决议。这允许不关心对齐的类特有分配函数优先于全局的关心对齐的分配函数。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1">new T<span class="sy4">;</span> <span class="co1">// 调用 operator new(sizeof(T))</span>
|
|
<span class="co1">// (C++17) 或 operator new(sizeof(T), std::align_val_t(alignof(T))))</span>
|
|
new T<span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 调用 operator new[](sizeof(T)*5 + overhead)</span>
|
|
<span class="co1">// (C++17) 或 operator new(sizeof(T)*5+overhead, std::align_val_t(alignof(T))))</span>
|
|
new<span class="br0">(</span><span class="nu0">2</span>,f<span class="br0">)</span> T<span class="sy4">;</span> <span class="co1">// 调用 operator new(sizeof(T), 2, f)</span>
|
|
<span class="co1">// (C++17) 或 operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)</span></pre></div>
|
|
</div>
|
|
<p>如果不抛出的分配函数(例如 <span class="t-c"><span class="mw-geshi cpp source-cpp">new<span class="br0">(</span><a href="cpp-memory-new-nothrow.html"><span class="kw699">std::<span class="me2">nothrow</span></span></a><span class="br0">)</span> T</span></span> 所选择的)因为分配失败返回空指针,那么 new 表达式会立即返回,而不会试图初始化对象或调用解分配函数。如果将空指针作为实参传给不分配的布置 new 表达式,而这会使得被选择的标准不分配布置分配函数返回空指针,那么行为未定义。</p>
|
|
<h4><span class="mw-headline" id=".E6.9E.84.E9.80.A0">构造</span></h4>
|
|
<p>new 表达式所创建的对象按照下列规则初始化:</p>
|
|
<ul>
|
|
<li>对于非数组的 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span>,在所得内存区域中构造单个对象。</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果没有 <span class="t-spar">初始化器</span>,那么对象会被<a href="cpp-language-default_initialization.html">默认初始化</a>。</li>
|
|
<li>如果 <span class="t-spar">初始化器</span> 是带括号的实参列表,那么对象会被<a href="cpp-language-direct_initialization.html">直接初始化</a>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果 <span class="t-spar">初始化器</span> 是花括号包围的实参列表,那么对象会被<a href="cpp-language-list_initialization.html">列表初始化</a>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>如果 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是数组类型,那么初始化一个数组的对象。</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果没有 <span class="t-spar">初始化器</span>,那么每个元素都会被<a href="cpp-language-default_initialization.html">默认初始化</a>。</li>
|
|
<li>如果 <span class="t-spar">初始化器</span> 是一对空括号,那么每个元素被<a href="cpp-language-value_initialization.html">值初始化</a>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果 <span class="t-spar">初始化器</span> 是花括号包围的实参列表,那么数组会被<a href="cpp-language-aggregate_initialization.html">聚合初始化</a>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果 <span class="t-spar">初始化器</span> 是带括号的实参列表,那么数组会被<a href="cpp-language-aggregate_initialization.html">聚合初始化</a>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>如果初始化因抛出异常而终止(例如来自构造函数),那么 new 表达式在已经分配了任何存储的情况下会调用适当的<a href="cpp-memory-new-operator_delete.html">解分配函数</a>:对于非数组 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-memory-new-operator_delete.html"><span class="kw689">operator delete</span></a></span></span>,而对于数组 <span class="t-spar">类型</span> 或 <span class="t-spar">new类型</span> 是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-memory-new-operator_delete.html"><span class="kw689">operator delete</span></a><span class="br0">[</span><span class="br0">]</span></span></span>。如果 new 表达式使用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy4">::</span><span class="me2">new</span></span></span> 语法,那么会在全局作用域查找解分配函数,否则如果 <code>T</code> 是类类型,那么会在 <code>T</code> 的作用域查找。如果失败的是常规(非布置)分配函数,那么会遵循 <a href="cpp-language-delete.html">delete 表达式</a>中所述的规则查找解分配函数。对于失败的布置 new,与之匹配的解分配函数中除第一个外的所有形参的类型,必须与布置 new 的各形参类型相同。对解分配函数的调用中,将先前从分配函数取得的值作为第一实参,<span class="t-rev-inl t-since-cxx17"><span>将对齐作为可选的对齐参数,</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>并将 <code>布置参数</code>(如果存在)作为额外的布置参数。如果找不到解分配函数,那么不会解分配内存。</p>
|
|
<h3><span class="mw-headline" id=".E5.86.85.E5.AD.98.E6.B3.84.E6.BC.8F">内存泄漏</span></h3>
|
|
<p>new 表达式所创建的对象(拥有动态存储期的对象)会持续到将 new 表达式所返回的指针用于匹配的 <a href="cpp-language-delete.html">delete 表达式</a>之时。如果指针的原值丢失,那么对象不可达且无法解分配:发生<i>内存泄漏 (memory leak)</i>。</p>
|
|
<p>对指针赋值时可能发生:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span><span class="sy2">*</span> p <span class="sy1">=</span> new <span class="kw4">int</span><span class="br0">(</span><span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 动态分配的 int 带值 7</span>
|
|
p <span class="sy1">=</span> nullptr<span class="sy4">;</span> <span class="co1">// 内存泄漏</span></pre></div>
|
|
</div>
|
|
<p>或指针离开作用域:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> p <span class="sy1">=</span> new <span class="kw4">int</span><span class="br0">(</span><span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span> <span class="co1">// 内存泄漏</span></pre></div>
|
|
</div>
|
|
<p>或因为异常:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> p <span class="sy1">=</span> new <span class="kw4">int</span><span class="br0">(</span><span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span>
|
|
g<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 可能抛出异常</span>
|
|
delete p<span class="sy4">;</span> <span class="co1">// 如果没有异常则 ok</span>
|
|
<span class="br0">}</span> <span class="co1">// 如果 g() 抛出异常则内存泄漏</span></pre></div>
|
|
</div>
|
|
<p>为了简化动态分配的对象管理,通常将 new 表达式的结果存储于<i><a href="cpp-memory.html#.E6.99.BA.E8.83.BD.E6.8C.87.E9.92.88">智能指针</a></i>中:<span class="t-rev-inl t-until-cxx17"><span><span class="t-lc"><a href="cpp-memory-auto_ptr.html">std::auto_ptr</a></span></span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span><span class="t-rev-inl t-since-cxx11"><span><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></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>。这些指针保证在上述情形中执行 delete 表达式。</p>
|
|
<h3><span class="mw-headline" id=".E5.85.B3.E9.94.AE.E8.AF.8D">关键词</span></h3>
|
|
<p><a href="cpp-keyword-new.html"><tt>new</tt></a></p>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p><a rel="nofollow" class="external text" href="https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies">Itanium C++ ABI</a> 要求数组分配开销在创建的数组元素类型为可平凡析构时为零。MSVC 也是这样。</p>
|
|
<p>某些实现(如 VS 2019 v16.7 前的 MSVC)在元素类型非可平凡析构的情况下非分配布置数组 new 时会有非零的数组分配开销,这从 <a rel="nofollow" class="external text" href="https://wg21.link/p1969#2382">CWG2382</a> 起不再遵从标准。</p>
|
|
<p>创建 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> <span class="t-rev-inl t-since-cxx17"><span>或 <a href="cpp-types-byte.html"><tt>std::byte</tt></a></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span> 的数组的非分配布置数组 new 能用于在给定的存储区域隐式创建对象:它结束与该数组重叠的对象的生存期,然后在该数组中隐式创建隐式生存期类型的对象。</p>
|
|
<h3><span class="mw-headline" id=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue74">CWG 74</a></td>
|
|
<td>C++98</td>
|
|
<td>第一维中的值必须拥有整数类型</td>
|
|
<td>容许枚举类型</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue299">CWG 299</a></td>
|
|
<td>C++98</td>
|
|
<td>第一维中的值必须拥有整数或枚举类型</td>
|
|
<td>容许类类型</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1748">CWG 1748</a></td>
|
|
<td>C++98</td>
|
|
<td>要求非分配布置 new 检查实参是否为空</td>
|
|
<td>空实参导致未定义行为</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1992">CWG 1992</a></td>
|
|
<td>C++11</td>
|
|
<td><span class="t-c"><span class="mw-geshi cpp source-cpp">new <span class="br0">(</span><a href="cpp-memory-new-nothrow.html"><span class="kw699">std::<span class="me2">nothrow</span></span></a><span class="br0">)</span> <span class="kw4">int</span><span class="br0">[</span>N<span class="br0">]</span></span></span> 可能抛出 <code>bad_array_new_length</code></td>
|
|
<td>改为返回空指针</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P1009R2">P1009R2</a></td>
|
|
<td>C++11</td>
|
|
<td>new 表达式中不能推导数组边界</td>
|
|
<td>容许推导</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2382">CWG 2382</a></td>
|
|
<td>C++98</td>
|
|
<td>非分配布置数组 new 能要求分配开销</td>
|
|
<td>不允许这种分配开销</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<ul>
|
|
<li><a href="cpp-language-constructor.html">构造函数</a></li>
|
|
<li><a href="cpp-language-copy_elision.html">复制消除</a></li>
|
|
<li><a href="cpp-language-delete.html">delete</a></li>
|
|
<li><a href="cpp-language-destructor.html">析构函数</a></li>
|
|
<li><a href="cpp-language-initialization.html">初始化</a>
|
|
<ul>
|
|
<li><a href="cpp-language-aggregate_initialization.html">聚合初始化</a></li>
|
|
<li><a href="cpp-language-default_initialization.html">默认初始化</a></li>
|
|
<li><a href="cpp-language-direct_initialization.html">直接初始化</a></li>
|
|
<li><a href="cpp-language-list_initialization.html">列表初始化</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|