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

📄 ch14.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<tt>}</tt><tt>void Date::setMonth(int m)</tt><tt>{</tt><tt>  get_lock();</tt><tt>  month = m;</tt><tt>  release_lock();</tt><tt>}</tt><tt>//etc.</tt></pre><p>This is tedious, and yet very simple to automate. The recurrent pattern is   very reminiscent of the "resource acquisition is initialization" idiom (discussed   in Chapter 5, "Object-Oriented Programming and Design"). You can define a class   whose constructor acquires a lock, and whose destructor releases it. For example</p><pre><tt>class LockDate</tt><tt>{</tt><tt>private:</tt><tt>  Date&amp; date;</tt><tt>public:</tt><tt>  LockDate(const Date&amp; d) : date { lock(&amp;d); }</tt><tt>  ~LockDate() { release(&amp;d); }</tt><tt>};</tt></pre><p>A real-world lock class would probably be templatized. It would also provide   timeouts and handle exceptions; however, the definition of <tt>LockDate</tt>   suffices for this discussion. The member functions of <tt>Date</tt> can now   be defined as follows:</p><pre><tt>int Date::getDay() const</tt><tt>{</tt><tt>  LockDate ld(this);</tt><tt>  return day;</tt><tt>}</tt><tt> /...and so on</tt><tt>void Date::getDay(int d)</tt><tt>{</tt><tt>  LockDate ld(this);</tt><tt>  day = d;</tt><tt> }</tt><tt>//etc.</tt></pre><p>This looks better than the original thread-safe version, but it's still tedious.   Standard C++, however, goes only that far. A fully automated thread safety requires   core language extensions.</p><p>It might not seem obvious from the example why language support for thread   safety is necessary. After all, instantiating a local object in every member   function is not unacceptably complicated or inefficient. The troubles begin   with inheritance. Invoking a non-thread-safe inherited member function might   have undefined results in this case. To ensure thread safety in inherited member   functions as well, the implementer of <tt>Date</tt> has to override every inherited   member function. In each override, a lock has to be acquired. Then, the parent   member function is invoked, and finally, the lock is released. With a little   help from the programming language, these operations can be made much easier.</p><p><b>Before Method and After Method</b></p><p>The CLOS programming language defines the concepts <i>before method</i> and   <i>after method</i>. A before method is a sequence of operations that precedes   the action of a method. An after method is a sequence of operations that succeeds   the action of a method. Thus, each method (member function) in CLOS can be thought   of as an object with a corresponding constructor and destructor. CLOS provides   default before method and after method for each user-defined method. By default,   the before method and after method do nothing. However, the user can override   them to perform initialization and cleanup operations. Adopting this concept   in C++ with slight modifications might simplify the implementation of thread-safe   classes. One direction is to provide identical before method and after method   for every member function of a class. That is, the before method and after method   are defined only once, but they are automatically invoked by every member function   of the class (except for the constructor and destructor). One of the benefits   of this approach is that new member functions that are added to the class automatically   become thread-safe, as do inherited member functions.</p><h3> <a name="Heading13">Extensible Member Functions</a></h3><p>Several programming languages enable the user to compose inherited member functions   in a derived class almost automatically. In C++, a member function of a derived   class overrides rather than extends the corresponding member of the base class.   It is possible to extend the inherited function by calling it explicitly before   performing any other operations in the overriding member function (see Chapter   5). The following example (repeated here for convenience) shows how it is done:</p><pre><tt>class rectangle: public shape</tt><tt>{</tt><tt>  //...</tt><tt>  virtual void resize (int x, int y) //extends base's resize()</tt><tt>  {</tt><tt><b>    shape::resize(x, y);</b>  //explicit call to the base's virtual function</tt><tt>    //add functionality</tt><tt>    int size = x*y;</tt><tt>  }</tt><tt>};</tt></pre><p>There are two problems with this approach. First, if the base class name changes,   the implementer of the derived class has to find every occurrence of the old   qualified name and change it accordingly. </p><p>Another problem is that some member functions are meant to be extended rather   than overridden. The best examples are constructors and destructors (which,   luckily, the compiler takes care of), but there are other such examples. The   serialization and deserialization operations that were discussed previously   also need to be extended rather than overridden in a derived class.</p><p>It is very tempting to solve the first problem by adding the keyword <tt>super</tt>   to the language. Smalltalk and other object-oriented languages already have   it. Why not let C++ programmers enjoy it as well? <tt>super</tt> refers to the   direct base class. It can be used in the following manner:</p><pre><tt>class rectangle: public shape</tt><tt>{</tt><tt>  //...</tt><tt>  void resize (int x, int y) //extends base's resize()</tt><tt>  {</tt><tt>    super.resize(x, y);  //the name of the base class is not necessary anymore</tt><tt>    //add functionality</tt><tt>    int size = x*y;</tt><tt>  }</tt><tt>};</tt><tt>class specialRect: public rectangle</tt><tt>{</tt><tt>void resize (int x, int y) //extends base's resize()</tt><tt>  {</tt><tt>    super.resize(x, y);  //calls recatngle::resize()</tt><tt>    //add more functionality</tt><tt>  }</tt><tt>};</tt></pre><p>However, <tt>super</tt> is ambiguous in objects that have multiple base classes.   An alternative solution is to add a different keyword to the language, <tt>extensible</tt>,   that instructs the compiler to insert a call of the base member function in   an overriding member function automatically. For example</p><pre><tt>class shape</tt><tt>{</tt><tt>public:</tt><tt>  extensible void resize();</tt><tt>}</tt><tt>class rectangle: public shape</tt><tt>{</tt><tt>  public:</tt><tt>  void resize (int x, int y) //extends base's resize()</tt><tt>  { //shape::resize() is implicitly invoked at this point</tt><tt>//add functionality</tt><tt>    int size = x*y;</tt><tt>  }</tt><tt>};</tt><tt>class specialRect: public rectangle</tt><tt>{</tt><tt>void resize (int x, int y) //extends base's resize()</tt><tt>  {<b> </b>//implicitly calls recatngle::resize()</tt><tt>    //...add more functionality</tt><tt>  }</tt><tt>};</tt></pre><p><tt>extensible</tt> is a specialized form of <tt>virtual</tt>, so the latter   is unnecessary. Surely, <tt>extensible</tt> solves the first problem: If the   base class name changes, the implementer of the derived class does not have   to change the definition of the member functions. The second problem is also   solved here: After a member function is declared <tt>extensible</tt>, the compiler   automatically sees that the corresponding member function of a derived class   first invokes the member function of the base class.</p><h3> <a name="Heading14">Dynamically Linked Libraries</a></h3><p>A typical C++ application consists of a statically linked executable that contains   all the code and data of the program. Although static linking is efficient in   terms of speed, it's inflexible: Every change in the code requires a complete   rebuild of the executable. When a dynamically linked library is used, the executable   does not need to be rebuilt; the next time the application is run, it automatically   picks up the new library version. The advantage of dynamically linked libraries   is a transparent upgrade of new releases of the dynamically linked library.   However, this transparent "drop in" model breaks under the object model of C++   if the data layout of an object changes in the new release of the library; this   is because the size of an object and the offset of its data members are fixed   at compile time. There have been suggestions to extend the object model of C++   so that it can support dynamic shared libraries better. However, the costs are   slower execution speed and size.</p><h3> <a name="Heading15">Rule-Based Programming</a></h3><p>Many commercial databases support <i>triggers</i>. A trigger is a user-defined   rule that instructs the system to perform specific actions automatically whenever   a certain data value changes. For example, imagine a database that contains   two tables, Person and Bank Account. Every row in Bank Account is associated   with a record in Person. Deleting a Person record automatically triggers the   deletion of all its associated Bank Account records. Rules are the equivalent   of triggers in software systems. William Tepfenhart and other researchers at   AT&amp;T Bell Laboratories have extended C++ to support rules (<i>UML and C++:   A Practical Guide to Object-Oriented Development</i>, p. 137). The extended   language is called R++ (the <i>R</i> stands for "rules"). In addition to member   functions and data members, R++ defines a third kind of class member: a rule.   A rule consists of a condition and an associated action that is automatically   executed when the condition evaluates to <tt>true</tt>. In C++, the programmer   has to test the condition manually in order to decide whether the associated   action is to be executed, usually by a <tt>switch</tt> statement or an <tt>if</tt>   statement. In R++, this testing is automated -- the system monitors the data   members listed in the rule's condition, and whenever the condition is satisfied,   the rule "fires" (that is, the associated action is executed). Rule-based programming   is widely used in artificial intelligence, debugging systems, and event-driven   systems. Adding this feature to C++ could considerably simplify the design and   implementation of such systems.</p><h2> <a name="Heading16">Conclusions</a></h2><p>Language extensions are needed to facilitate the implementation of operations   that otherwise might be more difficult or even impossible. However, there is   always a tradeoff involved. To use an analogy, adding an air conditioner to   a car decreases its fuel efficiency and degrades its performance (<i>Principles   of Programming Languages: Design, Evaluation and Implementation</i>, p. 327).   Whether it is a beneficial tradeoff depends on various factors, such as the   climate in the region where the car is used, the cost of fuel, the engine's   power, and the personal preferences of its users. Note that the air conditioner   can always be turned off to gain more power and increase the fuel efficiency.   Ideally, new language features will not impose a performance penalty of any   kind when they are not used. When the programmer deliberately uses them, they   should impose as little overhead as possible or no overhead at all. There is,   however, a notable difference between an air conditioner and language extensions:   Extensions interact with one another. For example, the imaginary keyword <tt>super</tt>   has an undesirable interaction with another language feature, namely multiple   inheritance. A more realistic example is template's template arguments. The   space between the left two angular brackets is mandatory:</p><pre><tt>Vector &lt;Vector&lt;char*&gt; &gt; msg_que(10);</tt></pre><p>Otherwise, the <tt>&gt;&gt;</tt> sequence is parsed as the right shift operator.   In other situations, the interaction is much more complex: Koenig lookup, for   instance, can have surprising results under some circumstances (as you read   in Chapter 8, "Namespaces").</p><p>This chapter has presented three major proposals for language extensions: garbage   collection, persistence, and concurrency. Suggestions for less radical extensions   are extensible members and rules. None of these is to be taken lightly. The   complexity involved in standardizing each of these is intensified even further   when they interact with each other. For example, a persistence model becomes   even more complicated in a thread-safe environment.</p><p>Considering the challenges that the designers of C++ have faced during the   past two decades, you can remain optimistic. If you are familiar with the prestandardized   implementations of container classes, RTTI, and exception handling of several   well known frameworks, you are probably aware of how the standardized container   classes, RTTI, and exception handling are much better in every way. This will   also be the case if any of the features that are discussed here become part   of the C++ Standard.</p><CENTER><P><HR>  <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>&#169; <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. Allrights reserved.</p></CENTER></BODY></HTML>

⌨️ 快捷键说明

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