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

📄 ch05.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<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><tt>class DateTime : public Date</tt><tt>{</tt><tt>private:</tt><tt>  int hthiss;</tt><tt>  int minutes;</tt><tt>  int seconds;</tt><tt>public:</tt><tt>//...additional member functions</tt><tt>};</tt></pre><p>Now assume that class <tt>Date</tt> is used mostly on display devices, so it   has to supply some method of converting its <tt>d</tt>,<tt>m</tt>,<tt>y</tt> 

⌨️ 快捷键说明

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