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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
  forced on the programmer. Pure procedural C++ code (legacy C code that is ported 
  to a C++ compiler, for example) does not pay for these features. In other words, 
  users -- almost without exception -- have a choice between higher-level features, 
  which impose a performance penalty, and lower-level features, which are free 
  from these performance penalties but are more susceptible to design modifications 
  and are harder to maintain. The "pay as you go" principle enables programmers 
  to use C++ in diverse application domains and apply different programming paradigms 
  according to their needs and priorities.</p>
<h2> <a name="Heading9">Possible Future Additions to C++</a></h2>
<p>It's hard to predict which new features will be added to C++ in the future, 
  mostly because it's hard to predict what programming in general will be like 
  five or ten years from now. However, automatic garbage collection, concurrency, 
  and object persistence are already implemented in many other object-oriented 
  programming languages; in the future, they might be added to C++ as well. Rule-based 
  programming and better support for dynamically linked libraries are other such 
  possible extensions that C++ might or might not have in the future. The following 
  sections discuss these features, and their incurred complications, in greater 
  detail.</p>
<h3> <a name="Heading10">Automatic Garbage Collection</a></h3>
<p>"If the programmer's convenience is that important, why doesn't C++ have a 
  garbage collector?" is an often-heard question (garbage collection is also discussed 
  in Chapter 11, "Memory Management"). Clearly, an automated garbage collector 
  can make the life of a programmer easier. However, unlike objects, virtual member 
  functions, and dynamic casts, the programmer does not have the freedom of choice 
  with garbage collection. If garbage collection is an automatic process that 
  is hidden from the programmer, it violates the "pay as you go" principle. The 
  cost of automatic garbage collection is forced on users, even if they prefer 
  to manage dynamic memory manually.</p>
<p>Is it possible to add automated garbage collection as a switch (very much like 
  the capability to turn off RTTI support in some compilers)? This is an interesting 
  question. Surely, in programs that do not use dynamic memory allocation, the 
  programmer might want to turn the garbage collector off. The real crux is with 
  programs that allocate memory dynamically. Consider the following example:</p>
<pre>
<tt>void f()</tt>
<tt>{</tt>
<tt>  int * p = new int;</tt>
<tt>  //...use p</tt>
<tt>}</tt>
</pre>
<p>When the garbage collector is switched on, the implementation will mark the 
  pointer <tt>p</tt> as unreferenced when <tt>f()</tt> exits. Consequently, in 
  the next invocation of the garbage collector, the memory pointed to by <tt>p</tt> 
  will be released. Adding a garbage collector for such simple cases is overkill, 
  though. The programmer can use an <tt>auto_ptr</tt> (<tt>auto_ptr</tt> is discussed 
  in Chapter 6, "Exception handling," and in Chapter 11) to achieve the same effect. 
  For example</p>
<pre>
<tt>void f()</tt>
<tt>{</tt>
<tt>  auto_ptr&lt;int&gt; p (new int);</tt>
<tt>  //...use p</tt>
<tt>} //auto_ptr's destructor releases p</tt>
</pre>
<p>Garbage collection is more useful when dynamic memory has to be released at 
  a different scope from where it was allocated. For example, virtual constructors 
  (which are discussed in Chapter 4, "Special Member Functions: Default Constructor, 
  Copy Constructor, Destructor, and Assignment Operator") enable the user to instantiate 
  a new object of the right type, without having to know the exact type of the 
  source object (the example is repeated here for convenience):</p>
<pre>
<tt>class Browser</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  Browser();</tt>
<tt>  Browser( const Browser&amp;);</tt>
<tt>  virtual Browser* construct()  </tt>
<tt>    { return new Browser; } //virtual default constructor</tt>
<tt>  virtual Browser* clone() </tt>
<tt>    { return new Browser(*this); } //virtual copy constructor</tt>
<tt>  virtual ~Browser();</tt>
<tt>//...</tt>
<tt>};</tt>
<tt>class HTMLEditor: public Browser</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  HTMLEditor ();</tt>
<tt>  HTMLEditor (const HTMLEditor &amp;);</tt>
<tt>  HTMLEditor * construct() </tt>
<tt>    { return new HTMLEditor; }//virtual default constructor</tt>
<tt>  HTMLEditor * clone() </tt>
<tt>    { return new HTMLEditor (*this); } //virtual copy constructor</tt>
<tt>  virtual ~HTMLEditor();</tt>
<tt>  //...</tt>
<tt>};</tt>
</pre>
<p>In a garbage collected environment, it is possible to use a virtual constructor 
  in the following way:</p>
<pre>
<tt>void instantiate (Browser&amp; br)</tt>
<tt>{</tt>
<tt>  br.construct()-&gt;view();</tt>
<tt>}</tt>
</pre>
<p>Here again, the system automatically registers the unnamed pointer that is 
  returned from <tt>br.construct()</tt> and marks it as unreferenced so that the 
  garbage collector can later destroy its associated object and recycle its storage. 
  In a non-garbage collected environment, <tt>instantiate()</tt> causes a memory 
  leak because the allocated object is never deleted (it might cause undefined 
  behavior as well because the allocated object is never destroyed). To enable 
  this programming practice, a garbage collector is mandatory rather than optional. 
  You might suggest that <tt>instantiate()</tt> is to be written as follows:</p>
<pre>
<tt>void instantiate (Browser&amp; br)</tt>
<tt>{</tt>
<tt>  Browser *pbr = br.construct();</tt>
<tt>  pbr-&gt;view();</tt>
<tt>  delete pbr;</tt>
<tt>}</tt>
</pre>
<p>This way, <tt>instantiate()</tt> can be used in a non-garbage collected environment 
  as well as in a garbage collected one: When the garbage collector is active, 
  the <tt>delete</tt> statement is ignored (perhaps by some macro magic) and the 
  dynamically allocated object is automatically released some time after <tt>instantiate()</tt> 
  has exited. The <tt>delete</tt> statement is executed only when the garbage 
  collector is inactive. However, there is another subtle problem here. </p>
<h4> The Problem with Late Destructor Invocation</h4>
<p>In a non-garbage collected environment, <tt>pbr</tt> is deleted right before 
  <tt>instantiate()</tt> exits, which means that the destructor of the dynamically 
  allocated object is also invoked at that point. Conversely, in a garbage collected 
  environment, the destructor will be activated at an unspecified time after <tt>instantiate()</tt> 
  exits. The programmer cannot predict when this will happen. It might take a 
  few seconds, but it can also take hours or even days before the garbage collector 
  is invoked the next time. Now suppose that the destructor of <tt>Browser</tt> 
  releases a locked resourcesuch as a database connection, a lock, or a modem. 
  The program's behavior in a garbage collected environment is unpredictable -- 
  the locked resource can cause a deadlock because other objects might be waiting 
  for it, too. In order to avert such a potential deadlock, destructors can perform 
  only operations that do not affect other objects, and locked resources have 
  to be released explicitly by calling another member function. For example</p>
<pre>
<tt>void instantiate (Browser&amp; br)</tt>
<tt>{</tt>
<tt>  Browser *pbr = br.construct();</tt>
<tt>  pbr-&gt;view();</tt>
<tt>  pbr-&gt;release(); //release all locked resources</tt>
<tt>  delete pbr;</tt>
<tt>}</tt>
</pre>
<p>This is, in fact, the predominant technique in garbage collected languages. 
  Then again, to ensure interoperability between a garbage collected environment 
  and a non-garbage collected one, programmers will have to write a dedicated 
  member function that releases locked resources that the class acquires -- even 
  if that class is used in a non-garbage collected environment. This is an unacceptable 
  burden and a violation of the "pay as you go" principle. The conclusion that 
  can be drawn from this discussion is that garbage collection cannot be optional. 
  It is nearly impossible to write efficient and reliable programs that work in 
  both environments. Either automatic garbage collection needs to be an integral 
  part of the language, or it is totally out (as is the case in C++ at present).</p>
<h4> Time-Critical Applications</h4>
<p>Garbage collection cannot be optional, as you have observed. Why not make it 
  an integral part of the language? Real-time systems are based on deterministic 
  time calculations. For example, a function that has to execute within a time 
  slot of 500 microseconds should never exceed its allotted time slice. However, 
  the garbage collection process is non-deterministic -- it is impossible to predict 
  when it will be invoked, and how long it will take. Therefore, languages that 
  offer automatic garbage collection are usually disqualified for use in time-critical 
  applications. Note that real-time programming is not confined to missile launching 
  and low-level hardware manipulation; most modern operating systems include time-critical 
  components that control the allocation of system resources among processes and 
  threads. Many communication systems are also deterministic by nature. Adding 
  an automated garbage collector to C++ would disqualify it from being used in 
  such application domains. Because a toggled garbage collector is also impractical, 
  C++, by design, is not a garbage collected language at present. Notwithstanding 
  the difficulties involved in garbage collection, there are some serious discussions 
  of adding garbage collection to C++. It is too early to determine if and when 
  this will happen.</p>
<h3> <a name="Heading11">Object Persistence</a></h3>
<p>Persistent objects can be stored in nonvolatile storage and used later in other 
  runs of the same program or in other programs. Storing the contents of an object 
  in persistent storage is called <i>serialization</i>. The process of reconstituting 
  a serialized object from a persistent repository is called <i>deserialization</i>, 
  or <i>reconstitution</i>. Other object-oriented languages support object persistence 
  directly by means of a library or built-in keywords and operators. C++ does 
  not support object persistence directly. Designing an efficient, general purpose, 
  platform-independent model of object persistence is quite a challenge. This 
  section exemplifies handmade solutions that make up for the lack of language 
  support for persistence. The difficulties and complications that are associated 
  with a handmade object persistence model demonstrate the importance of language 
  support.</p>
<h4> Serialization and Deserialization of Concrete Objects</h4>
<p>Consider the following class:</p>
<pre>
<tt>class Date</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int day;</tt>
<tt>  int month;</tt>
<tt>  int year;</tt>
<tt>  //constructor and destructor</tt>
<tt>public:</tt>
<tt>  Date(); //current date</tt>
<tt>  ~Date();</tt>
<tt>   //...</tt>
<tt>};</tt>
</pre>
<p>Storing a <tt>Date</tt> object is a rather straightforward operation: Every 
  data member is written to a persistent stream (usually this is a local disk 
  file, but it can also be a file on a remote computer). The data members can 
  be read from the stream at a later stage. For that purpose, two additional member 
  functions are required, one for storing the object and the other for reading 
  the stored object: </p>
<pre>
<tt>#include&lt;fstream&gt;</tt>
<tt>using namespace std;</tt>
<tt>class Date</tt>
<tt>{</tt>
<tt>//...</tt>
<tt>  virtual ofstream&amp; Write(ofstream&amp; archive);</tt>
<tt>  virtual ifstream&amp; Read(ifstream&amp;  archive);</tt>
<tt>};</tt>
<tt>ofstream&amp;  Date::Write(ofstream&amp; archive)</tt>
<tt>{</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;day), sizeof(day));</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt>
<tt>  return archive;</tt>
<tt>}</tt>
<tt>ifstream&amp;  Date::Read(ifstream&amp; archive)</tt>
<tt>{</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;day), sizeof(day));</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt>
<tt>  return archive;</tt>
<tt>}</tt>
</pre>
<p>In addition to the member functions <tt>Read()</tt> and <tt>Write()</tt>, it 
  is necessary to define a <i>reconstituting constructor</i>, which reads a serialized 
  object from a stream:</p>
<pre>
<tt>Date::Date(ifstream&amp; archive) //reconstituting constructor</tt>
<tt>{</tt>
<tt>  Read(arcive);</tt>
<tt>}</tt>
</pre>
<h4> Class Hierarchies</h4>
<p>For concrete classes such as <tt>Date</tt>, whose members are fundamental types, 
  making up for the lack of standardized persistence facilities is rather straightforward. 
  The serialization and deserialization operations merely store and read data 
  members, respectively. Note that the class's member functions are not serialized. 
  This is not a major issue of concern because the serialized object should be 
  a close approximation of the binary representation of the object in memory.</p>
<p>Handling derived classes and classes that contain member objects is more complicated: 
  The member functions <tt>Read()</tt> and <tt>Write()</tt> need to be redefined 
  in every class in the hierarchy. Likewise, a reconstituting constructor is required 
  for every class, as in the following example: </p>
<pre>
<tt>class DateTime: public Date</tt>

⌨️ 快捷键说明

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