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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<tt>  get_lock();</tt>
<tt>  day = d;</tt>
<tt>  release_lock();</tt>
<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. All
rights reserved.</p>
</CENTER>


</BODY>

</HTML>

⌨️ 快捷键说明

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