2.3 ANTECEDENTES GENERALES DE LA ZONA DE ESTUDIO
2.3.6 Mineralogía
pile only by placing it on the top of the pile. You cannot add a book beneath another one.
If you represent books by their titles alone, design a class that you can use to track the books in the pile on your desk. Specify each operation by stating its purpose, by describing its parameters, and by writing a pseu- docode version of its header. Then write a C++ interface for the pile’s methods. Include javadoc -style com- ments in your code.
C++ Classes
1
Contents
C1.1 A Problem to Solve 32
C1.1.1 Private Data Fields 33
C1.1.2 Constructors and Destructors 33 C1.1.3 Methods 34
C1.1.4 Preventing Compiler Errors 35
C1.2 Implementing a Solution 36 C1.3 Templates 37
C1.4 Inheritance 40
C1.4.1 Base Classes and Derived Classes 40 C1.4.2 Overriding Base-Class Methods 42
C1.5 Virtual Methods and Abstract Classes 44
C1.5.1 Virtual Methods 44 C1.5.2 Abstract Classes 45
Prerequisites
Appendix A Review of C++ Fundamentals Chapter 1 Data Abstraction: The Walls
T
hroughout this book we design abstract data types and implement them as data structures using C++. This C++ Interlude provides a refresher on C++ classes and then introduces additional C++ tools we can use to defi ne our abstract data types in a fl exible manner that maintains the wall between our design and the implementation.After reviewing classes, we will look at class templates . This C++ construct gives us the power to specify the data type of the items contained in a data structure in a very generic way. For example, if you had spent two weeks developing a great class to repre- sent a bag of integers, wouldn’t it be great if you could easily use the same code for a bag that holds strings? Class templates allow you to defi ne classes that are independent of the type of data stored in the data structure. When a client is ready to instantiate an ob- ject of the class, the client can specify the type of data the object holds.
C + +
Header ( .h ), or specifi cation , fi les in C++ provide a mechanism to partially separate the design of a class from the implementation in the source , or implementation ( .cpp ), fi le . The header fi le must also contain a description of both the data fi elds for the class and any private methods used by the class. A client does not need to know about the private methods or data fi elds to use the class in a program. To provide a public interface for an ADT, you can write an abstract base class, thereby separating design from implementation. An abstract base class allows the client to take full advan- tage of polymorphism when using our class.
To introduce these concepts, let’s look at a simple problem that illustrates all three of the funda- mental concepts of object-oriented programming: encapsulation, inheritance, and polymorphism.
C1.1
A Problem to Solve
Suppose that a friend who is creating a video game asked you to design and develop a group of classes to represent three types of boxes carried by the characters in the game. Each type of box can only hold one item. A character can put an item in the box or look at the item in the box. The three types of boxes are:
•
Plain box —a plain old box that holds only one item.•
Toy box —a box that has color and holds only one item.•
Magic box —a box that holds only one item, but magically changes it to the fi rst item that wasever stored in the box.
Let’s begin by designing an ADT plain box. It is the simplest of the three boxes, and we may be able to use aspects of its implementation for the other two boxes. Since game characters can only place items in the box or look at them, the box needs only two public methods, setItem to place an item in the box and getItem to get the stored item. Here is the UML notation for those methods:
+setItem(theItem: ItemType) +getItem(): ItemType
We can defi ne ItemType as the type of item stored in the box by using a typedef statement. For example, to have the box hold a double , we can write
typedef double ItemType;
To have the box hold another type of data, we need only to replace the word double with the new data type. Listing C1-1 shows the declaration of our class PlainBox that would appear in a header fi le. To save space here, we show only comments that describe the class element type and omit the specifi cations, preconditions, and postconditions. We have used the object-oriented concept of encapsulation to group together data—the item stored in the box—with the methods that operate on that data: the two constructors, a method setItem to change the item’s value, and a method getItem to return the item’s value.
LISTING C1-1 The header fi le for the class PlainBox
/** @file PlainBox.h */ #ifndef _PLAIN_BOX #define _PLAIN_BOX
// Set the type of data stored in the box
typedef double ItemType;
VideoNote
A Problem to Solve 33
Let’s look in detail at this C++ class declaration.
C1.1.1
Private Data Fields
The data fi eld item is declared in a private section of the class declaration. Every data fi eld in every class presented in this textbook is in a private section. This restricts access to the data fi eld to just the class in which it is defi ned. Typically, we provide methods—such as setItem and getItem— to access the data fi elds. In this way, the class controls how and whether other classes can access the data fi elds. This design principle should lead to programs that not only are easier to debug, but also have fewer logical errors from the beginning.
Clients and derived classes should not have direct access to the data fi elds of a class. If a class designer believes that a derived class might need to access or modify the data fi elds of the base class, you should still make the data fi elds private but provide protected methods so that any derived classes can access or modify the data. More information on access modifi ers, such as private and pro- tected , is presented in a later C++ Interlude.
As we build more complex data structures, we need to guarantee their integrity. This is simpler to do if we restrict access to the data fi elds to only our class. For example, suppose we have a class that stores items in an array and maintains a count of the number of items used in the array. Each time our class adds or removes an item from the array, the item count needs to be modifi ed to refl ect the change. If a client of our class had direct access to the array, the client could add and remove entries in the array and neglect to update the item counter. This counter would not accurately refl ect the number of entries in the array, resulting in, for example, the loss of data or an abnormal termination of the program.
C1.1.2
Constructors and Destructors
Classes have two types of special methods, called constructors and destructors. A constructor allo- cates memory for new instances of a class and can initialize the object’s data to specifi ed values. A
destructor destroys an instance of a class when the object’s lifetime ends. A typical class has several
// Declaration for the class PlainBox
class PlainBox { private: // Data field ItemType item; public: // Default constructor PlainBox(); // Parameterized constructor PlainBox( const ItemType& theItem);
// Method to change the value of the data field
void setItem( const ItemType& theItem); // Method to get the value of the data field ItemType getItem() const;
}; // end PlainBox #endif
constructors but only one destructor. For many classes, you can omit the destructor. In such cases, the compiler generates a destructor for you. For the classes in this C++ Interlude, the compiler-generated destructor is suffi cient. C++ Interlude 2 discusses how and why you would write your own destructor. In C++, a constructor has the same name as the class. Constructors have no return type—not even void— and cannot use return to return a value. A class can have more than one constructor, as is the case for the classPlainBox . One of the constructors, the default constructor , has no parameters. Typically, a default constructor initializes data fi elds to values that the class implementation chooses. Other constructors have parameters. These parameterized constructors initialize data fi elds to val- ues chosen by the client but approved by the constructor. The compiler decides which constructor to call by matching the argument list supplied by the client with the parameters of the available construc- tors. A match occurs when the arguments and parameters correspond in number, data type, and order.
Note:
If you do not defi ne any constructors for a class, the compiler creates a default constructor—one without parameters. Once you defi ne a constructor, the compiler does not create any of its own. Therefore, if you defi ne a parameterized constructor but not a default constructor, your class will not have a default constructor.When you declare an instance of the class, a constructor is invoked implicitly. For example, the statement
PlainBox myBox;
invokes the default constructor, which creates the object myBox and initializes the data fi eld item to a value given in the constructor’s defi nition. Notice that you do not include parentheses after myBox when invoking the default constructor. The statement
PlainBox myBox(specialValue);
invokes the parameterized constructor, which, as you will see, initializes the data fi eld item to specialValue . Thus, specialValue is stored in the box.
C1.1.3
Methods
As discussed in Chapter 1 , methods implement the algorithms that solve a problem. A method proto- type conveys three important pieces of information to a client wishing to use our class: the method name, the number and types of its parameters, and the method’s return type. The prototype describes the slit in the wall of abstraction. Prototypes for methods a client can use are in the public section of the class declaration.
An accessor method in a class accesses, or gets, the value of a data fi eld. Its name often begins with the word get . Accessor methods do not change the data fi elds of an object. An important part of the PlainBox class declaration is the accessor method getItem , which is labeled with the keyword const :
ItemType getItem() const;
This keyword is a signal to both the compiler and other programmers that the method does not change the data fi elds of the object. Another way to think of a const method is that the object is the same after calling this kind of method as it was before calling the method. Using const in a method declaration protects you as a programmer when you implement the algorithm for the method. As you write the code for a method declared withconst , the compiler can check your code to verify that you did not modify any data fi elds.
A Problem to Solve 35
A mutator method in a class changes the value of a data fi eld. Often, the name of a mutator method begins with the word set . The method setItem is an example of a mutator method. Mutator methods cannot be declared asconst . Likewise, constructors cannot be declared const , because they must initialize the data fi elds of an object.
Passing parameters by constant reference. The method setItem and the parameterized construc- tor both have a parameter theItem that is passed by constant reference , as you can see from their declarations:
void setItem( const ItemType& theItem); PlainBox( const ItemType& theItem);
Passing a parameter by constant reference provides several benefi ts to both the client of a class and the programmer implementing the class. Passing an argument by reference to a method, especially when the argument is a complex object, saves time and memory, since the method can access or modify the object without copying it. The risk with this technique is that the method has access to data declared outside of its class; the method has “broken through the wall” and can modify an item owned by the client that invoked the method. To keep the effi ciency of passing by reference and still protect the data of our client, we use the keyword const before the declaration of that parameter. The method treats the parameter as a constant that cannot be modifi ed. Using const with parameters passed by reference protects client objects and reduces the chance of side effects. Our method can still access and use an object passed by constant reference, but the compiler fl ags any modifi cations to the object as errors.