⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
  plain <tt>delete</tt> instead of <tt>delete[]</tt> to release an array of objects 
  allocated by <tt>new[]</tt> results in undefined behavior and should never happen. 
  Similarly, using <tt>delete[]</tt> to release a single object that was allocated 
  by plain <tt>new</tt> is also disastrous: It might cause memory leaks, heap 
  corruption, or a program crash.</p>
<p>Contrary to popular belief, the same rules apply to arrays of fundamental types 
  -- not just to arrays of objects. Although <tt>delete[]</tt> does not invoke 
  destructors in the case of fundamental types, it still has to retrieve the number 
  of elements in the array to calculate the complete size of the memory block. 
  For example</p>
<pre>
<tt>#include&lt;string&gt;</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt>  char *pc = new char[100];</tt>
<tt>  string *ps = new std::string[100];</tt>
<tt>  //...use pc and ps</tt>
<tt>  delete[] pc; //no destructors invoked, still delete[] is required</tt>
<tt>                   // for arrays allocated by new[]</tt>
<tt>  delete[] ps //ensures each member's destructor is called</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading18"> Exceptions and Operator new</a></h3>
<p>In pre-Standard C++, <tt>new</tt> returned a <tt>NULL</tt> pointer when it 
  failed to allocate the requested amount of memory. In this respect, <tt>new</tt> 
  behaved like <tt>malloc()</tt> in C. Programmers had to check the value that 
  was returned from <tt>new</tt> before they used it to make sure that it was 
  not <tt>NULL</tt>. For example</p>
<pre>
<tt>void f(int size) //anachronistic usage of new</tt>
<tt>{</tt>
<tt>  char *p = new char [size];</tt>
<tt>  if (p == 0)   //this was fine until 1994</tt>
<tt>  {</tt>
<tt>    //...use p safely</tt>
<tt>    delete [] p;</tt>
<tt>  }</tt>
<tt>  return;</tt>
<tt>}</tt>
<tt>const int BUF_SIZE = 1048576L;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  f(BUF_SIZE);</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>Returning a <tt>NULL</tt> pointer upon failure, however, was problematic.<i> 
  </i>(Note that the <tt>NULL</tt> pointer policy was applicable to both plain 
  <tt>new</tt> and <tt>new[]</tt>. Similarly, the modified behavior applies to 
  <tt>new</tt> as well as <tt>new[]</tt>.) It forced programmers to test the value 
  that was returned by every invocation of operator <tt>new</tt>, which is a tedious 
  and error-prone process. In addition, the recurrent testing of the returned 
  pointer can increase the size of the programs and add a runtime performance 
  overhead (you might recall that these are the drawbacks associated with the 
  return value policy, discussed in Chapter 6, "Exception Handling"). Failures 
  in dynamic memory allocation are rather rare and generally indicate an unstable 
  system state. This is exactly the kind of runtime errors that exception handling 
  was designed to cope with. For these reasons, the C++ standardization committee 
  changed the specification of <tt>new</tt> a few years ago. The Standard now 
  states that operator <tt>new</tt> throws an exception of type <tt>std::bad_alloc</tt> 
  when it fails, rather than returning a <tt>NULL</tt> pointer.</p>
<blockquote>
  <hr>
  <strong>CAUTION: </strong> Although compiler vendors have been sluggish in adopting 
  this change, most C++ compilers now conform to the standard in this respect, 
  and throw an exception of type <tt>std::bad_alloc</tt> when <tt>new</tt> fails. 
  Please consult your compiler's documentation for more details. 
  <hr>
</blockquote>
<p>A program that calls <tt>new</tt> either directly or indirectly (for example, 
  if it uses STL containers, which allocate memory from the free store) must contain 
  an appropriate handler that catches a <tt>std::bad_alloc</tt> exception. Otherwise, 
  whenever <tt>new</tt> fails, the program terminates due to an uncaught exception. 
  The exception-throwing policy also implies that testing the pointer that is 
  returned from <tt>new</tt> is completely useless. If <tt>new</tt> is successful, 
  the redundant test wastes system resources. On the other hand, in the case of 
  an allocation failure, the thrown exception aborts the current thread of execution 
  from where it was thrown -- so the test is not executed anyway. The revised, 
  standard-conforming form of the previously presented program looks similar to 
  the following:</p>
<pre>
<tt>void f(int size) //standard-conforming usage of new</tt>
<tt>{</tt>
<tt>  char *p = new char [size];</tt>
<tt>  //...use p safely</tt>
<tt>  delete [] p;</tt>
<tt>  return;</tt>
<tt>}</tt>
<tt>#include &lt;stdexcept&gt;</tt>
<tt>#include &lt;iostream&gt;</tt>
<tt>using namespace std;</tt>
<tt>const int BUF_SIZE = 1048576L;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  try</tt>
<tt>  {</tt>
<tt>    f(BUF_SIZE);</tt>
<tt>  }</tt>
<tt>  catch(bad_alloc&amp; ex)  //handle exception thrown from f()</tt>
<tt>  {</tt>
<tt>    cout&lt;&lt;ex.what()&lt;&lt;endl;</tt>
<tt>    //...other diagnostics and remedies</tt>
<tt>  }</tt>
<tt>  return -1;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading19"> Exception-Free Version of Operator new</a></h3>
<p>Still, under some circumstances, throwing an exception is undesirable. For 
  example, exception handling might have been turned off to enhance performance; 
  on some platforms, it might not be supported at all.</p>
<p>The Standardization committee was aware of this and added an exception-free 
  version of <tt>new</tt> to the Standard. The exception-free version of <tt>new</tt> 
  returns a <tt>NULL</tt> pointer in the event of a failure, rather than throwing 
  a <tt>std::bad_alloc</tt> exception. This version of <tt>new</tt> takes an additional 
  argument of type <tt>const std::nothrow_t&amp;</tt> (defined in the header <tt>&lt;new&gt;</tt>). 
  It comes in two flavors, one for plain <tt>new</tt> and another for <tt>new[]</tt>.</p>
<pre>
<tt>//exception-free versions of new and new[] defined in the header &lt;new&gt;</tt>
<tt>void* operator new(std::size_t size, const std::nothrow_t&amp;) throw();</tt>
<tt>void* operator new[](std::size_t size, const std::nothrow_t&amp;) throw();</tt>
</pre>
<p>The exception-free <tt>new</tt> is also called <i>nothrow new</i>. It is used 
  as follows:</p>
<pre>
<tt>#include &lt;new&gt;</tt>
<tt>#include &lt;string&gt;</tt>
<tt>using namespace std;</tt>
<tt>void f(int size) // demonstrating nothrow new</tt>
<tt>{</tt>
<tt>  char *p = new (nothrow) char [size]; //array nothrow new</tt>
<tt>  if (p == 0)</tt>
<tt>  {</tt>
<tt>    //...use p</tt>
<tt>    delete [] p;</tt>
<tt>  }</tt>
<tt>  string *pstr = new (nothrow) string; //plain nothrow new</tt>
<tt>  if (pstr == 0)</tt>
<tt>  {</tt>
<tt>    //...use pstr</tt>
<tt>    delete [] pstr;</tt>
<tt>  }</tt>
<tt>  return;</tt>
<tt>}</tt>
<tt>const int BUF_SIZE = 1048576L;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  f(BUF_SIZE);</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>The argument <tt>nothrow</tt> is defined and created in header <tt>&lt;new&gt;</tt> 
  as follows:</p>
<pre>
<tt>extern const nothrow_t nothrow;</tt>
</pre>
<p>Class <tt>nothrow_t</tt> is defined as follows:</p>
<pre>
<tt>struct nothrow_t {}; //an empty class</tt>
</pre>
<p>In other words, the type <tt>nothrow_t</tt> is an empty class (the empty class 
  idiom is discussed in Chapter 5, "Object-Oriented Program and Design") whose 
  sole purpose is to overload global <tt>new</tt>.</p>
<h3> <a name="Heading20"> Placement new</a></h3>
<p>An additional version of operator <tt>new</tt> enables you to construct an 
  object (or an array of objects) at a predetermined memory position. This version 
  is called <i>placement new</i> and has many useful applications, including building 
  a custom-made memory pool or a garbage collector. Additionally, it can be used 
  in mission-critical applications because there is no danger of allocation failure 
  (the memory that is used by placement <tt>new</tt> has already been allocated). 
  Placement <tt>new</tt> is also faster because the construction of an object 
  on a preallocated buffer takes less time. Following is an example of using placement 
  <tt>new</tt>:</p>
<pre>
<tt>#include &lt;new&gt;</tt>
<tt>#include &lt;iostream&gt;</tt>
<tt>using namespace std;</tt>
<tt>void placement()</tt>
<tt>{</tt>
<tt>  int   *pi = new int;     //plain new</tt>
<tt>  float *pf = new float[2]; //new []</tt>
<tt>  int   *p  = new (pi) int (5);  //placement new</tt>
<tt>  float *p2 = new (pf) float;  //placement new[]</tt>
<tt>  p2[0] = 0.33f;</tt>
<tt>  cout&lt;&lt; *p &lt;&lt; p2[0] &lt;&lt; endl;</tt>
<tt>  //...</tt>
<tt>  delete pi;</tt>
<tt>  delete [] pf;</tt>
<tt>}</tt>
</pre>
<h4> Explicit Destructor Invocation Is Required for an Object Created by Placement 
  new</h4>
<p>Destructors of objects that were constructed using placement <tt>new</tt> have 
  to be invoked explicitly. To see why, consider the following example:</p>
<pre>
<tt>#include &lt;new&gt;</tt>
<tt>#include &lt;iostream&gt;</tt>
<tt>using namespace std;</tt>
<tt>class C</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  C() { cout&lt;&lt; "constructed" &lt;&lt;endl; }</tt>
<tt>  ~C(){ cout&lt;&lt; "destroyed" &lt;&lt;endl; }</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  char * p = new char [sizeof ];  // pre-allocate a buffer</tt>
<tt>   C *pc = new (p) C;  // placement new</tt>
<tt>   //... used pc</tt>
<tt>  pc-&gt;C::~C(); // 1:explicit destructor invocation is required</tt>
<tt>  delete [] p; //2</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>Without an explicit destructor invocation in (1), the object that is pointed 
  to by <tt>p</tt> will never be destroyed, but the memory block on which it was 
  created will be released by the <tt>delete[]</tt> statement in (2).</p>
<h2> <a name="Heading21"> Exceptions During Object Construction</a></h2>
<p>As was previously noted, <tt>new</tt> performs two operations: It allocates 
  memory from the free store by calling an allocation function, and it constructs 
  an object on the allocated memory. The question is, does the allocated memory 
  leak when an exception is thrown during the construction process? The answer 
  is no, it doesn't. The allocated memory is returned to the free store by the 
  system before the exception propagates to the program. Thus, an invocation of 
  operator <tt>new</tt> can be construed as two consecutive operations. The first 
  operation merely allocates a sufficient memory block from the free store with 
  the appropriate alignment requirements. In the event of failure, the system 
  throws an exception of type <tt>std::bad_alloc</tt>. If the first operation 
  was successful, the second one begins. The second operation consists of invoking 
  the object's constructor with the pointer that is retained from the previous 
  step. Put differently, the statement</p>
<pre>
<tt>C* p = new C;</tt>
</pre>
<p>is transformed by the compiler into something similar to the following:</p>
<pre>
<tt>#include &lt;new&gt;</tt>
<tt>using namespace std;</tt>
<tt>class C{/*...*/};</tt>
<tt>void __new() throw (bad_alloc)</tt>
<tt>{</tt>
<tt>  C * p = reinterpret_cast&lt;C*&gt; (new char [sizeof ]); //step 1: allocate</tt>
<tt>                                                       // raw memory</tt>
<tt>  try</tt>
<tt>  {</tt>
<tt>    new (p) C;  //step 2: construct the objects on previously allocated buffer</tt>
<tt>  }</tt>
<tt>  catch(...)  //catch any exception thrown from C's constructor</tt>
<tt>  {</tt>
<tt>    delete[] p;  //free the allocated buffer</tt>
<tt>    throw;  //re-throw the exception of C's constructor</tt>
<tt>  }</tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading22"> Alignment Considerations</a></h2>
<p>The pointer that is returned by <tt>new</tt> has the suitable alignment properties 
  so that it can be converted to a pointer of any object type and then used to 
  access that object or array. Consequently, you are permitted to allocate character 
  arrays into which objects of other types will later be placed. For example</p>
<pre>
<tt>#include &lt;new&gt;</tt>
<tt>#include &lt;iostream&gt;</tt>
<tt>#include &lt;string&gt;</tt>
<tt>using namespace std;</tt>
<tt>class Employee</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  string name;</tt>
<tt>  int age;</tt>
<tt>public:</tt>
<tt>  Employee();</tt>
<tt>  ~Employee();</tt>
<tt>};</tt>
<tt>void func() //use a pre allocated char array to construct</tt>
<tt>            //an object of a different type</tt>
<tt>{</tt>
<tt>  char * pc = new char[sizeof(Employee)];</tt>
<tt>  Employee *pemp = new (pc) Employee;  //construct on char array</tt>
<tt>  //...use pemp</tt>
<tt>  pemp-&gt;Employee::~Employee(); //explicit destruction</tt>
<tt>  delete [] pc;</tt>
<tt>}</tt>
</pre>
<p>It might be tempting to use a buffer that is allocated on the stack to avoid 
  the hassle of deleting it later:</p>
<pre>
<tt>char pbuff [sizeof(Employee)];</tt>
<tt>Employee *p = new (pbuff ) Employee;  //undefined behavior</tt>
</pre>
<p>However, <tt>char</tt> arrays of automatic storage type are not guaranteed 
  to meet the necessary alignment requirements of objects of other types. Therefore, 
  constructing an object of a preallocated buffer of automatic storage type can 
  result in undefined behavior. Furthermore, creating a new object at a storage 
  location that was previously occupied by a <tt>const</tt> object with static 
  or automatic storage type also results in undefined behavior. For example</p>
<pre>
<tt>const Employee emp;</tt>
<tt>void bad_placement() //attempting to construct a new object</tt>
<tt>                     //at the storage location of a const object</tt>
<tt>{</tt>
<tt>  emp.Employee::~Employee();</tt>
<tt>  new (&amp;emp) const Employee; //  undefined behavior</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading23"> Member Alignment</a></h3>
<p>The size of a class or a struct might be larger than the result of adding the 
  size of each data member in it. This is because the compiler is allowed to add 
  additional <i>padding</i> <i>bytes</i> between members whose size does not fit 
  exactly into a machine word (see also Chapter 13). For example</p>
<pre>
<tt>#include &lt;cstring&gt;</tt>
<tt>using namespace std;</tt>
<tt>struct Person</tt>
<tt>{</tt>
<tt>  char firstName[5];</tt>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -