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

📄 ch14.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
  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><tt>{</tt><tt>private:</tt>

⌨️ 快捷键说明

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