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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<tt>}</tt>
</pre>
<h3> <a name="Heading35"> Detecting a Machine's Endian</a></h3>
<p>The term <i>endian</i> refers to the way in which a computer architecture stores 
  the bytes of a multibyte number in memory. When bytes at lower addresses have 
  lower significance (as is the case with Intel microprocessors, for instance), 
  it is called <i>little endian ordering</i>. Conversely, <i>big endian ordering</i> 
  describes a computer architecture in which the most significant byte has the 
  lowest memory address. The following program detects the endian of the machine 
  on which it is executed:</p>
<pre>
<tt>int main()</tt>
<tt>{</tt>
<tt>  union probe</tt>
<tt> {</tt>
<tt>    unsigned int num;</tt>
<tt>    unsigned char bytes[sizeof(unsigned int)];</tt>
<tt>  };</tt>
<tt>  probe p = { 1U }; //initialize first member of p with unsigned 1</tt>
<tt>  bool little_endian = (p.bytes[0] == 1U); //in a big endian architecture, </tt>
<tt>                                           //p.bytes[0] equals 0</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading36"> The Lifetime Of A Bound Temporary Object</a></h3>
<p>You can safely bind a reference to a temporary object. The temporary object 
  to which the reference is bound persists for the lifetime of the reference. 
  For example</p>
<pre>
<tt>class C</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int j;</tt>
<tt>public:</tt>
<tt>  C(int i) : j(i) {}</tt>
<tt>  int getVal() const {return j;}</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  const C&amp; cr = C(2); //bind a reference to a temp; temp's destruction</tt>
<tt>                      //deferred to the end of the program</tt>
<tt>  C c2 = cr;  //use the bound reference safely</tt>
<tt>  int val = cr.getVal();</tt>
<tt>  return 0;</tt>
<tt>}//temporary destroyed here along with its bound reference</tt>
</pre>
<h3> <a name="Heading37"> Deleting A Pointer More Than Once</a></h3>
<p>The result of applying <tt>delete</tt> to the same pointer after it has been 
  deleted is undefined. Clearly, this bug should never happen. However, it can 
  be prevented by assigning a <tt>NULL</tt> value to a pointer right after it 
  has been deleted. It is guaranteed that a <tt>NULL</tt> pointer deletion is 
  harmless. For example</p>
<pre>
<tt>#include &lt;string&gt;</tt>
<tt>using namespace std;</tt>
<tt>void func</tt>
<tt>{</tt>
<tt>  string * ps = new string;</tt>
<tt>  //...use ps</tt>
<tt>  if ( ps-&gt;empty() )</tt>
<tt>  {</tt>
<tt>    delete ps;</tt>
<tt>    ps = NULL; //safety-guard: further deletions of ps will be harmless</tt>
<tt>  }</tt>
<tt>  //...many lines of code</tt>
<tt>  delete ps; // ps is deleted for the second time. Harmless however</tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading38"> Data Pointers Versus Function Pointers</a></h2>
<p>Both C and C++ make a clear-cut distinction between two types of pointers -- 
  data pointers and function pointers. A function pointer embodies several constituents, 
  such as the function's signature and return value. A data pointer, on the other 
  hand, merely holds the address of the first memory byte of a variable. The substantial 
  difference between the two led the C standardization committee to prohibit the 
  use of <tt>void*</tt> to represent function pointers, and vice versa. In C++, 
  this restriction was relaxed, but the results of coercing a function pointer 
  to a <tt>void*</tt> are implementation-defined. The opposite -- that is, converting 
  data pointers to function pointers -- is illegal.</p>
<h2> <a name="Heading39"> Pointer Equality</a></h2>
<p>Pointers to objects or functions of the same type are considered equal in three 
  cases:</p>
<ul>
  <li>
    <p> If both pointers are <tt>NULL</tt>. For example</p>
  </li>
</ul>
<p></p>
<pre>
<tt>int *p1 = NULL, p2 = NULL;</tt>
<tt>bool equal = (p1==p2); //true</tt>
</pre>
<ul>
  <li>
    <p> If they point to the same object. For example</p>
  </li>
</ul>
<p></p>
<pre>
<tt>char c;</tt>
<tt>char * pc1 = &amp;c;</tt>
<tt>char * pc2 = &amp;c;</tt>
<tt>bool equal = (pc1 == pc2); // true</tt>
</pre>
<ul>
  <li>
    <p> If they point one position past the end of the same array. For example</p>
  </li>
</ul>
<p></p>
<pre>
<tt>int num[2];</tt>
<tt>int * p1 = num+2, *p2 = num+2;</tt>
<tt>bool equal = ( p1 == p2); //true</tt>
</pre>
<h2> <a name="Heading40"> Storage Reallocation</a></h2>
<p>In addition to <tt>malloc()</tt> and <tt>free()</tt>, C also provides the function 
  <tt>realloc()</tt> for changing the size of an existing buffer. C++ does not 
  have a corresponding reallocation operator. Adding operator <tt>renew</tt> to 
  C++ was one of the suggestions for language extension that was most frequently 
  sent to the standardization committee. Instead, there are two ways to readjust 
  the size of memory that is allocated on the free store. The first is very inelegant 
  and error prone. It consists of allocating a new buffer with an appropriate 
  size, copying the contents of the original buffer to it and, finally, deleting 
  the original buffer. For example</p>
<pre>
<tt>void reallocate</tt>
<tt>{</tt>
<tt>  char * p new char [100];</tt>
<tt>  //...fill p</tt>
<tt>  char p2 = new char [200]; //allocate a larger buffer</tt>
<tt>  for (int i = 0; i&lt;100; i++) p2[i] = p[i]; //copy</tt>
<tt>  delete [] p; //release original buffer</tt>
<tt>}</tt>
</pre>
<p>Obviously, this technique is inefficient and tedious. For objects that change 
  their size frequently, this is unacceptable. The preferable method is to use 
  the container classes of the Standard Template Library (STL). STL containers 
  are discussed in Chapter 10, "STL and Generic Programming."</p>
<h2> <a name="Heading41"> Local Static Variables</a></h2>
<p>By default, local static variables (not to be confused with static class members) 
  are initialized to binary zeros. Conceptually, they are created before the program's 
  outset and destroyed after the program's termination. However, like local variables, 
  they are accessible only from within the scope in which they are declared. These 
  properties make static variables useful for storing a function's state on recurrent 
  invocations because they retain their values from the previous call. For example</p>
<pre>
<tt>void MoveTo(int OffsetFromCurrentX, int OffsetFromCurrentY)</tt>
<tt>{</tt>
<tt>  static int currX, currY;  //zero initialized</tt>
<tt>  currX += OffsetFromCurrentX;</tt>
<tt>  currY += OffsetFromCurrentY;</tt>
<tt>  PutPixel(currX, currY);</tt>
<tt>}</tt>
<tt>void DrawLine(int x, int y, int length)</tt>
<tt>{</tt>
<tt>  for (int i=0; i&lt;length; i++)</tt>
<tt>     MoveTo(x++, y--);</tt>
<tt>}</tt>
</pre>
<p>However, when the need arises for storing a function's state, a better design 
  choice is to use a class. Class data members replace the static variables and 
  a member function replaces the global function. Local static variables in a 
  member function are of special concern: Every derived object that inherits such 
  a member function also refers to the same instance of the local static variables 
  of its base class. For example</p>
<pre>
<tt>class Base</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  int countCalls()</tt>
<tt>  {</tt>
<tt>    static int cnt = 0;  </tt>
<tt>    return ++cnt;</tt>
<tt>  }</tt>
<tt>};</tt>
<tt>class Derived1 : public Base { /*..*/};</tt>
<tt>class Derived2 : public Base { /*..*/};</tt>
<tt>// Base::countCalls(), Derived1::countCalls() and Derived2::countCalls</tt>
<tt>// hold a shared copy of cnt</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  Derived1 d1;</tt>
<tt>  int d1Calls = d1.countCalls(); //d1Calls = 1</tt>
<tt>  Derived2 d2;</tt>
<tt>  int d2Calls = d2.countCalls(); //d2Calls = 2, not 1</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>Static local variables in the member function <tt>countCalls</tt> can be used 
  to measure load balancing by counting the total number of invocations of that 
  member function, regardless of the actual object from which it was called. However, 
  it is obvious that the programmer's intention was to count the number of invocations 
  through <tt>Derived2</tt> exclusively. In order to achieve that, a static class 
  member can be used instead:</p>
<pre>
<tt>class Base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  static int i;  </tt>
<tt>public:</tt>
<tt>  virtual int countCalls() {  return ++i; }</tt>
<tt>};</tt>
<tt>int Base::i;</tt>
<tt>class Derived1 : public Base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  static int i; //hides Base::i</tt>
<tt>public:</tt>
<tt>  int countCalls() {  return ++i; } //overrides Base:: countCalls()</tt>
<tt>};</tt>
<tt>int Derived1::i;</tt>
<tt>class Derived2 : public Base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  static int i; //hides Base::i and distinct from Derived1::i</tt>
<tt>public:</tt>
<tt>  virtual int countCalls() {  return ++i; }</tt>
<tt>};</tt>
<tt>int Derived2::i;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  Derived1 d1;</tt>
<tt>  Derived2 d2;</tt>
<tt>  int d1Calls = d1.countCalls(); //d1Calls = 1</tt>
<tt>  int d2Calls = d2.countCalls(); //d2Calls also = 1</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>Static variables are problematic in a multithreaded environment because they 
  are shared and have to be accessed by means of a lock.</p>
<h2> <a name="Heading42"> Global Anonymous Unions</a></h2>
<p>An <i>anonymous union</i> (anonymous unions are discussed in Chapter 12, "Optimizing 
  Your Code") that is declared in a named namespace or in the global namespace 
  has to be explicitly declared <tt>static</tt>. For example</p>
<pre>
<tt>static union //anonymous union in global namespace</tt>
<tt>{</tt>
<tt>  int num;</tt>
<tt>  char *pc;</tt>
<tt>};</tt>
<tt>namespace NS</tt>
<tt>{</tt>
<tt>  static union { double d; bool b;}; //anonymous union in a named namespace</tt>
<tt>}</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  NS::d = 0.0;</tt>
<tt>  num = 5;</tt>
<tt>  pc = "str";</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading43"> The const and volatile Properties of an Object</a></h2>
<p>There are several phases that comprise the construction of an object, including 
  the construction of its base and embedded objects, the assignment of a <tt>this</tt> 
  pointer, the creation of the virtual table, and the invocation of the constructor's 
  body. The construction of a <cite>cv</cite><i>-qualified</i> (<tt>const</tt> 
  or <tt>volatile)</tt> object has an additional phase, which turns it into a 
  <tt>const</tt>/<tt>volatile</tt> object. The <tt>cv</tt> qualities are effected 
  after the object has been fully constructed.</p>
<h2> <a name="Heading44"> Conclusions</a></h2>
<p>The complex memory model of C++ enables maximal flexibility. The three types 
  of data storage -- automatic, static, and free store -- offer a level of control 
  that normally exist only in assembly languages.</p>
<p>The fundamental constructs of dynamic memory allocation are operators <tt>new</tt> 
  and <tt>delete</tt>. Each of these has no fewer than six different versions; 
  there are plain and array variants, each of which comes in three flavors: exception 
  throwing, exception free, and placement.</p>
<p>Many object-oriented programming languages have a built-in <i>garbage collector</i>, 
  which is an automatic memory manager that detects unreferenced objects and reclaims 
  their storage (see also Chapter 14, "Concluding Remarks and Future Directions," 
  for a discussion on garbage collection). The reclaimed storage can then be used 
  to create new objects, thereby freeing the programmer from having to explicitly 
  release dynamically-allocated memory. Having an automatic garbage collector 
  is handy because it eliminates a large source of bugs, runtime crashes, and 
  memory leaks. However, garbage collection is not a panacea. It incurs additional 
  runtime overhead due to repeated compaction, reference counting, and memory 
  initialization operations, which are unacceptable in time-critical applications. 
  Furthermore, when garbage collection is used, destructors are not necessarily 
  invoked immediately when the lifetime of an object ends, but at an indeterminate 
  time afterward (when the garbage collector is sporadically invoked). For these 
  reasons, C++ does not provide a garbage collector. Nonetheless, there are techniques 
  to minimize -- and even eliminate -- the perils and drudgery of manual memory 
  management without the associated disadvantages of garbage collection. The easiest 
  way to ensure automatic memory allocation and deallocation is to use automatic 
  storage. For objects that have to grow and shrink dynamically, you can use STL 
  containers that automatically and optimally adjust their size. Finally, in order 
  to create an object that exists throughout the execution of a program, you can 
  declare it <tt>static</tt>. Nonetheless, dynamic memory allocation is sometimes 
  unavoidable. In such cases, <tt>auto_ptr</tt>(discussed in Chapters 6 and 11, 
  "Memory Management") simplifies the usage of dynamic memory. </p>
<p>Effective and bug-free usage of the diversity of C++ memory handling constructs 
  and concepts requires a high level of expertise and experience. It isn't an 
  exaggeration to say that most of the bugs in C/C++ programs are related to memory 
  management. However, this diversity also renders C++ a multipurpose, no compromise 
  programming language.</p>
<CENTER>
<P>
<HR>
  <p><A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
<BR>
<BR>
</p><p></P>

<P>&#169; <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. All
rights reserved.</p>
</CENTER>


</BODY>

</HTML>

⌨️ 快捷键说明

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