📄 ch05.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD> <META NAME="Author" Content="Steph Mineart"> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <TITLE>ANSI/ISO C++ Professional Programmer's Handbook - Chapter 5 - Object-Oriented Programming and Design</TITLE> <link rel="stylesheet" TYPE="text/css" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>ANSI/ISO C++ Professional Programmer's Handbook</H1></CENTER><CENTER> <P><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> <HR></CENTER><H1 align="center">5</H1><h1 align="center"> Object-Oriented Programming and Design</h1><address>by Danny Kalev </address><ul> <li><a href="#Heading1">Introduction</a> <li><a href="#Heading2">Programming Paradigms</a> <ul> <li><a href="#Heading3">Procedural Programming</a> <li><a href="#Heading4">Object-Based Programming</a> <li><a href="#Heading5">Object-Oriented Programming</a> </ul> <li><a href="#Heading6">Techniques Of Object-Oriented Programming</a> <ul> <li><a href="#Heading7">Class Design</a> <li><a href="#Heading8">The "Resource Acquisition Is Initialization" Idiom</a> </ul> <li><a href="#Heading9">Classes and Objects</a> <li><a href="#Heading10">Designing Class Hierarchies</a> <ul> <li><a href="#Heading11">Private Data Members Are Preferable To Protected Ones</a> <li><a href="#Heading12">Declaring Virtual Base Class Destructors</a> <li><a href="#Heading13">Virtual Member Functions</a> <li><a href="#Heading14">Abstract Classes and Interfaces</a> <li><a href="#Heading15">Use Derivation Instead of Type-Fields</a> <li><a href="#Heading16">Overloading A Member Function Across Class Boundaries</a> <li><a href="#Heading17">Deciding Between Inheritance and Containment</a> <li><a href="#Heading18">The Holds-a Relation</a> <li><a href="#Heading19">Empty Classes</a> <li><a href="#Heading20">Using structs as A Shorthand for Public Classes</a> <li><a href="#Heading21">Friendship</a> <li><a href="#Heading22">Nonpublic Inheritance</a> <li><a href="#Heading23">Common Root Class</a> <li><a href="#Heading24">Forward Declarations</a> <li><a href="#Heading25">Local Classes</a> <li><a href="#Heading26">Multiple Inheritance</a> </ul> <li><a href="#Heading27">Conclusions</a> </ul><hr size=4><h2> <a name="Heading1">Introduction</a></h2><p>C++ is the most widely used object-oriented programming language today. The success of C++ has been a prominent factor in making object-oriented design and programming a de facto standard in today's software industry. Yet, unlike other object-oriented programming languages (some of them have been around for nearly 30 years), C++ does not enforce object-oriented programming -- it can be used as a "better C", as an <i>object-based</i> language, or as a generic programming language. This flexibility, which is unparalleled among programming languages, makes C++ a suitable programming language in any domain area -- real time, embedded systems, data processing, numerical computation, graphics, artificial intelligence, or system programming.</p><p>This chapter begins with a brief survey of the various programming styles that are supported by C++. Next, you will focus on various aspects of object-oriented design and programming.</p><h2> <a name="Heading2">Programming Paradigms</a></h2><p>A programming paradigm defines the methodology of designing and implementing software, including the building blocks of the language, the interaction between data structures and the operations applied to them, program structure, and how problems are generally analyzed and solved. A programming language provides the linguistic means (keywords, preprocessor directives, program structure) as well as the extra-linguistic capabilities, namely standard libraries and programming environment, to support a specific programming paradigm. Usually, a given programming language is targeted for a specific application domain, for example, string manipulation, mathematical applications, simulations, Web programming and so on. C++, however, is not confined to any specific application domain. Rather, it supports many useful programming paradigms. Now, for a discussion of some of the most prominent programming paradigms supported in C++.</p><h3> <a name="Heading3">Procedural Programming</a></h3><p>C++ is a superset of ISO C. As such, it can be used as a procedural programming language, albeit with tighter type checking and several enhancements that improve design and coding: reference variables, inline functions, default arguments, and <tt>bool</tt> type. Procedural programming is based on separation between functions and the data that they manipulate. In general, functions rely on the physical representation of the data types that they manipulate. This dependency is one of the most problematic aspects in the maintenance and extensibility of procedural software.</p><h4> Procedural Programming Is Susceptible To Design Changes</h4><p>Whenever the definition of a type changes (as a result of porting the software to a different platform, changes in the customer's requirement, and so on), the functions that refer to that type have to be modified accordingly. The opposite is also true: When a function is being changed, its arguments might be affected as well; for instance, instead of passing a struct by value, it might be passed by address to optimize performance. Consider the following:</p><pre><tt>struct Date //pack data in a compact struct</tt><tt>{</tt><tt> char day;</tt><tt> char month;</tt><tt> short year;</tt><tt>};</tt><tt>bool isDateValid(Date d); //pass by value</tt><tt>void getCurrentDate(Date * pdate); //changes its argument, address needed</tt><tt>void initializeDate (Date* pdate); //changes its argument, address needed</tt></pre><p>Data structures, such as <tt>Date</tt>, and the group of associated functions that initialize, read, and test it are very common in software projects in which C is the predominant programming language. Now suppose that due to a change in the design, <tt>Date</tt> is required to also hold the current time stamp in seconds. Consequently, a change in the definition of <tt>Date</tt> is made:</p><pre><tt>struct Date</tt><tt>{</tt><tt> char day;</tt><tt> char month;</tt><tt> short year;</tt><tt> long seconds;</tt><tt>}; //now less compact than before</tt></pre><p>All the functions that manipulate <tt>Date</tt> have to be modified to cope with change. An additional change in the design adds one more field to store millionths of a second in order to make a unique timestamp for database transactions. The modified <tt>Date</tt> is now</p><pre><tt>struct Date</tt><tt>{</tt><tt> char day;</tt><tt> char month;</tt><tt> short year;</tt><tt> long seconds;</tt><tt> long millionths;</tt><tt>};</tt></pre><p>Once more, all the functions that manipulate <tt>Date</tt> have to be modified to cope with the change. This time, even the interface of the functions changes because <tt>Date</tt> now occupies at least 12 bytes. Functions that are passed a <tt>Date</tt> by value are modified to accept a pointer to <tt>Date</tt>.</p><pre><tt>bool isDateValid(Date* pd); // pass by address for efficiency</tt></pre><h4> Drawbacks of Procedural Programming</h4><p>This example is not fictitious. Such frequent design changes occur in almost every software project. The budget and time overhead that are produced as a result can be overwhelming; indeed, they sometimes lead to the project's discontinuation. The attempt to avoid -- or at least to minimize -- these overheads has led to the emergence of new programming paradigms.</p><p>Procedural programming enables only a limited form of code reuse, that is, by calling a function or using a common user-defined data structure. Nonetheless, the tight coupling between a data structure and the functions that manipulate it considerably narrows their reusability potential. A function that computes the square root of a <tt>double</tt> cannot be applied to a user-defined <tt>struct</tt> that represents a <tt>complex</tt>, for example. In general, procedural programming languages rely on static type checking, which ensures better performance than dynamic type checking -- but it also compromises the software's extensibility.</p><p>Procedural programming languages provide a closed set of built-in data types that cannot be extended. User-defined types are either unsupported or they are "second class citizens" in the language. The user cannot redefine built-in operators to support them. Furthermore, the lack of abstraction and information hiding mechanisms force users to expose the implementation details. Consider the standard C functions <tt>atof()</tt>, <tt>atoi()</tt>, and <tt>atol()</tt>, which convert a C-string to <tt>double</tt>, <tt>int</tt>, and <tt>long</tt>, respectively. Not only do they force the user to pay attention to the physical data type of the return value (on most machines these days, an <tt>int</tt> and a <tt>long</tt> are identical anyway), they also prohibit the use of other data types.</p><h4> Why Procedural Programming Still Matters</h4><p>In spite of its noticeable drawbacks, procedural programming is still the preferred programming paradigm in some specific application domains, such as embedded and time critical systems. Procedural programming is also widely used in machine generated code because code reuse, extensibility, and maintenance are immaterial in this case. Many SQL interpreters, for example, translate the high-level SQL statements into C code that is then compiled.</p><p>Procedural programming languages -- such as C, Pascal, or Fortran -- produce the most efficient machine code among high-level programming languages. In fact, development teams that are reluctant to adopt object orientation often point to performance degradation as the major deterring factor.</p><p>The evolution of C++ is unique among programming languages. The job of its creators might have been a lot easier had they chosen to design it from scratch, without guaranteeing backward compatibility with C. Yet this backward compatibility is one of the its strengths: It enables organizations and programmers to benefit from C++ without having to trash hundreds of millions of lines of working C code. Furthermore, C programmers can easily become productive in C++ even before they have fully mastered object-oriented programming.</p><h3> <a name="Heading4">Object-Based Programming</a></h3><p>The limitations of procedural programming have led researchers and developers alike to find better methods of separating implementation details from interfaces. Object-based programming enables them to create user-defined types that behave like first class citizens. User-defined types can bundle data and meaningful operations in a single entity -- a <i>class</i>. Classes also support information hiding, thereby separating implementation details such as physical representation and underlying bookkeeping from the set of services that a class provides, or its <i>interface</i>. Users of a class are allowed to access its interface, but they cannot access its implementation details. The separation between the implementation -- which might vary rather frequently due to design changes, portability, and efficiency -- and the stable interface is substantial. This separation ensures that changes in the design are localized to a single entity -- the class implementation; the class users, on the other hand, are not affected. To assess the importance of object-based programming, examine a simple minded <tt>Date</tt> class:</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> char day;</tt><tt> char month;</tt><tt> short year;</tt><tt>public:</tt><tt> bool isValid();</tt><tt> Date getCurrent();</tt><tt> void initialize();</tt><tt>};</tt></pre><h4> Object-Based Programming Localizes Changes In Implementation Details</h4><p>Now suppose that you have to change the definition of <tt>Date</tt> to support time:</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> char day;</tt><tt> char month;</tt><tt> short year;</tt><tt> long secs;</tt><tt>public:</tt><tt> bool isValid();</tt><tt> Date getCurrent();</tt><tt> void initialize ();</tt><tt>};</tt></pre><p>The addition of a new data member does not affect the interface of <tt>Date</tt>. The users of <tt>Date</tt> don't even know that a new field has been added; they continue to receive the same services from the class as before. Of course, the implementer of <tt>Date</tt> has to modify the code of the member functions to reflect the change. Therefore, <tt>Date::initialize()</tt> has to initialize one more field. Still, the change is localized only to the definition of <tt>Date::initialize()</tt> because users cannot access the underlying representation of <tt>Date</tt>. In procedural programming, however, users can access the data members of <tt>Date</tt> directly.</p><h4> Abstract Data Types</h4><p>Classes such as <tt>Date</tt> are sometimes called <i>concrete types,</i> or <i>abstract data types</i> (not to be confused with <i>abstract classes</i>; see the sidebar titled "Abstract Data Types Versus Abstract Classes" later in this chapter). </p><p>These classes can meet a vast variety of needs in clean and easy-to-maintain
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -