• No results found

Object-Oriented Programming

Data Design and Implementation

HUMAN ANATOMY

2. To specify a place from which a value is to be retrieved: value = numbers[4];

2.4 Object-Oriented Programming

In Chapter 1, we said that functional design results in a hierarchy of tasks and that object-oriented design results in a hierarchy of objects. Structured programming is the implementation of a functional design, and object-oriented programming (OOP) is the implementation of an object-oriented design. However, these approaches are not entirely distinct: The implementation of an operation on an object often requires a func- tional design of the algorithm. In this section, we examine object-oriented programming in more depth.

Concepts

The vocabulary of object-oriented programming has its roots in the programming lan- guages Simula and Smalltalk. It can be very bewildering. Such terms and phrases as “sending a message to,” “methods,” and “instance variables” are sprinkled throughout the OOP literature. Although this vocabulary can seem daunting, don’t panic. There is a straightforward translation between these terms and familiar C++ constructs.

An object is a class object or class instance—that is, an instance of a class type. A

methodis a public member function, and an instance variableis a private data member.

Sending a messagemeans calling a public member function. In the rest of this book, we tend to mix object-oriented terms with their C++ counterparts.

Inheritance Inheritance is a mechanism whereby a hierarchy of classes is constructed such that each descendant class inherits the properties (data and operations) of its ancestor class. In the world at large, it is often possible to arrange concepts into an

inheritance hierarchy—a hierarchy in which each concept inherits the properties of the concept immediately above it in the

hierarchy. For example, we might classify different kinds of vehicles according to the inheritance hierarchy in Figure 2.8. Moving down the hierarchy, each kind of vehicle is both more specialized than its parent(and all of its ancestors) and more general than its

children (and all of its descendants). A wheeled vehicle inherits properties common to all vehicles (it holds one or more people and carries them from place to place) but has an additional property that makes it more specialized (it has wheels). A car inherits properties common to all wheeled vehicles, but has additional, more specialized properties (four wheels, an engine, a body, and so forth). The inheritance relationship

can be viewed as an is-a relationship. In this relationship, the objects become more

specialized the lower in the hierarchy you go.

Object-oriented languages provide a way for creating inheritance relationships among classes. You can take an existing class (called the base class) and create a new

Inheritance A mechanism used with a hierarchy of classes in which each descendant class inherits the prop- erties (data and operations) of its ancestor class Base class The class being inherited from

class from it (called the derived class). The derived class inherits all the properties of its base class. In par- ticular, the data and operations defined for the base class are now defined for the derived class. Note the

is-a relationship—every object of a derived class is

also an object of the base class.

Polymorphism Polymorphism is the ability to determine either statically or dynamically which of several methods with the same name (within the class

hierarchy) should be invoked. Overloading means

giving the same name to several different functions (or using the same operator symbol for different operations). You have already worked with overloaded operators in C++. The arithmetic operators are overloaded because they can be applied to integral values or floating-point values, and the compiler selects the correct operation based on the operand types. The time at which a function name or symbol is associated with code is called binding time(the name is bound to the code). With overloading, the determination of which particular implementation to use occurs statically (at compile time). Determining which implementation to use at compile time is calledstatic binding.

Dynamic binding, on the other hand, is the ability to postpone the decision of which operation is appropriate until run time. Many programming languages support overloading; only a few, including C++, support dynamic binding. Polymorphism involves a combination of both static and dynamic binding.

Encapsulation, inheritance, and polymorphism are the three necessary constructs in an object-oriented programming language.

Derived class The class that inherits

Polymorphism The ability to determine which of several operations with the same name is appropriate; a combi- nation of static and dynamic binding

Overloading Giving the same name to more than one function or using the same operator symbol for more than one operation; usually associated with static binding Binding time The time at which a name or symbol is bound to the appropriate code

Static binding The compile-time determination of which implementation of an operation is appropriate Dynamic binding The run-time determination of which implementation of an operation is appropriate

Vehicle

Wheeled Vehicle

C++ Constructs for OOP

In an object-oriented design of a program, classes typically exhibit one of the following relationships: They are independent of one another, they are related by composition, or they are related by inheritance.

Composition Composition (or containment) is the relationship in which a class contains a data member that is an object of another class type. C++ does not need any special language notation for composition. You simply declare a data member of one class to be of another class type.

For example, we can define a class PersonTypethat has a data member birth-

dateof class DateType. #include <string>

class PersonType {

public:

void Initialize(string, DateType); string NameIs() const;

DateType BirthdateIs() const; private:

string name;

DateType birthdate; };

Deriving One Class from Another Let’s use the class MoneyType introduced in a sidebar in Chapter 1 as the base class and derive a class that has a field that contains the name of the currency.

#include <string> class MoneyType {

public:

void Initialize(long, long); long DollarsAre() const; long CentsAre() const; private:

long dollars; long cents; };

class ExtMoneyType : public MoneyType {

Composition (containment) A mechanism by which an internal data member of one class is defined to be an object of another class type

public:

string CurrencyIs() const;

void Initialize(long, long, string); private:

string currency; };

ExtMoneyType extMoney;

The colon followed by the words public and MoneyType (a class identifier) says

that the new class being defined (ExtMoneyType) inherits the members of the class

MoneyType. MoneyType is called the base class or superclass and ExtMoneyType is called the derivedclass or subclass.

extMoneyhas three member variables: one of its own (currency) and two that it

inherits from MoneyType(dollarsand cents). extMoneyhas five member functions:

two of its own (Initializeand CurrencyIs) and three that it inherits from Money-

Type(Initialize, DollarsAre, and CentsAre). Although extMoneyinherits the pri- vate member variables from its base class, it does not have direct access to them. extMoneymust use the public member functions of MoneyTypeto access its inherited member variables.

void ExtMoneyType::Initialize

(long newDollars, long newCents, string newCurrency) {

currency = newCurrency;

MoneyType::Initialize(newDollars, newCents); }

string ExtMoneyType::CurrencyIs() const {

return currency; }

Notice that the scope resolution operator (::) appears between the type MoneyType

and the member function Initialize in the definition of the member function.

Because two member functions named Initialize now exist, you must indicate the

class in which the one you mean is defined. (That’s why it’s called the scope resolution operator.) If you fail to do so explicitly, the compiler assumes you mean the most recently defined one.

The C++ rule for passing parameters is that the actual parameter and its correspon- ding formal parameter must be of an identical type. With inheritance, however, C++ relaxes this rule somewhat. That is, the type of the actual parameter may be an object of a derived class of the formal parameter.

Note that inheritance is a logical issue, not an implementation one. A class inherits

the behavior of another class and enhances it in some way. Inheritance does notmean

inheriting access to another class’s private variables. Although some languages do allow access to the base class’s private members, such access often defeats the concepts of encapsulation and information hiding. With C++, access to the private data members of the base class is not allowed. Neither external client code nor derived class code can directly access the private members of the base class.

Virtual Functions In the previous section we defined two member functions with the

same name, Initialize. The statements

money.Initialize(20, 66);

extMoney.Initialize(20, 66, "francs");

are not ambiguous, because the compiler can determine which Initialize to use by

examining the type of the object to which it is applied. Sometimes, however, the com- piler cannot determine which member function is intended, so that the decision must be made at run time. If the decision is left until run time, the word virtual must precede the member function heading in the base class definition. We will have more to say about how C++ implements polymorphism when we use this mechanism later in the book.