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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<tt>{</tt>
<tt>  b.f(); //base::f or derived::f? resolution is delayed to runtime</tt>
<tt>}</tt>
<tt>//a separate translation unit</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  derived d;</tt>
<tt>  identify; // argument is an object derived from base</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>The function <tt>identify</tt> can receive any object that is publicly derived 
  from class <tt>base</tt> -- even objects of subclasses that were defined after 
  <tt>identify</tt> was compiled.</p>
<p>Dynamic binding has numerous advantages. In this example, it enables the user 
  to extend the functionality of <tt>base</tt> without having to modify <tt>identify</tt> 
  in any way. In procedural and object-based programming, such flexibility is 
  nearly impossible. Furthermore, the underlying mechanism of dynamic binding 
  is automatic. The programmer doesn't need to implement the code for runtime 
  lookup and dispatch of a virtual function, nor does he or she need to check 
  the dynamic type of the object.</p>
<h2> <a name="Heading6">Techniques Of Object-Oriented Programming</a></h2>
<p>Up until now, the discussion has focused on the general characteristics of 
  object-oriented programming and design. This part presents C++-specific practical 
  techniques and guidelines of object-oriented programming.</p>
<h3> <a name="Heading7">Class Design</a></h3>
<p>Classes are the primary unit of abstraction in C++. Finding the right classes 
  during analysis and design is perhaps the most important phase in the lifetime 
  of an object-oriented software system. The common guidelines for finding classes 
  state that a class should represent a real-world object; others maintain that 
  nouns in natural languages should represent classes. This is true to some extent, 
  but nontrivial software projects often have classes that exist nowhere except 
  the programming domain. Does an exception represent a real-world object? Do 
  function objects (which are discussed in Chapter 10, "STL and Generic Programming") 
  and smart pointers have an equivalent outside the programming environment? Clearly, 
  the relationship between real-world entities and objects is not 1:1.</p>
<h4> Finding the Classes</h4>
<p>The process of finding the right classes is mostly derived from the functional 
  requirements of the application domain. That is, a designer can decide to represent 
  a concept as a class (rather than, for example, a member function within a different 
  class or a global function) when it serves the needs of the application. This 
  is usually done by means of CRC (Class, Responsibility, Collaboration) cards 
  or any other method.</p>
<h4> Common Design Mistakes with Classes</h4>
<p>No two object-oriented languages are alike. The programming language also affects 
  the design. As you learned in Chapter 4, "Special Member Functions: Default 
  Constructor, Copy Constructor, Destructor, and Assignment Operator," C++ has 
  a distinct symmetry between constructors and destructors that most other object-oriented 
  languages do not have. Objects in C++ can automatically clean up after themselves. 
  C++ also enables you to create local objects with automatic data storage. In 
  other languages, objects can only be created on heap memory. C++ is also one 
  of just a few languages that support multiple inheritance. C++ is a strongly-typed 
  language with static type checking. As much as design gurus insist on separating 
  pure design from implementation artifacts (that is, language-specific behavior), 
  such language-specific features do affect the overall design. But of course, 
  design mistakes do not result only from the interference of other languages.</p>
<p>Object-orientation is not a panacea. Some common pitfalls can lead to monstrous 
  applications that need constant maintenance, that perform unsatisfactorily, 
  and that only eventually -- or never -- reach production. Some of these design 
  mistakes are easy to detect.</p>
<h4> Gigantic Classes</h4>
<p>There are no standardized methods for measuring the size of a class. However, 
  many small specialized classes are preferred to a bulky single class that contains 
  hundreds of member functions and data members. But such bulky classes do get 
  written. Class <tt>std::string</tt> has a fat interface of more than 100 member 
  functions; clearly, this is an exception to the rule and, to be honest, many 
  people consider this to be a compromise between conflicting design approaches. 
  Still, ordinary programs rarely use all these members. More than once I've seen 
  programmers extending a class with additional member functions and data members 
  instead of using more plausible object-oriented techniques such as subclassing. 
  As a rule, a class that exceeds a 20-30 member function count is suspicious.</p>
<p>Gigantic classes are problematic for at least three reasons: Users of such 
  classes rarely know how to use them properly; the implementation and interface 
  of such classes tend to undergo extensive changes and bug-fixes; and they are 
  not good candidates for reuse because the fat interface and intricate implementation 
  details can fit only a very limited usage. In a sense, large classes are very 
  similar to large functions -- they are noncohesive and difficult to maintain.</p>
<h4> Exposing Implementation Details</h4>
<p>Declaring data members with public access is, almost without exception, a design 
  flaw. Still, even vendors of popular frameworks resort to this deprecated programming 
  style. It might be tempting to use public data members because it saves the 
  programmer the bother of writing trivial <i>accessors</i> and <i>mutators</i> 
  (<i>getters</i> and <i>setters</i>, respectively). This approach cannot be recommended, 
  however, because it results in maintenance difficulties and it compromises the 
  class's reliability. Users of such classes tend to rely heavily on their implementation 
  details; even if they normally avoid such dependencies, they might feel that 
  the exposure of the implementation details implies that they are not supposed 
  to change. Sometimes there is no other choice -- the class implementer has not 
  defined any other method of accessing data members of a class. The process of 
  modifying or extending such classes becomes a maintenance nightmare. Infrastructure 
  components, such as <tt>Date</tt> or <tt>string</tt> classes, can be used dozens 
  of times within a single source file. It is not hard to imagine what it is like 
  when dozens of programmers, each producing dozens of source files, have to chase 
  every source line that refers to any one of these classes. This is exactly what 
  caused the notorious Year 2000 Bug. If, on the other hand, data members are 
  declared <tt>private</tt>, users cannot access them directly. When the implementation 
  details of the class are modified, only accessors and mutators need to be modified, 
  but the rest of the code remains intact. </p>
<p>There is another danger in exposing implementation details. Due to indiscriminate 
  access to data members and helper functions, users can inadvertently tamper 
  with the object's internal data members. They might delete memory (which is 
  supposed to be deleted by the destructor), or they might change the value of 
  a file handle, and so on, with disastrous results. Therefore, it is always a 
  better design choice to hide implementation details of an object.</p>
<h3> <a name="Heading8">The "Resource Acquisition Is Initialization" Idiom</a></h3>
<p>Many objects of various kinds share a similar characterization: They must be 
  acquired by means of initialization prior to their usage; then they can be used, 
  and then they have to be released explicitly. Objects such as <tt>File</tt>, 
  <tt>CommunicationSocket</tt>, <tt>DatabaseCursor</tt>, <tt>DeviceContext</tt>, 
  <tt>OperatingSystem</tt>, and many others have to be opened, attached, initialized, 
  constructed, or booted, respectively, before you can use them. When their job 
  is done, they have to be flushed, detached, closed, released, or logged out, 
  respectively. A common design mistake is to have the user request explicitly 
  for the initialization and release operations to take place. A much better choice 
  is to move all initialization action into the constructor and all release actions 
  into the destructor. This technique is called <i>resource acquisition is initialization</i> 
  (<i>The C++ Programming Language, 3rd ed.</i>, page 365). The advantage is a 
  simplified usage protocol. Users can start using the object right after it has 
  been created, without bothering with whether the object is valid or whether 
  further arbitrary initialization actions have to be taken. Furthermore, because 
  the destructor also releases all its resources, users are free from that hassle 
  too. Please note that this technique usually requires an appropriate exception 
  handling code to cope with exceptions that are thrown during construction of 
  the object.</p>
<h2> <a name="Heading9">Classes and Objects</a></h2>
<p>Unlike some other object-oriented programming languages, C++ makes a clear 
  distinction between a class, which is a user-defined type, and an object, which 
  is an instance thereof. There are several features for manipulating the state 
  of a class rather than the state of individual objects. These features are discussed 
  in the following sections.</p>
<h4> Static Data Members</h4>
<p>A static member is shared by all instances of its class. For that reason, it 
  is sometimes termed a <i>class variable</i>. Static members are useful in synchronization 
  objects. For example, a file lock can be implemented using a <tt>static</tt> 
  data member. An object that is trying to access this file has to check first 
  whether the file is being processed by another user. If the file is available, 
  the object turns the flag on and user can process the file safely. Other users 
  are not allowed to access the file until the flag is reset to false. When the 
  object that is processing the file is finished, it has to turn off the flag, 
  enabling another object to access it.</p>
<pre>
<tt>class fileProc</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  FILE *p;</tt>
<tt>  static bool Locked<cite>;</cite></tt>
<tt>public<b>:</b></tt>
<tt>//...</tt>
<tt><b><tt>  </tt></b>bool isLocked () const;</tt>
<tt>  //...</tt>
<tt>};</tt>
<tt>bool fileProc::Locked<cite>;</cite></tt>
</pre>
<h4> Static Member Functions</h4>
<p>A static member function in a class can access only other static members of 
  its class.. Unlike ordinary member functions, a static member function can be 
  invoked even when no object instance exists. For example</p>
<pre>
<tt>class stat</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int num;</tt>
<tt>public:</tt>
<tt>  stat(int n = 0) {num=n;}</tt>
<tt>  static void print() {cout &lt;&lt;"static member function" &lt;&lt;endl;</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  stat::print(); //no object instance required</tt>
<tt>  stat s(1);</tt>
<tt>  s.print();//still, a static member function can be called from an object</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>Static members are used in the following cases:</p>
<ul>
  <li> 
    <p> When all other data members of an object are also static</p>
  </li>
  <p></p>
  <li> When the function does not depend on any other object member (like <tt>print()</tt>, 
    in the previous example)</li>
  <p></p>
  <li> As a wrapper of a global function</li>
</ul>
<p></p>
<h4> A Pointer to Member Cannot Refer To a Static Member Function</h4>
<p>It is illegal to assign the address of a static class member to a pointer to 
  member. However, you can take the address of a static member function of a class 
  and treat it as if it were an ordinary function. For example</p>
<pre>
<tt>class A</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  static  void f();</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  void (*p) () = &amp;A::f; //OK, ordinary pointer to function</tt>
<tt>}</tt>
</pre>
<p>You can do this because a static member function is essentially an ordinary 
  function, which doesn't take an implicit <tt>this</tt> argument.</p>
<h4> Defining a Class Constant</h4>
<p>When you need a constant integer member in a class, the easiest way to create 
  one is by using a <tt>const</tt> <tt>static</tt> member of an integral type; 
  unlike other <tt>static</tt> data members, such a member can be initialized 
  within the class body (see also Chapter 2, "Standard Briefing: The Latest Addenda 
  to ANSI/ISO C++"). For example</p>
<pre>
<tt>class vector</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int v_size;</tt>
<tt>  const static int MAX  1024; //a single MAX is shared by all vector objects</tt>
<tt>  char *p;</tt>
<tt>public:</tt>
<tt>  vector() {p = new char[MAX]; }</tt>
<tt>  vector( int size)</tt>
<tt>  {</tt>
<tt>    if (size &lt;= MAX)</tt>
<tt>      p = new char[size] ;</tt>
<tt>    else</tt>
<tt>     p = new char[MAX];</tt>
<tt>  }</tt>
<tt>};</tt>
</pre>
<h2> <a name="Heading10">Designing Class Hierarchies</a></h2>
<p>After identifying a set of potential classes that might be required for the 
  application, it is important to correctly identify the interactions and relationships 
  among the classes to specify inheritance, containment, and ownership. The design 
  of class hierarchies, as opposed to designing concrete types, requires additional 
  considerations that are discussed in this section. </p>
<h3> <a name="Heading11">Private Data Members Are Preferable To Protected Ones</a></h3>
<p>Data members of a class are usually a part of its implementation. They can 
  be replaced when the internal implementation of the class is changed; therefore, 
  they need to be hidden from other classes. If derived classes need to access 
  these data members, they need to use accessor methods instead of directly accessing 
  data members of a base class. Consequently, no modification is required for 
  derived classes when a change is made in the base class. </p>
<p>Here's an example:</p>
<pre>
<tt>class Date</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int d,m,y //how a date is represented is an implementation detail</tt>
<tt>public:</tt>
<tt>  int Day() const {return d; }</tt>
<tt>};</tt>

⌨️ 快捷键说明

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