📄 index.html
字号:
<tt> int age; // int occupies 4 bytes</tt>
<tt> char lastName[8];</tt>
<tt>}; //the actual size of Person is most likely larger than 17 bytes</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt> Person person = {{"john"}, 30, {"lippman"}};</tt>
<tt> memset(&person, 0, 5+4+8 ); //may not erase the contents of</tt>
<tt> //person properly</tt>
<tt>}</tt>
</pre>
<p>On a 32-bit architecture, three additional bytes can be inserted between the
first and the second members of <tt>Person</tt>, increasing the size of <tt>Person</tt>
from 17 bytes to 20. </p>
<p>On some implementations, the <tt>memset()</tt> call does not clear the last
three bytes of the member <tt>lastName</tt>. Therefore, use the <tt>sizeof</tt>
operator to calculate the correct size:</p>
<pre>
<tt>memset(&p, 0, sizeof(Person)); </tt>
</pre>
<h2> <a name="Heading24"> The Size Of A Complete Object Can Never Be Zero</a></h2>
<p>An empty class doesn't have any data members or member functions. Therefore,
the size of an instance is seemingly zero. However, C++ guarantees that the
size of a complete object is never zero. Consider the following example:</p>
<pre>
<tt>class Empty {};</tt>
<tt>Empty e; // e occupies at least 1 byte of memory</tt>
</pre>
<p>If an object is allowed to occupy zero bytes of storage, its address can overlap
with the address of a different object. The most obvious case is an array of
empty objects whose elements all have an identical address. To guarantee that
a complete object always has a distinct memory address, a complete object occupies
at least one byte of memory. Non-complete objects -- for example, base class
subobjects in a derived class -- can occupy zero bytes of memory.</p>
<h2> <a name="Heading25"> User-Defined Versions of new and delete Cannot Be Declared
in a Namespace</a></h2>
<p>User-defined versions of <tt>new</tt> and <tt>delete</tt> can be declared in
a class scope. However, it is illegal to declare them in a namespace. To see
why, consider the following example:</p>
<pre>
<tt>char *pc;</tt>
<tt>namespace A</tt>
<tt>{</tt>
<tt> void* operator new ( size_t );</tt>
<tt> void operator delete ( void * );</tt>
<tt> void func ()</tt>
<tt> {</tt>
<tt> pc = new char ( 'a');</tt>
<tt> }</tt>
<tt>}</tt>
<tt>void f() { delete pc; } // A::delete or ::delete?</tt>
</pre>
<p>Declaring <tt>new</tt> and <tt>delete</tt> in namespace <tt>A</tt> is confusing
for both compilers and human readers. Some programmers might expect the operator
<tt>A::delete</tt> to be selected in the function <tt>f()</tt> because it matches
the operator <tt>new</tt> that was used to allocate the storage. In contrast,
others might expect <tt>delete</tt> to be called because <tt>A::delete</tt>
is not visible in <tt>f()</tt>. For this reason, the Standardization committee
decided to disallow declarations of <tt>new</tt> and <tt>delete</tt> in a namespace.</p>
<h2> <a name="Heading26"> Overloading new and delete in a Class</a></h2>
<p>It is possible to override <tt>new</tt> and <tt>delete</tt> and define a specialized
form for them for a given class. Thus, for a class <tt>C</tt> that defines these
operators, the following statements</p>
<pre>
<tt>C* p = new C;</tt>
<tt>delete p;</tt>
</pre>
<p>invoke the class's versions of <tt>new</tt> and <tt>delete</tt>, respectively.
Defining class-specific versions of <tt>new</tt> and <tt>delete</tt> is useful
when the default memory management scheme is unsuitable. This technique is also
used in applications that have a custom memory pool. In the following example,
operator <tt>new</tt> for class <tt>C</tt> is redefined to alter the default
behavior in case of an allocation failure; instead of throwing <tt>std::bad_alloc</tt>,
this specific version throws a <tt>const char *</tt>. A matching operator <tt>delete</tt>
is redefined accordingly:</p>
<pre>
<tt>#include <cstdlib> // malloc() and free()</tt>
<tt>#include <iostream></tt>
<tt>using namespace std;</tt>
<tt>class C</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> int j;</tt>
<tt>public:</tt>
<tt> C() : j(0) { cout<< "constructed"<<endl; }</tt>
<tt> ~C() { cout<<"destroyed";}</tt>
<tt> void* operator new (size_t size); //implicitly declared static</tt>
<tt> void operator delete (void *p); //implicitly declared static</tt>
<tt>};</tt>
<tt>void* C::operator new (size_t size) throw (const char *)</tt>
<tt>{</tt>
<tt> void * p = malloc(size);</tt>
<tt> if (p == 0)</tt>
<tt> throw "allocation failure"; //instead of std::bad_alloc</tt>
<tt> return p;</tt>
<tt>}</tt>
<tt>void C::operator delete (void *p)</tt>
<tt>{</tt>
<tt> free(p); </tt>
<tt>}</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> try</tt>
<tt> {</tt>
<tt> C *p = new C;</tt>
<tt> delete p;</tt>
<tt> }</tt>
<tt> catch (const char * err)</tt>
<tt> {</tt>
<tt> cout<<err<<endl;</tt>
<tt> }</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>Remember that overloaded <tt>new</tt> and <tt>delete</tt> are implicitly declared
as static members of their class if they are not explicitly declared static.
Note also that a user-defined <tt>new</tt> implicitly invokes the objects's
constructor; likewise, a user-defined <tt>delete</tt> implicitly invokes the
object's destructor. </p>
<h2> <a name="Heading27"> Guidelines for Effective Memory Usage</a></h2>
<p>Choosing the correct type of storage for an object is a critical implementation
decision because each type of storage has different implications for the program's
performance, reliability, and maintenance. This section tells you how to choose
the correct type of storage for an object and thus avoid common pitfalls and
performance penalties. This section also discusses general topics that are associated
with the memory model of C++, and it compares C++ to other languages.</p>
<h3> <a name="Heading28"> Prefer Automatic Storage to Free Store Whenever Possible</a></h3>
<p>Creating objects on the free store, when compared to automatic storage, is
more expensive in terms of performance for several reasons:</p>
<ul>
<li>
<p> <b>Runtime overhead</b> Allocating memory from the free store involves
negotiations with the operating system. When the free store is fragmented,
finding a contiguous block of memory can take even longer. In addition,
the exception handling support in the case of allocation failures adds additional
runtime overhead.</p>
</li>
<p></p>
<li>
<p> <b>Maintenance</b> Dynamic allocation might fail; additional code is required
to handle such exceptions.</p>
</li>
<p></p>
<li>
<p> <b>Safety</b> An object might be accidentally deleted more than once,
or it might not be deleted at all. Both of these are a fertile source of
bugs and runtime crashes in many applications.</p>
</li>
</ul>
<p></p>
<p>The following code sample demonstrates two common bugs that are associated
with allocating objects on the free store:</p>
<pre>
<tt>#include <string></tt>
<tt>using namespace std;</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt> string *p = new string;</tt>
<tt> //...use p</tt>
<tt> if (p->empty()!= false)</tt>
<tt> {</tt>
<tt> //...do something</tt>
<tt> return; //OOPS! memory leak: p was not deleted</tt>
<tt> }</tt>
<tt> else //string is empty</tt>
<tt> {</tt>
<tt> delete p;</tt>
<tt> //..do other stuff</tt>
<tt> }</tt>
<tt> delete p; //OOPS! p is deleted twice if isEmpty == false</tt>
<tt>}</tt>
</pre>
<p>Such bugs are quite common in large programs that frequently allocate objects
on the free store. Often, it is possible to create objects on the stack, thereby
simplifying the structure of the program and eliminating the potential for such
bugs. Consider how the use of a local <tt>string</tt> object simplifies the
preceding code sample:</p>
<pre>
<tt>#include <string></tt>
<tt>using namespace std;</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt> string s;</tt>
<tt> //...use s</tt>
<tt> if (s.empty()!= false)</tt>
<tt> {</tt>
<tt> //...do something</tt>
<tt> return;</tt>
<tt> }</tt>
<tt> else</tt>
<tt> {</tt>
<tt> //..do other stuff</tt>
<tt> }</tt>
<tt>}</tt>
</pre>
<p>As a rule, automatic and static storage types are always preferable to free
store.</p>
<h3> <a name="Heading29"> Correct Syntax for Local Object Instantiation</a></h3>
<p>The correct syntax for instantiating a local object by invoking its default
constructor is</p>
<pre>
<tt>string str; //no parentheses</tt>
</pre>
<p>Although empty parentheses can be used after the class name, as in</p>
<pre>
<tt>string str(); //entirely different meaning</tt>
</pre>
<p>the statement has an entirely different meaning. It is parsed as a declaration
of a function named <tt>str</tt>, which takes no arguments and returns a <tt>string</tt>
by value.</p>
<h3> <a name="Heading30"> Zero As A Universal Initializer</a></h3>
<p>The literal <tt>0</tt> is an <tt>int</tt>. However, it can be used as a universal
initializer for every fundamental data type. Zero is a special case in this
respect because the compiler examines its context to determine its type. For
example:</p>
<pre>
<tt> void *p = 0; //zero is implicitly converted to void *</tt>
<tt> float salary = 0; // 0 is cast to a float</tt>
<tt> char name[10] = {0}; // 0 cast to a '\0'</tt>
<tt> bool b = 0; // 0 cast to false</tt>
<tt> void (*pf)(int) = 0; // pointer to a function</tt>
<tt> int (C::*pm) () = 0; //pointer to a class member</tt>
</pre>
<h3> <a name="Heading31"> Always Initialize Pointers</a></h3>
<p>An uninitialized pointer has an indeterminate value. Such a pointer is often
called a <i>wild pointer</i>. It is almost impossible to test whether a wild
pointer is valid, especially if it is passed as an argument to a function (which
in turn can only verify that it is not <tt>NULL</tt>). For example</p>
<pre>
<tt>void func(char *p );</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> char * p; //dangerous: uninitialized</tt>
<tt> //...many lines of code; p left uninitialized by mistake</tt>
<tt> if (p)//erroneously assuming that a non-null value indicates a valid address</tt>
<tt> {</tt>
<tt> func(p); // func has no way of knowing whether p has a valid address</tt>
<tt> }</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>Even if your compiler does initialize pointers automatically, it is best to
initialize them explicitly to ensure code readability and portability.</p>
<h2> <a name="Heading32"> Explicit Initializations of POD Object</a></h2>
<p>As was previously noted, POD objects with automatic storage have an indeterminate
value by default in order to avoid the performance penalty incurred by initialization.
However, you can initialize automatic POD objects explicitly when necessary.
The following sections explain how this is done.</p>
<h3> <a name="Heading33"> Initializing Local Automatic Structs and Arrays</a></h3>
<p>One way to initialize automatic POD objects is by calling <tt>memset()</tt>
or a similar initialization function. However, there is a much simpler way to
do it -- without calling a function, as you can see in the following example:
</p>
<pre>
<tt>struct Person</tt>
<tt>{</tt>
<tt> long ID;</tt>
<tt> int bankAccount;</tt>
<tt> bool retired;</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> Person person ={0}; //ensures that all members of</tt>
<tt> //person are initialized to binary zeros</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>This technique is applicable to every POD struct. It relies on the fact that
the first member is a fundamental data type. The initializer zero is automatically
cast to the appropriate fundamental type. It is guaranteed that whenever the
initialization list contains fewer initializers than the number of members,
the rest of the members are initialized to binary zeros as well. Note that even
if the definition of <tt>Person</tt> changes -- additional members are added
to it or the members' ordering is swapped -- all its members are still initialized.
The same initialization technique is also applicable to local automatic arrays
of fundamental types as well as to arrays of POD objects :</p>
<pre>
<tt>void f()</tt>
<tt>{</tt>
<tt> char name[100] = {0}; //all array elements are initialized to '\0'</tt>
<tt> float farr[100] = {0}; //all array elements are initialized to 0.0</tt>
<tt> int iarr[100] = {0}; //all array elements are initialized to 0</tt>
<tt> void *pvarr[100] = {0};//array of void * all elements are initialized to NULL</tt>
<tt> //...use the arrays</tt>
<tt>}</tt>
</pre>
<p>This technique works for any combination of structs and arrays:</p>
<pre>
<tt>struct A</tt>
<tt>{</tt>
<tt> char name[20];</tt>
<tt> int age;</tt>
<tt> long ID;</tt>
<tt>};</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt> A a[100] = {0};</tt>
<tt>}</tt>
</pre>
<p> </p>
<h3> <a name="Heading34"> Union Initialization</a></h3>
<p>You can initialize a union. However, unlike struct initialization, the initialization
list of a union must contain only a single initializer, which must refer to
the first member in the union. For example</p>
<pre>
<tt>union Key</tt>
<tt>{</tt>
<tt> int num_key;</tt>
<tt> void *ptr_key;</tt>
<tt> char name_key[10];</tt>
<tt>};</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt> Key key = {5}; // first member of Key is of type int</tt>
<tt> // any additional bytes initialized to binary zeros</tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -