Library: Udžbenici Book Number: 16

Main Editor: prof. dr Safet Krkić

Technical Review: prof. dr Senad Burak prof. dr Dražena Tomić

Language Review: Authors

Book Covers: Mirza Hadžikadunić DTP: Authors

Copyright © Authors, 2005 FIT Mostar, 2005 Edicions de la Universitat de Leida, 2005

All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the copyright owners.

Printed by Štamparija "FOJNICA" .o.o. Fojnica

Supported by TEMPUS project CD JEP 16110-2001

CIP Katalogizacija u publikaciji Edicions de la Universitat de Leida Nacionalna i univerzitetska biblioteka Bosne i Hercegovine, Sarajevo

004.42(075.8)

RIBO, Josep Maria RIBO, Josep Maria Introduction to OOP with C++ / Introduction to OOP with C++ / Josep Maria Ribo, Ismet Maksumić, Josep Maria Ribo, Ismet Maksumić, Siniša Čehajić – Mostar: Siniša Čehajić – Mostar: Univerzitetska knjiga, 2005. – 333. str: graf. prikazi; 25 cm. – (Biblioteka Udžbenici; knj. 16)

Bibliografija: str. 331-332

ISBN 9958-603-22-5 ISBN 84-8409-199-6 1. Maksumić, Ismet 2. Čehajić, Siniša 1. Maksumić, Ismet 2. Čehajić, Siniša COBISS.BH-ID 14230534

Published by Univerzitetska knjiga Mostar, 2005 Edicions de la Universitat de Leida, 2005

Josep Maria Ribó Ismet Maksumić Siniša Čehajić

Introduction to OOP with C++

Published by Univerzitetska knjiga Mostar Edicions de la Universitat de Leida, July, 2005

Table of contents

Chapter 1: Principles od OOP. Classes. Objects...... 1 1.1. Modelling concepts...... 1 1.1.1. Abstract types...... 2 1.2. Concept of class ...... 4 1.3. Concept of object ...... 10 1.3.1. State ...... 11 1.3.2. Behaviour...... 11 1.3.3. Identity ...... 12 1.3.4. Classes vs. objects...... 13 1.3.5. The object orientation programming paradigm...... 14 1.4. Class contracts ...... 15 1.4.1. Defining a class contract...... 16 1.4.2. ...... 16 1.4.3. ...... 20 1.4.4. Class invariants ...... 21 1.4.5. Correctness of a class implementation...... 23 1.4.6. Example: Contract for the class Date...... 24 1.5. Detailed class and object definition in C++...... 26 1.5.1. Private and public members...... 29 1.5.2. Operation declaration and implementation ...... 30 1.5.3. Object creation...... 30 1.5.4. Access to members ...... 37 1.5.5. References vs. pointers ...... 39 1.5.6. Splitting class definition into different files...... 40 1.6. Some (pleasant) consequences of class definition ...... 42 1.7. Guided problems. The class MyString...... 44 1.7.1. The problem...... 44 1.7.2. Solution...... 45 1.7.3. The file Makefile...... 50 1.8. Guided problems. The word counter...... 50 1.8.1. The class WordCounter ...... 50 1.8.2. A client program ...... 56 1.8.3. The class WordCounterInterface...... 57 Chapter 2: Special members ...... 61 2.1. Constructors ...... 61 2.1.1. Notion of constructors...... 61 2.1.2. Constructor overloading...... 64 2.1.3. Default constructor...... 65 2.1.4. Default parameters ...... 65 2.1.5. Copy constructor...... 66 2.1.6. Construction call in the creation of arrays of objects...... 71 2.1.7. Creation of temporary objects...... 71 2.1.8. Dynamic object construction ...... 72

2.1.9. Construction of dynamic arrays of objects...... 73 2.1.10. An example ...... 74 2.2. Destructors ...... 75 2.2.1. Why are destructors necessary? The garbage ...... 75 2.2.2. The operator delete...... 78 2.2.3. Destructors in C++...... 78 2.2.4. Destruction of dynamic objects...... 80 2.2.5. Destructors and sharing of identity: warnings ...... 81 2.3. Friend functions and classes ...... 83 2.4. Operator overloading ...... 85 2.4.1. Operator = ...... 85 2.4.2. Operator ==...... 88 2.4.3. Operator []...... 89 2.4.4. Operator ()...... 90 2.4.5. Operators << and >>...... 91 2.5. What operations any C++ class should offer ...... 92 2.6. Guided problems. The class MyString...... 92 2.6.1. Solution: File MyString.h...... 93 2.6.2. Solution: File MyString.cpp...... 93 2.6.3. Solution: File userString.cpp ...... 97 Chapter 3: Generic classes and functions...... 99 3.1. Generic classes...... 99 3.1.1. Implementation of operations out of the template class definition ...... 102 3.1.2. Templates with more than one template parameter and non-type parameters...... 103 3.2. Generic functions...... 103 3.2.1. Incorporating functions as template parameters ...... 106 3.2.2. Explicit instantiation of a template function ...... 108 3.3. File organization with templates...... 109 3.4. Guided problems: A generic stack...... 110 3.4.1. The problem...... 110 3.4.2. Solution: File Stack.h...... 111 3.4.3. Solution: File userStack.cpp...... 113 Chapter 4: Associations. Composite objects. References ...... 117 4.1. Associations, aggregations and compositions...... 117 4.1.1. Associations ...... 117 4.1.2. Aggregations...... 120 4.1.3. Compositions ...... 121 4.1.4. A usual interpretation of aggregations...... 124 4.2. Constructors and destructors with subobjects...... 124 4.2.1. Construction...... 124 4.2.2. Destruction...... 128 4.3. Equality, copies and clones...... 129 4.3.1. Equality...... 129 4.3.2. Copy...... 136 4.3.3. Clone...... 142

4.4. Guided problem: The cyclic player...... 143 4.4.1. The class Player ...... 143 4.4.2. A careless deep copy for Player...... 143 4.4.3. A more cautious deep copy for Player...... 145 4.4.4. Generalizing the process of getting a deep copy...... 148 Chapter 5: Inheritance ...... 149 5.1. The meaning of inheritance...... 149 5.1.1. Class generalization and substitution principle...... 149 5.1.2. Subclasses and inheritance...... 151 5.1.3. Subclasses in C++...... 153 5.2. Constructors and destructors in derived classes...... 156 5.2.1. Constructors ...... 156 5.2.2. Destructors ...... 160 5.3. Member visibility and type of inheritance ...... 161 5.3.1. Member visibility...... 161 5.3.2. Types of inheritance...... 162 5.4. Different good (and bad) uses of inheritance...... 165 5.4.1. Specialization...... 165 5.4.2. ...... 166 5.4.3. Similarity...... 168 5.4.4. Generalization...... 172 5.4.5. Instantiation...... 173 5.4.6. Multiple inheritance ...... 173 5.4.7. Association...... 174 5.5. Guided problem: A hierarchy of persons...... 176 5.5.1. The problem...... 176 5.5.2. Solution: The class Date ...... 177 5.5.3. Solution: The hierarchy of persons ...... 178 5.5.4. Solution: A client that tests constructors and destructors ...... 179 Chapter 6: Polymorphism...... 183 6.1. Concept of polymorphism...... 183 6.2. Class conformance...... 187 6.2.1. Class conformance rule...... 187 6.2.2. Static type conversion of references and pointers...... 191 6.2.3. Dynamic type conversion of references and pointers ...... 192 6.2.4. User defined type conversion...... 195 6.3. Dynamic binding and virtual functions...... 197 6.3.1. The problem of early binding...... 197 6.3.2. Virtual functions ...... 199 6.3.3. Call to virtual functions ...... 200 6.4. Pure virtual functions and abstract classes...... 203 6.5. Virtual constructors and destructors ...... 207 6.5.1. Virtual constructors...... 207 6.5.2. Virtual destructors...... 209 6.6. Multiple inheritance...... 210 6.6.1. Inheritance of different members with the same signature ...... 212

6.6.2. The same member inherited along different paths...... 217 6.7. Polymorphic behaviour of friend functions ...... 219 6.8. Guided problem: Multiple inheritance. A double hierarchy of persons...... 220 6.8.1. Presentation of the double hierarchy...... 220 6.8.2. Incorporating multiple inheritance...... 222 6.8.3. Virtual inheritance ...... 224 6.8.4. Polymorphism and virtual inheritance (1)...... 227 6.8.5. Polymorphism and virtual inheritance (2)...... 229 6.9. Guided problems: A polymorphic and generic stack...... 231 6.9.1. The problem...... 231 6.9.2. Solution...... 231 6.9.3. An improvement to the class Stack...... 238 6.10. Guided problem: A polymorphic hierarchy of containers ...... 239 6.10.1. The problem...... 239 6.10.2. Why this hierarchy? ...... 240 6.10.3. Abstract class Container...... 240 6.10.4. Abstract class Stack ...... 243 6.10.5. Class ArrayStack...... 243 6.10.6. Class LinkedStack...... 246 6.10.7. Comparison between both implementations ...... 249 6.10.8. Using the stack implementations ...... 249 6.10.9. And a dynamic cast...... 251 6.11. Guided problem: Stack of stacks ...... 251 6.11.1. The problem...... 251 6.11.2. Solution hint...... 252 Chapter 7: Exceptions ...... 255 7.1. Exceptions in computer programs...... 255 7.2. Exception handling in C++ ...... 257 7.3. Exceptions specification ...... 265 7.4. Exceptions in Java...... 268 7.5. Guided problem: Array based stack class ...... 270 7.5.1. The problem...... 270 7.5.2. Simple solution ...... 271 7.5.3. More robust solution ...... 273 7.6. Guided problem: Vector class...... 276 7.6.1. The problem...... 276 7.6.2. The solution ...... 276 7.6.3. More robust solution (1) ...... 279 7.6.4. More robust solution (2) ...... 284 Chapter 8: Standard C++ Library ...... 285 8.1. What is the "Standard C++ Library"...... 285 8.2. Strings ...... 286 8.3. STL Containers and Iterators ...... 288 8.3.1. Sequential containers ...... 292 8.3.2. Associative containers...... 297 8.4. STL Algorithms ...... 300

8.4.1. find() ...... 300 8.4.2. search()...... 301 8.4.3. sort()...... 302 Appendix A: UML ...... 307 A.1 UML evolution...... 307 A.2 Some aspects of the UML...... 309 A.3 The Basic Building Blocks of UML ...... 310 A.3.1 Model elements (things)...... 310 A.4. Relationships...... 315 A.4.1. Association...... 315 A.5 The UML Diagrams...... 321 A.5.1 Use-Case Model Diagrams ...... 322 A.5.2 Static Structure Diagrams ...... 324 A.5.3 Interaction diagrams...... 327 A.5.4 State Diagrams ...... 329 A.5.5 Implementation diagrams...... 329 Bibliography ...... 331 Index...... 333

Chapter 1 Principles of OOP. Classes. Objects

1.1. Modelling concepts One important problem that a software engineer must face when he/she is to develop a software application in a specific domain is the following: The application domain is packed with human-oriented, high-level concepts (e.g., student, academic subject, professor, bill, etc.), which offer several natural ways to interact with them (e.g., a student may be enrolled in a subject, a professor may be responsible for some subjects, a student should pay an enrolment bill, etc.). However, the constructs offered by traditional programming languages (i.e., those which are not either object-oriented or object-based languages) to map those concepts are usually quite low-level and machine-oriented (array, integer, pointer...). The immediate consequence of this abstraction gap between what is needed by the software developer and what is offered by the is that any piece of software, ranging from medium-size programs to huge applications, becomes increasingly difficult to construct, test, maintain and reuse. The object-oriented programming (OOP) paradigm aims at bridging this abstraction gap, so that, we can construct a software application using directly entities that represent high-level domain concepts (student, subject, date, etc.). Probably, the most natural way in which a programming language can declare and use such high-level entities is by allowing the definition of new kinds of types (in addition to the predefined types as integer, char, array...). In the same way as predefined types are defined in terms of a set of operations that can be applied to the entities of those types (e.g., the predefined type integer has the operations +, -, *, / and remainder associated to; these operations can be applied to integers) it makes perfectly sense to define new high-level types giving the list of services they offer to their prospective users. To keep the same notation as with predefined types, we will call operations to the services offered by these new high- level types. Example We have to build an application to manage the academic issues of a university (e.g., syllabus, students, subjects, professors, enrolment, etc). In this context we can define a high-level type called Subject to refer to academic subjects so that it provides the following services or operations: • Set/get the title of the subject • Set/get the professor who is responsible for it

- 1 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• Set/get the contents of the subject • Set/get the term in which it is offered • Get the students enrolled in it • Add a new student enrolled in the subject • ... In this context, we may declare an entity sbj of type Subject and then set/get the title or the professor of sbj using the provided operations. ♦ ♦ ♦ Example As a part of the design of a race simulation application we have to model the concept of car. For that reason we may define a new high-level type called Car with the following services: • Start the car engine • Select the moving direction (ahead, reverse) • Move the steering-wheel some degrees to the right/left • Increase speed some kilometres per hour (accelerate) • Reduce speed some kilometres per hour (brake) • Switch on/off the lights • ... ♦ ♦ ♦ Notice that there can be different modelling of the same concept, corresponding to the various views that different users may have of that concept. For example, if the notion of car was modelled for a workshop management application, then the list of services should be different (e.g., date of the last oil change, problems detected in the car and not solved yet, state of the tyres, etc.).

1.1.1. Abstract types This modelling of high-level concepts by means of the definition of new types which provide a list of services constitutes a way to model concepts based on abstraction.

Abstraction is the property by which we describe an object focusing only in some aspects of it and forgetting deliberately other aspects which are not interesting for us for the time being.

Two different abstractions are involved in the definition of a new type:

- 2 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• In order to model a concept with a type we do not consider all its aspects, we just focus on those issues which are of interest for its users and forget the rest. For example, if we define the type Student for the administrative software application of a university, we may provide operations to get and set his/her name, id and subjects in which he/she has enrolled, but we will not be interested in his/her parents’ name and the name of his/her friends. • When we define a type as a set of operations which describe the behaviour of its entities, we forget about how that behaviour will be internally implemented. This abstract approach to define and use new types constitutes an excellent way to deal with complexity: we reduce it by separating the services offered by the type (what) from the type implementation (how). The implementation issues are deferred to a later time or are provided by somebody else (e.g., the authors of a library of types). As a result, we only have to focus on one aspect at a time. This issue is usually referred to as separation of concerns. Since we define these new high-level types from an abstract point of view, they may be called abstract types.

Definition (Abstract type) An abstract type denotes a set of entities characterized by a list of operations that may be applied to them together with a precise specification of each one of these operations. Usually, the list of operations that define a type and their specification are referred to as the type behaviour, type specification or the type contract. An abstract type is also called interface. The set of entities which share the operations defined for a type are called instances of that type.

As we have already mentioned, an example of abstract type may be the type Student. This is an abstract type whose instances are each one of the specific students (Joe, Ann ...) and its operations can be: set the name of a student, enrol a student in a subject, get the list of subjects in which a specific student has enrolled, etc. Usually, we will refer to abstract types just as types. Notice that the approach that we have taken is coincident to what we do in everyday life to manage complexity. For instance, cars are very complicated machines. However, the only thing that a car driver needs to care about is that when he/she presses the accelerator the car speeds up; he/she does not matter the background mechanics and electronics that make this action possible. In fact, it would be very difficult, if not impossible, to drive having in mind, at the same time, the functionality expected from the car (the what: speed up, change direction, brake, etc.) and the way in which this functionality is internally achieved (the how: in

- 3 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS which precise manner fuel is mixed with air and gets to the engine; which mechanical elements are involved in the transmission of the energy produced by the engine to the wheels, etc.). That is, abstraction is a good way to deal with complexity (both in everyday life and in software construction). This idea of abstraction (more precisely, data abstraction) is one of the most fundamental issues in object orientation and, in general, in software construction.

1.2. Concept of class We have defined an abstract type in terms of its behaviour (the list of the operations it provides along with their specification). However, in order to execute a software application that uses instances of an abstract type it is still necessary to choose a structure or representation for these instances (e.g., the way in which they will be stored, probably in the computer memory) and the implementation of the operations of the type in terms of that representation.

Definition (Type representation) The representation of the instances of a type is the way chosen by the designer to store these instances internally (usually in the computer memory), so that the applications that use those instances can be executed. This representation is performed in terms of lower-level elements defined previously by the type designer or provided by the programming language in which the type is being designed. Since all the instances of the type will be represented in the same way, we often talk about type representation. We may also refer to this concept as type structure.

Example

• An instance of the type Date can be represented in terms of three integers: day (1..31), month (1..12) and year (1900-2100). • An instance of the type String, which models a sequence of characters, can be represented internally in terms of an array in which the characters of the string are stored and an integer to state how many characters a specific instance of String has. An alternative representation for this type would be an array of characters finished with a special character mark (e.g., ’\0’, as in C++). • An instance of the type Student can be represented internally in terms of: - Two instances of the type String (to represent his/her name and id.). - An instance of the type Date (to represent his/her birth date).

- 4 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

- An array of instances of type String (to represent the list of subjects in which he/she has enrolled). Notice that, in order to represent the type Student we use already defined abstract types (i.e., String, Date). ♦ ♦ ♦

Definition (Implementation of the type operations) The implementation of the type operations is the implementation of the operations defined for the type in terms of the type representation.

Example The operation of concatenating two instances a and b of the type String can be implemented by creating a new array, copying to it (from the beginning) all the characters of the array that represents a and, after that and in subsequent positions, all the characters of the array that represents b. If we have chosen to represent the end of the string by means of a character mark, we should include this character mark in the new array that contains the concatenation of both strings, right after the last character of the concatenation. If a count integer has been chosen, then this integer should be appropriately set. ♦ ♦ ♦ Most object-oriented programming languages provide a construct that allows: • the abstract definition of types in terms of their operations • the representation of the type in terms of lower-level elements and • the implementation of those operations. This construct is called class and can be defined in the following way [Meyer1997]:

Definition (Class) A class is an abstract type together with its representation and implementation. A class may provide a partial type representation and/or implementation. In that case we call it an abstract or deferred class. A class which is not deferred is said to be effective.

That is: • A class is an abstraction used to model a (usually high-level) concept in an O.O. programming language defining the behaviour of that concept (in terms of a set of operations), an internal representation (or structure) for the instances of the concept and a specific implementation of its operations in terms of the representation that has been chosen.

- 5 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• Effective classes will have their structure defined and their operations specified and completely implemented. Deferred or abstract classes may have some parts of their structure not yet represented and/or some operations not yet implemented.

Example In the academic application we may need to use the notion of date. Therefore, we can define a class Date to deal with that concept. This class should offer services such as creating a date, getting/setting the day/month/year of a date, calculating the number of days between two dates, etc. A preliminary class definition could be made in C++ in the following way: class Date{ public: void create(int pday, int pmonth, int pyear, bool& err); int getDay(); int getMonth(); int getYear(); void setDay(int pday, bool& err); void setMonth(int pmonth, bool& err); void setYear(int pyear, boole& err); void copy(Date d); void addDays(int ndays); unsigned int daysinBetween(Date d); private: unsigned int day; unsigned int month; unsigned int year; }; void Date::create(int pday, int pmonth, int pyear, bool& err){ if (correctDate(pday,pmonth,pyear){ day=pday; month=pmonth; year=pyear; err=false; } else{ err=true; .... } } void Date::setDay(int pday, bool& err){ if (correctDate(pday,month,year)){ day=pday; err= false; } else err=true; } ....

- 6 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

The definition of the class Date is made so that the separation between the services offered by the class (what) and the implementation of those services (how) is kept (recall that this separation of concerns was one of the main aspects of the O.O. approach). Indeed the definition of the class Date has been done in two separate layers: • Class specification (or class behaviour) It is stated in the public part of the class definition in terms of a list of operation headers. These are the operations that any user of this class can apply to its instances. Notice that the fact that we enumerate the services (operations) supplied by the class does not mean that we are aware of what they do (i.e., their behaviour cannot be precisely inferred from their name and parameters1 ): we need a precise specification of these operations. We will discuss this issue in section 1.4. • Class implementation In its turn, class implementation is further split in two aspects: - Class representation (also called class structure): It follows the private label. The items day, month and year are the class attributes and they constitute the class representation. Class users cannot access the elements defined in the private part of the class. - Operation implementation: This implementation is usually given out of the class block (often in another file, as presented below). The name of each operation is preceded by the name of the class to which it belongs. This helps the compiler to associate each operation implementation to its class. Notice that if today is an instance of the class Date we may apply to it the operations of this class. For instance, we may apply to today the operation create(...) in the following way: today.create(12,11,2004,err); By no means can a user of the class Date access an element of the private part: today.day=9; //ERROR!!!! invalid access to the private part //of Date ♦ ♦ ♦ What we have learned from the previous example can be stated more precisely with the following definition:

1In this book we will use indistinctly the terms parameter and argument

- 7 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Definition (Class definition in two layers) A class will be defined using two separated layers: • Interface or specification layer. It includes: - Enumeration of the services offered by the class (i.e., the class behaviour). The services offered by the class are called methods or operations and are enumerated by means of their header or signature. In C++, this enumeration of method headers comes up following the label public: in the class definition. - Detailed specification of the class behaviour. This specification is also called class contract and includes the class invariant and the and for each operation. Class contracts are usually contained in a separated file. They are presented in section 1.4. • Implementation layer. It includes: - Class structure (or class representation). All the attributes that compose a class instance. These attributes constitute the internal representation of a class, that is, the data that any instance of the class should store in order to offer the intended class behaviour. These attributes may be either values of predefined types (e.g., int) or instances of other classes. The user programs cannot access these attributes. In C++, attributes appear following the label private: in the class definition. - Operation implementation. It is constituted by the implementations of the operations that form the class interface (and, hence, provide the class behaviour). This implementation is carried out in terms of the attributes that constitute the class representation (and, optionally, in terms of other auxiliary class operations). In C++, operation implementation often comes up after the brace ’};’ that occur at the end of the class definition. This is explained in section 1.5.

Definition (Class member) Attributes and operations are the building blocks that are used to define the structure and behaviour of a class and are called class members. All class instances have the same attributes and can be accessed by means of the same set of operations.

- 8 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

This two-layered class definition leads to a natural separation of roles between the class user and the class implementer: • The class user (or class client) is only interested in the interface layer. He/she will use the class operations without caring about the implementation layer. The class construct, as it is defined in most programming languages, enforces this idea by forbidding the user any access to the implementation layer. This policy is called encapsulation or information hiding (see fig. 1.1). • On the other hand, the class implementer provides the implementation layer for the class.

The link between both roles is provided by the class contract: The class user commits him/herself to using the class operations in the precise way stated by the contract. On the other hand, the class implementer commits him/herself to implementing the class so that it provides the services stated by the contract in the exact way in which they have been stated (see section 1.4).

Figure 1.1: Notion of information hiding

It should be clear by now that making such a separation and, furthermore, keeping both levels completely separated is important for, at least, two good reasons: 1. Complexity reduction in application development The user of the class Date can use the high-level services offered by this class (e.g., addDays(ndays)) without having to bother, at the same time, about how those services are implemented (i.e., how should we add a certain number of days to a date and which new date would be obtained when doing so? ). In other words, abstraction of the low-level implementation details helps in managing complexity.

- 9 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

2. Representation independence and easier reuse The user of the class Date will not notice those changes that are made in the class definition, as long as they do not modify the services offered by the class. The class designer may change the class structure or the implementation of the class services in order to find a more efficient alternative or in order to comply with new class requirements. However, if these changes conform with the services previously offered by the class, the old users will be able to use the new class definition without having to change their applications. We present an example of this issue below.

1.3. Concept of object

Definition (Object) An object is a run-time instance of some class [Meyer1997].

This essentially means that an object has the structure defined by the class and it is possible to apply to it the operations defined by the class interface.

Example In C++ we can create an object of the class Date as follows: Date birthday; The object birthday is an instance of the class Date. This means that it has the attributes day, month and year associated to and that we may apply to it the Date operations. This is done (in C++) in the following way: birthday.create(10,11,1990,err); This applies the operation create with the parameters provided to the object birthday. As a consequence of this application, birthday is now initialized to the date 10-nov-1990 (see fig. 1.2).

Figure 1.2: Object birthday after the call to birthday.create(10,11,1990,err);

- 10 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Other operations may be applied to this object. For instance: d=birthday.getDay(); If d has been defined as an integer variable, d will have the value of 10 at the end of this operation. ♦ ♦ ♦ Objects may be characterized by three aspects [Booch1991]: state, behaviour and identity. 1.3.1. State

Definition (Object state) The state of an object is constituted by the object attributes together with the information stored in them at a given run time instant. Obviously, the state of an object may change along the time.

The object birthday may have, at a given instant, the following state: day=12, month=10; year=2002. The state of an object may change as a result of the application of an operation on that object.

1.3.2. Behaviour

Definition (Object behaviour) The behaviour of an object describes how other objects (or, in general, a piece of software) may interact with it. The interaction with an object obj is always carried out by means of the operations defined by the class of which obj is an instance. Those operations provide the object behaviour.

Different kinds of operations may be considered.

Definition (Types of operations) There may be considered three different kinds of operations according to what they do to the state of the class instance to which they are applied: • Creators or constructors. They create and initialize the instance to which they are applied. • Modifiers. They modify the state of the instance to which they are applied. • Consultors or queries. They return some piece of the state of the instance to which they are applied but they do not modify this state.

- 11 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

The issue of constructors needs to be presented in much more detail. In Chapter 2 we will learn that C++ defines a special kind of operations which are called implicitly when an object of a class is created (hence, operations of the sort create(...) will not be, in general, necessary). Example The operations of the class Date may be classified in: • Creators: create • Modifiers: setDay, setMonth, setYear, copy, addDays • Queries: getDay, getMonth, getYear, daysInBetween ♦ ♦ ♦ const operations Notice that while creators and modifiers do change the state of an object, consultors do not. In C++, this fact can be stated by declaring the consultor operations as const: class Date{ public: int getDay() const; ... private: ... }; int Date::getDay() const { return day; } Private operations In general, not all class operations belong to the class interface (and, thus, are available to class users). Some operations may be used only to support the implementation of other operations and will not be offered to class users as class operations. We will refer to the former as public operations and to the latter, as private operations.

1.3.3. Identity

Definition (Object identity) Identity is the property of an object which makes it different from any other object [Khoshafian, Copeland Booch]

- 12 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

In usual O.O. programming languages, there is not a single way to retrieve a specific object of a class C. It may be retrieved: • By means of a variable of class C • By means of a pointer variable. • By means of a reference variable. Furthermore, at a specific instant, many pointers and references may refer to the same object (i.e., to the same identity). This fact brings up some difficulties regarding object identity and is the source of many errors in O. O. programming. Issues such as equality, copy, cloning, identity share, etc. are bound to object identity and are not trivial. They are discussed in Chapter 2.

1.3.4. Classes vs. objects

It is important to make the difference between class and object (or class instance) clear. A class is the description of the structure and behaviour that are shared by all the objects which are instances of that class. The objects are the real entities (allocated in the computer memory) which have the structure described by the class and to which class operations can be applied. Each object has a separated identity. For the sake of an example, we may say that the difference between a class and an object is similar to that between a car model description and a specific car of that model. The car model description defines the set of features that characterize the cars of that model (type of engine, lamps, specific design, etc.). A car model description is not a real car. It is only some verbose document with a long list of features and, may be, some pictures or designs. A real car is made out of metal, plastic and wires. However, we can expect to find in an actual car of a specific model, all the features that have been described in the car model description. There are some functionalities which cannot be provided by the approach we have presented so far. For instance, given an object: how a program can know the name of the class of that object or the operations defined by that class. Even more, how is it possible to program the application of an operation to an object whose class is only known at execution time. In order to answer these questions appropriately it is necessary to consider classes, themselves, as objects to which it is possible to apply operations like getName() (which gets the name of the class), getOperations(), (which gets the list of operations of the class), etc.

- 13 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

If we consider a class as an object (call it c), another question arises: of which class is c an instance? (i.e., which is the class of a class? ). A is a class whose instances are, themselves, classes. Some languages (like Java) define a class in their APIs which has the role of a metaclass (in the case of Java, this class is called Class). Metaclasses are beyond the scope of this book. However, more information can be found in Appendix A.

1.3.5. The object orientation programming paradigm it should be clear by now that a program designed using the object orientation programming paradigm can be described as a collection of objects, instances of different classes, that collaborate among them by calling the operations defined in the classes of which they are instances. An object o1 of class C1 that calls some operation of another object o2 of class C2 is a client or user of the class C2 (we may also say that class C1 is a client or user of class C2). On the other hand, class C2 is a provider of some services for class C1. A class may be, at the same time, client of a another class and provider of a third one. Example: The class Student is a “client” of the class Date because it uses the class Date in order to store/access the birthdate of a student. In its turn, the class Student is a provider for the class UniversityStudents. This latter class is intended to store all the students enrolled in the university. These relationships are shown in figure 1.3

Figure 1.3

- 14 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

1.4. Class contracts

The specification layer of the class definition involves the detailed specification of the class behaviour, which is usually referred to as class contract. As we have mentioned above, the class contract represents the meeting point between the class user (client) and the class implementer. The idea is the following:

Definition (Class contract or class specification) The class contract (or class specification) states precisely the operations offered by a class and their precise behaviour. The class user commits to use the class operations in the way stated by the class contract. On the other hand, the class implementer commits to implementing the class so that it provides the services stated by the contract in the exact way in which they have been stated.

O.O. programming is a paradigm where some traditional challenges for software engineering can be faced in a quite natural way. However, some of these issues, like software reuse and modular software construction (by a team of developers working possibly concurrently), can only be achieved if a precise and accurate contract policy has been enforced by class developers. The developer of a class A which uses a class B developed by another team member should know exactly how class B behaves in order to design class A. The developers of a class library which provides some functionality for a specific environment (e.g., a programming language) should explain to its prospective users the exact behaviour of their classes by means of a contract and, of course, those classes should conform to that contract.

The specification of class behaviour by means of a contract is essential in O.O. programming. Every class must have a contract. The class contract must state what does the class do instead of how does it do it. That is, the class contract should not be written in terms of the class representation, nor should it describe the details of the implementation of the class operations.

- 15 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS 1.4.1. Defining a class contract

Definition (Structure of class contracts) Class contracts are defined by means of the following elements: • Precondition and postcondition assertions, associated to each class operation, which state the operation behaviour precisely. • Class invariants associated to the class, which states the global properties of the class.

In the following sections we will provide some insight on each one the elements that constitute a class contract.

1.4.2. Preconditions

Definition (Precondition) A precondition is an assertion attached to a class operation which establishes those constraints which should be true at the beginning of the execution of that operation. This assertion must be guaranteed by the client of the class, not by the operation to which that operation has been attached.

Preconditions should be written in terms of: • The class interface members (i.e., the public members of the operation). • The object to which the operation is applied. • The operation parameters (particularly, the input or input/output parameters). • They should never contain anything concerning the class representation (i.e., the private members of the class) Preconditions must be guaranteed by clients before calling the operation. Therefore, the operation implementation should not check whether the precondition holds or not. This is client’s responsibility. In fact, including redundant checkings in operation implementations leads to a redundant and less reliable and comprehensible code. [Meyer1997] contains an excellent discussion about this particular issue. We can choose between weak preconditions (void or almost void preconditions which put –almost– no requirement on the client call) or strong ones (preconditions which constrain the client call).

- 16 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Example We may choose between two different preconditions for the operation create(d,m,y): • Precondition: void . This means that the client may produce a call today.create(d,m,a) without any special checking of the three parameters. In this case, the implementation of create(d,m,y) will be responsible for ensuring that the three parameters constitute a correct date. If this is not the case, it should manage the error in some way: - By means of a (boolean) error parameter (err) which becomes true if d-m-y are not a correct date: today.create(d,m,y,err) or - Throwing an exception object (exception management mechanisms will be presented in Chapter 7).

• Precondition: d-m-a represents a correct date later than 1-01-1900 . This means that the client, before calling today.create(d,m,y) should make sure that d-m-y constitutes a correct date. In this case, the operation implementation will not check d-m-y to ensure that it is a correct date. Hence, no error will be produced by the operation. ♦ ♦ ♦

The criterion to decide which kind of precondition should be applied may be the following:

Assign the responsibility for checking a condition to the part (user or operation implementation) which can take it in a more natural way.

In the example, it is clear that the Date class is the expert who has the knowledge to decide whether a date is correct or not. Since this class does not offer a public operation to check the correctness of a date, the most natural approach is that this correctness is checked by the implementation of the Date operations. Hence, we associate a void precondition to the operation create and we make it responsible for managing the error: create(d,m,y,err). On the other hand, doing this way, we get a robust operation that yield controlled results in any circumstances. In most occasions, a void precondition is preferable since it leads to a more robust solution (as we have seen in the previous example). However, sometimes, non-void preconditions are more natural. We present next various examples of the two kinds:

- 17 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Example • The class IntegerStack models a collection of elements of type integer, so that the insertions and deletions of integers in the collection are performed at the top of it (i.e., the last element to get into the stack is the first to get out). One of the operations of the class IntegerStack is the following: void pop(); This operation removes from the stack its uppermost element. It is an error to try to apply the pop() operation to an empty stack. How should we deal with this error? - A void precondition and the error is managed by the implementation of the operation by returning a boolean (output parameter) which is true if it has been attempted to remove an element from an empty stack (i.e., void pop(bool& err); ). - A precondition which states that the stack is not empty In this case, the second alternative is more natural because most of the algorithms that work with stacks never try to pop an element from an empty stack. Usually, these algorithms follow a pattern similar to the following: void someStackAlgorithm(IntegerStack p) { //.... while (!p.empty()){ //.... p.pop(); //the stack p is not empty } //... }

Thus, using a void precondition would lead to check the emptiness of the stack twice: at the loop condition and within the implementation of pop(). Furthermore, the implementation of pop() will become easier. • The class PhoneDirectory provides an operation long getPhoneNumber(char namePerson[]) which obtains the telephone number of a specific person (identified by name). We may have: 1. Pre: Void (better solution) 2. Pre: namePerson is in the directory (worse solution) In the case 1, if the namePerson is not in the directory, the caller should be warned in some way (e.g., returning 0; by means of an output boolean parameter: phone=getPhoneNumber(name, notFound);

- 18 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

or launching an exception –see Chapter 7–). Hence, the user can call this operation without having to bother about whether the person name is in the directory or not. In general, in case 2, before calling this operation, the user will have to look for namePerson in the directory (with an operation of the kind: bool existsPerson(char namePerson[])) and call getPhoneNumber(...) , only in the affirmative case. However, this will probably generate two searches of namePerson within the directory: one in existsPerson(...) and the other one in getPhoneNumber(...), since, in order to get the number, this operation will probably have to locate first the person. Therefore its efficiency will drop. The option 1 is preferred. • The class PhoneDirectory provides an operation void insertPhone(char namePerson[], long phoneNb) which inserts the pair (namePerson, phoneNb) into the directory. However, the directory may be full and this insertion may not be possible. We may have: 1. Pre: Void (better solution) 2. Pre: the directory is not full (worse solution) The fact that the directory is full or not is an implementation detail. A public operation to make the user aware of this fact is not advisable in this case (even if that operation were provided, the information yielded by it could not be trusted if the directory was to be updated concurrently). It seems clear that it is a responsibility of the operation insertPhone(...) to warn the caller if the insertion cannot be done because the directory is full. The option 1 will be preferred. An error parameter should be included or an exception should be launched (see Chapter 7). • The class BinarySearch has been defined to encapsulate the algorithm of binary search on an array of integers. It offers an operation: bool searchinteger(int v[], int n, int i) which searches the integer i within v[0..n-1] and returns true if the integer is found and false otherwise. Recall that a binary search can only be performed on an array if that array has been sorted. We may have: 1. Pre: void (worse solution) 2. Pre: the array v is sorted in ascending order and has been initialized in the range of indexes [0..n-1] (better solution) The case 1 forces the operation to check whether the array v is sorted. However, this checking compromises the performance of the operation (which should be O(log n) and now will be O(n)). Furthermore, how does the operation know if v[0..n] has been properly initialized? Choice 2 is clearly the best in this case. ♦ ♦ ♦

- 19 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Some authors advocate for using (almost) exclusively void preconditions and, hence, get robust operations [Liskov]. We prefer the use of the naturality criterion. If in application of such criterion, the non-void precondition is chosen (and hence, a non-robust operation is obtained), it is always possible to add another operation which mimics the first one but with a void precondition. This second operation would be used in the small number of cases in which the first one is not appropriate and a robust operation is required.

1.4.3. Postconditions

Definition (Postcondition) A postcondition is an assertion associated to a class operation which establishes those properties that should hold at the end of the execution of that operation, provided that the precondition held at the beginning of its execution. This assertion must be guaranteed by the class implementer.

One important issue implied by this definition is that the operation implementation is only committed to reaching the postcondition at the end of the operation execution if the precondition held at the beginning of that execution. Therefore, as we have mentioned above, the operation implementation can rely on the fact that the precondition holds at the beginning and should not check it.

The postcondition of an operation will show usually two different forms according to the type of the operation: • Creators and modifiers: The postcondition should contain the way in which the object to which the operation has been applied has been modified as a result of such operation. It is important to notice that the postcondition should focus on which changes have taken place on the object rather than on how these changes have been implemented. • Consultors: The postcondition should indicate which part of the object state is obtained by the operation and where it is obtained. In addition, it should indicate that the state of the object to which the operation has been applied has not changed with respect to its precondition. For both kinds of operations the postcondition should also explain what the operation will do in the case that some error is encountered. Two habitual behaviours used by the operations to react to this case are the following:

- 20 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• Use a parameter or a returned result to inform the caller about the error situation or • Raise an exception object. This is the preferred way to face error situations by O.O. environments. We will present exceptions it in Chapter 7. A postcondition may contain the following items: • The public members of the class to which the operation is associated • The operation parameters. The postcondition should reference all the output and input/output parameters of the operation (including its returned result, if it is a function). Usually, it will also reference all the input parameters (however, this is not strictly compulsory). • The object to which the operation is applied. In the postcondition of an operation op applied to an object x (x.op(a,b)) we may reference the state of the object x right before the beginning of the execution of op and after it. The state of x before the operation execution will be notated x@pre. This notation is taken from the OCL standard notation to write constraints [OCL]. Example: void addDays(int ndays); Call: x.addDays(ndays) Pre: void Post: The resulting date x is the date x@pre plus ndays days. • Nothing else should come up in a postcondition.

Particularly, any use of the private members of a class in either the postcondition or the precondition is explicitly and strictly forbidden!!!

Some examples of postconditions may be found below.

1.4.4. Class invariants

Operation preconditions and postconditions allow us to state precisely the behaviour of an operation and to determine which will be the state of an object after the application of a specific operation to it. However, there are other properties that should always be satisfied by any class instance regardless of the operations that have been applied to them. These properties constitute the so-called class invariant. For example, we may require in our Date class that any object which is an instance of this class represents a date posterior to 1-01-1900. This constraint would be a part of the class invariant of the class Date.

- 21 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Definition (Class invariant) A class invariant is an assertion that expresses certain constraints on the state of class objects (i.e., objects which are instances of a class). These constraints should hold: • After the creation of an object of that class (i.e., after the application of a creator/constructor operation to a class object). • Before and after the application to a class object (which has already been created) of any operation which belongs to the class interface with arguments which satisfy the class invariants of their respective classes. In the case of creator (or constructor) operations, the class invariant is only required after their execution. That is, the class invariant should hold after the creation of an object and should be maintained by any operation which is applied afterwards to that object (provided that the operation has valid arguments).

The objective of class invariants is to state explicitly all those properties that should always be true (except for the transition instants of operation execution) in order to ensure the integrity of the class objects. For that reason, the class invariant should be satisfied immediately after the application of the creation operation to an object and should not be broken by any other public operation applied to it. Let us consider some examples: Example • The class invariant associated to the class Date may be the following: Inv: Any object of the class should model a valid date posterior or equal to 1-01-1900 • The class invariant associated to the class IntegerStack may be the following: Inv: Any object of the class IntegertStack should model a stack which contains a number of integers between 0 and N • The class invariant associated to the class PhoneDirectory may be the following: Inv: Any object of the class PhoneDirectory should contain a collection of pairs (name, phone), where name is a string and phone, a long. There cannot be two pairs with the same name. ♦ ♦ ♦ Some remarks should be made concerning invariants, which introduce some aspects that will be presented in later chapters: • If an error occurs during the execution of an operation, that error should be detected and the state of the class objects that have been involved with the

- 22 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

error should be left in a controlled state (i.e., a state that satisfies the class invariant). If this is not possible, the program should stop immediately or the uncontrolled objects should be destroyed. Using a boolean parameter to signal these error situations is a poor way to deal with them. We have mentioned several times that Chapter 7 will provide a better solution based on raising exceptions. • Notice that there exist a period between the declaration of an object and its creation (by means of the create operation) in which the invariant does not hold, thus, the state of that object is uncontrolled: bool err; Date d; //object declaration

//"uncontrolled period". No operation should be //applied to d

//mo=d.getMonth(); //INCORRECT!!!!

d.create(1,11,1990,err); //object creation mo=d.getMonth(); //Now, this is OK ....

During that period, no operation (apart from create) should be applied to the object. It is possible to reduce the uncontrolled period to nothing by joining the object creation to the object declaration. This can be done by means of a special kind of operations called constructors that most O.O. programming languages define. You will become aware of them in Chapter 2 • We have said that class invariants consist of constraints associated to the state of class objects. In some cases, it would also be possible to have a class invariant constraining some aspects concerning the class itself, instead of a particular class object. For instance, we may have a class invariant which states that the number of objects instances of the class Date which can exist at the same time is lower than 30.

1.4.5. Correctness of a class implementation With the definitions of operation precondition and postcondition and of class invariant, the notion of correctness of a class implementation can be stated.

Definition (Correctness of a class implementation) Consider a class C with class invariant I and an interface given by the operations op , op , ..., op (op is the creation operation). Each 0 1 n 0

- 23 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

operation i (0 ≤ i ≤ n has a contract associated with precondition pre and i postcondition post . i The implementation of a class is considered to be correct if: 1. The implementation of op finishes in a state that satisfies I and post , 0 0 provided that before its execution, was satisfied. pre0 2. The implementation of op (1 ≤ i ≤ n) finishes in a state that satisfies I and i , provided that before its execution: posti • and I were satisfied. prei • All the arguments of op which are instances of some class D satisfy i the class invariant associated to D.

In particular, notice that a (non-creation) operation can be called only if we can guarantee that the class invariant holds (i.e., if the object has been properly created before).

1.4.6. Example: Contract for the class Date • Class invariant: Any object of the class should model a valid date posterior or equal to 1-01-1900. • Class preconditions and postconditions: - void create(int pday, int pmonth, int pyear, bool& err) (creator) * Call: dat.create(pday, pmonth, pyear, err); * Pre: void * Post: dat contains the date pday-pmonth-pyear and err=false. If pday-pmonth-pyear is not a correct date posterior to 1- 01-1900, dat is 1-01-1900 and err=true - int getDay() (consultor) * Call: d=dat.getDay(); * Pre: void * Post: d is the day of the date dat. dat=dat@pre. Notice that we do not incorporate to the precondition the assertion: dat represents a well-formed date (i.e., the operation dat.create(d,m,y,err) has been applied to dat).

- 24 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

This is already established by the class invariant, and we suppose that the object dat on which the operation getDay() is applied satisfies the class invariant. - unsigned int getMonth() - unsigned int getYear() Similar to getDay() - void setDay(int pday, bool& err) (modifier) * Call: dat.setDay(pday, err); * Pre: void * Post: dat has the same month and year as dat@pre. pday is the new day of the date dat and err=false. If the date pday-dat.getMonth()-dat.getYear() is not correct, dat=dat@pre and err=true - void setMonth(int pmonth, bool& err) - void setYear(int pyear, bool& err) Similar to setDay(...) - void copy(Date d); (modifier) * Call: dat.copy(dat2); * Pre: void * Post: dat gets the value of dat2 (dat=dat2) Again, notice that we do not need to state in the precondition any constraint concerning the correctness of dat2. We suppose that this argument is a valid one (i.e., an argument that complies with the class invariant of Date). - bool equals(Date d); (consultor) * Call: b=dat.equals(dat2); * Pre: void * Post: b is true if dat has the same value as dat2. dat has not been modified. - void addDays(unsigned int ndays) (modifier) * Call: dat.addDays(ndays); * Pre: void * Post: dat is the date dat@pre plus ndays days. The class contract will be written in a textual file associated to the file(s) containing the class definition (see below). Sections 1.8 and subsequent contain more examples of class contract.

- 25 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS 1.5. Detailed class and object definition in C++

C++ is a O.O. programming language which allows us to define classes using the two-layer approach that we have presented in section 1.2 above. In this section we will discuss the different aspects of class definition in C++. First, we introduce an example with a complete class definition. Then, the rest of the section is devoted to present the most relevant aspects of the example. This section is intended just for introductory purposes. Some of these aspects will be presented in a more detailed way in successive chapters. Example This example shows a complete definition and use of a class in C++. This definition is split in three files. In addition we show another file with a program that uses the class (this is called client or user program). • File Date.txt It contains the class contract as it has been presented in section 1.4.6 • File Date.h #ifndef DATA_H #define DATA_H #include using namespace std; class Date{ private: unsigned int day; unsigned int month; unsigned int year;

bool correctDate(unsigned int pday, unsigned int pmonth, unsigned int pyear) const; int leap(unsigned int y) const; public: void create(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& err);

void setDay(unsigned int pday, bool& err); void setMonth(unsigned int pmonth, bool& err); void setYear(unsigned int pyear, bool& err);

unsigned int getDay() const; unsigned int getMonth() const; unsigned int getYear() const;

void copy(Date d); bool equals(Date d) const;

void addDays(unsigned int ndays); }; #endif

- 26 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• File Date.cpp #include #include "Date.h" using namespace std; int Date::leap(unsigned int pyear) const { if (pyear%4==0 && pyear%100!=0) return 1; else return 0; } bool Date::correctDate(unsigned int pday, unsigned int pmonth, unsigned int pyear) const { unsigned int max;

if (pmonth==1 || pmonth==3 || pmonth==5 || pmonth==7 || pmonth==8 || pmonth==10 || pmonth==12){ max=31;} else if (pmonth==2){ max=28+leap(pyear);} else max=30;

return (pday<=max && pmonth<=12 && pyear>=1900) ; } void Date::create(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& err) { if (correctDate(pday, pmonth, pyear)) { err=false; day=pday; month=pmonth; year=pyear; } else { err=true; day=1; month=1; year=1900; } } void Date::setDay(unsigned int pday, bool& err) { if (correctDate(pday,month,year)) { day=pday; err=false; } else err=true; }

- 27 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS void Date::setMonth(unsigned int pmonth, bool& err) { if (correctDate(day,pmonth,year)){ month=pmonth; err=false; } else err=true; } void Date::setYear(unsigned int pyear, bool& err) { if (correctDate(day,month,pyear)){ year=pyear; err=false; } else err=true; } unsigned int Date::getDay() const { return day; } unsigned int Date::getMonth() const { return month; } unsigned int Date::getYear() const { return year; } void Date::copy(Date d) { day=d.day; month=d.month; year=d.year; } bool Date::equals(Date d) const { return (day==d.day && year==d.year&& month==d.month); } void Date::addDays(unsigned int ndays) { //.... }

• File User.cpp #include "Date.h" #include using namespace std;

- 28 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS int main() { bool err; unsigned int d,m,y; Date today;

cin>>d>>m>>y; today.create(d,m,y,err);

if (!err) cout<

The different aspects of this example are discussed in the following sections.

1.5.1. Private and public members Class members in C++ may be either public or private 2 . • Public members are those members that are visible from any function extern to the class. They come up following the label :public. The members (usually operations) that constitute the class interface are declared as public. In the example, create(...); getDay(); getMonth(); etc. are public members. • Private members are those members which are only visible from the operations of the class in which they have been defined3 . They come up following the label :private (by default, class members are considered private). The attributes that constitute the class representation (or class structure) are usually declared as private. In addition, a class may also have private operations, which are useful to help in the implementation of public operations. In the example, the attributes day, month, year are private attributes. The operations correctDate(...) and leap(...) are also private (see file date.h). Private operations are useful in order to implement class operations in a more structured way and also to avoid redundancy of code.

2there is, still, the possibility of protected members, which are presented in section 5.3.1. 3Actually, they are also visible from the so-called friend actions, as we will see in section 2.3.

- 29 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Notice, in this case, that the private operation correctDate(...) is called from the implementations of create(...), setDay(...), setMonth(...) and setYear(...). Notice also that private operations does not belong to the class interface and hence, they should not made available to class users. As it is shown in file date.h, public members are preceded by the label public: while private members are preceded by private:.

1.5.2. Operation declaration and implementation • Operations are declared by writing their header either in the private or public parts of the class (depending on whether they are private or public operations). • Operations may be implemented in two different places: - Right after the declaration: int getDay(){return day;}

- After the brace (};) which concludes the definition of the class. In this case, we have to indicate that the operation belongs to a specific class, prefixing its name by the class name followed by ::, as is shown next: unsigned int Date::getDay() { return day; }

If the second alternative is chosen, the operation declaration comes up in the .h file (e.g., date.h) while the operation implementations can be usually found in the .cpp file (e.g., date.cpp); see the example. Afterwards we will go back to this distribution.

1.5.3. Object creation Once a class has been defined, it is possible to create objects, which are instances of that class. In C++ we may create both automatic and dynamic objects. In addition, we may create pointers to objects and also references to objects. In the following sections we summarize how these entities may be created.

- 30 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Automatic objects

Definition (Automatic object) An automatic object is an object created by means of a declaration of the kind: T x; (e.g., Date birthday;). The declaration associates a name and an identity to the declared object, which makes it different from any other object. An automatic object is created whenever its declaration is executed and is destroyed at the end of the block in which that declaration comes up. In particular, notice that any local variable (i.e., a variable which is declared within the scope of a function) is destroyed at the end of that function.

In order to complete the creation of an object (including its initialization) an implicit call to a constructor is automatically performed. Constructors are presented in Chapter 2.

Pointers to objects

Definition (Pointer) A pointer is a program entity that may have as value one of the following: • The memory address where an object of a certain type is stored. • The value 0 (or NULL), which means that it is not associated to any object at the moment. • An undefined value (when a pointer is declared). The declaration of a pointer states to which type (or class) of objects it may point.

A pointer to an object, which is an instance of the class Date, is created in the following way:

Date* pbirthday;

This sentence declares the program entity named pbirthday as a pointer to some object of the class Date. However, it does not make it point to any particular object. It has an undefined value. It is important to state clearly that this sentence does not create an object of the class Date (i.e., pbirthday is not an object).

- 31 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• We can make it point to an existing object in the following way:

Date birthday; pbirthday=&birthday;

&x denotes the address of the object x.

• We may access the object to which the pointer is referring by:

Date day; day=*pbirthday;

The effect of this code is shown in fig. 1.4.

Dynamic objects

Definition (Dynamic object) A dynamic object is an object created using the operator new: T* px; px=new T; The execution of this sentence creates an object of the class T pointed by the pointer called px. (e.g.,Date* panniversary; panniversary=new Date;) A dynamic object is created by allocating dynamic memory. A dynamic object is unnamed. It is accessed by means of a pointer. A dynamic object is created when the new operator is executed and is destroyed using the operator delete: delete px; (not at the end of the block in which it has been created).

Dynamic objects are sometimes called objects allocated dynamically. Dynamic objects may be used to define arrays whose dimension is not known until run time (i.e., dimension not known at compilation time): void f(int n) { Date* v;

v=new Date[n]; //Date v[n]; would be INCORRECT, //since n is not a constant expression //.... };

- 32 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Fig. 1.4 shows the effect of this code.

More than one pointer may point to a dynamic object. If zero pointers point to a dynamic object, then it becomes garbage (see Chapter 2).

Figure 1.4: Creation of objects and pointers

Figure 1.4. shows graphically the operations that have been presented. References to objects

Definition (Reference) A reference is a program entity such that: • It is associated to a specific object • It is an alternative way to refer to the object to which it is associated. In fact, it is an alias of that object. • It is associated to an object since the creation of the reference. It cannot be associated to any other object within the definition block of the reference and it must be associated to an object at all times.

- 33 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

References are defined and used in the following way:

Date today; // today is an object of class Date Date& reftoday=today; //reftoday is a reference to the object today reftoday is a reference to the object today. From now on, reftoday is an alias for the object today; today.create(10,11,2004,err); and reftoday.create(10,11,2004,err); are equivalent.

Figure 1.5: Creation of a reference to an object

References may be used in different situations. For instance: • Pass of parameters for input/output and output parameters. void exchange (int& a, int& b) { int aux;

aux=a; a=b; b=aux; } int main() { int x=10; int y=20;

exchange(x,y); //x=20, y=10 return 0; }

Within the function exchange(..), a and b are two references (two aliases) of the objects x and y. Any update of a or b within exchange(..) is, actually, an update of x or y, respectively.

- 34 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• To pass parameters which should behave polymorphically (see Chapter 6). • To pass parameters (even input parameters) and avoid a copy of the parameter. If we use the normal parameter pass (i.e., pass by value): void f(List l) { // Procedure implementation } int main() {

List li;

//Insert thousands of elements into list before //calling f(li)......

f(li); //Not a good idea!!!! }

this parameter pass may create l as a copy the entire list li. This would be very time and space consuming. It can be avoided by passing a reference to li: void f(List& l) { // Procedure implementation } int main() {

List li;

//Insert thousands of elements into list before //calling f(li)......

f(li); //Now is better }

If we want to enforce that the parameter is not modified by the function we may declare the parameter as const: void f(const List& l) { // Procedure implementation //Now l cannot be modified within f(l) }

- 35 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Additional remark: On the other hand, the declaration of the parameter as const also allows the call to the function f(l) with a constant parameter, which, otherwise, is not possible. In particular, the following calls are correct if the parameter of f() has been defined as const: int main() { List l1; //...... Fill in l1 const List l2=l1;

f(l1); //Also possible if the parameter is not const f(l2); //Only possible if the parameter is const f(List(l1)); //Only possible if the parameter is const

return 0; }

The meaning of these calls will be explained in Chapter 2, when constructors are presented. • Return values of functions without making a copy of the returned value.

Date& getMostRecentDate(Date v[], int n) { int i, imostrecent;

imostrecent=0; for (i=0;i

The function getMostRecentDate(..) returns a reference to the most recent of the dates contained in an array (it relies on a function moreRecent(...) which states whether a particular date is more recent than another). This function can be used in different ways like the following: - In the right side of an assignment

Date recent; recent=getMostRecentDate(v,n);

Notice that, although getMostRecentDate(..) does not create a copy of the most recent date in the array (instead, it returns a reference to it), a copy of that date is created later by the operator = .

- 36 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

- In the left side of an assignment This is a clever way to update the object returned by a function. Suppose that we want to change the most recent date of an array to today:

Date today; today.create(10,12,2004,err); getMostRecentDate(v,n)=today;

The object date to which the result of the function refers gets the value of today. Notice that this would have not been possible with a non-reference return. Notice that a function can never return a reference to a local variable (since a reference must always refer to an existing object and the local variable will be destroyed when the execution reaches the end of the function). Object construction deserves much more attention. Chapter 2 is devoted to this issue. In this chapter we will review and extend some of the aspects we have introduced here.

1.5.4. Access to members

As we already know a program may consult and modify the state of an object x which is an instance of the class C by means of applying to x the members defined at the public part of the class C. We will present in this section how we may apply a member to the different kinds of objects defined in C++. Application of a member to an automatic object

Date birthday; bool err; birthday.create(10,12,2003,err);

This notation implies that we are invoking the operation create with the parameters 10, 12 and 2003 on the automatic object called birthday.

Application of a member to a class instance referred to by a pointer A pointer may point both to an automatic object or to a dynamic one. In both cases, the call to a class member on the object referenced to by the pointer is performed in the same way:

- 37 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Date* pbirthday; pbirthday=&birthday;

(*pbirthday).setMonth(11,err); pbirthday->setMonth(11,err);

Both calls to setMonth(..) on the object referenced by pbirthday are equivalent.

Warning: It is an error to apply an operation to a pointer which is not pointing to any particular object: Date* px; px->create(10,11,2002,err); //ERROR. px does not point to any object

Application of a member to a class instance referred to by a reference A reference is an alias which can be used to refer to an existing object. We can use indistinctly the object identifier or the reference to access to it.

Date& rbirthday=birthday; //birthday is an already existing object //of class Date rbirthday.setMonth(11,err); birthday.setMonth(11,err); //Both are equivalent

Accessing members from within a class operation A program which is a user (client) of a class (say Date) will create objects (instances of the class Date) and apply public members to those objects:

Date birthday; birthday.setMonth(11,err);

However, when we access a class member from within the implementation of a class operation we do not prefix it with any object: void Date::setMonth(unsigned int m, bool& err) { //..... month=m; //.... }

- 38 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

When we access a class member from within the implementation of a class operation (e.g., setMonth(...)), that member is applied to the object to which that operation has been applied. In the example, the attribute month of the class Date which is accessed within the implementation of the operation setMonth(...) refers to the attribute month of the object birthday since setMonth(11,err) is applied to birthday (by means of the instruction birthday.setMonth(11,err)).

Sometimes it is necessary, or at least, convenient, to make it explicit, within the implementation of an operation, the object to which that operation is applied. In that case, we will use the pointer this

The entity this can be used within the implementation of a class operation and it is defined as a pointer to the object to which this operation is applied at execution time. The object itself can be accessed by *this.

Example We may add an operation void copy2(Date dat2) to the class Date such that, when it is applied to an object dat: dat.copy2(dat2); it copies the object dat into the parameter dat2. The implementation of copy2 could be the following: void Date::copy2(Date dat2) { dat2=*this; }

This refers to the object on which copy2 has been called (in this example dat). An alternative implementation for this operation is dat2.day=this->day; ♦ ♦ ♦

1.5.5. References vs. pointers C++ allows the definition of both pointers and references. They are intended to refer to other existing objects. Therefore, they are not objects by themselves.

- 39 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

It is important not to get confused between both concepts. We summarize next the differences:

• References are used as aliases to refer to objects. That is, we can access to an object using indistinctly the reference or the object identifier. However, we have just one object.

Date x; Date& y=x;

x.create(10,11,2004,err); y.create(10,11,2004,err); //Both are equivalent

• However, if we use pointers, we have to dereference them in order to access to the object to which the pointer refers.

Date x; Date* y;

y=&x; x.create(10,11,2004,err); y->create(10,11,2004,err); (*y).create(10,11,2004,err); //the three of them are equivalent

• Pointers can refer to “nowhere”. When a pointer is declared it may not be initialized and, hence, it points to an undefined position. Furthermore, a pointer can be initialized to 0 (or NULL), meaning that it does not point to any object. If we try to access a member of an object referred to by a pointer which points to an undefined position or to nowhere, an error will occur. • When a reference is declared it must be initialized as referring to an existing object.

Date x; Date& y; //INCORRECT

Date* px; //CORRECT The pointer points to an undefined position //however, it should be initialized soon!!!

Date& y=x; //CORRECT

1.5.6. Splitting class definition into different files Class definition in C++ is usually split into different files as it is illustrated in example that opens section 1.5. In this section we present the guidelines to distribute the definition and use of a class in different files.

- 40 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

If we want to define a class called Name we will create the following files: • File Name.h (or header file). It contains the class definition: class Name{..}; with: - The attributes that constitute its class structure (in its private part) - The headers of its private and public operations If we write the operation implementation right after its declaration (which is, in general, not encouraged), the file name.h will also contain the implementation of the operations. • File Name.cpp (or implementation file). It contains: - #include "name.h" (i.e., the inclusion of the class definition). - The implementation of the private and public class operations. • File Name.txt (or documentation file). It contains the class contract as it has been presented in 1.5. This is a text file (it can have any other extension; in particular, it may be an html file). • File User.cpp (or client/user file). It contains: - #include "Name.h" - Some functions (including a main(){...} function) that use the class Name. This file is not a part of the class definition.

This structure of files is a bit different if the defined class is a template class (see Chapter 3). In that case, there is no file name.cpp. Its contents are inserted into the file Name.h. This will be presented in Chapter 3.

Generation of the executable file In order to generate the executable file, we need to compile separately all the source files (.cpp) and link together the files that result from this separate compilation. In the example of the class Date we would have:

1. Compile Date.cpp Using the GNU C compiler, this could be done by issuing the instruction: g++ -c Date.cpp This instruction generates an object file called date.o. This file contains the machine code for the class and the operation implementations. However, it is not directly executable since it has no main function. 2. Compile User.cpp

- 41 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Using the GNU C compiler, this could be done by issuing the instruction: g++ -c User.cpp This generates an object file called user.o. This file contains the machine code for the main program (user of the class Date). However, it is not directly executable since it has several calls to operations whose code is not known by user.o (e.g., create(...)).

3. Link both object files Using the GNU C compiler, this could be done by issuing the instruction: g++ User.o Date.o -o exec This generates an executable file called exec. This file is directly executable. This process may be automated by using the make utility or the project utilities of different compilation environments. The explanation of such utilities is beyond the scope of this chapter (see Appendix A).

1.6. Some (pleasant) consequences of class definition The approach we have presented to define new kinds of entities by means of classes has some pleasant properties that we have mentioned throughout this chapter. In this section we summarize them.

• Classes as abstract definition of new types O.O. programming is based on data abstraction. In fact, we may say that data abstraction is the main way proposed by this programming paradigm to deal with the complexity inherent to the construction of medium to big size software applications. The main point is to notice that the addition of a new type in a programming language involve two conceptually different things: the definition of the services offered by the type and the implementation of those services in terms of the language constructs. Keeping both issues separated contributes to reduce the complexity of the management of those types. O.O. programming languages provide the notion of class as a way to define new types by separating the services offered from their implementation. As a consequence, the user of these classes only has to care about what services are offered by them. By abstracting the implementation issues, he/she reduces the complexity of its management. This approach based on abstraction is exactly the same as what we do in our normal life when we operate when complex machines (e.g., when we drive a car). It is also the approach used by other branches of the engineering to deal with complexity.

- 42 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• Encapsulation and information hiding The above mentioned abstract approach is enforced by information hiding. To ensure that the class user does only access the class services, its implementation aspects are considered private. The user of a class can only access its public issues, which are constituted by the services offered by the class. That is, the implementation is hidden from the user. As a consequence, we get independence of a class from its implementation. Since the programs that use a class cannot access its implementation, this implementation may change (for instance, to make it more efficient) without affecting those programs. The only requirement is that the class contract does not change. For instance, a class that models complex numbers may change its implementation from Cartesian coordinates to polar ones, without affecting the programs that worked with that class (may be, they will notice that certain operations will be performed more efficiently). We will discover in Chapter 5 that the visibility of class features should be finer: some class members should be available to some class users only. This will be related to inheritance. • Modularity and reuse One of the most important challenges of software engineering is reuse. The goal of reuse is to be able to construct software modularly by reusing already constructed software modules, so that we do not have to design from the scratch every new piece of software. The class, in O.O. programming, constitutes a very complete and natural reusable module, which encapsulate data abstractions, but also functional abstractions (recall that they supply a list of operations which implement services). Class reuse is easy through its interface, which provides a contract. Information hiding ensures the robustness of the user applications that reuse a class with respect to implementation improvements in it. However, the best class properties concerning reuse are still to come: one very important requirement that should be provided by reusable units in order to make reuse possible is adaptability. It is difficult to be able to reuse a module in many different contexts without any adaptation. A specific unit will make a good reusable module only if it provides mechanisms to be easily adapted to different situations. Classes provide those mechanisms: inheritance, aggregation, polymorphism and genericity make it possible to adapt a class so that it can be reused in different contexts. These mechanisms will be presented in the next few chapters. • Relationships between classes Concepts in real life are linked between them by means of different types of relationships. For instance, the concept “person” is more general than the

- 43 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

concept “woman”. An instance of concept “car” should contain five instances of the concept “wheel”. The concept “collection” can be constituted by “persons”, “cats”, “documents” or whatever. Since one main purpose of O.O. programming is to model reality and we use classes in order to do so, it is necessary that O.O. programming languages provide tools to model these kinds of relationships between classes. At least, they should offer: - Class aggregation: An object of a class can be part of the structure of an object of another class (e.g. an object of class Wheel can be a part of an object of class Car). We devote Chapter 4 to this relationship. - Class specialization-generalization: We can define hierarchies of classes. The higher a class comes up, the more general the concept it models is (e.g., the class Person will be more general than the class Man). Chapter 5 is devoted to generalization. - Genericity: The definition of some classes will accept other classes as parameters. For instance: we may define a class: CollectionOfItems as a collection of objects of a specific class Item. However, Item will be a parameter of CollectionOfItems. When an object of CollectionOfItems is created at execution time, an actual parameter will be bound to Item stating the specific type of the items that will compose the collection. In this way, we may create collections of students, of persons, etc. We study genericity in Chapter 3. The O.O. paradigm we have introduced in this chapter enriched with the features to model class relationships turns out to be a very strong and convenient tool to construct software.

1.7. Guided problems. The class MyString

1.7.1. The problem 1. Implement a class MyString to model strings of undefined length. It should provide, at least, the following operations: • Creator operation that takes a character array ended in ’\0’ as parameter and converts it into a MyString object. • Get the ith character of a MyString object • Operations to copy and compare two MyString objects • Operation to concatenate two MyString objects • Operation to check whether a MyString object is a substring of another one Use the following interface:

- 44 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS class MyString{

//Some private part.... public:

void create(); void create(char st[]); char getIthChar(unsigned int pos, bool& err) const; void putChar(char c, int pos, bool& err); unsigned int getLength() const; unsigned int substring(const MyString& c); bool equals(const MyString& c); void copy(const MyString& c); bool lowerOrEqual(const MyString& c); void concat(char* c);

void get(istream& c,char test); };

1.7.2. Solution File MyString.txt This file contains the specification of the class MyString. It may look like the following: Class MyString This class models a string of characters with its usual operations

*Operations:

*Creator: void create(char* st);

Call: s.create(st); Pre: st is an array of chars that ends in '\0' Post: s is a MyString that contains the characters of st.

void create();

Call: MyString s; Pre: void Post: s is a MyString with no characters ("")

*Modifiers: void putChar(unsigned int pos, char c, bool& error);

Call: s.putChar(pos,c,error); Pre: void Post: s is the same MyString as s@pre except for the character at

- 45 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

position pos, which is now c and error=false.

If pos is beyond the length of s then error=true and s=s@pre void concat(MyString& st);

Call: s.concat(st); Pre: void Post: s contains s@pre followed by st. void get(istream& is, char c)

Call: s.get(is,c); Pre: void Post: s contains the next sequence of characters that have been read from the input stream is. The character c is the character that acts as final mark for the string. void reverse(); ... void copy(MyString& st); ...

*Consultors bool equals(MyString& st); ... char getCharPos(unsigned int pos, bool& error); ...

File MyString.h This file contains the representation of the class MyString and the operation headers. #ifndef MYSTRING_H #define MYSTRING_H

#include using namespace std; class MyString{

char* st; public:

void create(); void create(char st[]);

char getIthChar(unsigned int pos, bool& err) const; unsigned int getLength() const; unsigned int substring(const MyString& c) const; bool equals(const MyString& c) const;

- 46 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

void copy(const MyString& c); bool lowerOrEqual(const MyString& c) const; void concat(char* c);

void get(istream& c,char test); };

#endif

Remarks: • We have chosen to represent a string by means of an array reserved dynamically with a mark (\0) to signal its end. Notice that, it is crucial to know at which array position the string finishes (i.e., how many chars in the array are valid). This can be signalled in two ways: by means of a final mark (the way we have chosen) or by means of a counter that indicates the number of characters of the string.

File MyString.cpp This file contains the implementation of the operations declared in MyString.h. We use the operations provided by the library cstring, which we include. #include "MyString.h" #include #include using namespace std; void MyString::create() { st=new char[1]; st[0]='\0'; } void MyString::create(char pst[]) { int i=0;

st=new char[strlen(pst)+1]; strcpy(st,pst); } char MyString::getIthChar(unsigned int pos, bool& err) const { if (pos

- 47 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

unsigned int MyString::getLength() const { return strlen(st); }

unsigned int MyString::substring(const MyString& c) const { ///EXERCICE: IMPLEMENT IT return 0; } bool MyString::equals(const MyString& c) const { int i=0; int l; l=getLength(); if (c.getLength()!=l) return false; //if both are of different //lengths, they cannot be equals else{ while (c.st[i]==st[i] && i

i++; } return (st[i]<=c.st[i]); } void MyString::concat(char* c) { int i,j,l1,l2;

char* aux;

aux=new char[getLength()+strlen(c)+1]; i=0;j=0; while (st[i]!='\0'){ aux[i]=st[i]; i++; }

- 48 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

while(c[j]!='\0'){ aux[i]=c[j]; j++; i++; } aux[i]='\0'; st=aux; } void MyString::get(istream& c, char test) { const int NCAR=10; int i; bool end=false; char aux[NCAR+1];

st=new char[1]; st[0]='\0';

while (!end){ c.get(aux[0]); i=0; while (aux[i]!=test && i

if (aux[i]==test){ aux[i]='\0'; end=true; } else aux[i+1]='\0';

this->concat(aux); }

}

Remarks: • Notice how the create operations reserve memory dynamically:

st=new char[strlen(pst)+1]; //reserve for the attribute st //the amount of memory needed by //the characters of pst

strcpy(st,pst); //Copy the parameter pst to the //attribute st. We use the C library //cstring. It relies on the fact that //pst ends in \0

• If we use the operation create(), then, at least we reserve one character for ’\0’

- 49 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

• The implementation of the copy and concat operations creates garbage (the previous value of st gets lost). In next chapter we will show how to avoid these situations. • The operation lowerOrEqual relies on the fact that the arrays of characters end in ’\0’, which, by definition has a value of 0. File userString.cpp This file contains a program that uses the class MyString

#include "MyString.h" int main() { MyString ss1, ss2, ss3;

ss2.create("kjkj"); ss1.create(); ss3.create(); ss3.get(cin,'\n');

if (ss1.lowerOrEqual(s2)) cout<<"ss1 lower or equal"<

return 0; }

1.7.3. The file Makefile userString: userString.o MyString.o g++ MyString.o userString.o -o userString userString.o: MyString.h MyString.cpp userString.cpp g++ -c userString.cpp

MyString.o: MyString.h MyString.cpp g++ -c MyString.cpp

1.8. Guided problems. The word counter

1.8.1. The class WordCounter Create and test a class WordCounter with the objective of keeping a counter of a series of words (which have been read from the standard input or from a text file) such that it is possible to say: the word “miew” has come up 3 times, the word “piolin” has come up 2 times, the word “umbrella” has come up 1 time.... The class WordCounter should have the following interface:

- 50 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS class WordCounter {

//Some private part.... public:

void create(); void countWord(MyString& w, bool& error); int getNumbOccurrences(MyString& w) const; int getNumbOccurrences(int j) const; void getWord(int i, MyString& w) const; int getNumbWords() const; void getListOfWords(MyString c[], int& numbWords) const; };

Here is an informal specification of the class operations: • void create() Creator Creates a word counter without any words. • void countWord(MyString& w, bool& error) Counts another occurrence of the word w. If there is no space left in the word counter (i.e., it is full of words), it activates the error parameter. • int getNumbOccurrences(MyString& w) Get the number of occurrences of the word w that the word counter has counted • int getNumbWords() Get the number of different words that have been stored in the word counter. • int getNumbOccurrences(int j) Get the number of occurrences of the j-th word of the word counter. The meaning of j-th word is implementation dependant. However, j should have a value such that: 0 ≤ j ≤ getNumbWords()-1 • void getWord(int j, MyString& w) Get the j-th word of the word counter. The meaning of j-th word is the same as in getNumbOccurrences(j). • void getListOfWords(MyString c[], int& numbWords) Returns an array of objects of type MyString (indexed from 0 to numbWords-1) with all the different words counted by the word counter.

Notice that, in order to design the class WordCounter, we have reused the class MyString. In fact, we model a word as an object of the class MyString. Class reuse is one of the main issues in object-oriented programming.

- 51 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Solution: File WordCounter.txt

This file contains the specification of the class WordCounter. Class WordCounter

Goal:

The class stores a set of words and provides information on how many of each are stored in the class.

An object of this class can store as many as N=100 different words.

Operations:

//Creator: void create();

Call: wc.create(); Pre: void Post: wc is an empty instance of the class WordCounter (i.e., it does not contain any word).

//Modifiers void countWord(MyString& st, bool& error);

Call: wc.countWord(st,error); Pre: Void Post: wc contains the same words as wc@pre and the word st counted once more. If the word st leads to an overflow on wc then wc=wc@pre and error=true void deleteWord(MyString& st); ....

//Consultors unsigned int getNbOccurrences(MyString& st);

Call: nb=wc.getNbOccurrences(st); Pre: Void Post: nb is the number of occurrences of the word st in wc. unsigned int getNbDifferentWords(); .... void getWordNb(unsigned int i, MyString& st); .... unsigned int getNbOccurrences(unsigned int i); .....

- 52 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Solution: File WordCounter.h This file contains the representation of the class WordCounter and its operation headers. #ifndef WC_H #define WC_H

#include "MyString.h" const int N=100; class CountInfo{ public: MyString word; int noccur; }; class WordCounter{

CountInfo counter[N+1]; int size; int searchWord(MyString& w) const;

public:

void create(); void countWord(MyString& w, bool& error); int getNumbOccurrences(MyString& w) const; int getNumbOccurrences(int j) const; void getWord(int i, MyString& w) const; int getNumbWords() const; void getListOfWords(MyString c[], int& numbWords) const;

}; void WordCounter::create() { size=0; } void WordCounter::countWord(MyString& w, bool& error) { int i;

error=false; i=searchWord(w); if (i==size && size

- 53 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS int WordCounter::getNumbOccurrences(MyString& w) const { int i; i=searchWord(w);

if (i=0 && j=0 && i

/*********************************** int WordCounter::searchWord (MyString& w) const

Returns the index of the array counter where the string w is stored. If w is not stored in counter, it returns the first empty index of the array (which corresponds to the position where w should be inserted) *************************************/ int WordCounter::searchWord (MyString& w) { int i=0;

(counter[size].word).copy(w); while (!(w.equals(counter[i].word))){ i++; } return i; } #endif

- 54 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Remarks: • The word counter has been represented by means of an array counter of (word, numb-of-occurrences) pairs. Those pairs have been encapsulated in a class called CountInfo. Since it is a very simple class which will be used exclusively by WordCounter we make public its attributes and we do not provide operations to get/set them. It would have also been correct to hide the attributes and provide operations. counter[i] (0<= i

- 55 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

that the operation implementation is more efficient and elegant). To guarantee that we have an additional position in which the sentry can be put, we define the capacity of the array counter to be N+1.

1.8.2. A client program Now we want to design a client program implementing a user interface which shows a menu with the following choices: 1. Count another word 2. Show the counter of a word (i.e., how many times a specific word is stored in the word counter) 3. Show the counter of all the different words stored in the word counter (i.e., the word “table” has 2 occurrences in the word counter, the word “umbrella” has 1 occurrence in the word counter....). 4. Exit Which option would you take and why? (a) Add a new operation (say showMenu ) to the class WordCounter or (b) Create a new class called WordCounterInterface which will take the responsibility for interacting with the user? To help you answer this question, suppose the following: What would happen if we wanted to have two different interfaces with the user: 1 the previous one, based on a menu and 2 a new one which will read a list of words from the standard input (usually, the keyboard) and write in the standard output the number of occurrences of each word. Sometimes we will use the interface 1 and sometimes the interface 2. Which of both solutions ( (a) or (b) ) will be more helpful? Which solution provides a more independent, cohesive and elegant set of classes? Solution Clearly, the best solution is the second one. If the first operation was implemented (option a), a new operation should be added to the class WordCounter for each different interface with the user. Always try to decouple the classes which are responsible for the program logic from those that interact with the user. This decoupling will lead to a higher independence between both sets of classes and it will make the connection of different user interfaces with the same logic classes easier. Therefore, we will have the following interaction between objects:

- 56 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

Figure 1.6: Interaction between objects in the word counter program

1.8.3. The class WordCounterInterface 1. Implement the class WordCounterInterface 2. Put it all together (the classes MyString, WordCounter and WordCounterInterface) and test the whole program. 3. After that, change WordCounterInterface for WordCounterInterface2 which has the following interface: void readWords(WordCounter& wc); void showResult(WordCounter& wc);

How many changes have you had to do? Specifically, have you had to do any change to the class WordCounter? Are you convinced of the goodness of the solution (b)?

Solution: File WordCounterInterface.h

#include "WordCounter.h" #include "MyString.h" class WordCounterInterface{

public: void showMenu(WordCounter& wc); }; void WordCounterInterface::showMenu(WordCounter& wc) { int i,j,no; bool exit=false;

MyString w,wread,w1,w2,w3,w4;

w1.create("1");

- 57 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

w2.create("2"); w3.create("3"); w4.create("4");

do { cout<<"\n\n\n*************************"<

if (wread.equals(w1)) { cout<<"****Enter a word to be counted"<

} else if (wread.equals(w2)) { cout<<"****Enter the word which counter " <

} else if (wread.equals(w3)) {

for (j=0;j

Solution: File mainProgram.h #include "WordCounterInterface.h" #include "WordCounter.h" #include "MyString.h" int main() { WordCounterInterface wci; WordCounter wc;

- 58 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

wci.showMenu(wc); return 0; }

Solution: File WordCounterInterface2.h #include "WordCounter.h" #include "MyString.h" class WordCounterInterface2{

public: void readWords(WordCounter& wc); void showResult(WordCounter& wc); }; void WordCounterInterface2::readWords(WordCounter& wc) { MyString wfi("xxx"); MyString w;

w.get(cin,' '); while (!(w.equals(wfi))){ wc.countWord(w); w.get(cin,' '); } } void WordCounterInterface2::showResult(WordCounter& wc) { int j; MyString w;

for (j=0;j

Notice that WordCounterInterface2 can be used instead of WordCounterInterface without any change in the implementation of the WordCounter class. When you design, always strive for decoupling classes.

- 59 - CHAPTER 1: PRINCIPLES OF OOP. CLASSES. OBJECTS

- 60 -

Chapter 2 Special members: constructors, destructors, overloaded operators

This chapter will be devoted to present in detail three kinds of class members which are somehow special: constructors, destructors and overloaded operators. Some of the issues we will present in this chapter have been introduced or motivated in Chapter 1. What we will do here will be to review the already presented issues and to take a deeper insight of them. This is the case of constructors. Although their need has been introduced in Chapter 1, we have delayed to this chapter the presentation of constructors themselves, since we feel that this is an important topic which deserves much more attention. Destructors have to do with dynamic objects and garbage. Both things have been mentioned in Chapter 1, but now they will be presented in far more detail. Overloaded operations have not at all the same importance as constructors or destructors. However, they constitute an interesting feature provided by C++ and its presentation is necessary. This chapter also introduces the notion of friend functions and friend classes. They are not actually special class members (in fact, they are not class members at all), but this chapter seemed the correct place to introduce them: on the one hand, they bear certain similarity with class members and, on the other hand, they are essential to overload certain operators.

2.1. Constructors

2.1.1. Notion of constructors According to what has been presented in Chapter 1, before using an object which is an instance of a specific class, it is necessary to create it. In the case of automatic objects, its creation process consists of two steps: 1. Declare an object identifier (i.e., a variable) as an instance of that specific class and allocate memory space for it. For example: Date today; With this statement, the identifier today is bound to an object of the class Date, for which some memory is reserved to store the attributes that compose the object structure (see fig. 2.1(a)). 2. Initialize that object using one of the class operations which are intended to do so. For example,

- 61 - CHAPTER 2: SPECIAL MEMBERS

today.create(pday, pmonth, pyear, err); The result of this operation is illustrated in fig. 2.1(b). It completes the creation of an object which satisfies the class invariant.

Figure 2.1: Declaration (a) and initialization (b) of an object

However, this procedure suffers from one important drawback: The programmer, after declaring the object, may forget about calling the initialization operation. In this case, the object may be used without a proper initialization, thus, without ensuring that the class invariant holds for it. The result of this situation is usually harmful. The solution for this problem seems clear: to unify in a single operation the object declaration (including the memory reservation) and the object initialization. This single operation which performs the two responsibilities is called constructor.

Definition (Constructor) A constructor is a class operation which creates an object as an instance of an specific class. This object creation involves: • Allocate memory to store the structure of that new object. • Initialize its attributes in a proper way (so that the class invariant holds for the created object).

Definition (Constructors in C++) A C++ constructor for the class A is an operation associated to the class A such that: • It is called A (i.e., It has the same name as the class) • It has no return type (not even void) • It may contain some instructions intended to initialize a newly created object of class A.

C++ constructors are called implicitly when the object is to be created.

- 62 - CHAPTER 2: SPECIAL MEMBERS

Example In the class Date presented in 1.6, we may transform the creation operation (create(...)) into a constructor in the following way: class Date{ private: //As before //... public:

Date(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& error);

//Rest of the operations, as before };

Date::Date(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& error) { if (correctDate(pday, pmonth, pyear)){ error=false; day=pday; month=pmonth; year=pyear; } else {error=true;} }

If the parameters passed to the constructor do not constitute a valid date, the forth parameter (error) becomes true. This error parameter should be checked after each call to the constructor. This is not an elegant way to deal with error situations. As it has been mentioned before, Chapter 7 will present a much better way which can be easily applied to constructors and other class operations. This constructor can be used in order to create a new date in the following way: bool err; Date today(10,12,2003,err);

The previous instruction generates a call to the constructor of the class Date with the given parameters. As a result of this call, the object today is initialized to 10- 12-2003 and err=false. As usual, the constructor code may also come up within the class definition: class Date { private: //As before......

- 63 - CHAPTER 2: SPECIAL MEMBERS public: Date(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& error) { if (correctDate(pday, pmonth, pyear)){.....} else{....} }

//Rest of the operations, as before }; ♦ ♦ ♦

2.1.2. Constructor overloading In C++ it is possible to define several constructors for a single class, as long as they all have different parameters (so that, it is possible to differentiate one from the other). Example In this example we add a new constructor for the class Date: class Date{ private: //As before...... public: Date(){day=1; month=1; year=1900;} Date(unsigned int pday, unsigned int pmonth, unsigned int pyear, bool& error) { //As before...... }

//Rest of the operations, as before };

We could call this constructors in the following ways:

Date d1(10,12,2001,err); //Call to the second constructor //d1=10-12-2001

Date d2; //Call to the first constructor //d2=1-1-1900

Date d3(); //SYNTAX ERROR!! Constructors without //parameters are called without //parenthesis ♦ ♦ ♦

- 64 - CHAPTER 2: SPECIAL MEMBERS 2.1.3. Default constructor A constructor which have no parameters is called default constructor (see the previous example). If the programmer does not define any constructor for a specific class, the compiler creates a default one (which does not do anything). Therefore, if the class Date did not have any constructor defined, the sentence: Date d; would be still valid. It would generate a call to the default constructor created by the compiler. However, if the class Date has some constructor, the compiler does not create a default one. For this reason, if the class Date defines only the constructor with four arguments: Date(int pday, int pmonth, int pyear, int& error) then the following sentence is an error: Date d; Since now the class Date does not have any constructor with no parameters.

2.1.4. Default parameters A constructor may incorporate parameters with default value. Example class Date{ private: //As before...... public: Date(unsigned int pday=1, unsigned int pmonth=1, unsigned int pyear=1900) { //...... } };

The instruction Date d; will initialize d=1-1-1900. On the other hand, the instruction: Date d(1,10,2001); is still possible and will initialize d=1-10-2001.

Beware of ambiguities generated by default parameters. For instance if the class Date has two constructors defined:

- 65 - CHAPTER 2: SPECIAL MEMBERS

Date() {day=1; month=1; year=2000;}

Date(unsigned int pday=1, unsigned int pmonth=1, unsigned int pyear=1900) {day=pday; month=pmonth; year=pyear;} then, the call: Date d; would be ambiguous. ♦ ♦ ♦ 2.1.5. Copy constructor A copy constructor is a constructor which has, as its only argument, a reference to an object of the class for which the constructor is defined. Example The copy constructor for the class Date could be the following: class Date{ private: //As before...... public: Date(const Date& pd) { day=pd.day; month=pd.month; year=pd.year; } //..... };

Notice that we copy all the attributes of the object that we are creating (the one on which the copy constructor is called) with the corresponding attributes of the parameter. In this way we make the object that we are creating a copy of the parameter. Notice also that within the implementation of the copy constructor we may access the private part of the class Date, thus pd.day, pd.month, pd.year are correct. ♦ ♦ ♦ Although it is not required, the argument of a copy constructor is often a const reference. This has two effects: • The copy constructor cannot change the value of the argument (the argument has been declared as const and, hence, it is read-only). • It is possible to call the copy constructor with a const argument, like in the following example: const Date d(10,12,1980); Date d2(d);

- 66 - CHAPTER 2: SPECIAL MEMBERS

The copy constructor can be called in two different ways:

Date d1(10,10,1990); Date d2(d1); //Call to the copy constructor of Date //(d2=10-10-1990)

Date d3=d1; //This is also a call to the copy constructor //of Date (d3=10-10-1990)

Default copy constructor If the copy constructor is not supplied by the programmer, the compiler will provide one by default. This default copy constructor copies all the attributes of the argument to the object which is being created. However, if some parts of the structure of the object which is passed in the argument have been allocated dynamically, then the default copy constructor may not work as expected. In particular, the newly created object and the argument of the copy constructor may share their identity as it is shown in figure 2.2, and this may have unpleasant consequences (a change made on a1 may modify a2 too).

Figure 2.2: Sharing of identity induced by the default copy constructor

Consider the following example: Example class Person{ char* name; int age; char sex; public: Person(); Person(char* pname, int page, char psex) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; }

- 67 - CHAPTER 2: SPECIAL MEMBERS

void setName(char* pname)... void setSex(char psex)... void setAge(int page)... //...... };

Figure 2.3: Sharing of identity induced by the default copy constructor

The class Person does not have a user-defined copy constructor. The compiler provides a default copy constructor with the behaviour shown in fig. 2.3. As a result, the following program will destroy the attribute name of p: int main() { Person p2("John",20,'m'); Person p(p2);

p2.setName("Ann"); .... }

This would not happen if a copy constructor would have been defined by the class designer: Person::Person(const Person& p){ age=p.age; sex=p.sex; name=new char[strlen(p.name)+1]; strcpy(name, p.name); } ♦ ♦ ♦

- 68 - CHAPTER 2: SPECIAL MEMBERS

We will encounter the same problem in section 2.4.1 (overloading of the assignment operator). Furthermore, the issue of sharing identities of objects is studied in more detail in Chapter 4. C++ programs use copy constructors mainly in the following contexts: • Explicit creation of an object with the copy of another one, already created This is the normal usage of copy constructors in declarations.

Date today(10,12,2000,err); Date d(today);

• Argument passing (by value) An argument of a class C passed by value to a function f is copied to that function using the copy constructor. void f(Date x) {....} int main() { Date a(....); ...... f(a); }

In this example the copy constructor of the class Date is used to copy the value of a onto x (i.e., internally, the following operation is called: Date x(a);) • Objects returned by functions If a function returns an object x of class C, a temporal object of class C is created and initialized with the state of x by means of a call to the copy constructor of C.

Date f() {Date x; ..... return x; } int main() { Date a; ..... a=f(); }

- 69 - CHAPTER 2: SPECIAL MEMBERS

In this example, the object x of class Date returned by f() is copied into a temporal object by means of the copy constructor of the class Date. This temporal object is copied to the object a by means of the = operator (more details in 2.1.7). This process is illustrated in figure 2.4. Notice that if the function returns a reference to an object, then there is no need of copy constructor, since no new object is created.

Figure 2.4: Creation of a temporal object with the copy constructor

• Initializers in class aggregation Sometimes one or more attributes of a class A is itself an object of another class B. class B{ .... }; class A{ B x; ..... };

When this happens, the creation of an object of class A involves the creation of another object of class B and this may be achieved by means of a call to the copy constructor of the class B. This issue will be presented more thoroughly in Chapter 4. • Exception handling This topic will be presented in Chapter 7.

- 70 - CHAPTER 2: SPECIAL MEMBERS 2.1.6. Construction call in the creation of arrays of objects An array of objects is created in the usual form: Date adates[10]; This creation involves: • The creation of the array itself. • The creation of each object that composes the array. This is achieved by means of a call to the default constructor of the class of the array components. That is, the sentence Date adates[10]; generates ten calls to the default constructor of the class Date. Notice that it is necessary that the class Date has a default constructor defined.

2.1.7. Creation of temporary objects In the last few sections we have presented the use of constructors in declarations (i.e., sentences which declare variables as instances of some class):

Date today(10,9,2001,err);

The same constructor could have been used in a different way:

Date today; today= Date(10,9,2001,err);

However, in this case, the process of construction of the object today is different. In particular, it involves the creation of a temporary object. The process is as follows: 1. Creation of an object called today of the class Date using the default constructor. 2. Creation of a temporary object of the class Date which has no identifier associated with the value 10-9-2001. 3. Call to the assignment operator (=) which assigns the temporary object to today. Other situations which involve the creation of a temporary object include the following: • Call to a constructor to create an actual parameter within the call to a function

- 71 - CHAPTER 2: SPECIAL MEMBERS

void f(Date d) {.....} int main() { .... f(Date(10,9,2001,err)); }

A temporary object, invisible to the programmer, is created with the value 10-9-2001 (let us call temp to this temporary object, although it has no identifier in the program). The object d (the argument of the function f) is created with a copy of temp using the copy constructor of the class Date (i.e., an implicit call is done to Date d(temp)). • Return of an object by a function

Date f() { Date d(10,11,2001,err); .... return d; }

In this case, a temporary object (e.g., temp) is created by means of a call to the copy constructor of the class Date with argument d (Date temp(d)). temp is the object returned by the function f and is invisible to the programmer. This object may be assigned to another by using the assignment operator: today=f();

2.1.8. Dynamic object construction Apart from automatic objects (whose construction has been presented above), another big family of objects do exist: dynamic ones (see Chapter 1). Let us now present how the constructors are called in dynamic object creation. Consider the following code:

Date* pd1; pd1=new Date(1,1,2000,err); pd1 is a pointer that may refer to an object of class Date. However, it points to an undefined place when it is created: Date* pd1;.

- 72 - CHAPTER 2: SPECIAL MEMBERS

The operator new creates a new object of class Date dynamically (by means of a call to the constructor of the class Date with four arguments) and returns a pointer to this newly created object (see figure 2.5). The operator new may be called without parameters. In particular, the sentence: pd1 = new Date; creates dynamically a new object of class Date by means of a call to the default constructor of the class.

Figure 2.5: Dynamic creation of an object

2.1.9. Construction of dynamic arrays of objects The operator new can be used to create arrays of objects dynamically. For instance, the following code creates an array of 100 dates. This creation is made dynamically by calling to the default constructor of the class Date.

Date* vd; vd=new Date[100];

Some remarks are worth noticing: • As a result, the size of the array may be selected at execution time: void f(int n) { Date* vd=new Date[n]; }

Recall that the following is not correct, since the dimension of an automatic array must be a constant expression. void f(int n) { Date vd[n]; //INCORRECT. DIMENSION SHOULD BE //A CONSTANT EXPRESSION }

- 73 - CHAPTER 2: SPECIAL MEMBERS

• The class of the array components (in this example, Date) must have a default constructor. Otherwise, it is not possible to create each one of the array components.

2.1.10. An example

#include using namespace std; class Person{ char* name int age; char sex;

public: Person(){} Person(char* pname, int page, char psex) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; } void setName(char* pname){...} void setSex(char psex){...} void setAge(int page){...} //...... }; int main() { Person* vp;

vp=new Person[5]; //this generates 5 calls to the //default constructor

vp[0].setName("John"); vp[0].setAge(20); vp[0].setSex('m'); //..... }

Figure 2.6 shows graphically the results of the program.

- 74 - CHAPTER 2: SPECIAL MEMBERS

Figure 2.6: Creation of a dynamic array of Person

2.2. Destructors

Definition (Destructors) Destructors are class operations which are responsible for deallocating objects of that class when those objects are not to be used anymore.

2.2.1. Why are destructors necessary? The garbage As it has been presented in Chapter 1, it is possible, usual and beneficial to create dynamic objects (i.e., using the new operator). When these objects are not useful anymore, they may become garbage.

Definition (Garbage) We call ’garbage’ to a memory space which has been reserved dynamically (by means of the new operator), which has become unreachable and which cannot be reallocated.

In order to explain this definition, we give two examples: Example

Date* p; Date d1(10,12,2000,err); //(1) p=new Date(1,1,2001,err); //(2) p=new Date(4,4,2003,err); //(3) p=&d1; //(4)

- 75 - CHAPTER 2: SPECIAL MEMBERS

In this example, instructions (3) and (4) generate garbage (see fig. 2.7): (3) A new object of class Date is allocated dynamically with the state 4-4- 2003. p points to this new object. As a consequence, the Date object to which p pointed before (the one with state 1-1-2001) has become unreachable. (4) Now, p points to the automatic object d1. Again, the dynamic object created at (3) becomes unreachable. Therefore, both dynamic objects created in this example have become unreachable in the end. Both dynamically created objects have become garbage.

Figure 2.7: An example of garbage creation ♦ ♦ ♦ The previous example may make us think that garbage can only be generated if we create dynamic objects by calling explicitly the operator new. However, this operator may also be called implicitly when an automatic object is created. We present an example next: Example class Person{

char* name int age; char sex;

public:

Person(char* pname, int page, char psex) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; }

- 76 - CHAPTER 2: SPECIAL MEMBERS

//...... };

void f() { int i=1; Person p("John",20,'m'); //(1) .... }

The execution of the function f() creates an automatic object p of class Person. No dynamic object seems to have been created. However, the Person constructor allocates dynamically an array of characters. When the execution control reaches the end of the function f(), the non-dynamic part of p is deallocated automatically in the same way as the integer local variable i is deallocated. You have probably guessed that the dynamic part of p (i.e., its name) will remain in the memory as garbage (no pointer will refer to it). This process is shown in figure 2.8.

Figure 2.8: An example of garbage creation involving an automatic object ♦ ♦ ♦

The ecologists (system managers) warns us about the serious problems caused by the destruction of the environment (the memory) in our lives (our programs). As a consequence, they advocate for a policy of recycling and posterior reuse of the generated garbage. This policy can be carried out following two different strategies: 1. Periodically, a process called garbage collector is launched with the responsibility for detecting and deallocating all the garbage existing in the memory. This policy is followed by some O.O. programming languages (like Java or ). The problem of this approach is that the system performance decline as a consequence of the periodical execution of the garbage collector. 2. It is the programmer who deallocates the dynamic memory which is not to be used anymore just before becoming garbage. This strategy is far more efficient but it is also lower level since the programmer cannot abstract the memory management.

- 77 - CHAPTER 2: SPECIAL MEMBERS

C++ adopts an intermediate strategy: the programmer deallocates herself/himself the objects which are not to be used anymore by programming a destructor for the class of that object. However, the programmer will not have to call this destructor explicitly. It will be called automatically and implicitly whenever the object life finishes. In the following sections we will learn how C++ deals with destructors and we will show some examples of each one of the previous situations.

2.2.2. The operator delete Before presenting destructor operations in C++, we should talk about the delete operator, which will be used by the former. This operator deallocates a chunk of memory that has been allocated dynamically by using the new operator. int* pi; pi=new int; //(1) delete pi; //(2)

Sentence (2) deallocates the integer created dynamically by (1). Without it, this integer would have become garbage. The operator delete can also be used to deallocate an dynamic array in the following way: int* pi; pi=new int[13]; //(1) delete [] pi; //(2)

The compiler records how many integers it has allocated by the instruction (1). The sentence (2) deallocates that number of integers starting at the integer pointed to by pi.

2.2.3. Destructors in C++

Definition (Destructor in C++) In C++ a destructor for a class C is an operation of the class C such that: • It has the same name as the class preceded by ’~’ (~C) • It takes no argument and returns no value

- 78 - CHAPTER 2: SPECIAL MEMBERS

• The destructor operation of the class C is called implicitly in two situations: • At the end of the scope of an automatic object, instance of the class C. • When the operator delete is called to deallocate an object of class C that was created dynamically. That is, the operator delete called to deallocate an object of class C calls implicitly the destructor of C. • The destructor of a class C can also be called explicitly on the object x in the following way: x.~C();

We have stated that the destructor of the class C is called when the scope of an automatic object of class C (e.g., called x) is about to end. This may happen, among others, in the following cases: • x is a local variable in the function f(..) and the execution control has reached the end of f(..). • x is a passed-by-value argument of the function f(C x....) and the execution control has reached the end of f(..). • x is an aggregated object of another object y of class D and y is being destroyed (this is presented in Chapter 4). We remark two additional aspects concerning destructors: • There can only be one destructor per class • If the class definition does not provide any destructor, the compiler provides a default one, void. Example In this example we show how the class Person could add a destructor in order to deallocate the attribute name. class Person{ char* name int age; char sex; public: Person(char* pname, int page, char psex) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; } ~Person() { delete [] name; } //...... };

- 79 - CHAPTER 2: SPECIAL MEMBERS

void f() { Person p("John", 20, 'm'); .... }

When the execution control reaches the end of f() the destructor of Person is called automatically. This destructor is responsible for deallocating the piece of memory allocated by the operator new of the constructor ( see (1) in fig. 2.9). After this implicit call, the non-dynamic part of the object p (see (2) in figure 2.9) is also deallocated in the same way as any other local variable.

Figure 2.9: Destructors for automatic objects ♦ ♦ ♦

2.2.4. Destruction of dynamic objects In this section we present an example which will illustrate how the operator delete calls implicitly the destructor of a class. Example Consider the class Person with the definition that we gave before. However, in this case we will create a dynamic object of this class. void f() { Person* pp;

pp=new Person("John", 20, 'm'); //(1) .... delete pp; //(2) }

- 80 - CHAPTER 2: SPECIAL MEMBERS

Sentence (1) allocates dynamically an object of class Person by calling its constructor. In particular, this constructor allocates an array of characters for the attribute name. Sentence (2) deallocates that object in the following way: • It calls the destructor of the class Person. This destructor is responsible for deallocating the array of characters name. • It frees the rest of the memory of the dynamic object. Notice that if the destructor of Person had not been defined, the array of characters which the attribute name points to would have remained as garbage, even after the deleting sentence (2) (see fig. 2.10).

Figure 2.10: Destructors for dynamic objects ♦ ♦ ♦

2.2.5. Destructors and sharing of identity: warnings Destructors should be used carefully for a number of reasons. One of them is the destruction of an object which shares part of its identity with another one. In this case, the destruction of one of them may lead to the destruction of the other. Consider the following example which uses, again, the class Person in the same way as it has been defined previously. Example int main() {

Person p("anne",20,'f'); //(1) Person* p2;

p2=new Person("John",20,'m'); //(2)

- 81 - CHAPTER 2: SPECIAL MEMBERS

*p2=p; //(3)

delete p2; //(4) }

Fig. 2.11 shows the situation after the execution of the instructions (1), (2) and (3). Notice that p and *p2 shares a part of its identity (the array of characters pointed to by name). Fig. 2.11 also shows what happens after the deletion of *p2: p also loses the array pointed at by name.

Figure 2.11: Destructors for dynamic objects with identity sharing

♦ ♦ ♦

- 82 - CHAPTER 2: SPECIAL MEMBERS

This example shows that the sharing of identity between objects may have harmful consequences. If it is required for a specific application, we should work cautiously in order to avoid loss of information. Last, in the presented example, we could have avoided the problem overloading the assignment operator (’=’) so that it copies also the dynamic part (name) of the object. Section 2.4 is devoted to operator overloading.

2.3. Friend functions and classes Friend functions are functions that violate one of the most important principles of object-orientation: the inaccessibility to the private part of a class from a function which is not a member of that class.

Definition (Friend function) A friend function of the class C is a function f(..) such that: • f(...) is not a member of the class C • However, f(...) has access to the private part of C Friend functions are declared as in the following example:

Example class C { private: int x public: ..... friend void f(...); }; void f(...) { C c; c.x=10; }

Notice that within the implementation of f(...) it is possible to access x, which belongs to the private part of C. ♦ ♦ ♦

In a similar way, it is possible to define friend classes:

- 83 - CHAPTER 2: SPECIAL MEMBERS

Definition (Friend class) A friend class F of the class C is a class such that the implementations of the F operations have the right to access the private part of C The following example shows how friend classes may be declared: class C{ private: int x public: ..... friend class F; }; class F{ public: void opf(){C c; c.x=10;}

};

Friend classes and functions violate one of the most important rules of object orientation. Thus, they should not be used unless it is absolutely necessary. In general the use of friend functions and classes will be restricted to the following cases: • If a couple of classes are to be designed so that they are mutually dependent (both at specification and at implementation levels). The visibility of the other class that these classes require may not be naturally achieved by means of public operations. In that case, each class can be made friend of the other. An example of this situation is the class List, which models a sequence of elements and the class ListIterator, which models a way of traversing the list and getting its elements. This situation may also happen between a class and a function, which is not a member of the class but it depends on it. In that case, the function may be declared as a friend of that class. • To overload some operators. In the following few sections we will discover that several operators may be overloaded by means of a friend function, while some others can be overloaded exclusively by means of a friend function. For this reason we have introduced friend functions and classes right before presenting operator overloading.

- 84 - CHAPTER 2: SPECIAL MEMBERS 2.4. Operator overloading Some operations, like comparisons, assignments, object input/output, etc. are useful for many types: both to predefined types (int, float, etc.) or to user defined classes (Person, Date, etc.). Therefore, it will be usual that in the definition of a new class we find the definition of such operations necessary or, at least convenient, the definition of such operations. Two approaches can be taken to deal with this situation: • To create a new operation with a new name (e.g., assignPerson(...)) • To create a new operation which has the name of the operator which is traditionally used to carry out that functionality (e.g., =). This is called operator overloading. If the second approach is taken we will have the same set of operators that will be applied to different classes. Therefore we will have a higher degree of standardization in class operations. When we use a class, if a certain operator (e.g., assignment operator) is meaningful for that class, we can expect to have the operator = overloaded for it. Operator overloading is a special case of function overloading. In previous sections we have overloaded the constructor operations.

Definition (Function overloading) To overload a function consists in defining a new function which has the same name as another existing one. The arguments of the new function will be different (in number or type or both) from the already existing one. Operators are a specific case of function that can be overloaded. In general, the compiler is responsible for deciding at compilation time which function definition should be associated to a call to an overloaded function (early binding). This decision depends on the type of the object on which the function is called and/or the type of its arguments. However, in the case of polymorphic functions (see Chapter 6), the compiler cannot decide the function definition that should be bound to each call. In this case, the binding is performed at execution time (late binding).

Function overloading (and also late binding) are the basis of polymorphism, which is a very important concept of O.O. programming and is presented in Chapter 7. In the following few sections we will show how to overload the most frequent C++ operators (=, ==, (), [], <<, >>).

2.4.1. Operator = The assignment operator (’=’) is used to assign an object of a given class to another one (of the same class). It works in the following way:

- 85 - CHAPTER 2: SPECIAL MEMBERS

Person p1("John", 20, 'm'); Person p2; p2=p1; //(1):Assignment

The statement (1) applies the assignment operator to the object p2 using p1 as argument. As a result, the state of p2 becomes ("John", 20, ’m’). The assignment operator is generated by default by the compiler. That is, when a class is defined, the assignment operator can be applied to its objects. However, it is likely that the default definition does not suit our interests. The default definition of the assignment operator copies the non-dynamic parts of the object, but it does not make a copy of its dynamically generated parts (recall that in section 2.1.5, we mentioned that the same situation happens with the default copy constructor). Example The default assignment operator for the class Person does something similar to the following: const Person& operator=(const Person& p2) { name=p2.name; sex=p2.sex; age=p2.age;

return *this; }

Figure 2.12: An assignment definition

- 86 - CHAPTER 2: SPECIAL MEMBERS

As a consequence, the assignment p=p2; leads to the situation shown in fig. 2.12(a), in which the objects p and p2 share a part of their identity. Successive changes to the name of p or p2 will lead to a change in the name of the other object or, even worse, to an information loss. We came across this very problem when talking about the default copy constructor (section 2.1.5). Chapter 4 studies the problem of sharing of identity more thoroughly. If we want to avoid the situation shown in the previous example, we may overload that operator in the following way:

class Person{ //....as before..... public: //.....as before.... Person& operator=(const Person& p) { delete [] name;

name=new char [strlen(p.name)+1]; strcpy(name,p.name); age=p.age; sex=p.sex;

return *this; } }; ♦ ♦ ♦ Notice the following aspects in the example: • We delete the previous contents of the attribute name, so that we do not generate any garbage when we assign to the object the new name. • We avoid the identity sharing by replicating the dynamic attribute (name). Both objects do not share it anymore (see fig. 2.12 (b) ). • By forcing that the argument p is constant, we make it read-only and, thus, it cannot be modified within the operator definition. • By forcing that the result of the operator is not void but a reference to Person we imply two things: - We can compose assignments or apply another operation directly to the result of an assignment: p1=p2=p3; //O.K. This would have not been correct if the return type had been void. - The (temporary) object we are returning is not a copy of the object that receives the assignment but just a reference (alias) to it.

- 87 - CHAPTER 2: SPECIAL MEMBERS

• Since the returned reference is const we cannot modify it. For instance, if a new operation setAge(int) were defined in the class Person with the obvious meaning, the following sentence would generate a compilation error: (p=p2).setAge(90); //(1) However, if the header of the assignment operator had been: Person& operator=(const Person& p) then, the call (1) would have been correct. Copy constructor and operator = It is important to state clearly the difference between the copy constructor (e.g., Person(const Person&) and the assignment operator (e.g. Person& operator=(const Person&)). • Copy constructor. It creates a new object with the same state as its only argument. The copy constructor is used mainly in: 1. Creation of new objects in declarations Person p(p2); or Person p=p2; 2. Pass-by-value of arguments 3. Function return 4. Initializers in class aggregation 5. Exception handling Uses 1, 2 and 3 are explained in 2.1.5. The issue of initializers is presented in Chapter 4 and Chapter 6. Exception handling is shown in Chapter 7. • Assignment operator. It copies the value of the argument object to an already existing one (i.e., it does not create a new object). Since it does not create a new object but modifies an existing one, this operator may generate garbage. For this reason, it should delete any dynamic data which is not used anymore referred to from the modified object. This operator is used whenever an object is assigned to another using = (except for the declarations: Person p=p2;, which use the copy constructor).

2.4.2. Operator == Example This is the proposal of equality operator for the class Person:

bool operator==(const Person& p) const { return (strcmp(p.name, name)==0 && p.sex==sex && p.age==age); }

- 88 - CHAPTER 2: SPECIAL MEMBERS

Notice that, since this operator is not supposed to modify the state of the object to which it is applied, it has been labelled const. ♦ ♦ ♦ Since the application of this operator is symmetric (i.e., both compared objects play a similar role in the comparison) it may not be natural to apply the operator to one of them. We can think of overloading the == operator by means of a friend function: class Person{ //.... public: //..... friend bool operator==(const Person& p, const Person& p2); }; bool operator==(const Person& p, const Person& p2) { return (strcmp(p.name, p2.name)==0 && p.sex==p2.sex && p.age==p2.age); }

Notice that, since the friend function is not a class member, it needs two arguments.

2.4.3. Operator [] This operator is often used to access the elements of a container class by index (e.g., to access the first, the second, ... the nth element of the container). In the following example we define a class IntVector to encapsulate and improve arrays of integers. Example class IntVector{

int* v; int max; public: //......

int operator[](int i){ if (i>=0 && i

- 89 - CHAPTER 2: SPECIAL MEMBERS

IntVector ivec(MAX);

n=ivec[2]; //Call to the [] operator }

In Chapter 7 we present this example generalized and in detail. ♦ ♦ ♦ Other types can be used either as argument or as return types. For instance: Example class Dictionary{ //...... public: //.... char* operator[](char* d){

//access to the word d of the //dictionary and return its meaning } }; int main() { Dictionary d;

cout <<"The meaning of onion is" <

2.4.4. Operator () This operator is used to encapsulate algorithms within classes. Consider the following example: Example class SortAlgorithm { public: void operator()(int v[], int n){ //Algorithm to sort the array of integers v[0..n] } }; int main() { SortAlgorithm sort; const int N=100; int w[N]; fillWithInts(w);

sort(w,99); //Call to the operator () }

- 90 - CHAPTER 2: SPECIAL MEMBERS

In this example we encapsulate the algorithm to sort an array v of n integer values (0..n-1) within the class SortAlgorithm by means of the operator (). Then we may create an object sort of the class SortAlgorithm and an integer array w. This array can be sorted just with the call: sort(w,99); which corresponds to a call to the operator () on the object sort and with arguments w and 99: sort.operator()(w,99) //DO NOT USE IT THIS WAY, //USE IT: sort(w,99); ♦ ♦ ♦

2.4.5. Operators << and >> This operators are meant to send an object to an output stream (<<) and to get an object from an input stream (>>). We can outline them here giving the example of overloading the operator << for the class Person. Example class Person{ //.... public: //..... friend ostream& operator<<(ostream& c, const Person& p2); }; ostream& operator<<(ostream& c, const Person& p) { c<<"name: "<

return c; } ♦ ♦ ♦

The fact that an ostream object is returned helps to chain this operator:

Person p1(...); Person p2(...); Person p3(...); cout<

- 91 - CHAPTER 2: SPECIAL MEMBERS

If c were not returned, then the previous program should have been implemented like this: cout<

Notice that this operator is always overloaded as a friend function. It cannot be overloaded as a class operation.

2.5. What operations any C++ class should offer The C++ compiler associates to any user-defined class the following operations: default constructor, copy constructor, assignment operator (=) and destructor. However, these operations are defined by the compiler in a trivial way. Usually, a customized definition of these operations will be needed. It is a good C++ programming practice to provide an implementation of these four operations for most defined classes.

2.6. Guided problems. The class MyString In this section we add constructors, destructor and overloaded operators to the class MyString which has been developed in Chapter 1. The idea is to substitute: • The constructors for the create operations • The overloaded operators ==, =, <= for equals, create and lowerOrEqual and to add: • A destructor • the overloaded operator << • The overloaded operator + to provide another implementation for concat(..) In the following section the result is presented:

- 92 - CHAPTER 2: SPECIAL MEMBERS

2.6.1. Solution: File MyString.h

#ifndef CADENA_H #define CADENA_H

#include using namespace std; class MyString{ char* st; public:

MyString(); MyString(char st[]); MyString(const MyString&);

char getIthChar(unsigned int pos, bool& err) const; unsigned int getLength() const; unsigned int substring(const MyString& c) const; bool operator==(const MyString& c) const; MyString& operator=(const MyString& c); bool operator<=(const MyString& c) const; MyString& operator+(const MyString& c); void concat(char* c);

friend ostream& operator<<(ostream& c, const MyString& ca); void get(istream& c,char test); };

#endif

2.6.2. Solution: File MyString.cpp

#include "MyString.h" #include #include using namespace std;

MyString::MyString() { st=new char[1]; st[0]='\0'; }

MyString::MyString(char pst[]) { int i=0;

st=new char[strlen(pst)+1]; strcpy(st,pst); }

- 93 - CHAPTER 2: SPECIAL MEMBERS

MyString::MyString(const MyString& c) { int i=0;

st=new char[c.getLength()+1]; for(i=0;i<=c.getLength();i++){ st[i]=c.st[i]; } }

MyString::~MyString() { delete [] st; }

char MyString::getIthChar(unsigned int pos, bool& err) const { if (pos

unsigned int MyString::getLength() const { return strlen(st); } unsigned int MyString::substring(const MyString& c) const {

///EXERCICE: IMPLEMENT IT return 0; } bool MyString::operator==(const MyString& c) const { int i=0; int l; l=getLength(); if (c.getLength()!=l) return false; else{ while (c.st[i]==st[i] && i

- 94 - CHAPTER 2: SPECIAL MEMBERS

MyString& MyString::operator=(const MyString& c) {

int i=0;

delete[] st;

st=new char[c.getLength()+1]; strcpy(st,c.st);

return *this; } bool MyString::operator<=(const MyString& c) const { int i=0; while (st[i]==c.st[i] && st[i]!='\0'){

i++; }

return (st[i]<=c.st[i]); }

MyString& MyString::operator+(const MyString& c) { int i,j,l1,l2;

MyString* aux;

aux=new MyString;

l1=getLength(); l2=c.getLength(); aux->st=new char[l1+l2+1]; for (i=0;ist[i]=st[i]; } for (j=0;jst[i]=c.st[j]; i++; } aux->st[i]='\0';

return *aux; }

void MyString::concat(char* c) { int i,j,l1,l2;

char* aux;

aux=new char[getLength()+strlen(c)+1]; i=0;j=0;

- 95 - CHAPTER 2: SPECIAL MEMBERS

while (st[i]!='\0'){ aux[i]=st[i]; i++; } while(c[j]!='\0'){ aux[i]=c[j]; j++; i++; } aux[i]='\0'; delete [] st; st=aux; } ostream& operator<<(ostream& c, const MyString& ca) { c<

delete [] st; st=new char[1]; st[0]='\0';

while (!end){ c.get(aux[0]); i=0; while (aux[i]!=test && i

if (aux[i]==test){ aux[i]='\0'; end=true; } else aux[i+1]='\0';

this->concat(aux); } }

Remarks: • In the operator =, we avoid garbage by deallocating the previous value of the object (delete [] st;). • The header MyString& operator=(const MyString& c); is the standard C++ way to overload this operator. The const reference parameter allows calls of the sort:

- 96 - CHAPTER 2: SPECIAL MEMBERS

MyString s=MyString("hello"); The fact that the operator returns a MyString object allows: s1=s2=s3;

2.6.3. Solution: File userString.cpp

#include "MyString.h" int main() { MyString ss1, ss3; MyString ss2("kjkj");

ss3.get(cin,'\n'); MyString ss4(ss2); ss1=ss2+ss3;

cout<

if (ss1<=ss1) cout<<"ok"<

if (ss2<=ss1) cout<<"ok"<

if (ss1==ss1) cout<<"ok"<

if (ss1==ss3) cout<<"error"<

if (ss3==ss1) cout<<"error"<

bool err; cout<

return 0; }

- 97 -

- 98 -

Chapter 3 Generic classes and functions

3.1. Generic classes Consider the classes ListOfIntegers and ListOfCharacters4 : class ListOfIntegers{ int v[N]; int top; public: ListOfIntegers(){top=0;} void insertLast(int x){v[top]=x; top++;} void removeLast(){top--;} int getLast() const {return v[top];} bool emptyList() const { return (top==0);} }; class ListOfCharacters{ char v[N]; int top; public: ListOfCharacters(){top=0;} void insertLast(const char x){v[top]=x; top++;} void removeLast(){top--;} int getLast() const {return v[top];} bool emptyList() const { return (top==0);} };

Their specification and implementation are exactly the same, except for the fact that in one case, the elements stored in the list are integers and, in the other case, they are characters. That is, we have found out that the behaviour of a list (insert an element, remove an element, retrieve an element, etc.) does not depend on the type of elements that constitute the list. The type of elements that may be contained in a list are virtually infinite. Each application may need a list which stores different kinds of components. Therefore, we could end up with dozens of different classes List: ListOfIntegers, ListOfCharacters, ListOfStudents, ListOfCinemas, ListOfWhatever... which would be essentially equivalent.

4This is a very basic implementation. Errors coming from list overflow or underflow have not been taken into account

- 99 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

Something similar occurs with any class that models a collection of elements. A clear improvement to this situation may be to define a generic class List which describe the behaviour of a list independently of the type of the elements that constitute it. The particular type of these elements will be a parameter of the generic class List and will be given when an object list is created. This can be done in C++ by means of the so called template classes, as is shown in the following example: template class List{ T v[MAX]; int top; public: List(){top=0;} void insertLast(const T& x) {v[top]=x; top++;} void removeLast(){top--;} T getLast() const {return v[top];} bool emptyList() const {return top==0;} };

This is a template class definition. It defines a template class List which depends on the type parameter T (called template parameter). This parameter will be instantiated when a specific list object is created (see below). Notice that the private part of the class definition declares an array of MAX elements of generic type T (instead of int or char, as we did in the last couple of examples). The same happens in the header of the operations insertLast(const T& x) and T getLast();. A template class definition, as the previous one (List) can be instantiated in the following way: class Student{ //... public: Student(char* pname, char* pid, char* paddress){...} //... }; int main() { List lintegers; List lchars; List lstud;

Student s("Joe", "123987E", "12, Barbecue Street");

lintegers.insertLast(1); lchars.insertLast('d'); lstud.insertLast(s); ..... }

- 100 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

Three list objects have been created. One of them (lintegers) will hold integer objects, another (lchars) character objects and the last one (lstud), objects of the class Student. Each one of them has been created by a different instantiation of the template parameter (T) of the template class List. An important point is the following one:

The class List (or List) is not an actual class but just a template class (that is, an instruction manual on how to create a real class). The real class will be created when such template class is instantiated (i.e., when its template parameter T is substituted by an actual type). Each specific instantiation will yield a different class. In the last example three different classes were created: List, List and List Notice that the declaration: List lchars; not only does it create an object (lchars) but also a class (List).

We summarize all these issues in the following definition:

Definition (Generic class) A class A is generic if its definition depends on one or more types (usually, classes) that act as parameters of A. A generic class is instantiated when the type parameter is substituted by an actual type.

C++ offers a tool called templates to define generic classes (and functions: see section 3.2).

Definition (Template class definition) A template class definition is a class definition which depends on one or more parameter types (called template parameters). This template class definition is used in order to create an actual class by instantiating the template parameter with a specific type when an object instance of that class is created. An actual class A does not exist until its template parameter has been instantiated. The following notation is used:

- 101 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

• Definition: template class A{ //The definition of A may depend on T }; • Instantiation: A a; // The template parameter T has been // instantiated with int

3.1.1. Implementation of operations out of the template class definition

In the previous example we have implemented the class operations within the class definition. However, they could have been implemented outside, using the following notation: template class List{ T v[MAX]; int top; public: List(); void insertLast(const T& x); void removeLast(); T getLast() const; bool emptyList() const; }; template List::List(){top=0;} template void List::insertLast(const T& x) {v[top]=x; top++;} template void List::removeLast(){top--;} template T List::getLast() const {return v[top];} template bool List::emptyList() const {return top==0;}

Notice that this form of operation definition considers those operations themselves as template operations. We will show more things about this idea in section 3.2.

- 102 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS 3.1.2. Templates with more than one template parameter and non-type parameters

A template class may have more than one template parameter and also, it may have parameters which are not types. Let us consider an example: Example We want to define a class called MyVector to store as many as N elements of a type T. We decide to use a template and, furthermore, to make the vector capacity a template parameter. template class MyVector{

T v[N]; public:

MyVector(){} T& operator[](unsigned int i){ if (i vint; MyVector vstud;

vint[0]=3; //.... }

In this example, the template class MyVector has two template parameters: the type of the vector components (T) and the capacity of the vector (N). Therefore, it is possible to define a vector vint which can store as many as 100 integers and another one vstud with a capacity of 300 students. ♦ ♦ ♦

3.2. Generic functions In addition to classes, functions can also be generic. A generic function is a function definition which depends on one (or more) template parameters (usually types). These parameters are instantiated when the function is called.

- 103 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

An example of how template functions may be useful is the following sort algorithm: template void sort (T v[], int n) { int i, j, min, aux; i=0; while (i

sort(x,10); //(1) sort(z,11); //(2) //..... }

• In the case (1), the template parameter T is instantiated with int since the argument of the function sort is an array of integers. At the point (1) , the compiler creates a function called sort in which the occurrences of T have been substituted for int. Notice that the function is created only when it is called; not before. • On the other hand, in the case (2), the template parameter T is instantiated with char. In the same way as before, it is at point (2) when the compiler creates the function sort following the instructions contained in the template function sort

Notice that the code of the sort(..) function uses the operator < to compare two elements of the array. This forces the fact that any type that instantiates the template parameter T should have the operator < overloaded (and this information should come up in the specification of the sort function). Therefore, the following code would be incorrect if the class Student had not the operator < defined:

- 104 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS class Student{ char* name; char* id; //.... }; int main() { Student s[11];

fill(s); sort(s,10); //..... }

It would be correct if an operator like the following was defined for the class Student: bool operator<(Student& s1, Student& s2){

//Return true if s1 is lower than s2 according //to the defined criteria (e.g., names or id). }

Observation: The specification of a template function or class should contain any special requirements that are to be applied to the instantiations of the template parameters.

Example template void sort (T v[], int n)

Pre: v[0..n] is initialized Post: v[0..n] contains the same values as v@pre[0..n] and v[0..n] is sorted ascendently Remarks: The type that instantiates T must have the operator bool operator<(T& x, T& y) defined (either as a friend function or as a class operation). ♦ ♦ ♦

- 105 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS 3.2.1. Incorporating functions as template parameters Would it be possible to use the sort(..) function to sort an array of students by both criteria (first, by name and then by id)? This is possible by adding another template parameter: the comparing function. This is the procedure that we should follow to do that: 1. We define a class that overloads the operator () for each criterion by which we want to compare two students (e.g., name and id). class CompaNames{ public: bool operator()(Student& s1, Student& s2){ return strcmp(s1.getName(),s2.getName())<0; } }; class CompaIds{ public: bool operator()(Student& s1, Student& s2){ return strcmp(s1.getId(),s2.getId())<0; } };

These classes can be used in the following way: int main() { bool b1, b2; CompaNames lower1; CompaIds lower2;

Student s1("Joe", "92345EKG"); Student s2("Peter", "11144RRR");

b=lower1(s1,s2); //b=true (since "Joe"<"Peter") b2=lower2(s1,s2); //b2=false (since "92345EKG">"11144RRR") //..... }

Notice that an object of the class CompaNames or CompaIds can be used as if it was a function call: lower1(s1,s2). The instruction lower1(s1,s2) generates a call to: bool CompaNames::operator()(Student& s1, Student& s2) 2. Redesign the template function sort(...) adding a template parameter that will be instantiated with a class that has the bool operator()(T&,T&) overloaded (like CompaNames and CompaIds). The object used to compare two elements should be provided too:

- 106 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

template void sort (T v[], int n, Comp& lower) { int i, j, min, aux;

i=0; while (i

CompaNames lower1; CompaIds lower2; Student st[11];

fill(st);

sort(st,10,lower1); //(1):Sorts st by name

sort(st,10,lower2); //(2):Sorts st by id }

In the case (1) the template parameter Comp is instantiated with CompaNames (since this is the type of lower1). In the case (2), Comp is instantiated with CompaIds). Notice that the specification of the template function sort(...) should state that the class that instantiate the parameter Comp should overload the operator: bool operator()(T&,T&). The function sort(...) is a very primitive form of the so called generic algorithms, that is, algorithms which provide a functionality (such a search, a traversal, a sorting...) that can be applied to many different collection of elements (in this case, the sort(...) function can be used to sort an array of many component types using many different criteria to compare elements. Template functions, together with iterators, are very useful to design generic algorithms (see sections 8.3 and 8.4).

- 107 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS 3.2.2. Explicit instantiation of a template function Notice that in the previous cases, the compiler can deduce the type with which T should be instantiated. There exist situations in which this is not possible. Consider the following example:

template void f(int i) { x=new T;

//Do something with x

}

In this case, the template function should be instantiated explicitly:

int main() { f(); }

Example We may use this idea to simplify the sort template function. The third parameter of the sort template function (i.e., the object to call the comparing function) is a bit artificial. We can remove it, include a local variable Comp lower; within the function and use the call: sort(st,10); //(1):Sorts st by name Notice that the long call is now necessary since the compiler cannot infer the class that will be used for the object comparison. template void sort (T v[], int n) { int i, j, min, aux; Comp lower; i=0; while (i

- 108 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

int main() { CompaNames lower1; CompaIds lower2; Student st[11];

fill(st);

sort(st,10); //(1):Sorts st by name

sort(st,10); //(2):Sorts st by id }

3.3. File organization with templates A difficulty to work with templates is the following: Consider a file user.cpp which uses a template class A defined in a file a.h and with its operations defined in another file a.cpp (in the usual way): • a.h: template class A{ //.... public: void f(T p); //.... };

• a.cpp:

#include "a.h" template void A::f(T p){//....}

• user.cpp:

#include "a.h" int main() { A x; int i=0;

x.f(i); //.... }

- 109 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

When the file user.cpp calls a template function on the parameter x: x.f(i), the compiler should create an actual function (as it has been stated in section 3.2):

A::f(int i){....}

However, the instructions manual to create such function (i.e., the template definition of f) is not available to the compiler. Indeed, such template definition is located in another compilation unit: a.cpp, which will become available only at link time, when it is too late to create the function (since it is the compiler the responsible for doing so). As a result, this will not work. We suggest, as the easiest way to compile code with template definitions, including such definitions in the same compilation unit as the program in which they are called. In the previous example, this may be achieved by putting the implementation of the template function f(...) into the file a.h, which will be included by user.cpp: • a.h: template class A{ //.... public:

void f(T p); //.... }; template void A::f(T p){//....}

3.4. Guided problems: A generic stack 3.4.1. The problem 1. Implement a template class Stack to model generic stacks of component elements of type T. A stack is a data structure that follows a LIFO strategy to manage elements (i.e., last input, first output). We can imagine a stack as a pile of elements so that you insert elements at the top and you removes elements also from the top. The class Stack should provide, at least, the following operations: • Constructor that takes the stack capacity as parameter • Destructor • Push (to add an element of type to the top of the stack)

- 110 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

• Pop (to remove the uppermost element of the stack) • Top (to retrieve the uppermost element of the stack) • Operator << overload 2. Test the class Stack with T=int (stack of integers) and with T=MyString (stack of strings) 3. Implement the class Vehicle with the attributes brand, model and price class Vehicle{

char* brand; char* model; unsigned long price; public: Vehicle(){} Vehicle(char* pmodel, char* pbrand, unsigned long pprice) { //.... } ~Vehicle(){//....} friend ostream& operator<< <>(ostream& c, Vehicle& v); };

4. Create a stack of vehicles and check that the destructor of the class Stack calls the destructor of the class Vehicle

3.4.2. Solution: File Stack.h const int DEFAULTCAP=100; template class Stack{

T* t; int tops; int capacitys; public:

Stack();

Stack(unsigned int scap);

~Stack(); void push(const T& x);

void pop(); T* top1();

- 111 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

T& top2();

bool emptyStack(); int getSize(); int getCapacity();

friend ostream& operator<< <>(ostream& c, Stack& s); }; template Stack::Stack(){ t=new T[DEFAULTCAP]; tops=0; capacitys=DEFAULTCAP; } template Stack::Stack(unsigned int scap){ t=new T[scap]; tops=0; capacitys=scap; }

template Stack::~Stack() { delete[] t; } template void Stack::push(const T& x) { if (tops void Stack::pop() { if (tops>0){ tops--; } } template T* Stack::top1() { T* x; if (tops>0){ x=new T(t[tops-1]); return x; } else return 0; }

- 112 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

template T& Stack::top2() { T* x; if (tops>0){ x=new T(t[tops-1]); return *x; } } template bool Stack::emptyStack() { return tops==0; } template int Stack::getSize() { return tops; }

template int Stack::getCapacity() { return capacitys; } template ostream& operator<<(ostream& c, Stack& x) { int i; for (i=0;i

return c; }

3.4.3. Solution: File userStack.cpp

#include #include #include "Stack.h" using namespace std; class Vehicle{

char* brand; char* model; unsigned long price;

- 113 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS public:

Vehicle(){} Vehicle(char* pmodel, char* pbrand, unsigned long pprice) { brand=new char[strlen(pbrand)+1]; model=new char[strlen(pmodel)+1];

strcpy(brand, pbrand); strcpy(model, pmodel); price=pprice; } ~Vehicle(){cout<<"vehicle destructor"<

Stack p;

p.push(v1); p.push(v2); p.push(v3);

cout<

return 0; }

Remarks: • The specification of the class Stack should include some constraints concerning the type with which T will be instantiated: - The type that will instantiate T must have the operator << overloaded - It is convenient that such type has the operator = overloaded too to avoid any sharing of identity between the copied object and the object that receives the copy (see Chapter 4).

- 114 - CHAPTER 3: GENERIC CLASSES AND FUNCTIONS

• At the end of the main() function, the destructor of Stack is called implicitly. Since the representation of a stack consists of an array of objects of type T, the destructor of T is also called (implicitly) to deallocate each one of the objects of the array. This is fully explained in Chapter 4. • Notice the declaration of the operator<<:

friend ostream& operator<< <>(ostream& c, Stack& s);

The notation <> is used in order to enforce that this operator is a template and, therefore, there should be a different instance of it for each different class obtained from an instantiation of the template parameter T. • An operation cannot return a reference to a local variable (why? ). For this reason, one of the two different top() operations that we have defined (T& top2() ) returns a reference to a new object of class T created dynamically. The following would not be possible: template T& Stack::top2() { T x;

if (tops>0){ x=t[tops-1]; return x; //INCORRECT!!! Returns a reference //to a local variable } }

However, the implementation that we have proposed may generate garbage. Why? How would you solve this? • This implementation of the class Stack would not work if it was required that the stack could contain objects of type T (i.e., the class that instantiates T) and any subclass of T. A new implementation that supports this ability will be presented in Chapter 6.

- 115 -

- 116 -

Chapter 4 Associations. Composite objects. References

So far we have presented classes and objects in isolation. However, the real systems we want to model are far richer than that: they are constituted by many different objects which are related one to the other. Therefore, the O.O. paradigm, which is intended to model real systems, should provide means to relate classes.

There are two main relationships that can be established between classes within the paradigm of object orientation: 1. Association: A class instance is linked in some semantic way with one or more instances of another class (e.g., An instance of the class Employee is linked with an instance of the class Company; therefore, we can establish an association between both classes). 2. Generalization: A class is more general than another one (e.g., the class Vehicle is more general than the class Car).

In addition to these couple of relationships, we may consider instantiation as another relationship between classes: a class can be defined as a particular instantiation of a template class (e.g., a List of integers is an instantiation of a List of Something; see Chapter 3); therefore, we can establish a relationship between the template class and the instantiated one. However, from a rigorous point of view, this is not exact since template classes are not proper classes. They only become actual classes when they have been instantiated. This chapter is devoted to the first one of these relationships (association). Chapter 5 explores generalization and, as mentioned, Chapter 3 deals with template classes and instantiation.

4.1. Associations, aggregations and compositions

4.1.1. Associations An association between a pair of classes is a broad relationship useful to establish semantic links between the instances of both classes. The meaning of association can be stated in the following way:

- 117 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Definition (Association) An association between the classes A and B establishes a semantic relationship between these classes, according to which, a specific instance of one class (e.g., A), is linked with 0, 1 or various instances of the other one (B). The extension of an association is constituted by the pairs (a,b) of linked instances (a is an instance of A and b is an instance of B).

This definition will be made more comprehensible with the following list of examples: • The association works-for can be stated between the classes Employee and Company. It means that a specific instance of Employee (e.g., John) is linked (i.e., works for) a specific company (e.g., ACME). It may happen that John works for more than one company. It could also happen that John was unemployed for some time. • The association is-child-of can be established between the class Person and itself. A specific instance of this class, say Ann, may be the child of two specific instances of the same class (e.g., Peter and Joan); it could also happen that one (or both) of the parents was unknown, in which case, Ann would be linked to just one (or none) of the instances of Person. • The staff of a company is constituted of head of departments, commercial managers and administrative staff. Therefore, we may establish associations between the classes CompanyStaff and each one of the others: HeadDept, CommercialMgr and AdminStaff. These associations would link the staff of the company my-company with the head of department Ann and with the commercial manager Peter. • A car is made out of many components. for instance, five wheels, one steering wheel, some seats, one engine (which, in its turn is composed of many parts)... For instance, we can establish an association between the classes Car and Wheel, which would link the car with plate LL2312B with the five wheels with identifiers: 1166P, 1299P, 8912P, 1112Q, and 2323S. Fig. 4.1 shows how these associations can be represented using UML (see Appendix A). The numbers at both ends of the association indicates the cardinality of each end (e.g., an employee can work at 0, 1 or more companies); a child may have 0, 1 or 2 known parents; a car has exactly 5 wheels. We have presented binary associations (i.e., associations with two constituent classes). However, it is possible to link in one association more than two classes. For example, a Person has a Contract to work for a Company. The contract has some attributes as starting date, end date, salary, etc. Since a person may have several contracts with the same company (i.e., at different dates), we can model this situation with a ternary association which involves the three entities. However, in this book, we will restrict to binary associations.

- 118 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.1: Representation of associations using UML

Implementing associations Associations are usually implemented by means of pointers to the referred class. For example: class Employee{ private: char* name; char* id; Company* comp; //in the case that an employee can work at one //company at maximum. Otherwise: // List lcomp; public: ... };

This implementation is depicted in fig. 4.2.

- 119 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.2: Implementation of associations

4.1.2. Aggregations A special case of association is constituted by the ownership (has-a or whole-part) relationship between classes.

Definition (Aggregation) An aggregation is a special kind of association between exactly two classes which represents a whole-part relationship between a (whole) class and another (part) class. The instances of the former are constituted by instances of the latter. We say that an instance of the whole class has one or several instances of the part class. Aggregations can be organized in a hierarchical way (i.e., forming trees of concepts: A is an aggregation of Bs, which, in their turn, are aggregations of Cs). However, aggregations cannot generate loops (e.g., C cannot be, at the same time, an aggregation of As).

Some of the above presented examples of associations can be considered as aggregations: • The staff of a company can be seen as the aggregation of some heads of department, commercial managers and administrative staff. • A car is composed of wheels, seats, a carburettor, a fuel tank, etc.

Fig. 4.3 shows the way in which aggregations can be modelled in UML (see Appendix A for more information about UML). The black diamond is used for compositions, which are a particular case of aggregations, as it is presented next.

- 120 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.3: Modelling of aggregations using UML

4.1.3. Compositions

Definition (Composition) A composition is a strong form of aggregation. It has two additional properties: • Any instance of the part class can participate in, at most, one composition at a given instant. (However, it can be deleted from one composition and added into another). • When the composite object is deleted, all its parts are (usually) deleted with it.

If we reconsider our previous examples of aggregations we will discover that not all of them can be considered compositions: • In the company staff example, we have to take into account that a particular instance of AdminStaff (e.g., John) may work for two different companies at the same time. On the other hand, if one (or both) of them closes down, John should go on existing: he may also be a citizen, with a wife and some children and he may play some sport during the weekend (provided that these aspects are also modelled in our network of classes). That is, the company staff is not an example of class composition.

- 121 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

• In the car example, a specific component (e.g., a wheel) can only be a part of one car at a time. Furthermore, if that car is removed, all its parts will be (in general) removed with it. The car example is an example of class composition.

Implementing composition Compositions are usually implemented in some of the following ways: • Using pointers to the component instances (i.e., in the same way as general associations/aggregations). class Car{ private: char* model; Wheel* wc[5]; Carburetor* cb; ... public: ... };

If we do it this way we must make sure in the construction of an object of the composite class (e.g., Car) that each one of its components are properly initialized. This can be achieved in two different ways: - By referencing a specific component (e.g., cb=new Carburetor(...); or cb=c;, where c is a pointer to an existing instance of the class Carburetor. - By making the pointer 0 (NULL) if no actual component can be added yet (e.g., cb=NULL;) In the same way, when a composite object is removed, its destructor should take care of deleting all its components. Notice that this way of implementation keeps the component objects separated from the composite one. Notice that the composite could be deleted without affecting its components (they are, actually, independent objects). Furthermore, it would be perfectly possible that one component was a part of more than one composite object. Thus, if we choose to implement a composition with pointers to components, we should take special care of both issues. • By means of subobjects. A subobject is a part of the structure (representation) of the object to which belongs, hence, it is attached permanently to it.

- 122 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

class Car{ private: char* model; Wheel wc[5]; Carburetor cb; ... public: ... };

In general, this implementation fits more appropriately in the notion of composition. It enforces the fact that the parts are inseparable from the whole. By construction, the components cannot be a part of any other composite object (at least easily) and, if the composite object is deleted, its components will be deleted with it, because they are a permanent part of it. In any case, it is important to enforce that when a composite object is created, all its subobjects are properly created and that when a composite object is removed, all its subobjects are also removed. We deal with this issue in section 4.2.

Both implementation alternatives are shown in fig. 4.4.

Figure 4.4: Composition implementation by means of subobjects and pointers

- 123 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES 4.1.4. A usual interpretation of aggregations Sometimes, the has-a relationship between two classes has the following meaning: the class A can be represented internally in terms of the class B. We may also say that the class A is a client or a user of the class B. For instance, the class Stack can be represented in terms of the class List (that is, as a list of elements of type T). Then, the stack operations will be written in terms of the list operations. Stackis a client of List.

4.2. Constructors and destructors with subobjects When composite classes are implemented by means of subobjects, the construction of an object of the aggregated class involves the call to some constructor of the component class. Something similar occurs with destructors: when an object of an aggregated class is destroyed, the destructor of the component class is also called. In the following sections we discuss these issues.

4.2.1. Construction Consider the class Person that was presented in Chapter 2 with a new attribute birth of class Date. Notice that now, Person may be considered an aggregate with component class Date. class Person{

char* name int age; char sex; Date birth;

public:

Person(char* pname, int page, char psex) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; }

//...... }; class Date{ private: unsigned int day; unsigned int month; unsigned int year;

- 124 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

public:

Date(){day=1; month=1; year=2000;} Date(unsigned int pday=1, unsigned int pmonth=1, unsigned int pyear=2000) {day=pday; month=pmonth; year=pyear;} Date(const Date& da){day=da.day;month=da.month; year=da.year;}

//.....More operations };

We create an object of class Person in the following way: Person p("Ann", 18, ’f’); Since birth is a subobject of Person (specifically, a subobject of p), the construction of this object p involves the following steps: 1. A call to the constructor of Person with the arguments "Ann", 18 and ’f’. The first thing done by this constructor is: 2. A call to the default constructor of Date to construct a Date object which will be used to initialize the attribute birth of the object p. Recall that the default constructor of Date initialized a date to the value 1-1-1900. 3. The constructor of Person (which had been called but not yet executed) is executed now. It will initialize the attributes name, age and sex of the object p. As a result, p.birth will be initialized to 1-01-1900 (see fig. 4.5).

pbirth: name: age: 18 sex: ’f’ birth: 1 1 1900

Figure 4.5: Construction of an aggregated class using a default constructor

Initializers You will have guessed by now that the use of the default constructor of the subobject sometimes is not the best idea. We may want to customize the construction of the subobject (i.e., indicate which is the actual birth date of Ann, instead of the improbable 1-01-1900). We may do this by means of initializers:

- 125 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Person(char* pname, int page, char psex, Date& pbirth) :birth(pbirth) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; }

The notation :birth(pbirth) indicates that the attribute pbirth should be initialized by a call to the copy constructor of the class Date with parameter pbirth (notice that the constructor of the class Person now has the birth date as a new parameter). Notice also that this call comes up right before the starting point of the code of the Person class constructor. It would have also been possible to call the constructor: Date(int pday, int pmonth, int pyear); in the following way:

Person(char* pname, int page, char psex, int pday, int pmonth, int pyear) :birth(pday,pmonth,pyear) { name=new char[strlen(pname)+1]; strcpy(name,pname); age=page; sex=psex; }

Definition (Initializers) Let C be an aggregate class which contains as attribute, a subobject attrib of class D (D attrib;). That is:

class C{ //..... D attrib; //..... };

Let D(T1 arg1, ..., Tk argk) be a constructor of the class D. The expression :attrib(arg1,...,argk) is called initializer and constitutes a call to the above-mentioned constructor of the class D, with the purpose of initializing the subobject attrib. Initializers may come up only in constructors right before the starting point of their implementation:

- 126 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

C(....) :attrib(arg1,...,argk) { //Implementation of the C constructor }

More than one initializer may be called in a constructor:

C(....) :attrib(arg1,...,argk), attrib2(arg21,...,arg2k) { //Implementation of the C constructor }

Definition (Rule to construct objects of aggregate classes) Let C be an aggregate class which contains as attributes some subobjects of class D1, D2, ..., Dn.

class C{ private: D1 at1; D2 at2; ... Dn atn; };

The construction of an object of class C will involve (in the order given): 1. A call to some constructor of the class C (the constructor body is not executed yet). 2. A call to some constructor of the classes D1, D2, ..., Dn to initialize the subobjects. The constructors of the classes D1, D2, .. Dn to be called will be indicated by means of initializers within the C constructor (right before its body). If no initializers are given, the default constructor of the classes D1, D2, .. or Dn will be used. 3. The execution of the body of the C constructor. The constructors of the C subobjects are called in the order in which the attributes have been declared (i.e., D1, D2, ..., Dn). However, it is not advisable to make an implementation rely on the order in which such constructors are called.

- 127 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES 4.2.2. Destruction The process of destruction of an object of an aggregate class involves the (automatic) call to the destructors of its subobjects (if any). That is, the destruction of an object of the class Person involves a call to the destructor of the class Date. This call is necessary to clean up all the dynamic storage allocated in the construction of a Date. The definition of class Date does not involve any dynamic memory allocation. However, consider a slightly different definition of Date: class Date{ private: int day; char* month; int year; public: Date(int pday, char* pmonth, int pyear){ day=pday; year=pyear; month=new char[strlen(pmonth)+1]; strcpy(month,pmonth); } ~Date() { delete [] month; } };

Now examine the following code: int main() { Date annBirth(20,"march",1984); Person p("Ann", 20,'f', annBirth);

//Do things.... return 0; }

The following (automatic) destruction process will take place at the end of the main program: 1. Call to the destructor of the class Person 2. Call to the destructor of the class Date Notice that if the Date destructor were not called, the string month would become garbage. Notice also that while the process of construction is performed bottom-up, the destruction process is carried out top-down.

- 128 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

4.3. Equality, copies and clones

4.3.1. Equality The equality operation determines whether two program entities are equal regarding the semantics of the concepts that they are modelling. We will use the operator == in order to compare two entities. In particular, c1==c2; evaluates true if both entities (c1 and c2) are considered equal. As shown in Chapter 2, we may consider two different headers for this operator: bool operator==(const C&) const; //as a class member bool operator==(const C&, const C&); //as a friend function

The notion of equality in OOP may have various meanings in different contexts. For that reason it is important to present those meanings accurately. First of all, we should distinguish between pointer equality and object equality

Pointer equality

Definition (Pointer equality) Two pointers (declared as pointers to type T) are equal if either: 1. They are both null pointers or 2. They both refer to the same object of type T

Notice that according to this definition, two pointers will not be considered equal if they are referring two different objects which hold the same values in their attributes. In fig. 4.6, p and q are equal pointers. However, p and p2 are not (they point to different objects, regardless their values). We will use the predefined operator == to compare pointers (it is not necessary to overload it).

- 129 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.6: Equality between pointers

Object equality Two different notions of object equality may be established: • Shallow equality (also referred to as one-level equality) The idea behind shallow equality is that two objects are equal in a shallow way if they store the same values for non-dynamic attributes and their corresponding pointer attributes refer exactly to the same object.

Definition (Shallow equality) Two objects x and y of the same type are equal in a shallow way if their respective attributes are equal one to one (the i-th attribute of x should be equal to the i-th attribute of y) according to the following criterion: - If the attribute is a primitive type (bool, int, float, double, long, char), the values of that attribute should be the same for both objects. - If the attribute is a pointer (e.g., char*, Person*...), both pointers should be equal, according to the notion of pointer equality given above. - If the attribute is a subobject (e.g., Person), both subobjects (i.e., both attributes) should be equal in a shallow way.

Fig. 4.7 shows a typical situation in which two objects (of the class Person) would be considered shallowly equal. Notice that they have the same value for their attributes of primitive types, their attributes birth (of class Date) are shallowly equals and the pointers to their names point to the same character array.

- 130 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.7: Shallow equality between objects

Notice that two objects shallowly equal share a part of their identity (in this case, their name). A change in the name attribute of one of them will lead to a change in the name of the other. The designer should be aware of this situation and decide whether this design is suitable for each particular situation. A shallow equality operation may be defined, either by overloading the equality operator (==) or by defining a new operation shallowEqual(...). • Deep equality (also referred to as structural equality) The meaning of deep equality is that two objects will be considered equal if they have the same run-time structure with identical values in its non- dynamic attributes (at any point of that run-time structure).

Definition (Deep equality) An object is equal in a deep way to itself. Two objects x and y of the same type are equal in a deep way if their respective attributes are equal one to one (the i-th attribute of x should be equal to the i-th attribute of y) according to the following criterion: - If the attribute is a primitive type (bool, int, float, double, long, char), the values of that attribute should be the same for both objects. - If the attribute is a pointer (e.g., char*, Person*...), both pointers should be different and the objects referred to by both pointers should be equal in a deep way. - If the attribute is an object (e.g., Person), both objects (i.e., the attributes) should be equal in a deep way.

- 131 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Example Consider the following C++ program: class Player { char name[20]; int age; Player* playfriend; Team* team;

public: //..... } class Team { char name[60]; int foundationYear;

public: //..... }

At run-time we may have defined some players and teams with the relationships shown in figure 4.8. In this case, the following pairs are equal in a deep way: (p1, p5), (p2, p6) and (t1, t7) Notice that only the pair (t1, t7) is considered equal in a shallow way.

Figure 4.8: Deep equality between objects (the players example) ♦ ♦ ♦

The run-time structure of p5 (with pointers to p6 and t7) constitutes a deep copy of p1 (see section 4.3.2).

- 132 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

The way in which associations are implemented (i.e., by means of subobjects or references to independent objects, as shown before) has an important influence in object equality. Consider the following example:

Example We describe a car by means of a model and two components: a wheel and an engine. Figure 4.9 models this composition by means of references to independent objects; in this case, two car instances (namely, c1 and c2) of model “Opal Samantha”, which have as components a wheel with reference 12345 and an engine with reference 45612. • c1 and c2 are shallowly distinct, in spite of having the same structure and the same values. The reason for this is that they refer to different instances of Wheel and Engine. They would be considered shallowly equal if both cars would refer to the same instance of Wheel and Engine. • c1 and c2 are deeply equal since their structure is identical (i.e., they are constituted by the same kinds of components with identical values, regardless of the fact that the specific instances of those components are different). Figure 4.10 presents an implementation of this composition by means of subobjects (which is probably the best alternative in this case). In this case, c1 and c2 are shallowly and deeply equal. The designer will decide the appropriate notion of equality according to the semantics and implementation of each specific class.

Figure 4.9: Deep equality between objects (the car example)

- 133 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.10: Shallow equality between aggregates implemented with subobjects (the car example) ♦ ♦ ♦ A deep comparison of two objects should be implemented either by overloading the == operator appropriately or by defining a new operation deepEqual(...) in the class. Example This example shows how the operator == could be overloaded for the car example. class Car{ char model[20]; Wheel* wheel; Engine* engine; public: //..... bool operator==(const Car& c) const; }

//Shallow version: bool Car::operator==(const Car& c) const { return (strcmp(model, c.model)==0 && wheel==c.wheel && engine==c.engine); }

//Deep equal version: (Warning: only one overload //of operator==(..) is possible for a specific class) bool Car::operator==(const Car& c) const { return (strcmp(model, c.model)==0 && *wheel==*(c.wheel) && *engine==*(c.engine)); }

- 134 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

The example shows two versions of the operator==: the shallow version and the deep one. The deep version relies on deep versions of the operator== for the classes Engine and Wheel. Notice, finally, that only one version of the operator== can be implemented for a specific class. The other equality can be implemented another operation (e.g., deepEquals(...)). ♦ ♦ ♦ The == overloading should be done carefully if the class for which the operator == is overloaded can generate some cyclic structure (as in the example shown in figure 4.11).

Figure 4.11: Cyclic structure

In section 4.4 (Guided problem) you will find more about this.

Considerations regarding equality Considerations on whether to choose shallow or deep equality: • Object semantics. The meaning of the objects may determine the choice of shallow or deep equality for a specific class. In the player-team example a shallow equality could be enough. It is reasonable that two players that have the same value for the attributes name and age and that refer to the same instance of the classes Team and Player (for playfriend) are considered equal. However, if we consider a data structure (for example, a binary tree of integer values) we need the notion of deep equality to compare two objects. It

- 135 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

is clear that the two trees (a1 and a2) presented in fig. 4.12 should be considered equal, and this will only be possible if they are compared deeply.

Figure 4.12: A deep equality is necessary for comparing both trees

Sometimes, the semantics of the objects that have to be compared may suggest other ways of comparison. In the player-team example, if we make the assumption that the attribute name of the class Player is identifier (i.e., there are no two players with the same name and two players with the same name are identical), then we may conclude that two instances of the class Player are equal if they have the same value in the attribute name, without having to check the rest of the attributes. • Efficiency. It should be taken into account that a shallow comparison is usually far more efficient that a deep one. A deep comparison implies following the entire run-time object structure, which can be quite big (just imagine a tree with thousands of nodes). • Cycling. If a deep equality is chosen, beware of cycle structures in the overloading of the == operator. • Coexistence. If both equality meanings should coexist, two operations may be defined: - A == operator, which will have the meaning of shallow equality and - a new deepEqual operation, which will implement the deep equality.

4.3.2. Copy The copy operation replicates the state of a program entity onto another existing one. We will use the assignment operator (=) to denote this operation. c1=c2; denotes the copy of the state of the entity c2 onto the entity c1. The header of the operator= is the following:

C& operator=(const C& c2);

- 136 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Many issues that have been shown regarding equality make sense also in the case of copy. In particular, we may distinguish between pointer copy and object copy. On the other hand, within object copy, we may refer to shallow copy and deep copy.

Pointer copy

Definition (Pointer copy) A copy of a pointer to an object pc2 onto another pointer pc1 makes both pointers refer to the object to which pc2 referred. The copy of a pointer does not lead to the copy of the object to which it refers.

Object copy (shallow)

Definition (Shallow copy) A shallow copy of an object c2 onto another object c1 overwrites the state of c1 so that c1 and c2 are shallowly equal. A shallow copy should not generate garbage.

Any C++ class have a predefined assignment operator (=) which performs a shallow copy. This operator may be overloaded, if necessary. An acceptable example of shallow copy is the assignment of players, presented above. Let us consider two players (p1 and p2) and let us assign p2 to p1: p2=p1; The result of this operation is shown in figure 4.13. Notice that, now p2 and p1 are equal in a shallow way. Notice also that part of the identity of both objects (the team and the playfriend attributes) are shared. Last, recall that this is the default behaviour offered by the assignment operator (=), if it is not overloaded. However, keep in mind that the default assignment operator may generate garbage.

- 137 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.13: A shallow copy

Object copy (deep)

Definition (Deep copy) A deep copy of an object c2 onto another object c1 overwrites the state of c1 so that c1 and c2 are deeply equal. A deep copy should not generate garbage.

Example An example in which a deep copy may be necessary is the copy of a data structure which has a dynamic part (i.e., a part created at execution time by means of the new operator), for instance, the copy of a binary tree. The class IntBinaryTree, shown next, defines trees of integer nodes like the one presented in figure 4.14. class Node{ public: int val; Node* leftchild; Node* rightchild; }; class IntBinaryTree{ Node* root;

Node* cloneStructure(Node* nod); public: //...... Some operations

- 138 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES void shallowCopy (const IntBinaryTree& t) { deallocate(); //An operation to deallocate the current value //of the object that will receive the copy of t root=t.root;

} void deepCopy (const IntBinaryTree& t) { deallocate(); //An operation to deallocate the current value //of the object that will receive the copy of t root=cloneStructure(root); }

Node* cloneStructure(Node* nod) { Node* nodeaux;

if (nod!=NULL){ nodeaux=new Node;

nodeaux->val=nod->val; nodeaux->leftchild=cloneStructure(nod->leftchild); nodeaux->rightchild=cloneStructure(nod->rightchild); } return nodeaux; } cloneStructure(...)is a private operation of the class IntBinaryTree The call a2.shallowCopy(a1); leads to the situation depicted in figure 4.15(a), with the same tree structure shared by the two root pointers which represent a1 and a2. On the other hand, the call a2.deepCopy(a1) results in the replication of the tree structure of a1 (see figure 4.15(b)). In this case, the deep version seems clearly superior since the shallow copy causes that any change in any of the trees affects the other one. Imagine, for instance, that the class IntBinaryTree has an operation (setRootValue) intended to assign a value to its root: void setRootValue(int n){ root->val=n; }

The call a1.setRootValue(9); will result in a change in both a1 and a2, which, in general, is not wanted (see fig. 4.16). This is another example of the anomaly that we call identity sharing and it is one of the problems of shallow copy. On the other hand, notice that shallow copy provides a more efficient solution since it saves the traversal and replication of all the tree structure (however, notice also that if we keep the requirement of avoiding garbage, we may have to traverse the

- 139 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES tree structure of the old a1 value in order to deallocate it, both in the shallow and deep cases). Usually, instead of defining a shallowCopy(...) or deepCopy(...) operation, we will overload appropriately the assignment operator (=).

Figure 4.14: A binary tree structure

Figure 4.15: The shallow and deep copies between binary trees

- 140 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.16: Anomaly with shallow copy ♦ ♦ ♦ Considerations regarding copy In the last two sections we have pointed out some considerations that should be taken into account in the choice of either a deep copy or a shallow copy operation. We summarize here those considerations: • Semantics of the classes. This semantics may determine the choice between shallow or deep copy. For instance, in the player example, it may be appropriate that a player and its copy refer to the same team (and, in consequence, any change made on that team is available to both). This situation may not be acceptable in the tree example. In this latter case, we want to keep two completely independent trees, after the copy, thus, a deep copy may be chosen which replicates the entire structure. • Identity sharing. Always take into account the consequences of the fact that two different objects share their dynamic structure and consider if this is acceptable or not in the particular context for which those classes have been designed. • Efficiency. Shallow copies tend to be more efficient than deep ones, since they do not involve the copy of all the object structure. • Cyclic structures. Programming a deep copy should take into account that cyclic structures could lead to a non-termination problem (see guided problems, section 4.4). • Coexistence. If in doubt, it is possible to define two different meanings (i.e., shallow and deep) for copy. Each one will be used in the appropriate situation. This can be dealt with by overloading the assignment operator (=) for the shallow copy and creating a new deepCopy(...) operation.

- 141 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES 4.3.3. Clone

Definition (Clonation) The clone operation creates a new object with a state which is exactly the same as that of an existing one (which is called the cloned object) and returns a pointer to the newly created object. The clone operation for the class C has the following signature: C* clone(); It returns a pointer to an object which is a copy of the object on which the operation is applied.

Notice the difference between a copy and a clonation: in the first case, the object which receives the copy already exists, while in the second case, the clone is a brand new object. As in the copy case, we may perform a deep or a shallow clonation. • Shallow clonation Example We perform a shallow clonation in the player example.

Player* Player::clone() { Player* aux;

aux=new Player;

strcpy(aux->name,name); aux->age=age; aux->playfriend=playfriend; aux->team=team;

return aux; } ♦ ♦ ♦ • Deep clonation Example We propose a deep clonation in the tree example:

IntBinaryTree* IntBinaryTree::clone() { IntBinaryTree* aux; aux=new IntBinaryTree; aux->root=cloneStructure(root); return aux; }

- 142 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

cloneStructure(...)is a private operation of the class IntBinaryTree (see its code above). ♦ ♦ ♦ Chapter 6 presents polymorphism. In that moment more issues about clonation will come up.

4.4. Guided problem: The cyclic player The objective of this problem is to implement a deep copy operation for the class Player (see 4.3.1).

4.4.1. The class Player We propose the following class Player, in which the attribute team has been removed, for the sake of simplicity. class Player{ private: char name[20]; int age; Player* playfriend; public: Player(){ strcpy(name,""); age=0; playfriend=NULL;} Player(char* pname,int page, Player* pfriend) { strcpy(name,pname); age=page; playfriend=pfriend; }

void setFriend(Player& p) { playfriend=&p; } void deepCopy(const Player& p2) { //?????????? }

//More operations...... };

4.4.2. A careless deep copy for Player 1. At first glance, a simple action which replicated recursively the structure of a player would be enough to solve the problem. Which would be that recursive action?

- 143 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Solution: class Player{ private: //..... void cloneStruct(Player* dst, Player* orig) { if (orig!=NULL){ strcpy(dst->name,orig->name); dst->age=orig->age; dst->playfriend=new Player; cloneStruct(dst->playfriend,orig->playfriend); } else dst=NULL; } public: //.... void deepCopy(Player& p2) { cloneStruct(this,&p2); } //....More operations };

Notice that a private operation cloneStruct(...) has been introduced. This operation does the following: (a) Copies the name and the age of the player pointed to by its second argument (orig) to the player pointed at by the first one (dst). (b) If the playfriend attribute of the origin player is not NULL, a new player is created and assigned to dst->playfriend and.... (c) ... The playfriend attribute of the origin player is copied recursively to this newly created player 2. However, this is not a good solution. Why? Solution: This causes an infinite recursion when cyclic structures are to be replicated. Moreover, the dynamic memory would be rapidly exhausted. A cyclic structure would occur in a situation as easy as the following: player 1 is the friend of player 2 and the other way round. Fig. 4.17 shows a cyclic structure and the replicated structure generated by the operation above.

- 144 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Figure 4.17: A cyclic structure that would cause an infinite recursion

4.4.3. A more cautious deep copy for Player 3. So, which could be the general approach to avoid the problem of non- termination we have just detected? Solution: The idea will be to record in some way the objects that we have replicated. Thus, when we encounter the friend of a specific player (say, p) we will replicate it only if it was not replicated before. That is, we are creating a new player q which should be a copy of an existing player p. If the playfriend of p has already been replicated (because we came across it before), the attribute playfriend of q will refer to the already existing copy of the playfriend of p. 4. A good solution in order to record those objects, would be to create a class PointerTable intended to be a container of the pointers to the players that have come up during the copy process. In particular, an element of the container PointerTable could be a pair:

- 145 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

(pointerToTheOriginalPlayer, pointerToTheCopyOfThatPlayer) In this way, given a pointer to player, we will be able to determine if that player has come up before and, in this case, which was its copy. Design the class PointerTable appropriately. Which basic operations should it offer? Solution: The two main operations that the class PointerTable should offer are the following: • void store(Player* p, Player* copiedp) This operation stores at the table the pair (p,copiedp) (p is a pointer to Player and copiedp is the pointer to the copy of the player pointed at by p. • void getStoredCopy(Player* p, Player*& copiedp) If the player pointed at by p is stored in the table, this operation obtains a pointer copiedp to its copy. Otherwise, copiedp=NULL. We propose a very basic representation of PointerTable by means of an array of Pair. Each Pair has two pointers (to the original player and its copy). For the sake of simplicity, we have not used more complex structures nor have we managed the situation in which more than N players had to be replicated. Since the class Pair has a very simple structure and it has been designed only to be used by PointerTable, we make its attributes public and we do not define operations to access them. It would have also been correct defining them as private and providing some operations to access them. class Pair{ public: Player* originalp; Player* copyp; }; class PointerTable{ private: Pair v[N]; int nitems; public: PointerTable(){nitems=0;}

void store(Player* p, Player* copiedp) { v[nitems].originalp=p; v[nitems].copyp=copiedp; nitems++; }

- 146 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES

Player* getStoredCopy(Player* p) { int i=0; while (i

5. With the class PointerTable implemented, now it is easy to create a deep copy of a player. Implement the operation deepCopy of the class Player. Use the class PointerTable Solution:

void deepCopy(Player& p2) { PointerTable pt; cloneStruct(&p2,this,pt); }

void cloneStruct(Player* orig, Player* dst, PointerTable& pt) { pt.store(orig,dst); strcpy(dst->name,orig->name); dst->age=orig->age;

dst->playfriend=pt.getStoredCopy(orig->playfriend);

if (dst->playfriend==NULL && orig->playfriend!=NULL){ dst->playfriend=new Player; cloneStruct(orig->playfriend,dst->playfriend,pt); } }

• cloneStruct(orig,dst,pt) gets a deep copy of the player pointed to by orig onto the existing player dst. pt is the pointer table used to record the already copied players. • The call dst->playfriend=pt.getStoredCopy(orig->playfriend); stores in dst->playfriend the copy of orig->playfriend (if it has been already constructed). Otherwise, dst->playfriend==NULL

- 147 - CHAPTER 4: ASSOCIATIONS. COMPOSITE OBJECTS. REFERENCES 4.4.4. Generalizing the process of getting a deep copy Would it be possible to generalize the process of creating a deep copy of a player to any object (i.e., not necessarily a player)? The answer is yes. In fact, some languages like Java supply a certain degree of persistence which consists in creating a deep copy of the run-time structure of an object (which is an instance of virtually any class) and writing it in a file so that it can be retrieved afterwards. How is this possible? The idea is to use the notions of inheritance and polymorphism that will be presented in Chapter 5 and Chapter 6.

- 148 -

Chapter 5 Inheritance

5.1. The meaning of inheritance

5.1.1. Class generalization and substitution principle A main aspect of object-orientation is the modelling of concepts of the problem domain by means of abstract types, which, in turn, are implemented using classes. However, domain concepts do not come up in an isolated way. Instead, each one is related to others in different manners. One of these manners is association, which has been discussed in Chapter 4. Another one is generalization. This chapter deals with the latter. Let us start with an example: An application may need to define classes to model different kinds of vehicles (e.g., cars, vans, lorries, etc.). As a first step, we can define a class for each one of them: Vehicle, Car, Lorry, Van. Then, we may consider the fact that the class Vehicle is more general than any one of the other classes (e.g., Car). By saying that “Vehicle is more general than Car” we mean the following issues: • A car is a special kind of vehicle. We often say that “a car is a vehicle”, which actually means that any instance of the class Car can be seen also as an instance of the class Vehicle. • The set of instances of the class Car is, actually, a subset of the instances of the class Vehicle. • Anything which is true for a vehicle is also true for a car (i.e., the invariant of the class Vehicle should also hold for the class Car; for instance: if any well-constructed vehicle must have an owner, then any well-constructed car must have an owner too). • Any attribute held by a vehicle is also held by a car (e.g., brand, model, owner, etc) and any operation that can be applied to a vehicle, can also be applied to a car (e.g., setOwner(...)). However, notice that a car could have other attributes or operations which are not defined for general vehicles (e.g., doorNumber, which probably has no sense for lorries). • In any place, within a program, in which a vehicle is expected, a car may come up (e.g., if a procedure expects a vehicle as a parameter, it should be happy to get a car as such parameter). This property is called substitution principle (see fig. 5.1).

- 149 - CHAPTER 5: INHERITANCE

Figure 5.1: Substitution principle on a function

The latter issue, the substitution principle, is the criterion that is used to determine whether a class is more general than another one.

Definition (Generalization relationship between classes) We say that a class A is more general than another class B (or that the class B is more particular than the class A) if at any part of a program, an instance of the class A can be substituted by an instance of the class B without any observable change in its behaviour. That is: • The operations that can be applied to instances of A can also be applied to instances of B and with the same meaning. • The structure of class A is also shared by class B. The opposite is not necessarily true. We also say that the type implemented by the class B is a subtype of the type implemented by the class A.

For example, we may say that the class Car is more particular than the class Vehicle, which can also be stated in the following way: the type implemented by the class Car is a subtype of the type implemented by the class Vehicle5 . A clue to identify the fact that a class is more general than another one is the so called is-a test. If the sentence “A X is a Y” makes sense, it probably means that Y is more general than X. For instance, “a car is a vehicle”, “a man is a person”, “a director is an employee”... Recall that these examples should be read more precisely

5Recall from Chapter 1 that classes can be seen as implementations of abstract types

- 150 - CHAPTER 5: INHERITANCE as follows: “any instance of the class Car can be seen also as an instance of the class Vehicle” In conclusion, generalization is a convenient relationship between classes that allows us to define a new class as a specialization (i.e., a particular case) of another already existing one. Using generalization relationships to relate appropriate classes leads to a better modelling of the domain. Generalization between classes can be characterized by means of the so called substitution principle which, in combination with polymorphism (see Chapter 6), provides a very powerful tool for software engineering.

5.1.2. Subclasses and inheritance In this section we present subclasses and inheritance as the tools provided by O.O. programming languages to implement the generalization relationship between classes. We will show that not only do both issues lead to a better domain modelling but they also provide a significant reuse in software construction.

Definition (Subclass) A subclass B is a class that keeps the same structure (attributes) and behaviour (operations) that another class A (which is called B’s superclass). We say that B inherits the structure and operations of A. In addition, the subclass B may add new attributes or operations to those inherited from A.

A subclass is also called derived class whereas a superclass is sometimes called base class. A specific class may be at the same time the subclass of a class and the superclass of another. This leads to hierarchies of classes. Figure 5.2 shows a hierarchy of vehicles (it uses the notation defined by UML; see Appendix A). The set of descendants of a class A is composed by all the subclasses of A and recursively, all the descendants of those subclasses. In the same way, the set of ancestors of A is composed by the superclass of A and any ancestor of that superclass. ProfessionalVehicle is a subclass of Vehicle (and Vehicle is a superclass of ProfessionalVehicle). Car is a descendant of Vehicle (and Vehicle is an ancestor of Car).

- 151 - CHAPTER 5: INHERITANCE

Figure 5.2: Vehicle hierarchy

From the definition of subclasses, ancestors and descendants we may infer the following feature:

Member application to a class instance Let c be an instance of class C. The application of the member m to the instance c (c.m) requires that the member m has been defined either in C or in an ancestor of C.

The notions of subclass and inheritance implement in a natural way the generalization relationship between classes (which is their purpose). However, they can be used in other ways as it is shown in section 5.4

- 152 - CHAPTER 5: INHERITANCE 5.1.3. Subclasses in C++ The vehicle hierarchy shown in fig. 5.2, above, may be implemented in C++ as shown next. class Vehicle{ private: char* brand; char* model; int year; public: Vehicle(char* pbrand, char* pmodel, int pyear); void getModel(char* pmodel) const; void setModel(char* pmodel); void getBrand(char* pbrand) const; void setBrand(char* pbrand); int getYear(); void setYear(int pyear) const; void showFeatures() const; }; class ProfessionalVehicle :public Vehicle{ private: char* companyOwner; int maxLoad; public: ProfessionalVehicle(char* pbrand, char* pmodel, int pyear, char* powner, int pmaxLoad); void getCompOwner(char* powner) const; void setCompOwner(char* powner); int getMaxLoad(); void setMaxLoad(int load) const; void showFeatures() const; }; class PersonalVehicle :public Vehicle{ private: char* owner; public: PersonalVehicle(char* pbrand, char* pmodel, int pyear, char* powner); void getOwner(char* powner) const; void setOwner(char* powner); void showFeatures() const; }; class Car :public PersonalVehicle{ private:

int maxSpeed; char* colour; int doorNbr;

- 153 - CHAPTER 5: INHERITANCE public: Car(char* pbrand, char* pmodel, int pyear, char* powner, char* pcolour, int pdoornbr); //...... void showFeatures() const; }; class Van :public ProfessionalVehicle{ public: Van(char* pbrand, char* pmodel, int pyear, char* powner, int pmaxLoad); void showFeatures() const; //...... };

Some remarks are worth mentioning concerning this code: • We state that a class A is a subclass of another class B in the following way: class A :public B {....}; class ProfessionalVehicle :public Vehicle {....};

The label public may be substituted by protected or private, with the meaning explained in section 5.3. • The class ProfessionalVehicle: - Inherits the attributes brand, model and year from Vehicle. - It also inherits the operations: get/setModel(..), get/setBrand(..), get/setYear(..). Therefore, if pv is an instance of ProfessionalVehicle, the following calls are correct: pv.getModel(mod); pv.setModel("Pocus");

- Adds the new attributes companyOwner and maxLoad to those inherited from Vehicle. - Adds the operations: get/setCompOwner(..), get/setMaxLoad(..) to those operations inherited from Vehicle. Therefore, if pv is an instance of ProfessionalVehicle, the following calls are correct: pv.getCompOwner(cown); pv.setCompOwner("Locus INC");

- 154 - CHAPTER 5: INHERITANCE

- Redefines the meaning of the operation showFeatures(). This operation is inherited from Vehicle. However, its behaviour should be different in both classes: * In the class Vehicle, showFeatures() should show the attributes brand, model and year. * In the class PrivateVehicle, it should show, in addition, the attributes maxLoad and companyOwner. For this reason, the operation showFeatures() must be redefined for the class ProfessionalVehicle. We say that the subclass overrides the operation of the superclass. • The redefinition of the operation showFeatures() is as follows: - Implementation of showFeatures() for the class Vehicle: void Vehicle::showFeatures() { cout<<"brand: "<

- Redefinition of showFeatures() for the class ProfessionalVehicle: void ProfessionalVehicle::showFeatures() { Vehicle::showFeatures(); cout<<"owner company: "<

Notice that the implementation of ProfessionalVehicle::showFeatures() contains a call to the operation showFeatures() of its superclass (Vehicle::showFeatures()). This call will be responsible for showing the common attributes defined in the class Vehicle (brand, model, year). Notice also that with this redefinition it is possible to create objects of the classes Vehicle and ProfessionalVehicle and get different behaviours when the operation showFeatures() is called on them: Vehicle v("Forrd", "Pocus", 2001); ProfessionalVehicle pv("Renol", "speice", 2004, "Gong Inc", 4600); //...... v.showFeatures(); vp.showFeatures();

- 155 - CHAPTER 5: INHERITANCE

v.showFeatures() will call Vehicle::showFeatures() (and show the features brand, model and year). pv.showFeatures() will call PersonalVehicle::showFeatures() (and show the features brand, model, year, companyOwner and maxLoad). • The classes Car and Van may also override showFeatures(). Example: void Van::showFeatures() { cout<<"Van. Features:"<

5.2. Constructors and destructors in derived classes

5.2.1. Constructors The first important issue to be mentioned in this section is the following:

A subclass does not inherit the constructors defined in its superclass

Therefore, as it may have been expected, all classes should define their own constructors. However, there is a relationship between the subclass constructor and its superclass one. Let us consider the hierarchy of vehicles presented in section 5.1.3. The sentence ProfessionalVehicle pv("forrd","forrdvan", 2003, "Loads Ltd", 5000); generates a call to the constructor of the class ProfessionalVehicle. This constructor should call the constructor of the class Vehicle (ProfessionalVehicle superclass) in order to initialize the vehicle attributes and perform all the operations which are necessary in order to construct a vehicle properly. In the same way, when we create an instance of the class Car (hence, we call the constructor of this class), the constructors of its ancestors (the classes PersonalVehicle and Vehicle) should be called too. The constructor of PersonalVehicle will be responsible for setting the attribute owner and the constructor of Vehicle will set brand, model and year. In addition, each constructor may also carry out whatever is necessary to create a class instance. These reflections can be generalized as follows:

The constructor of a subclass calls the constructor of its superclass.

- 156 - CHAPTER 5: INHERITANCE

This call may be performed in different ways: • Explicitly Before the starting of the code of the subclass constructor, the superclass constructor is called:

ProfessionalVehicle::ProfessionalVehicle(char* pbrand, char* pmodel, int pyear, char* powner, int pmaxLoad) :Vehicle(pbrand, pmodel, pyear) {

maxLoad=pmaxLoad; companyOwner=new char[strlen(powner)+1]; strcpy(companyOwner,powner); }

The initializer :Vehicle(pbrand, pmodel, pyear) is a call to the constructor of the class Vehicle. The superclass should contain a constructor with the parameters passed to the initializer. As a result of the call:

ProfessionalVehicle pv("forrd","forrdvan", 2003, "Loads Ltd", 5000);

the attributes defined in the class ProfessionalVehicle (i.e., companyOwner="Loads Ltd" and maxLoad=5000) are initialized directly from the constructor parameters. The Vehicle attributes (i.e., brand="forrd", model="forrdvan" and year=2003) are initialized through the Vehicle constructor (which is called by means of the initializer: :Vehicle(pbrand,pmodel,pyear) ). Recall that initializers were presented in 4.2.1.

- 157 - CHAPTER 5: INHERITANCE

Figure 5.3: Constructors and derived classes

• Implicitly If no initializer is used at the beginning of the subclass constructor, then the compiler adds a call to the default superclass constructor (i.e., the constructor without arguments). In this case, the default constructor must be defined for the superclass. Recall that if no constructor has been defined for a specific class, the compiler provides the default one for that class. However, if a specific constructor (with parameters) has been defined by the programmer for the superclass, then the compiler will not add the default one. In this case, the programmer should either add a default constructor to the superclass or make sure that he/she has included in the subclass constructor an initializer that calls the existing superclass constructor. Example The following code is incorrect since the class Vehicle has no default constructor and the constructor of the class ProfessionalVehicle does not include any call to the Vehicle constructor.

ProfessionalVehicle::ProfessionalVehicle(char* pbrand, char* pmodel, int pyear, char* powner, int pmaxLoad) { maxLoad=pmaxLoad; companyOwner=new char[strlen(powner)+1]; strcpy(companyOwner,powner); }

- 158 - CHAPTER 5: INHERITANCE

It would be correct by adding the following constructor to Vehicle:

Vehicle::Vehicle() { year=0; strcpy(brand, "defaultBrand"); strcpy(model, "defaultModel"); }

♦ ♦ ♦ Copy constructors and derived classes If the subclass has a copy constructor, it can be designed by means of an initializer that calls the copy constructor of its superclass. Example We add a copy constructor to the class Vehicle:

Vehicle::Vehicle(const Vehicle& v2) { brand=new char[strlen(v2.brand)+1]; strcpy(brand,v2.brand); model=new char[strlen(v2.model)+1]; strcpy(model,v2.model); year=v2.year; }

Now, we add a copy constructor to the class ProfessionalVehicle (which is a subclass of Vehicle):

ProfessionalVehicle(const ProfessionalVehicle& pv) :Vehicle(pv) { maxLoad=pv.maxLoad; companyOwner=new char[strlen(pv.companyOwner)+1]; strcpy(companyOwner,pv.companyOwner); }

Notice the initializer :Vehicle(pv). It is responsible for calling Vehicle::Vehicle(const Vehicle& v) (see figure 5.3). This is possible in application of the substitution principle presented in section 5.1.1: a professional vehicle (pv) is a particular case of a vehicle. Hence, wherever a vehicle is expected a professional vehicle may come up. That is, we can call Vehicle::Vehicle(pv). The substitution principle is crucial to deal with polymorphism, which will be presented in Chapter 6. ♦ ♦ ♦

- 159 - CHAPTER 5: INHERITANCE

5.2.2. Destructors An object of a derived class may have some features of the base class (which may have been allocated by the constructor of the base class). The deallocation of those features is a responsibility of the destructor of the base class. For this reason:

The destructor of a subclass calls implicitly the destructor of its superclass.

Example Let us consider the implementation of the destructors for the classes Vehicle and ProfessionalVehicle.

Vehicle::~Vehicle(){ delete [] brand; delete [] model; }

ProfessionalVehicle::~ProfessionalVehicle(){ delete [] companyOwner; }

Let us consider now the following main program: void f() {

ProfessionalVehicle v("Forrd", "Pocus", 2003,"Loads Ltd",5000); .... }

The function f() creates an object (v) of class ProfessionalVehicle with the parameters established by the constructor of this class, which will initialize the attributes. At the end of f(), an implicit call to the destructor of the class ProfessionalVehicle (derived class) is done. This destructor is responsible for: • Deallocate the space reserved for the attribute companyOwner (attribute of the derived class). • Call the destructor of the base class (i.e., Vehicle()) which will be in charge of deallocating the space reserved for the attributes brand and model. This process is presented in figure 5.4. This figure also presents what would happen if the destructor of the base class Vehicle were not defined.

- 160 - CHAPTER 5: INHERITANCE

Figure 5.4: Destructors for derived classes

(2) This part has been deallocated by the destructor of the class ProfessionalVehicle. This destructor has been called implicitly at the end of the f() function. (1) This part has been deallocated by the destructor of the class Vehicle. This destructor has been called implicitly by the destructor of ProfessionalVehicle. If the destructor of the class Vehicle had not exist, part (1) would have become garbage. ♦ ♦ ♦

5.3. Member visibility and type of inheritance

5.3.1. Member visibility Chapter 1 presented the visibility of class members. Given a class A, the kind of visibility of its members (private and public) states which functions will be able to access them. Private members of class A are visible only: • Within the implementation of any operation of A • Within the implementation of any function which is a friend of A. On the other hand, public members of class A are visible within any function. In addition to private and public members, C++ allows the definition of a new category of members: protected members.

Definition (Protected members) Protected members of class A are visible only: • Within the implementation of any operation of A • Within the implementation of any operation of any class which is derived from A.

- 161 - CHAPTER 5: INHERITANCE

• Within the implementation of any function which is either a friend of A or a friend of a class that has been derived from A. Protected members are preceded by the label protected:.

Example Consider the following classes (see fig. 5.5). class A { private: int x; void f1(); protected: int y; void f2(); public: int z; void f3(); }; class B :public A { private: int t; public: void g(); }; class C { public: void h(); };

• From the code of the functions f1(), f2() and f3() it will be possible to use all members of class A, g() and h(). • From the code of the function g() it will be possible to use y, z, t, f2() and f3(). However, f1() and x will not be accessible. • Finally, from the code of h() it will be possible to use z, g() and f3(). However, y, f2(), x, f1() and t will not be accessible. ♦ ♦ ♦

5.3.2. Types of inheritance The visibility control of the members of a specific class B, as it has been presented in section 5.3.1, states the visibility of the members defined in B, which means from which functions (operations of B and extern functions) the members defined in B are accessible. However, it has not been established the visibility control for those members that have not been directly defined in B but inherited from a base

- 162 - CHAPTER 5: INHERITANCE class A. More specifically, it has not been stated in which way an object b of class B, within a function extern to A and B will be able to access the members that B inherits from A.

Public members are preceded by ’+’, protected members by ’#’ and private members by ’-’. Figure 5.5: Types of inheritance

For instance, let us extend the previous example with a class D which is a subclass of the base class B and which is defined in the following way (see fig. 5.5): class D :public B{ public:

void fd() { z=10; //Is z public in B? y=10; //Is y protected in B? } };

• y has been defined protected in the class A. Is it inherited in B as a protected attribute and hence accessible in D::fd()? • z has been defined public in the class A. Is it inherited in B as a public attribute and hence accessible in D::fd()? The answer to these questions depends on the type of inheritance from A to B.

Definition (Types of inheritance) Let A be a class and B be a subclass of A. The type of inheritance by which B is derived from A states the visibility that the functions which are extern to A and B will have of the members that B inherits from A.

- 163 - CHAPTER 5: INHERITANCE

There are three types of inheritance: public, protected and private, which are noted in the following way: class B :private A{...}; class B :protected A{...}; class B :public A{...}; The meaning of each type is the following: • :public The members that B inherits from A have (for functions that are extern to A and B) the same visibility as in A. That is, they are considered by those extern functions: - Public of B if they were public in A. - Protected of B if they were protected in A. - Private of B if they were private in A. • :protected The members that B inherits from A are considered (by those functions which are extern to A and B): - Protected of B if they were public in A. - Protected of B if they were protected in A. - Private of B if they were private in A. • :private The members that B inherits from A are considered (by those functions which are extern to A and B) always private of B.

Consider again the example with the class D, derived from B, that has been added above: class D :public B{ public:

void fd() { z=10; //Is z public in B? y=10; //Is y protected in B? } };

The correctness of the code of the function fd() will depend on the type of inheritance of B with respect to A: • class B :public A {....};

- 164 - CHAPTER 5: INHERITANCE

z will be considered by fd() as a public attribute of B and y will be considered as a protected attribute of B. Therefore fd(), which has been defined in D (a subclass of B), will be able to use both attributes. • class B :protected A {....}; Both z and y will be considered by fd() as protected attributes of B. Therefore fd() will be able to use both attributes. Notice that the code of the function h() defined in C, which is not a subclass of B, cannot use any of them. • class B :private A {....}; Both z and y will be considered by fd() as private attributes of B. Therefore fd(), which has been defined in a class different from B, will not be able to use any of the attributes. The inheritance type most used is the public one, since it keeps for the inherited members the same visibility as they had in the base class. However, in certain cases (specially when the superclass contains implementation details which should not be known by the users of the subclass) the private or protected types of inheritance may be used.

5.4. Different good (and bad) uses of inheritance Subclasses and inheritance are convenient to implement the notion of generalization. This is its main and most adequate purpose. When we use inheritance in this sense we say that the subclass implements a subtype. However, subclasses can be used for other purposes than for implementing subtypes. We enumerate in this section a list with some of those possible uses. It is important to make it clear that some of these other uses of generalization may lead to incorrect designs or implementations. We discuss these issues in the following sections.

5.4.1. Specialization This is the common use of inheritance. The example of a vehicle hierarchy that we have presented throughout his chapter illustrates it. Its most relevant features are the following: • The subclass is a particular case of the superclass (usually, the is-a rule works for this use of inheritance: “a car is a vehicle”) • The subclass satisfies the parent specification (concerning class operations and invariants). • In addition the subclass may refine the superclass by adding new features to those inherited from the superclass and new invariants to those defined for the superclass. That is, the subclass may add restrictions to the superclass. • The substitution principle is applicable (wherever an object of the superclass is expected, an object of the subclass may come up).

- 165 - CHAPTER 5: INHERITANCE

• The subclass is a subtype of the superclass • The superclass is split into subclasses which model disjoint concepts. That is: the sets of instances of the subclasses are disjoint; there cannot be an instance of vehicle which is, at the same time an instance of Lorry and of Car (this issue will be relaxed when multiple inheritance is presented; see Chapter 6) If possible, we should restrict to this use of inheritance.

5.4.2. Interface A particular case of the specialization use of inheritance arises when the superclass is a pure interface: it just defines a set of (specified) operations but no implementation for these operations (recall from Chapter 1 that a class like this is called a deferred class). The motivation of such a superclass is to guarantee that its subclasses will provide (and override) at least the operations specified in the superclass. Usually, this superclass is called interface. In certain languages (such as Java) interfaces are defined as language constructs. In C++, abstract classes can be used to deal with interfaces. Abstract classes are shown in Chapter 6 (in which polymorphism is presented). Example Let us imagine the superclass Polygon with the subclasses Square, Rectangle, Triangle, Pentagon, etc. All these subclasses should have some operations like getArea(), and getPerimeter(). However, the class Polygon does not have enough information to implement them. As a result, the class Polygon will just specify these operations. They will be implemented in its subclasses. Polygon will be designed in C++ as an abstract class with the so-called pure virtual operations: virtual double getArea()=0; which do not have implementation associated. Chapter 6 will present virtual operation and abstract classes. class Polygon { public: virtual double getArea()=0; //Post: Obtains the area of the polygon on which it is called

virtual double getPerimeter()=0; //Post: Obtains the perimeter of the polygon on which it is called

//... }; class Square :public Polygon { double edgeLength;

- 166 - CHAPTER 5: INHERITANCE public: double getArea(){return edgeLength*edgeLength;} double getPerimeter(){ return edgeLength*4;} //More operations.... }; class Rectangle:public Polygon { double edgeLength1; double edgeLength2; public: double getArea(){return edgeLength1*edgeLength2;} double getPerimeter(){ return edgeLength1*2 + edgeLength2*2;} //More operations.. };

♦ ♦ ♦ The use of interfaces is very common when different ways to implement a type are provided (which is usual, for instance, in libraries of data structures). For this reason, sometimes the subclasses that inherit from an interface are called realizations or implementations of that interface. Consider the following example: Example The type SequenceOfInteger (a sequence whose elements are integers) may be implemented in at least two different ways: as an array and as a linked list. Fig. 5.6(a) shows the array implementation for the sequence {1,5,6} and fig. 5.6(b) shows a linked list implementation for the same sequence.

Figure 5.6: Alternative implementations of a sequence of integers

Each implementation will be encapsulated in one class (namely, ArraySeqOfInt and LinkedSeqOfInt, respectively). Both classes should offer the same set of operations (e.g., insertFirst(x), insertLast(x),

- 167 - CHAPTER 5: INHERITANCE getElementPosition(p), etc.). These operations will be specified in a common superclass called SequenceOfIntegers which will turn out to be an interface and will be implemented differently by each one of its subclasses. The subclasses ArraySeqOfInt and LinkedSeqOfInt are realizations of the interface SequenceOfIntegers. We present here that implementation. class SequenceOfIntegers{ public: virtual void insertFirst(int x)=0; virtual void insertLast(int x)=0; virtual int getElementPosition(int p)=0; ... }; class ArraySeqOfInt :public SequenceOfInteger{

int seq[N]; int nelems; public: void insertFirst(int x){...} void insertLast(int x){....} int getElementPosition(int p){return seq[p];} };

class LinkedSeqOfInt :public SequenceOfInteger{ class Node{ public: int elem; Node* next; };

Node* first; int nelems; public: void insertFirst(int x){...} void insertLast(int x){....} int getElementPosition(int p){...} };

♦ ♦ ♦ Recall that inheritance from an interface is a particular case of specialization. More about interfaces and realizations in Chapter 6. Virtual operations and abstract classes are also presented in Chapter 6.

5.4.3. Similarity The subclass bears some similarities with the superclass and, for this reason, the subclass is modelled “as if it were” the superclass (new features may be added to

- 168 - CHAPTER 5: INHERITANCE the subclass if necessary). However, it cannot be considered as a subtype of the superclass. Consider these two examples: • We have a class Employee defined with operations to get/set the employee name, identification (probably, the social security number or the driver license id.), age, address, name of the company for which he/she works, salary, category and more. We define now a subclass of Employee called Student. Notice the following issues: - By no means is Student a subtype of Employee. A specific student is not an employee. The substitution principle does not apply here. - Student shares with Employee some features (name, identification, address, age...). - Since Student is not a subtype of Employee, there are some Employee operations which are not applicable to the class Student, for instance, get/set salary, category and company (although in this last case it could be reinterpreted for students as the university in which they study) - New features and operations should be defined for the class Student to deal with specific aspects of students (e.g.: the course in which a student is enrolled, the university in which she/he studies, etc). • We have a class List with the usual operations to insert an element in the position i; to get/remove the element of the position i, to test whether a specific element belongs to the list, etc. We define the class Set as a subclass of List. However Set is not a subtype of List (a set is a collection of elements with no repetition and accessed in no particular order, whereas a list is a sequence of elements with repetitions allowed and with an order established among them). As in the previous case, some List operations will be applicable for Set (e.g., the belonging test), some others will make no sense (e.g., get the element ith) and a set will require new operations (e.g., merge, intersection, etc). This use of inheritance has several problems, hence, it should be avoided whenever possible: • C++ and other OOP languages do not preclude programmers from applying the substitution principle (even if it has no sense since the subclass is not a subtype of the superclass). For example, we may have a procedure like: void promote(Employee& emp) intended to be applied to promote an employee. However, a programmer could call it using a student as parameter, which would be inappropriate.

- 169 - CHAPTER 5: INHERITANCE

• The subclass inherits and offers to its clients all the public operations of the superclass (even those operations which have no sense for the subclass, since the subclass is not a subtype). For example, the operation getIth(int i, T& x) which gets (in x) the ith element of the list would be inherited by Set. However, it does not make any sense in the latter class. Alternatives to inheritance for similarity We present here some methods to avoid or diminish the problems of this use of inheritance. The most natural solution should be chosen in each specific situation. 1. Composition Use a composition (aggregation) relationship (see Chapter 4) instead of a class-subclass relationship. Example Instead of making Set a subclass of List, we may use List as a representation for Set (i.e., Set will be a client of List). The Set operations will be implemented in terms of the List operations. template class List{ T l[N]; int nelems; public: List(){nelems=0;}

void insert(const T& x, int i){ //error management..... l[i]=x; }

void get(T& x, int i){ //error management x=l[i]; }

int getNElems(){ return nelems; }

bool belongs(const T& x){ //..... } //More operations....

}; template class Set{ List s; public: Set(){...}

- 170 - CHAPTER 5: INHERITANCE

void insert(const T& x){ if (!s.belongs(x)) s.insert(x,1); else ; //error management..... }

bool belongs(const T& x){ return s.belongs(x); }

int getNElems(){ return s.getNElems(); }

void intersection (const Set& s2){ //.... } };

♦ ♦ ♦ 2. Factoring Identify a common superclass S (which is a supertype of the two similar classes) and define them as subclasses of S. For example, in the Employee-Student example, we may define a class Person which is a superclass (and a supertype) of both Employee and Student. The operations for getting/setting name, identification, address, etc. may be defined for Person, and inherited by Student and Employee while other features which are specific, either of students or employees will be defined in the respective class. Using factoring we have turned a non-subtype inheritance into a subtype one. However, this technique cannot be used if the given class hierarchy cannot be modified. 3. Hiding the parent operations We may keep one of the similar classes subclass of the other but using a private inheritance. In this way, the operations of the superclass will not be offered by the subclass. Example class Employee{ char* name; char* id; char* address; int age; char* companyName .... public: char* getName(){...} char* getCompanyName(){...} ... };

- 171 - CHAPTER 5: INHERITANCE

class Student :private Employee{ //name, id, address, age inherited List lsubj; .... public: char* getName(){ return Employee::getName();} };

♦ ♦ ♦ Note that with this solution: - Both classes share the common structure (name, id, address, age...) and - The operations of Employee (some of which are not of interest for Student, like getCompanyName()) are not offered by Student to its clients - Student can implement some of its own operations in terms of those of Employee (e.g., getName()). Notice that this solution does not preclude the use of an object of the subclass as a parameter of a function that expects an object of the superclass: promote(Employee& e) may still be called with a student. This is probably the worst solution to the similarity situation.

5.4.4. Generalization This form of inheritance is used when the subclass is more general (instead of more specialized) than the superclass. For example, an application to manage the client accounts of a savings bank defines the class Account which models a bank account which operates in euros. In particular, it keeps track of the operations performed on an account. This class defines, among other, the operation getBalance() which returns the balance of the account in euros. To make it possible to have accounts in different currencies, a new class MultiCurrencyAccount is defined as a subclass of Account. Some new operations get/setCurrency(...) are defined and getBalance() is overridden so that it can provide the balance information in the correct currency. Clearly, MultiCurrencyAccount is more general than Account (i.e., Account can be seen as the particular case of MultiCurrencyAccount which can only manage currency in euros). Making MultiCurrencyAccount a subclass of Account is a very artificial and inelegant solution. It would be far better to do the other way round. That is: to define a MultiCurrencyAccount as a base class and, (in the case that accounts in euros had specific features), a EuroAccount as a subclass.

- 172 - CHAPTER 5: INHERITANCE

Inheritance for generalization should be avoided. It can be acceptable in very few situations. For example, if we are working on an already designed class hierarchy which cannot be modified.

5.4.5. Instantiation A common source of errors in OO design and programming is the confusion between inheritance and instantiation. This may happen when we model an instance of a class A as if it were an A subclass or the other way round. It is important to make it clear the difference between both concepts. • A subclass defines a set of instances which is a subset of the instances defined by the base class. As we already know, this set of instances is defined in terms of the list of features (attributes and operations) of the base class and, possibly, some new attributes and/or operations. A subclass defines features. In general, it does not give value to features. For example, Employee is a subclass of Person because it defines the set of instances of the class Employee in terms of the features that characterize persons (e.g., name, id, birthdate, etc.) and, in addition, the company for which they work. Employee does not give value to the features of Person. • An instance of a class gives a value to the features defined by that class. It does not define new features. It does not create a new concept (possibly derived from an existing one) which can be instantiated For example, John is an instance of Employee; i.e., it gives a value to the features that define an Employee (e.g., name=”John”, id=”34981j”, company=”ACME”...). It does not define new features. On the other hand, it has no sense to create instances of “John”. John itself is an instance.

5.4.6. Multiple inheritance The examples we have presented so far have a common feature: any subclass has one superclass at most. However, it is possible that a specific class has more than one superclass. For example, the class MarriedEmployee could be a subclass of both MarriedPerson and Employee. The situation can be even more complicated, since both superclasses can be, at the same time, subclasses of the class Person (see fig. 5.7). The fact that a subclass has more than one superclass is called multiple inheritance. It brings up several difficulties (e.g., motivated by a common ancestor as in fig. 5.7) which can be explained better once polymorphism has been presented. Therefore, we defer the introduction of this kind of inheritance to Chapter 6.

- 173 - CHAPTER 5: INHERITANCE

Figure 5.7: Multiple inheritance

5.4.7. Association A confusion that may arise in object-oriented programming and design is to use subclasses and inheritance to express a conceptual association between classes. For instance, if we want to express that a company has employees, it is incorrect to model this situation by means of a class-subclass relationship as follows (see fig. 5.8(a) )

class Company{ ..... }; class Employee :public Company{ .... };

This would mean that an employee is a special kind of company, which is not the case. What we want to establish, instead, is that an employee is associated to a company by means of an association that we could name “works-for” (fig. 5.8 (b)). This may be implemented as follows: class Employee{

Company* workingComp; ..... };

- 174 - CHAPTER 5: INHERITANCE

Figure 5.8: Difference between generalization and association

The following reflections can be useful in order to decide if a couple of classes must be linked by means of a generalization relationship (hence, using inheritance) or an association relationship. • Inheritance induces a relationship between classes: any instance of the subclass is also an instance of the superclass (which is usually more general). Therefore, an instance of the subclass inherits the superclass features. Notice that this relationship affects to all the instances of the subclass and that it cannot change at run time. For example, we may establish an inheritance relationship between the classes Employee and Supervisor. Indeed, a supervisor is a particular category of employee (other categories may include director, programmer, clerk...). Any specific supervisor must have all the features of an employee (e.g., i.d., salary, contract expiration....), which are inherited from the superclass. • Association induces a relationship between class instances: an instance of one class may be connected with zero, one or more instances of the other class. Therefore, the association does not necessarily affect to all the instances of the classes (which was the case of inheritance). Furthermore, the particular instances of one class connected with the instances of the other one may change at execution time. For example, we may establish an association between the classes Employee and Supervisor. This association may mean that a specific instance of Supervisor is responsible for the work of a set of instances of Employee. The set of employees for which a supervisor is responsible may change at execution time (which cannot happen with inheritance). This relationship does not require by any means that a supervisor instance has the same features that an employee instance. Fig. 5.9 shows both relationships between these classes

- 175 - CHAPTER 5: INHERITANCE

Figure 5.9: An association and a generalization between the same classes

5.5. Guided problem: A hierarchy of persons

5.5.1. The problem 1. Implement a class Person with the following pattern: class Person{ char passportId[9]; int age; Date birthdate; char* name; public:

Person(){cout<<''I am a void person constructor''} Person(char* ppassid, int page, Date& pbirth, char* pname) { //..... }

~Person() { //..... } void printFeatures(ostream& c) { //Print the features of a person to the stream c //(usually c=cout) } };

2. Implement a subclass of Person called MarriedPerson with the following pattern: class MarriedPerson :public Person{

Date marriageDate; char* partnername; public: MarriedPerson(){} MarriedPerson(char* ppassid, int page, Date& pbirth, char* pname, Date& pmarr, char* ppart)

- 176 - CHAPTER 5: INHERITANCE

// It should call the superclass constructor { //..... }

~MarriedPerson() { //.... }

void printFeatures(ostream& c) { //....It should call printFeatures from the superclass } };

3. Investigate in which order the constructors and destructors of the superclass and component classes are called.

5.5.2. Solution: The class Date Since we need a class Date in order to record the birth date and the marriage date, we reuse the class presented in Chapter 1. We have made some changes in order to justify the addition of a destructor (the month has been represented as an array of chars which is reserved dynamically in the constructor and deallocated in the destructor). In order to trace later the use of constructors and destructors we have added to both a message which is presented in the standard output.

#include #include using namespace std; class Date{ int day; char* month; int year; public: Date(){cout<<"Default constructor of Date"<

day=pday; year=pyear; month=new char[strlen(pmonth)+1]; strcpy(month,pmonth); }

- 177 - CHAPTER 5: INHERITANCE

Date(const Date& pd) { cout<<"Copy constructor of Date"<

day=pd.day; year=pd.year; month=new char[strlen(pd.month)+1]; strcpy(month,pd.month); } ~Date() { cout<<"Destructor of Date"<

friend ostream& operator<<(ostream& o, Date& d); }; ostream& operator<<(ostream& o, Date& d) { o<

5.5.3. Solution: The hierarchy of persons class Person { char passportId[9]; int age; Date birthdate; char* name; public: Person(){ cout<<"Default constructor of Person"<

Person(char* ppassid, int page, Date& pbirth, char* pname) :birthdate(pbirth) { cout<<"Constructor of Person with 4 params"<

~Person() { cout<<"Destructor of Person"<

- 178 - CHAPTER 5: INHERITANCE

void printFeatures(ostream& c) { c<<"passport="<

partnername=new char[strlen(ppart)+1]; strcpy(partnername,pname); } ~MarriedPerson() { cout<<"Destructor of MarriedPerson"<

MarriedPerson p2 ("11111111",27,birth,"Ann",marrd,"Mark"); cout<<"\n\n Features of p2"<

- 179 - CHAPTER 5: INHERITANCE

The result of the execution of this program is the following:

(1) Constructor of Date with 3 params (2) Constructor of Date with 3 params (3) Copy constructor of Date (4) Constructor of Person with 4 params (5) Copy constructor of Date (6) Constructor of MarriedPerson with 6 params

(7)Features of p2 passport=11111111 name=Ann age=27 birth date=10-APR-1990 marriage date=20-JUN-2010 partner name=Ann

(8) Destructor of MarriedPerson (9) Destructor of Date (10) Destructor of Person (11) Destructor of Date (12) Destructor of Date (13) Destructor of Date

Let us explain how each output line has been produced: • (1): The Date constructor called by:

Date birth(10,"APR",1990);

• (2) The Date constructor called by:

Date marrd(20,"JUN",2010);

• (3), (4), (5), (6) All of them are generated by

MarriedPerson p2 ("11111111", 27, birth, "Ann", marrd, "Mark"); in the following way: - (3) The constructor of MarriedPerson, even before the start of its execution, calls the constructor of its superclass: Person. The constructor of Person, before its execution, calls the constructor of Date in order to initialize the attribute birth to the date 10-APR-1990. (3) is generated at that moment.

- 180 - CHAPTER 5: INHERITANCE

- (4) After the initialization of its attribute birth, in (3), now the constructor of Person is executed. This completes the initialization of the attributes that p2 inherits from its superclass. (5) and (6) are responsible for initializing the p2 attributes corresponding to the class MarriedPerson. - (5) First of all the constructor of the component class (Date) is called in order to initialize the marriage date. - (6) Finally, the last attribute of MarriedPerson (partnername) is initialized by means of the execution of the MarriedPerson constructor. This completes the construction of p2 • (7) This corresponds to the execution of p2.printFeatures(cout); • (8)-(11) They correspond to the process of destruction of the object p2. All the involved destructors have been called implicitly at the end of main(). Notice that the destruction is made in the reverse order to the construction. Let us present the process: - (8) Destructor of MarriedPerson. The process of destruction of p2 starts with the call to the destructor of MarriedPerson. - (9) The destructor of MarriedPerson invokes implicitly the destructor of Date to deallocate marriagedate - (10) Once the attributes of the class MarriedPerson of p2 have been deallocated, now it is the turn of the attributes of p2 inherited from the class Person: First of all the destructor of Person is called. - (11) Finally, the destructor of Date is called by the destructor of Person in order to deallocate the attribute birth. The destruction of p2 is completed. • (12) Destruction of the object marrd • (13) Destruction of the object birth

- 181 -

- 182 -

Chapter 6 Polymorphism

6.1. Concept of polymorphism Inheritance and the substitution principle offer a new approach to program design : a program may use objects whose actual type is not known until run time. This idea improves a great deal the abstraction, elegance and reusability of the resulting programs. Let us consider the following example. A second-hand vehicle dealer wants to provide their prospective customers with on- line information about the features of the vehicles on sale. To achieve this goal, the software engineer who is responsible for the application designs the following function intended to show on the standard output the features of a specific vehicle (which is passed to the function as parameter). void informCustomer(const Vehicle& v) { cout<<"The features of the vehicle you have selected are:"<

v.showFeatures(); //(1)

cout<<"Please, contact the counter to know " <<"more details\n\n"<

Clearly, this function may be called to know the information about any vehicle of any type which is on sale in the store. Sometimes, it will be called using an instance of the class Car as parameter. In other occasions, an instance of the class Van or Lorry will be used. In any case, the function should work properly. That is, the following code:

Car c("renol", "marrane", 2002, 180, "black", 3); Van v("renol", "hard", 2000, "ACME LTD", 5000); informCustomer(c); informCustomer(v); should yield the following result: The features of the vehicle you have selected are: brand: renol model: marrane year: 2002 max speed: 180 colour: black doors number: 3 Please, contact the counter to know more details

- 183 - CHAPTER 6: POLYMORPHISM

The features of the vehicle you have selected are: brand: renol model: hard year: 2000 owner company: ACME LTD maximum load allowed: 5000 Please, contact the counter to know more details

Notice the following crucial points: • In the first case, the parameter v of informCustomer(v) is a Car instance and hence, the call to v.showFeatures() (1) is bound with Car::showFeatures() (hence, it produces the brand, model, year, maximum speed, colour and number of doors) • However, in the second case, the parameter v is a Van instance and the same call to v.showFeatures() (1) is bound with Van::showFeatures() (hence, it produces the brand, model, year, owner company and maximum load allowed). The ability of the parameter v to refer in two different executions of informCustomer(v) to instances of different classes (Car and Van; both conforming with Vehicle) and of the call v.showfeatures(); to behave differently according to the run-time type of v is called polymorphism.

Definition (Polymorphism) Polymorphism is the feature by which: • A reference to an object may refer to instances of different types at different moments during the execution of an application and • A call to a class operation may behave in a different way according to the class, at run time, of the object to which that operation has been applied.

From this definition, we can establish clearly the two features that should be provided by programming languages in order to offer polymorphism: 1. A notion of class conformance, so that a reference (e.g., v of class C) may refer to different classes (which conform with C) at different moments during the execution of a program. The substitution principle provides this notion of class conformance. According to this principle, a reference v of class C may refer to any object of a class descendant of C. 2. The ability to bind a call to a class operation with an implementation of that operation at run time, instead of at compilation time, as traditional languages do (notice that, due to the previous feature, an element may refer to different classes and we are not sure about the specific class to which it is referring until run time). This ability is called dynamic binding.

- 184 - CHAPTER 6: POLYMORPHISM

Vehicle

PersonalVehicle ProfessionalVehicle

Car Van Lorry

void Car::showFeatures(){....}

void informCustomer(Vehicle& v ){ v is a Car v.showFeatures();

} v is a Van void Van::showFeatures(){....}

Figure 6.1: Features to support polymorphism

These two features are shown in figure 6.1 and will be explained in more detail in the next two sections. Programming languages which supported polymorphism were rare before the irruption of the object-oriented paradigm. In a hypothetical traditional language which allowed the definition of classes but which did not support the two features that yields polymorphism, this problem could have been solved by: (a) Making v be a generic pointer (which could point to objects of any type). (b) Adding a type attribute to the class Vehicle, which would state the actual class of the object at run time (e.g., 1: Vehicle, 2: Van, 3: Car, etc.). When we created an instance of some class of the vehicle hierarchy, we would initialize appropriately that type attribute. And when we needed to know that type (e.g., in performAction() to call the appropriate showFeatures() function) we would just consult that type attribute. Using (a) and (b), the new program would look something similar to: class Vehicle{ //...previous attributes int type; public: //...previous operations void setType(int t){type=t;} int getType() {return type;} }; void informCustomer(void* v) //v is a generic pointer that //can point to anywhere { switch (((Vehicle*)v)->getType()){ case 1: ((Vehicle*)v)->Vehicle::showFeatures(); break;

- 185 - CHAPTER 6: POLYMORPHISM

case 2: ((Van*)v)->Van::showFeatures(); break; case 3: ((Car*)v)->Car::showFeatures(); break; ...... } }

This solution could work but it would suffer from some important drawbacks: • Lack of elegance The code of informCustomer() needs a conditional instruction (a switch has been chosen), codification for the different classes, several casts (i.e., explicit type conversions, which you may forget about) and a strange pointer parameter which can point to something different to a Vehicle and lead to execution problems. Therefore, this solution is dirty, inefficient, error prone and less comprehensible. These issues are easily understood if this code is compared with the previous version of the function. • Reuse difficulties The code of informCustomer() must be modified and recompiled whenever new kinds of vehicles are added to the hierarchy. This makes it difficult the reuse of the hierarchy of vehicles (whenever a new kind of vehicle is added, all applications that use that hierarchy should be updated appropriately). In contrast, the first polymorphic solution provides some clear benefits: • The code of void informCustomer(Vehicle& v) is independent of the actual type of the parameter (as long as v refers to some object whose class belongs to the vehicle hierarchy). This function will work properly regardless of the actual class of v. • The code of informCustomer(Vehicle& v) is elegant and simple. • Both the function informCustomer(Vehicle& v) and the vehicle hierarchy are more reusable. If, in the future, more vehicle classes are defined (e.g., SportCar), the function informCustomer(...) will show the actual features of those new vehicle classes, with no need of either recompilation or foreseeing those features before the definition of the new classes.

Therefore, it becomes clear that polymorphism is a very powerful tool to design elegant and reusable programs. However, as we have already made clear, a programming language that wants to support it needs to provide two features (a notion of class conformance and dynamic binding) which deserve some more attention. The next two sections are devoted to them.

- 186 - CHAPTER 6: POLYMORPHISM 6.2. Class conformance

6.2.1. Class conformance rule The first important ability in order to achieve polymorphism is the fact that a program element may be bound to different (but conforming) classes. As a result, at run-time, that element may behave differently according to the specific class to which, at that moment, is bound (e.g., v, declared as a reference to class Vehicle may refer, both to a Car and to a Van and execute accordingly: v.Car::showFeatures() or v.Van::showFeatures() ). This behaviour will be possible only if Car and Van conforms with Vehicle. Class conformance in OOP is a consequence of the application of the substitution principle that was shown in Chapter 5: since an instance of a subclass can be seen as an instance of the superclass, wherever we expect an instance of the superclass in a program, we can get an instance of the subclass. We say that the subclass conforms with the superclass. This idea is generalized in the following definition.

Definition (Conformance of classes) A class B conforms to another class A if B is a descendant of A. We consider a class as a descendant of itself, so that a class conforms to itself.

Definition (Binding of program elements to conforming classes) In a C++ program, a reference or a pointer to an object of class A can be bound at different moments during the program execution to different objects of any class B such that B conforms with A. This is only possible with pointers and references. It is not possible with automatic objects declared to be of class A or with function parameters passed by value.

We will illustrate this issue in two different contexts: • Parameter binding Consider the following function: void foo(Vehicle& v) {

//...Some things

v.showFeatures();

//...More things }

- 187 - CHAPTER 6: POLYMORPHISM

The foo(...) function should accept as parameter any object whose class conforms with (is a descendant of) Vehicle. C++ behaves in this way only if the parameter is passed as a reference or as a pointer: - void foo(Vehicle& v){...} Parameter v passed as a reference: descendants of Vehicle (e.g., ProfessionalVehicle, Car) may be passed to foo. See figure 6.2(c). - void foo(Vehicle* v){...} Parameter v passed as a pointer: pointers to descendants of Vehicle may be passed to foo. See figure 6.2(b). - void foo(Vehicle v){...} Parameter v passed as an object of the class Vehicle: if descendants of Vehicle are passed to foo, an information loss may occur. Consider, for instance, the call:

ProfessionalVehicle pv(....); //.... foo(pv);

Notice that the parameter passing by value carried out by C++ for the parameter pv induces its copy to the formal parameter v. This copy may lead to an information loss (see figure 6.2(a)). (a) (b)

Forrd ProfessionalVehicle pv(...); pv: ProfessionalVehicle* ppv; ppv: Forrd Pocus foo(pv); ppv=new ProfessionalVehicle(...); Pocus 2003 2003 foo(pv); Loads Ltd Loads Ltd 5000 5000 v points to the same pv is copied into v ==> object that ppv: OK!! void foo(Vehicle v) loss of information!!!! void foo(Vehicle* v) v: {....} {....} v: Forrd Pocus 2003

ProfessionalVehicle pv(...); v,pv: Forrd foo(pv); Pocus 2003 Moves Ltd 5000

void foo(Vehicle& v) In the function foo, v is an {....} alias of pv: OK!! (c) Figure 6.2: Class conformance in C++

- 188 - CHAPTER 6: POLYMORPHISM

• Assignment of references Let us consider the function: void f(Vehicle& rv, Car& rc) {

rv=rc; //(1) CORRECT

rc=rv; //(2) INCORRECT!!!!! } int main() { Vehicle v(...); Car c(...);

f(v,c); //(3) }

rv v: brand, model, year have been copied rc c: maxspeed, colour, owner, doornumber have been lost After rv=rc; Figure 6.3: Assignment of references

(1): rc is a reference to a Car, which conforms with Vehicle (i.e., rc may be considered itself to be a Vehicle. Therefore this assignment is correct. In fact, (1) is a call to the following operation of the class Vehicle: Vehicle& operator=(const Vehicle&); Recall that this operation is provided by default by the compiler (or has been overloaded by the class designer). Notice that this operation may accept a reference to Car as parameter because Car conforms with Vehicle. Notice that if the reference rv does not refer to an object of class Car but to an object of class Vehicle (as in (3) ), then there will be loss of information, since several attributes of rc will not be copied to rv (i.e., doornumber, maxspeed, owner, colour) (see figure 6.3).

- 189 - CHAPTER 6: POLYMORPHISM

(2): rv is a reference to Vehicle and it does not conform with Car (i.e., rv cannot be seen, in general, as a Car). Therefore this assignment is not correct. Notice that (2) is a call to the following operation of the class Car (provided by default by the compiler): Car& Car::operator=(const Car&); It is clear that the class of rv (Vehicle) does not conform with Car, hence it is an incorrect call. The class designer could have provided the operation: Car& operator=(const Vehicle&); with which, the call (2) would have been correct. • Assignment of pointers Let us consider the function: void f(Vehicle* pv, Car* pc) { pv=pc; //(1) CORRECT pc=pv; //(2) INCORRECT!!!!! } int main() { Vehicle v(....); Car c(...); f(&v,&c); }

It is a similar situation to the previous case. A pointer to Vehicle cannot be converted to a pointer to Car because Vehicle does not conform with Car. On the other hand, notice that a copy of pointers does not involve a call to the assignment operator (=) of the class Vehicle. Instead, the assignment pv=pc; means that pv does not point anymore to the vehicle to which it pointed and now it will point to the car pointed to by pc (see figure 6.4). Finally, notice that the call f(&c, &c) would have been also correct (a car is a vehicle! ! ). v: pv

c: pc

After pv=pc; Figure 6.4: Assignment of pointers

- 190 - CHAPTER 6: POLYMORPHISM

In certain situations a program may need to convert an object of a base class to another of a subclass. This may happen if the operation that should be called on that object is different according to its type at run time. Consider the following example: void foo(Vehicle& v) { int dn,ml;

if (``v is a Car'')

dn=v.getDoorNumber(); //Incorrect: Vehicle does not have //the operation getDoorNumber()

else if (``v is a Lorry'')

ml=v.getMaxLoad(); //Incorrect: Vehicle does not have //the operation getMaxLoad() }

Notice that the calls v.getDoorNumber() and v.getMaxLoad() will not work since neither the class Vehicle nor any class to which Vehicle conforms (i.e., a Vehicle ancestor) do not define these operations. These calls will generate a compilation error. In these cases, the rules of class conformance that we have presented in this section may be too restrictive for the needs of a specific program. In those occasions, C++ provides some tools in order to overcome the limitations imposed by the class conformance rules. However, we should use carefully these rules, for they may lead to errors. They are presented in the following sections.

6.2.2. Static type conversion of references and pointers A reference/pointer to an object of a superclass can be considered as if it was of a subclass by means of a explicit static type conversion (static cast): dn=static_cast(v).getDoorNumber(); It is also possible: dn=((Car&)v).getDoorNumber(); This static type conversion (from a superclass to a subclass) can only be applied to pointers and references to objects (unless a specific conversion operator has been defined, as presented in 6.2.4), not to objects themselves. Needless to say that the static type conversion must be used very carefully: if the converted object is not of the expected subclass (in the example Car), the

- 191 - CHAPTER 6: POLYMORPHISM application of the operation of the subclass (e.g., getDoorNumber()) to an object of the superclass will lead to some execution error. The programmer should know exactly what he/she is doing before using this conversion.

6.2.3. Dynamic type conversion of references and pointers A better option is usually the dynamic type conversion (again, applicable to references and pointers to objects; not to objects themselves). A dynamic type conversion (dynamic cast) of a superclass into a subclass converts a pointer or reference to an object which has been declared to be of the superclass into a pointer or reference to an object of the subclass, provided that the run time type of the object referred to by the pointer or the reference is indeed the subclass. Otherwise, an exception is raised. To present how it works, let us consider again the previous example. We will show how dynamic cast works with pointers and with references.

Dynamic cast for pointers void foo(Vehicle* v) { int dn; Car* pc;

pc=dynamic_cast(v);

if (pc!=0) { dn=pc->getDoorNumber(); } else { //this time v does not point to a Car } }

If the vehicle to which v points in a specific execution of the function foo(...) is of class Car the result of the dynamic cast will be a pointer to the same object, but regarded as a Car (not just as a Vehicle, like v). Therefore, it will be possible to apply on that object the Car operation getDoorNumber(). If v does not point to a Car (maybe it points to a Lorry) then, the result of the dynamic cast is NULL (0) and it is not possible to apply any Car operation to it.

- 192 - CHAPTER 6: POLYMORPHISM

Dynamic cast for references void foo(Vehicle& v) { int dn;

try{ dn=(dynamic_cast(v)).getDoorNumber(); } catch (bad_cast){ //This time v was not of type Car, //may be in the next execution .... } }

The way to notice that a specific dynamic cast has been unsuccessful cannot be by comparing the result of the dynamic cast to zero:

if (dynamic_cast(v))==0) //INCORRECT {...}

This is incorrect since a reference cannot be compared to zero (by definition, a reference always refers to some object). For these cases, C++ raises a standard exception called bad_cast. This exception is raised when the dynamic cast ends unsuccessfully (the reference did not referred to an object of the class to which it is wanted to be converted). Type identification (This section uses the concept of virtual operation which will be presented in section 6.3. You can skip it for the moment) In addition to dynamic cast, C++ provides a specific way to make explicit the type of a reference (or of the object pointed to by a pointer) at run time: the typeid() operator. To illustrate the way it works, we consider again the previous example: void foo(Vehicle& v) { int dn,ml;

if (typeid(v)==typeid(Car)){ dn=(static_cast(v)).getDoorNumber(); } else if (typeid(v)==typeid(Lorry)){ ml=(static_cast(v)).getMaxLoad(); } else //...... }

- 193 - CHAPTER 6: POLYMORPHISM

Notice that the use of the typeid() operator makes it unnecessary the use of the dynamic cast (which is, however, possible). The static cast is sufficient, since it is guarded by a typeid() operator which guarantees that the static cast provides a conversion to the correct type of the object. Notice also that the typeid() operator will obtain the run time type of the parameter provided that this parameter has been declared of a polymorphic class (i.e., a class with virtual methods6 ). The operator typeid() returns a reference to an object of a class type_info which provides information concerning object types at run time. Specifically, two operations that can be applied to objects of this class are the operator == (which has been used in the example above) and the operation char* name() to get the name of the type of an object: cout << typeid(v).name(); Notice that the operator typeid() may be applied to: • References to objects, as in the above example: ...typeid(v)... • Objects pointed to by pointers: void foo(Vehicle* v){.... typeid(*v)==typeid(Car).....} • Type identifiers, as in the above example: ... typeid(Car)... The use of the class type_info requires the inclusion of . The dynamic cast and the typeid() operator constitute the so called Run Time Type Information (RTTI). They are quite powerful and some times necessary tool for OOP. However, keep in mind that their use (specially of typeid()) often leads to the sort of programs that we presented at the beginning of the chapter and which are far from elegant and reusable at all: if the type of v is T1 then execute f1(...) else if the type of v is T2 then execute f2(...) else if the type of v......

It is better (whenever possible) to use virtual functions7 appropriately and simply write: v.f();

6A virtual method is a method for which the binding of the method call to a function that implements it is done at run time (dynamic binding), allowing a polymorphic behaviour of it. Virtual methods are presented in section 6.3. In this section we will see that the class Vehicle should define the method showFeatures() to be virtual. 7See section 6.3

- 194 - CHAPTER 6: POLYMORPHISM and let the run time control determine to which specific version of f() (according to the run time type of v) should the call be applied. Therefore, consider the operator typeid() and also dynamic cast as a last resort (see guided problem 6.11 for an example).

6.2.4. User defined type conversion The programmer can define new type conversions by means of: • Constructors • Conversion operators We will show both of them in the following sections: Constructors and type conversions A class A may add a constructor with a parameter of another class B. This is useful to convert an object of a class B to another object of class A.

Example 1: class Complex{ float re; float im; public: Complex(float a){re=a; im=0.0;}

//... }; int main() { Complex c(7.3); //c=7.3 + 0*i

return 0; }

Example 2: Consider the simplified Vehicle hierarchy: class Vehicle { char* brand; char* model; int year; public:

Vehicle(char* br, char* mod, int ye){ //...as usual... }

- 195 - CHAPTER 6: POLYMORPHISM

Vehicle(Vehicle& v){ brand=new char[strlen(v.brand)+1]; strcpy(brand,v.brand); model=new char[strlen(v.model)+1]; strcpy(model,v.model); year=v.year;

} }; class Car :public Vehicle{ int maxSpeed; int doorNbr; public: Car (Vehicle v) :Vehicle(v) { maxSpeed=100; doorNbr=5; cout<<"constructor"<

Car c(v);

return 0; }

The Car constructor generates a Car object out of a Vehicle. It simply sets default values to the specific attributes of Car and calls the copy constructor of Vehicle to set the common ones.

Conversion operator Constructors cannot achieve two issues regarding type conversion: • Convert a class object into a predefined type (e.g. int) This operations would be performed by the constructor of int. Since int is not a class, it has no constructor to do this. • Convert an object of a new class (B) into an object of an already defined class (A). In order to do this, it would be necessary to modify the specification and the implementation of A, since the constructor operator that performs the conversion should be in A. Of course, this is not an elegant solution (usually, it is simply not acceptable). Both limitations can be overcome by conversion operators.

- 196 - CHAPTER 6: POLYMORPHISM

Definition (Conversion operator) The operator A::operator T() const; is an operator which converts an object of class A to the type T Tcan be either a user defined class or a predefined type

Notice that: • Operator T is defined in class A • It does not have any return type

Example: class Complex{ float re; float im; public: //.... Complex(float r, float i){ re=r; im=i; } operator float() const {return re;} }; int main() { Complex c(1.2, 2.0); float i=c; }

The instruction i=c; expects c to be a float. Therefore, it uses implicitly the Complex::operator float() const conversion operator in order to get it.

6.3. Dynamic binding and virtual functions

6.3.1. The problem of early binding In section 6.1, the following solution was presented for the problem of creating an application that showed on-line the features of the vehicles on sale by a second- hand vehicle dealer:

- 197 - CHAPTER 6: POLYMORPHISM

void informCustomer(const Vehicle& v) { cout<<"The features of the vehicle you have selected are:"<

v.showFeatures();

cout<<"Please, contact the counter to know " <<"more details\n\n"<

It was expected that this function shown the correct features regardless of the class of the object with which it was called (brand, model, year, door number and max speed; if v was a car and brand, model, year, max load and company owner; if v was a van). This behaviour is not possible in traditional languages, which create the binding between a function call (v.showFeatures()) and the code to which this call is associated at compilation time, that is, before knowing the actual type that the object v will have at run time (this is called early binding). Since, at compilation time, the only thing known by the compiler is that v will refer to a Vehicle; it associates the call v.showFeatures() to the function Vehicle::showFeatures(). Therefore, at execution time the car/van specific features will not be shown. That is, if we execute something of the sort:

Car c("renol", "marrane", 2002, 180, "black", 3); Van v("renol", "hard", 2000, "ACME LTD", 5000);

informCustomer(c); informCustomer(v); in a language with early binding, we will get:

The features of the vehicle you have selected are: brand: renol model: marrane year: 2002 Please, contact the counter to know more details

The features of the vehicle you have selected are: brand: renol model: hard year: 2000 Please, contact the counter to know more details

Languages which support polymorphism should bind the call to a class operation with its particular code at run time. This is called late binding or dynamic binding.

- 198 - CHAPTER 6: POLYMORPHISM

6.3.2. Virtual functions C++ provides a late binding, hence polymorphic behaviour, only for the so-called virtual functions.

Definition () A virtual function is a function which has the following features: • It is an operation of some class. A function which is not a class operation cannot be virtual. • The binding between the call to a virtual function and the code of that function is done at run time (not at compilation time, as it is usual), according to the run-time class of the object on which the virtual function is called. Therefore, virtual functions may exhibit a polymorphic behaviour. • A virtual function provides the feature of late binding only if it is called on an object reference or a pointer. It does not provide this characteristic if it is called on an object. Virtual functions are declared within the class definition by preceding them with the label virtual.

class A{ public: virtual void f(); ... };

• If a class operation is defined as virtual, it will probably be overridden by its subclasses.

Example Consider again the vehicle hierarchy presented in Chapter 5. If we prefix the operation showFeatures() by the label virtual we get the intended polymorphic behaviour. class Vehicle{ //...... public: //Some operations.... virtual void showFeatures() const; }; class ProfessionalVehicle :public Vehicle{ //.... public:

- 199 - CHAPTER 6: POLYMORPHISM

//Some operations virtual void showFeatures() const; }; class Car :public PersonalVehicle{ //.... public: virtual void showFeatures() const; }; class Van :public ProfessionalVehicle{ //.... public: virtual void showFeatures() const; };

Now, the following code yields the expected results: void informCustomer(const Vehicle& v) { cout<<"The features of the vehicle you have selected are:"<

v.showFeatures();

cout<<"Please, contact the counter to know " <<"more details\n\n"<

informCustomer(c); informCustomer(v); return 0; } ♦ ♦ ♦

An operation that has been defined virtual at a certain base class is considered virtual in all its subclasses. For that reason, in the subclasses it is not necessary to prefix the overridden virtual operations with the label virtual.

6.3.3. Call to virtual functions As it has been established in the definition, we will only get the polymorphic behaviour for those virtual functions which are called with either a reference or a pointer to an object. Let us study in detail the different cases:

- 200 - CHAPTER 6: POLYMORPHISM

First case: Call to a virtual function with automatic objects of different types: int main() { Car c("renol", "marrane", 2002, 180, "black", 3); Van v("renol", "hard", 2000, "ACME LTD", 5000);

c.showFeatures(); //(1) v.showFeatures(); //(2) return 0; }

The calls (1) and (2) are resolved at compilation time ( (1) is bound with Car::showFeatures() and (2) with Van::showFeatures() ). They are not polymorphic calls. The dynamic binding mechanism is not invoked here.

Second case: Call to a virtual function on a parameter passed by value: void informCustomer(const Vehicle v) { cout<<"The features of the vehicle you have selected are:"<

v.showFeatures(); //(1)

cout<<"Please, contact the counter to know more details\n\n"<

informCustomer(ca); //(2) informCustomer(va); //(3) return 0; }

As in the first case, the call (1) is resolved at compilation time without using the dynamic binding mechanism. The call (1) will be bound with Vehicle::showFeatures(). Only the attributes brand, model and year will be shown in both calls to informCustomer(..) ( (2) and (3) ). Notice that v is a Vehicle that receives a copy of a car (ca) and a van (va). However, an information loss occurs in this copy and the attributes which are specific of the class Car or Van are not copied.

- 201 - CHAPTER 6: POLYMORPHISM

Third case: Call to a virtual function on a parameter passed by reference: void informCustomer(const Vehicle& v) { cout<<"The features of the vehicle you have selected are:"<

v.showFeatures(); //(1)

cout<<"Please, contact the counter to know more details\n\n"<

informCustomer(c); //(2) informCustomer(v); //(3) return 0; }

In this case, the call (1) is resolved at execution time using the dynamic binding mechanism, since it is called with a parameter passed by reference. The call (1) will be bound with Car::showFeatures() in the case (2) and with Van::showFeatures(), in the case (3). In both cases, the appropriate attributes will be shown.

Fourth case:Call to a virtual function on a parameter, which is a pointer: void informCustomer(const Vehicle* v) { cout<<"The features of the vehicle you have selected are:"<

v->showFeatures(); //(1)

cout<<"Please, contact the counter to know more details\n\n"<

informCustomer(&c); //(2) informCustomer(&v); //(3) return 0; }

- 202 - CHAPTER 6: POLYMORPHISM

Since the call (1) is performed on a pointer, the dynamic binding mechanism is invoked. The call (1) will be bound with Car::showFeatures() in the case (2) and with Van::showFeatures(), in the case (3). In both cases, the appropriate attributes will be shown.

6.4. Pure virtual functions and abstract classes We stated in Chapter 1 that a class is the implementation of a type. There are occasions in which this implementation should not be complete. A class which provides an incomplete implementation of a type is called a deferred or an abstract class. Since the implementation of an abstract class is not complete it has no sense to create instances of that class. Abstract classes often correspond to very general classes at the root of a class hierarchy. They provide an incomplete implementation because at that abstract level it is not known how to implement some class operations or how to represent the class itself. Abstract classes define a set of operations. Some (or all) of these operations have not implementation and will be overridden by their subclasses. Therefore, abstract classes define an interface containing some operations that must be implemented by their subclasses. As a consequence of this idea we get one of the most interesting issues concerning abstract classes:

Abstract classes allow a powerful polymorphic behaviour: we know which operations can be applied to any subclass of an abstract class, even if we do not know which specific subclass will be provided at run time.

Consider the following three examples of abstract classes: • The class RegularPolygon can be an abstract class since some operations defined by this class (e.g., getArea(), getLength() cannot be implemented for a general regular polygon, for which the number of edges is not known. For this reason, it has no sense to create an object of class RegularPolygon. RegularPolygon will have several subclasses (Square, Triangle...) which will be obligated to offer and implement conveniently those operations. • The class Map models a container which stores a set of pairs such that there are not two pairs with the same key (the key is usually a string that identifies the information stored in its associated value). This class should be implemented in such a way that the access to the element that has a specific key is fast. This class may offer operations like the following ones: - m.insertElement(key,value)

- 203 - CHAPTER 6: POLYMORPHISM

- value=m.retrieveElement(key) - m.removeElement(key) - ... However, there exist many ways to implement a map (e.g., with an array of pairs; using a hash technique; using some tree structure –binary search trees, B trees–; etc.). Depending on the implementation strategy that we take we will implement the previous operations in one way or another. At the level of the Map class, we do not have enough information to implement them. However, it is a good idea to define the Map class as the root of the hierarchy of all map implementations since, in this way, we can: - Establish the set of operations that any map implementation should offer. Thanks to this, we will be able to work with maps in a polymorphically. For example, we may define a function that has a Map as parameter and apply to that parameter any operation defined in the abstract class Map. We can do this even if we do not know which specific implementation of Map will be given at run time:

void workWithMaps(Map& m) { .... m.insertElement(...); x=m.retrieveElement(...); m.removeElement(...); ... } This will be explored in problem 6.11. - Implement those elements which are common to all implementations (e.g., an operation to get the number of pairs in the map can be implemented here). Each different implementation strategy of the Map class can be defined as a Map subclass which will implement the Map operations according to the specific implementation strategy. • Finally, the class Vehicle itself could be defined as an abstract class if in our application we were only interested in creating instances of more concrete classes (such as Car or Lorry). This means that the fact that a class is defined as abstract or not may depend on the context in which it is defined.

Abstract classes in C++ are defined in the following way:

- 204 - CHAPTER 6: POLYMORPHISM

Definition (Abstract class in C++) An abstract class in C++ is a class which has at least one operation defined in the following way: virtual ReturnType f(...)=0; These operations which are virtual and equalled to zero are called pure virtual functions. A pure virtual function cannot have any implementation in the abstract class. It must be overridden in its descendants. There cannot be any object which is an instance of an abstract class

Example class RegularPolygon{

int edgeNb; float edgeLength; public: RegularPolygon(int edgen, float edgel) { edgeNb=edgen; edgeLength=edgel; } int getEdgeNumber(){return edgeNb;} float getEdgeLength(){return edgeLength;} virtual float getArea()=0; }; class Square :public RegularPolygon{ public:

Square(float edgel) :RegularPolygon(4,edgel) {} virtual float getArea() { return getEdgeLength()*getEdgeLength(); } }; class Triangle :public RegularPolygon{

float height; public: Triangle(float edgel) :RegularPolygon(3,edgel) { height=sqrt(edgel*edgel-edgel*edgel/4); } virtual float getArea(){return getEdgeLength()*height/2;} };

- 205 - CHAPTER 6: POLYMORPHISM

Remarks: • The class RegularPolygon has one pure virtual function (getArea(), hence it is an abstract class. It is not possible to define instances of RegularPolygon: RegularPolygon rp; is incorrect. • It is not necessary that all operations defined for an abstract class are pure virtual. If at this level of definition the information to implement some operation is already available, it can be implemented (this is what happens with getEdgeLength() and getEdgeNumber()). However, these operations will not be called directly on objects of class RegularPolygon (which cannot exist) but on its subclasses. • The constructor of a class cannot be virtual (more on this in section 6.5). Notice that the constructor of RegularPolygon is called from the constructor of its subclasses. • The pure virtual function getArea() is overridden in each one of the two subclasses (in them it is possible to know how to calculate this area). Notice that it is possible to define instances of Triangle and Square, since all its operations and all the operations that they inherit have some implementation. ♦ ♦ ♦ Example Consider the following code. template class Map{ int elemNb; public:

Map(){elemNb=0;} int getNbElements() {return elemNb;} virtual void insertElement(char* key, const T& value)=0; virtual T retrieveElement(char* key)=0; virtual void removeElement(char* key)=0; }; template class Pair{ public: char* key; T val; }; template class ArrayMap :public Map{

Pair v[N];

public:

- 206 - CHAPTER 6: POLYMORPHISM

ArrayMap() :Map() {} virtual void insertElement(char* key, const T& value){...} virtual T retrieveElement(char* key){...} virtual void removeElement(char* key){...} }; template class HashMap :public Map{

//Representation of a Map as a Hash table.....

public:

HashMap(....){...} virtual void insertElement(char* key, const T& value){...} virtual T retrieveElement(char* key){...} virtual void removeElement(char* key){...} };

It contains an abstract base class called Map (which, in addition, is a generic class) that declares several pure virtual functions which are overridden in its subclasses. Each subclass (i.e., HashMap and ArrayMap) provides a specific implementation of a map. Most implementation details have been skipped. ♦ ♦ ♦

6.5. Virtual constructors and destructors In some occasions it may be useful that a class constructor or destructor is virtual. In this section we discuss how to deal with this topic.

6.5.1. Virtual constructors The constructors of a class cannot be virtual. Recall that a virtual function call is bound to a function code at run time depending on the type of the object on which it has been called. Since, in this case, the object has not been created yet (this is precisely the purpose of the constructor), its execution-time type cannot be determined. Is this a limitation? Usually, when we create an object we know the type of that object at compilation time, since we are its creators. Is there any situation in which we may need a virtual constructor? This can happen if we have to make a copy of an already created object whose execution-time type is not known at compilation time. For example, a function void foo(Vehicle& v) needs to make a copy of its argument. However, its argument may be of any class within the Vehicle hierarchy, therefore the following solution is not correct:

- 207 - CHAPTER 6: POLYMORPHISM

void foo(Vehicle& v) { Vehicle* vcopy; //...... vcopy=new Vehicle(v); //.... }

This will create a dynamic object of type Vehicle and will make the pointer vcopy point to that object. But what will happen if v, at run time is of a descendant of Vehicle (e.g. Car)? The solution is to use the clone() function presented in Chapter 4, which makes a copy of the object on which it is called. All classes in the Vehicle hierarchy should define a clone() function. These functions should be virtual in order to achieve the required polymorphic behaviour. class Vehicle{ //..... public: //..... virtual Vehicle* clone() { Vehicle* aux;

aux=new Vehicle(brand,model,year); return aux; } //..... }; class ProfessionalVehicle :public Vehicle{ public:

//..... virtual Vehicle* clone() { Vehicle* aux;

getBrand(br); getModel(mod); aux=new ProfessionalVehicle(br, mod,getYear(), companyOwner, maxLoad); return aux; } //..... };

Notice that from the class ProfessionalVehicle it is not possible to access the private attributes of the class Vehicle. For this reason, the accessors to those

- 208 - CHAPTER 6: POLYMORPHISM attributes are used (getBrand(...), getModel(...), getYear(...) ). If the attributes of the class Vehicle had been defined as protected, the use of the ’get’ functions would not have been necessary. If the classes in the Vehicle hierarchy have defined a copy constructor (which is the right thing), the implementation of the clone() operation becomes easier: class ProfessionalVehicle :public Vehicle{ public:

//..... virtual Vehicle* clone() { return new ProfessionalVehicle(*this); } //..... };

Section 6.9 shows a practical usage of object clonation.

6.5.2. Virtual destructors Destructors can be virtual, thus it is possible to deallocate an object, even if the type of that object is not known at compilation time. Consider the following function, which has to deallocate an object whose type is not known at compilation time. void foo(Vehicle* v) { //...... delete v; //.... }

This will work properly if the destructors of the classes in the Vehicle hierarchy have been defined virtual: class Vehicle{ //..... public: //..... virtual ~Vehicle() { delete [] brand; delete [] model; } //..... };

- 209 - CHAPTER 6: POLYMORPHISM

class ProfessionalVehicle :public Vehicle{ public:

//..... virtual ~ProfessionalVehicle() { delete [] companyOwner; } //..... };

Recall that destructors of derived classes call implicitly the destructors of their base classes. Again, section 6.9 shows a practical use of virtual destructors.

6.6. Multiple inheritance Sometimes, complex relationships arise between the classes that model reality. One of those is multiple inheritance, which comes up when a class may be seen as a particular case of more than one class, thus it may have more than one parent. For example, in the context of a car rental company, it may make sense the class RentCar is both a Car and a RentableObject (another class that may come up in the design of the application for the company). This idea is presented in fig. 6.5.

Figure 6.5: Multiple inheritance

It is clear that an instance of RentCar should inherit the members that may be applied to it as a Car (getMaxSpeed(), getDoorNumber(), getModel()...) and as a RentableObject (getRentId(), getCurrentRenter()...). Hence, the following program should be correct:

- 210 - CHAPTER 6: POLYMORPHISM int main() { int dn, id; RentCar c(....);

dn=c.getDoorNumber();

id=c.getRentId();

return 0; }

The notation that C++ uses in order to define a class that inherits from more than one other classes is the following: class RentCar :public Car, public RentableObject{ //..... public: RentCar(....) { //.... } //..... };

Sometimes, multiple inheritance is used to associate to an object-actor the multiple roles he/she may play. For instance, if we want to model a software development team, we may consider classes such as Designer, Tester, Programmer, ProjectManager..... If in some projects, the same individual must play two roles, it may be appropriate to use multiple inheritance to model such a situation, as in the following example. class Programmer{ public:

void program(){....}

} class Tester{ public:

void test(){....} } class ProgrammerTester :public Programmer, public Tester { //.... }

- 211 - CHAPTER 6: POLYMORPHISM int main() { ProgrammerTester pt (...);

pt.program(); pt.test(); }

This idea of using multiple inheritance to model the different roles or groups of functionalities that an object may exhibit is sometimes expressed by means of interfaces (see Chapter 1 and section 6.4). It is fundamental to distinguish between the is-a and has-a relationships. For instance, a Programmer is a Person, hence, it may be correct to model the class Programmer as a subclass of Person. On the other hand, a programmer works for a Company, but we will not model the class Programmer as a subclass of Company (as in figure 6.6(a) ). A correct modelling is the one presented in fig. 6.6(b).

Figure 6.6: A design flaw

Multiple inheritance has two inherent and important problems: 1. Inheritance of two operations with the same signature from both parents 2. Inheritance of a member from a common ancestor using two different paths. We will present them in the following sections.

6.6.1. Problem 1: inheritance of different members with the same signature Each one of the two (or more) superclasses of a class may have defined a specific operation with the same signature (header) and possibly with different meanings. As a result, the subclass will inherit the same signature from two (or more) different

- 212 - CHAPTER 6: POLYMORPHISM classes. This will lead to an ambiguity when those operations are called on the object of the subclass. This idea is illustrated in the following example (see fig. 6.7): we add to the class Car the operation int getPrice() which obtains the price of that car. In addition, we add to the class RentableObject the operation void getPrice() which gets the price of one week rental of that object. As a consequence, the class RentCar will inherit both operations and will consider ambiguous a call to getPrice() on a RentCar object.

Both operations are inherited by RentCar. Which one is called? Figure 6.7: Ambiguity caused by multiple inheritance class RentableObject{

int weekRentPrice;

//..MORE ATTRIBUTES public:

RentableObject(int weekpr){ weekRentPrice=weekpr; }

virtual int getPrice(){ return weekRentPrice; }

//MORE OPERATIONS....

};

- 213 - CHAPTER 6: POLYMORPHISM class Car{

int purchasePrice; //MORE ATTRIBUTES.... public:

Car(int ppr){ purchasePrice=ppr; }

virtual int getPrice(){ return purchasePrice; }

}; class RentCar :public Car, public RentableObject{

public:

RentCar(int purchasep, int rentp) :Car(purchasep),RentableObject(rentp) {} //MORE OPERATIONS AND ATTRIBUTES..... }; int main() { int p; RentCar c(8000,200);

p=c.getPrice(); ///!!!!!AMBIGUOUS return 0; }

We may adopt different strategies to solve this problem: 1. Qualify the ambiguous operation when it is called on a subclass object: int main() { int p,p2; RentCar c(8000,200);

p=c.RentableObject::getPrice(); // Rental price per week: 200

p2=c.Car::getPrice(); // Purchase price: 8000 }

This solution allows a polymorphic behaviour for calls in many cases:

- 214 - CHAPTER 6: POLYMORPHISM void foo1(RentableObject& ro) { ro.getPrice(); }

The call to ro.getPrice() within foo1(ro) works properly both if ro is an instance of RentableObject and if it is an instance of RentCar. In both cases it will call RentableObject::getPrice() without any ambiguity. On the other hand, the function: void foo2(Car& c) { c.getPrice(); }

The call to c.getPrice() within foo2(c) works properly both if c is an instance of Car and if it is an instance of RentCar. In both cases it will call Car::getPrice() without any ambiguity. However, if we have a base class CompanyObject for all the classes we have defined (see fig. 6.8), then the call to o.getPrice(...) within foo3(o) would be ambiguous. void foo3(CompanyObject& o) { o.getPrice(); //!!!!!!AMBIGUITY!!!! }

Figure 6.8: The ambiguity persists

- 215 - CHAPTER 6: POLYMORPHISM

2. Define different operation names in the subclass: class RentCar :public Car, public RentableObject{ public:

int getPrice(){return RentableObject::getPrice();} int getPurchasePrice(){return Car::getPrice();} };

This removes the ambiguity of the code: int main() { int p; RentCar c(8000,200);

p=c.getPrice(); //Week rental price p2=c.getPurchsePrice(); //Purchase price

}

However, it does not allow the use of polymorphism in order to get the purchase price of a rent car: void foo1(Car& c) { c.getPrice(); //We want to get the purchase price } int main(){

RentCar rc(8000,200);

foo1(rc);

return 0; }

The call to c.getPrice() within foo1(rc) will get the week rental price, not the purchase price as it could be expected.

- 216 - CHAPTER 6: POLYMORPHISM 6.6.2. The same member inherited along different paths A different problem arises with those members (e.g., x) defined in an ancestor A of the subclass D, which is, at the same time, ancestor of both superclasses of D (B and C). This situation forms a diamond as the one depicted in fig. 6.9. In this case, class D inherits the member x along two different paths: A-B-D and A- C-D. The question is: D should inherit x one or twice? In most cases we will want that D inherits x just once. However, there are cases in which it would be better that x was inherited twice. C++ allows both possibilities.

Figure 6.9: Diamond in multiple inheritance

In the following we present each one of them: Virtual inheritance The most usual case is that in which the subclass D should inherit just one copy of the members of A. This behaviour will be obtained declaring B as a virtual subclass of A and C as a virtual subclass of A: class A{ public: int x; }; class B :public virtual A{}; class C :public virtual A{}; class D :public B, public C{};

- 217 - CHAPTER 6: POLYMORPHISM

int main() { D d;

d.x=10; }

In the example of vehicles, we could define in the common ancestor CompanyObject a member called identifier, which would indicate that all kinds of company objects should have an identifier. This member should not be duplicated in classes such as RentCar which have been obtained from multiple inheritance. Therefore, the inheritance will be virtual: class CompanyObject{

int identifier; //More attributes and operations }; class Car :public virtual CompanyObject{...}; class RentableObject :public virtual CompanyObject{...}; class RentCar :public RentableObject, public Car{...}; int main(){ RentCar rc(...);

rc.identifier=1000; //O.K. }

Some problems come up when explicit calls to superclass constructors have to be done in the context of virtual multiple inheritance. Section 6.8 presents a complete example to gain a deeper insight into this problem. Duplicated inheritance In limited cases we want to inherit the members of the common ancestors twice. In such cases, we get the so called duplicated inheritance. This is exactly what we have if the label virtual is not used in the definition of the inheritance. The class D inherits two copies of the member x. For this reason, the call to the member x on an object of class D will be ambiguous: int main() { D d; d.x=10; ///!!!!ERROR AMBIGUITY!!! }

This ambiguity can be solved by qualifying the member x with the class from which it is inherited:

- 218 - CHAPTER 6: POLYMORPHISM

• The copy of the member x which the object d inherits from the class B: d.B::x=10; • The copy of the member x which the object d inherits from the class C: d.C::x=100; In the car rental company example, this behaviour could be interesting if a member responsible is defined for the class CompanyObject. The class Car would inherit this member with the meaning “person who is responsible for the car maintenance”. However, the class RentableObject would inherit the member responsible with the meaning “person who takes care of the administrative procedures concerning the rental of this object”. A rent car should have both responsible persons, hence it could be acceptable the use of a duplicated inheritance. However, it is not as clear as the non-duplication case.

6.7. Polymorphic behaviour of friend functions Some operators are always (e.g., <<) or, at least, often (e.g., ==) implemented as a friend function instead of as a member function (i.e., an operation). This fact raises the difficulty that friend functions cannot be declared virtual (only member functions can); hence, they cannot behave polymorphically. How is it possible to program an operator implemented as a friend function (or any other friend function) so that it behaves polymorphically? A simple answer consists in calling a virtual member function from the friend function. Consider the following example: class Vehicle{

//.... public: //... virtual void showFeatures(ostream& c){ c<<"Brand: "<

friend ostream& operator<<(ostream& c, Vehicle& v); }; ostream& operator<<(ostream& c, Vehicle& v) { v.showFeatures(c); return c; }

Notice that the task of sending the description of v to the stream c has been delegated to the virtual function showFeatures(c). The call

- 219 - CHAPTER 6: POLYMORPHISM v.showFeatures(c); will be associated to the function corresponding to the run time type of v, achieving thus a polymorphic behaviour. Notice also that it is not necessary to define the operator << as a friend function of each specific subclass of Vehicle. The previous overloaded << operator will be called for any object v which is a subclass of Vehicle.

6.8. Guided problem: Multiple inheritance. A double hierarchy of persons

6.8.1. Presentation of the double hierarchy Implement a double hierarchy: Person superclass of Married, Single, Widow, Divorced and Person superclass of Child, Young, MiddleAged, Old. Remarks: • The class MarriedPerson should include an attribute partnername and an operation with the following header: char* MarriedPerson::getName() which returns the name of the partner of the person to which it is applied. • The class MiddleAgedPerson should include an attribute bankname and an operation with the following header: char* MiddleAgedPerson::getName() which returns the name of the bank where this person has his/her accounts. • Make all the attributes of all classes protected. Solution

#include #include #include "Date.h" using namespace std; class Person{ protected: char passportId[9]; int age; Date birthdate; char* name;

public:

Person(){}

- 220 - CHAPTER 6: POLYMORPHISM

Person(char* ppassid, int page, Date& pbirth, char* pname) :birthdate(pbirth) { age=page; strcpy(passportId,ppassid); name=new char[strlen(pname)+1]; strcpy(name,pname); }

~Person() { delete [] name; } virtual void printFeatures(ostream& c) { c<<"passport="<

MarriedPerson p2 ("11111111",27,birth2,"Ann",marrd,"Mark"); MiddleAgedPerson p3("11111111",27,birth2, "Peter","International Bank");

cout<<"Married: partner name: "<

cout<<"Middle aged: bank name: "<

MiddleAgedMarried p4("11111111",27,birth2,"Ann",marrd, "Mark","intercontinental bank");

cout<<"Middle aged and married person: bank name :" //(3) << p4.MiddleAgedPerson::getName()<

cout<<"Middle aged and married person: partner name :" //(4) << p4.MarriedPerson::getName()<

Remarks: • Since p2 is a MarriedPerson, (1) writes the name of the partner of p2 (i.e., “Mark”) • Since p3 is a MiddleAgedPerson, (2) writes the name of the bank of p3 (i.e., “International Bank”) • Since p4 is a MiddleAgedMarriedPerson, the call p4.getName() would be ambiguous. For that reason, we prefix the call with the name of the class

- 223 - CHAPTER 6: POLYMORPHISM

in which the getName() operation that we want to call is defined: hence, (3) gets the bank name and (4), the partner name. Another possibility would have been to define two new operations in MiddleAgedMarriedPerson:

char* MiddleAgedMarriedPerson::getName() { return MarriedPerson::getName(); }

char* MiddleAgedMarriedPerson::getBankName() { return MiddleAgedPerson::getName(); }

If this had been done, we could have accessed the partner name and the bank name without the prefix of the class to which the operation belonged:

p4.getBankName(); //gets the bank name p4.getName(); //gets the partner name

However, it would have compromised the polymorphism (see below). • Notice that the constructor of MiddleAgedmarried calls explicitly the constructors of MarriedPerson and MiddleAgedPerson.

6.8.3. Virtual inheritance The implementation that we have provided in the last section has a problem: how many instances of the attributes of the class Person (name, age, personId, birthdate are inherited by an instance of the class MiddleAgedMarried? If we want that it inherits just one instance of them, what could we do? Solution Any instance of the class MiddleAgedMarried will inherit two instances of each of the attributes/operations of Person. This is due to the fact that, by default, attributes and operations of the root common class are inherited following two different paths: Person-MarriedPerson-MiddleAgedMarried and Person-MiddleAgedPerson-MiddleAgedMarried. For this reason, there would be an ambiguity in a call to an operation inherited from Person (for example, p4.getPersonName() in the main function). In order to make it precise, we should do:

- 224 - CHAPTER 6: POLYMORPHISM p4.MarriedPerson::getPersonName(); which refers to the getPersonName() function inherited through MarriedPerson or p4.MiddleAgedPerson::getPersonName(); which refers to the getPersonName() function inherited through MiddleAgedPerson. int main() { MiddleAgedMarried p4(....); char* nam; nam=p4.getPersonName(); //ERROR: AMBIGUOUS CALL nam=p4.MiddleAgedPerson::getPersonName(); //Correct nam=p4.MarriedPerson::getPersonName(); //Correct }

The problem is even more apparent with attributes: the attributes defined in the class Person are duplicated in an instance of the class MiddleAgedMarried because they are inherited along two different paths. Therefore, p4 has two names, two ages... If they were defined as public in the class Person it would be possible to do: int main() { MiddleAgedMarried p4(....);

p4.age=80; //ERROR: AMBIGUOUS ATTRIBUTE p4.MiddleAgedPerson::age=90; //Correct (if age was public) p4.MarriedPerson::age=70; //Correct (if age was public) }

It is clear that, according to the usual semantics of these attributes in the class Person, this is not acceptable. It can be solve by making the inheritance virtual, as follows: class Person{ //as before }; class MarriedPerson :virtual public Person{ //as before }; class MiddleAgedPerson :virtual public Person{ //as before };

- 225 - CHAPTER 6: POLYMORPHISM class MiddleAgedMarried :virtual public MiddleAgedPerson, virtual public MarriedPerson {

public:

MiddleAgedMarried(char* ppassid, int page, Date& pbirth, char* pname, Date& pmarr, char* ppart, char* pbank): Person(ppassid,page,pbirth,pname), MiddleAgedPerson(ppassid,page,pbirth,pname,pbank), MarriedPerson(ppassid,page,pbirth,pname,pmarr,ppart) { }

virtual void printFeatures(ostream& c) { MarriedPerson::printFeatures(c);

c<<"Bank name="<

nam=p4.getPersonName(); //Now it is correct!! p4.age=90; //Also correct (if age was declared //public in Person)

}

A consequence of the use of virtual inheritance is that when an object of the class MiddleAgedMarried is called, the constructors of the classes MiddleAgedPerson and MarriedPerson (which are called by the constructor of MiddleAgedMarried) cannot call the constructor of Person (otherwise, that constructor would be called twice and would duplicate the inherited attributes and operations). As a result, the constructor of class Person is explicitly called from the constructor of MiddleAgedMarried:

MiddleAgedMarried(char* ppassid, int page, Date& pbirth, char* pname, Date& pmarr, char* ppart, char* pbank): Person(ppassid,page,pbirth,pname), MiddleAgedPerson(ppassid,page,pbirth,pname,pbank), MarriedPerson(ppassid,page,pbirth,pname,pmarr,ppart) { }

If it is not done like this, the constructor of MiddleAgedMarried will call the default constructor of Person.

- 226 - CHAPTER 6: POLYMORPHISM

6.8.4. Polymorphism and virtual inheritance (1) The following function (test(p)) is a user of the class Person. What is necessary in order to ensure that the function test(p) prints properly the features of a person regardless of the specific type of the person parameter (p)? That is, if the person is married, test should print the marriage date and the partner name. On the contrary, if the person is a divorced person, test should print the last divorce date....). void test(Person& p) { p.printFeatures(cout); cout<<"------\n\n"<

Solution Two things are necessary: • The operation printFeatures(c) defined in the class Person should be declared virtual so that it may behave polymorphically (with a late binding between the call and the implementation). • The function test(p) should get as parameter either a reference or a pointer to Person. Since both issues hold, the following main() function will show the expected behaviour: int main() { bool err; Date birth(10,"APR",1990,err); Date birth2(10,"MAI",1980,err); Date marrd(10,"APR",2001,err); Date marrd2(1,"MAI",2000,err);

Person p1 ("12345678",21,birth,"Joe");

MarriedPerson p2 ("11111111",27,birth2,"Ann",marrd,"Mark");

MiddleAgedPerson p3("11111111",27,birth2,"Peter", "International Bank");

MiddleAgedMarried p4("12345678",21,birth,"Joe",marrd2,"Paula", "Commerce bank");

cout<<"***Polymorphism of the action test****\n\n"<

cout<<"Features of a Person"<

cout<<"\nFeatures of a Married Person"<

- 227 - CHAPTER 6: POLYMORPHISM

test(p2); cout<<"\nFeatures of a Middle-aged Person"<

test(p3); cout<<"\nFeatures of a Middle-aged and Married Person"<

The result will be:

***Polymorphism of the action features****

Features of a Person passport=12345678 name=Joe age=21 birth date=10-APR-1990 ------Features of a Married Person passport=11111111 name=Ann age=27 birth date=10-MAI-1980 marriage date=10-APR-2001 partner name=Mark ------Features of a Middle-aged Person passport=11111111 name=Peter age=27 birth date=10-MAI-1980 bank name=International Bank ------Features of a Middle-aged and Married Person passport=12345678 name=Joe age=21 birth date=10-APR-1990 marriage date=1-MAI-2000 partner name=Paula Bank name=Commerce bank ------

- 228 - CHAPTER 6: POLYMORPHISM

6.8.5. Polymorphism and virtual inheritance (2) Consider now the following couple of functions: void partnerName(MarriedPerson& p) {

cout<<"Person name="<

MarriedPerson p2 ("11111111",27,birth2,"Ann",marrd,"Mark");

MiddleAgedPerson p3("11111111",27,birth2,"Peter", "International Bank");

MiddleAgedMarried p4("12345678",21,birth,"Joe",marrd2,"Paula", "Commerce bank");

cout<<"***Polymorphism of the actions partnerName " <<"and bankName****\n\n" <

partnerName(p2); //(1) bankName(p3); //(2)

partnerName(p4); //(3) bankName(p4); //(4) }

• Taking into account that MiddleAgedMarried inherits two operations getName():

- 229 - CHAPTER 6: POLYMORPHISM

MarriedPerson::getName() and MiddleAgedPerson::getName() Are the calls to getName() done within (3) and (4) ambiguous? Why? • Which will be the result of the execution of the main function above? Solution There is no ambiguity in those calls: partnerName(p)takes as parameter a reference to a MarriedPerson. Therefore p.getName() is a call to MarriedPerson::getName(), and shows the partner name (even in the case (3) in which p is linked to an object of the class MiddleAgedMarried. Put it another way, the function partnerName(p) considers p strictly as a MarriedPerson and does not pay attention to other features that p may have inherited from other lines that do not include MarriedPerson. The same argument may be used for bankName(p) which will print the bank name of p4, regardless of the fact that p4 also inherits getName() from MarriedPerson. Notice that: • there would have been an ambiguity in the call partnerName(p4;) if this function had been defined as follows: void partnerName(Person& p) {

cout<<"Person name="<

s.insertTop(Student("Anna",20,'f')); //Call with a temporal object

s.insertTop(st); //Call with an object declared as constant

.... }

- The header of getTop(): T* getTop() (specifically the return type: T*) deserves a more detailed explanation. We have three possibilities to declare the return type of this operation: * T getTop() const; The returned object will be copied (using the copy constructor of the class T) to a temporal object of the class T. For that reason, if the object that has been actually returned is an instance of a subclass of T, there will be a loss of information. This choice should not be used if a polymorphic behaviour is required. * T& getTop() const; In this case, a reference to an object, which has been computed within getTop() is returned. If that object is an instance of a subclass of T, that instance will be returned; thus, everything is correct.

- 232 - CHAPTER 6: POLYMORPHISM

However, there may be a problem related to the way in which this function will be used: If we do something similar to: T x; x=s.getTop(); This code performs a call to the operator = of T. This operator will lead to an information loss if the reference returned by getTop() refers to an object of a subclass of T. If the operation is used as follows, there will be no problem: foo(s.getTop()); with: void foo(T& x){...} * T* getTop() const; This operation does not have any problem, since: T* x; x=s.getTop(); does not mean a call to the operator = of the class T but just a copy of pointers. Therefore, we will get a polymorphic behaviour regardless of the way in which it is used. The differences between the three operations are shown in figure 6.10.

- 233 - CHAPTER 6: POLYMORPHISM

(1): T p; p=f(); f() returns an object by value

(1) T f() T p; { T x; p=f(); ...get the appropriate element on x Returns a copy of x on a return x; (2) temporal object (tmp) p=tmp; } created with the copy constructor: (3) T tmp(x);

   Problems if S x; instead of T x; x: tmp: p:    (Copy of x obtained (where S is a subclass of T)       with T::operator=(...) )   

(2): T p; p=f(); f() returns a reference

(1) T p; p=f(); T& f() { Returns a reference to *x on a T* x=new T; temporal reference object: p=tmp; ...get the appropriate element on *x (3) return *x; T& tmp=*x; (2) }    p:  tmp, x:  (Copy of *x obtained Problems if S* x; instead of T* x;     with T::operator=(...) ) (where S is a subclass of T)   if we assign the result of f() to another object

(3): T* p; p=f(); f() returns a pointer

T* f() T* p; (1) { p=f(); T* x=new T; ...get the appropriate element on *x Returns a pointer to *x on a return x; temporal pointer: p=tmp; (2) (3) } T* tmp=x;

  p: x:  No problems!!  tmp: (p points to the same object as x)  Figure 6.10: Three different headers for getTop()

• Representation (1): Since the capacity of the stack is restricted to N=100 elements and insertions/deletions are carried out at the end of the list, it seems reasonable to implement it as an array. Furthermore, since the insertions/deletions take place at the end, we may record the number of elements of the stack (and the point of the array in which the insertions/deletions should occur with an index top):

- 234 - CHAPTER 6: POLYMORPHISM

const int N=100; template class Stack{ T v[N]; //NOT COMPLETELY CORRECT!!! SEE BELOW int top; public: //... };

• Representation (2): Each index of the array v should be prepared to store an object of class T or any subclass of T. However, an array declared T v[N] reserves for each index the amount of memory necessary to allocate an object of class T. This makes it impossible to allocate there an object of a subclass of T, which may have more attributes defined. For example, T may be instantiated with Vehicle, which has the attributes brand, model and year. Car, a descendant of Vehicle adds to the attributes inherited from Vehicle, owner, colour, doornumber and maxspeed. The class Stack could be defined in the following way: template class Stack{ T* v[N]; int top; public:

Stack(); ~Stack(); void insertTop(const T& x); void removeTop(); T* getTop() const; bool isEmpty() const; };

Each index of the array v contains a pointer to an object of the class T, which (by the application of the substitution principle) may point to any instance from a class descendant of T (see fig. 6.11).

- 235 - CHAPTER 6: POLYMORPHISM

Figure 6.11: Stack representation

• Operation implementation. insertTop(...): Since we should make a copy of the argument to be inserted and since we do not know at compilation time the type of that argument (it can be any descendant of T), we cannot do something of the sort: void Stack::insertTop(const T& x) { v[top]=new T(x); // INCORRECT: // new T? or one of its subclasses??? top++; }

What we need is an operation that produces an exact copy of the object on which it is called. This operation should behave differently according to the run-time type of the object on which it is called. Therefore, we will use the clone() operation of class T in order to copy the argument x. This operation should be defined as virtual: void Stack::insertTop(const T& x) { v[top]=x.clone(); // CORRECT! top++; }

Some controls to avoid an array overflow are left to the reader.

- 236 - CHAPTER 6: POLYMORPHISM

Notice that any class that instantiate T at run time will have to implement a clone() operation. This remark must be made explicit in the specification of the class List. • Rest of operations:

Stack::Stack(){top=0;} Stack::~Stack() { int i;

for(i=0;i::insertTop(const T& x) { v[top]=x.clone(); top++; } void Stack::removeTop() { top--; delete v[top]; }

T* Stack::getTop() const { T* x;

x=v[top-1]->clone();

return x; } bool Stack::isEmpty() const { return top==0; }

• User program: int main() { Stack lv;

Car c(...); Lorry lo(...);

lv.insertTop(c); lv.insertTop(lo);

- 237 - CHAPTER 6: POLYMORPHISM

while (!lv.isEmpty()) { cout<<*(lv.getTop())<

}

• Class Vehicle: The class Vehicle and all its subclasses should implement a virtual operation clone():

virtual Vehicle* Vehicle::clone() { return new Vehicle(*this); }

virtual Vehicle* Car::clone() { return new Car(*this); }

This implementation of clone() assumes that the classes Vehicle and Car have a copy constructor. If this was not the case, the implementation would be different; how?

6.9.3. An improvement to the class Stack Now, remove the assumption that the list has a maximum capacity of N. Allow the programmer to decide the capacity of the list whenever it is created.

Solution: • Main idea: The main idea of the solution is that the array should be reserved dynamically. • Operations: The constructor header will change as follows: Stack(int cap ); • Representation: In order to be able to do something like: v=new T*[cap]; we should have the following representation:

- 238 - CHAPTER 6: POLYMORPHISM template class Stack{ T** v; int capacity; int top; public:

Stack(int capac); ~Stack(); void insertTop(const T& x); void removeTop(); T* getTop() const; bool isEmpty() const; };

• Implementation of Stack(int capac)

Stack::Stack(int capac){ top=0; capacity=capac; v=new T*[capac]; }

Instead of T* we could have used another class:

template class TPointer{ public: T* a; };

and then: template class Stack{ TPointer* v; int capacity; int top; //.... };

6.10. Guided problem: A polymorphic hierarchy of containers 6.10.1. The problem A container class is a class whose instances contain collections of elements of a certain type. For example, a stack is a container class since a stack instance contains a collection of elements of a certain type structured in a LIFO (Last Input, First

- 239 - CHAPTER 6: POLYMORPHISM

Output) way. Different kinds of containers may structure elements in different ways. The objective of this problem is to design and implement the hierarchy of containers of figure 6.12.

Figure 6.12: Container hierarchy 6.10.2. Why this hierarchy? This hierarchy illustrates some benefits of the polymorphism in OOP. Let us have a look to the meaning of the different classes in the hierarchy: 6.10.3. Abstract class Container This is an abstract class that constitutes the root of the container hierarchy. It defines those operations that may be applied to any container, regardless of the particular way in which that container structures its constituent elements. These operations may be the following ones: • unsigned int getSize() const; Returns the number of elements that constitute the container object when this operation is called • bool isEmpty() const; Returns true if the container object does not contain any element and false, otherwise. • bool operator==(const Container& c) const; Compares two container objects. • void setFirst(); Sets the container cursor on the first element of the container object according to some criterion which is not specified (i.e., it may depend to the particular container). This operation and the following three are used in order to iterate over the elements of the container object. This iteration is performed using the so- called container cursor which refers to the current element in the iteration.

- 240 - CHAPTER 6: POLYMORPHISM

• T* getCurrent() throw (NoObjectException); Returns a pointer to the element referred to by the container cursor (i.e., the current element in the iteration process). • void setNext() throw (NoObjectException); Moves the container cursor to the next element of the container cursor. This next element is determined using a criterion which is not specified. • bool isEnd(); Returns true if the iteration process has finished. Since Container is an abstract class: 1. We can assume that, at least, some of those operations will not be implemented. For example, the way to implement the operation setFirst(), getNext()... will largely depends on the representation of the particular container descendant of the class Container. 2. it will not be possible to create instances of this class: Container c; is forbidden. This root class Container is useful for at least two reasons: • The remaining classes will inherit all these operations. That is, it will be possible to apply the operations isEnd(), getSize()... to any object instance of any class that belongs to this hierarchy. • The user of this hierarchy will be able to use objects without knowing their actual specific type: Consider a function of a library implemented by ACME-Software corp. with the following header: Container* foo(); This function returns a container with some elements inserted in it. The specification of this function deliberately does not say the specific kind of container that is returned by it. This strategy gives more freedom to the implementers (also the freedom to change it in a future version of the library) and does not burden the user with a information he/she does not need. For example: Container* pc; pc=foo(); At this point, the user does not know whether the exact type of pc is an ArrayStack or a LinkedStack or may be some other type of container that we have not considered in this example. However, nothing prevents the user from applying to pc the operations defined for Container. For instance, the user can perform an iteration over pc even without knowing its specific type: T* x;

- 241 - CHAPTER 6: POLYMORPHISM

pc->setFirst(); while(! pc->isEnd()) x=pc->getCurrent(); pc->setNext();

Implementation of Container class NoObjectException{}; template class Container{ protected: unsigned int nElems;

public:

Container(); virtual ~Container(){} virtual unsigned int getSize() const; virtual bool isEmpty() const; virtual bool operator==(const Container& c) const =0; virtual void setFirst() =0; virtual T* getCurrent() throw (NoObjectException) =0; virtual void setNext() throw (NoObjectException) =0; virtual bool isEnd() =0; };

template Container::Container(){ nElems=0; } template unsigned int Container::getSize() const { return nElems; } template bool Container::isEmpty() const { return nElems==0; }

Remarks • An attribute has been defined (nElems) to count the elements of a container object. It makes sense that this attribute is common to all the container types that may be created now or in the future. For this reason, the operations that deal with this attribute (isEmpty() and getSize()) are implemented here.

- 242 - CHAPTER 6: POLYMORPHISM

• Descendants of Container will need to access the attribute nElems when they add or remove one element of the container object. In order to make this access easier, nElems has been declared as protected. • The remaining operations (particularly, those that are useful to iterate on a container object) are left as pure virtual. They are largely dependant of the representation of specific containers and will be implemented there. 6.10.4. Abstract class Stack Stackis an abstract class that defines those operations that should be associated to stack containers. This class is abstract because there may be different ways to represent stacks (e.g., in linked memory or in sequential memory or in disk...). Each subclass of Stack contains a particular way to represent-implement stacks. Therefore, each Stack subclass will be a concrete class and will implement all the operations: • The pure virtual operations of Container: ==, setFirst, getCurrent, setNext, isEnd • The pure virtual operations of Stack: insertTop, removeTop, getTop

Implementation of Stack class EmptyStackException{}; class FullStackException{};

template class Stack :public Container { public:

Stack(); ~Stack(){} virtual void insertTop(const T&) throw (FullStackException) =0; virtual void removeTop() throw (EmptyStackException) =0; virtual T* getTop() const throw (EmptyStackException) =0; }; template Stack::Stack(){}

6.10.5. Class ArrayStack This class contains the implementation of a stack by means of an array (thus, in sequential memory). This implementation is largely the same as that presented in problem 6.9.

- 243 - CHAPTER 6: POLYMORPHISM

const unsigned int N=100;

template class ArrayStack :public Stack {

T* v[N]; int top; int current;

public:

ArrayStack(); ArrayStack(ArrayStack&); ~ArrayStack(); bool operator==(const Container&) const; virtual void insertTop(const T&) throw (FullStackException); virtual void removeTop() throw (EmptyStackException); virtual T* getTop() const throw (EmptyStackException); void setFirst(); T* getCurrent() throw (NoObjectException); void setNext() throw (NoObjectException); bool isEnd(); }; template ArrayStack::ArrayStack() { top=0; current=-1; } template ArrayStack::~ArrayStack() {} template void ArrayStack::insertTop(const T& t) throw (FullStackException) { if (top==N) throw FullStackException();

v[top]=t.clone(); top++; nElems++; } template T* ArrayStack::getTop() const throw (EmptyStackException) {

if (isEmpty()) throw EmptyStackException(); return v[top-1]->clone(); }

- 244 - CHAPTER 6: POLYMORPHISM template void ArrayStack::removeTop() throw (EmptyStackException) {

if (isEmpty()) throw EmptyStackException();

delete v[top-1]; top--; nElems--; }

template void ArrayStack::setFirst() { current=top-1; } template bool ArrayStack::isEnd() { return current==-1;

} template T* ArrayStack::getCurrent() throw (NoObjectException) { if (isEnd()) throw NoObjectException(); else return v[current]->clone(); }

template void ArrayStack::setNext() throw (NoObjectException) { if (isEnd()) throw NoObjectException(); else current--; } template bool ArrayStack::operator==(const Container& c) const { //EXERCICE FOR THE READER...... }

Remarks • Notice that this implementation implements all the operations left as pure virtual in its ancestors (==, setFirst, getCurrent, setNext, isEnd, insertTop, removeTop and getTop. • A significant addition of this class (with respect to that of problem 6.10) is

- 245 - CHAPTER 6: POLYMORPHISM

the operations intended to iterate on a container object (setFirst, setNext, setEnd, getCurrent). The implementation of these operations is not difficult to follow in the above code. It is based on the definition of an attribute current that points, at each instant, to the current element within the iteration process.

6.10.6. Class LinkedStack This class contains the implementation of a stack by means of linked nodes of dynamic memory. Each node contains a pointer to the element that it stores and a pointer to the following one. Fig. 6.13 shows graphically this representation.

Empty stack: s: nelems: 0

first: NULL

Non empty stack: s: nelems: 3 NULL first:

john ann joan 123456 2223334 1299999 18 19 18 Figure 6.13: Stack representation with linked nodes

Implementation of LinkedStack template class Node{ public: T* val; Node* next; }; template class LinkedStack :public Stack {

Node* first; Node* current;

- 246 - CHAPTER 6: POLYMORPHISM

public:

LinkedStack(); LinkedStack(LinkedStack&); ~LinkedStack(); bool operator==(const Container&) const; virtual void insertTop(const T&) throw (FullStackException); virtual void removeTop() throw (EmptyStackException); virtual T* getTop() const throw (EmptyStackException); void setFirst(); T* getCurrent() throw (NoObjectException); void setNext() throw (NoObjectException); bool isEnd();

};

template LinkedStack::LinkedStack() { first=NULL; current=NULL; }

template LinkedStack::~LinkedStack() { Node* aux;

while (first!=NULL) { aux=first; first=first->next; delete aux; } } template void LinkedStack::insertTop(const T& t) throw (FullStackException) { Node* aux;

aux=new Node; aux->val=t.clone(); aux->next=first; first=aux; nElems++; } template T* LinkedStack::getTop() const throw (EmptyStackException) { if (isEmpty()) throw EmptyStackException(); return first->val->clone(); }

- 247 - CHAPTER 6: POLYMORPHISM

template void LinkedStack::removeTop() throw (EmptyStackException) { Node *aux;

if (isEmpty()) throw EmptyStackException(); aux = first; first= first->next; delete aux; nElems--; } template void LinkedStack::setFirst() { current=first; } template bool LinkedStack::isEnd() { return current==NULL; } template T* LinkedStack::getCurrent() throw (NoObjectException) { if (isEnd()) throw NoObjectException();

return (current->val)->clone(); } template void LinkedStack::setNext() throw (NoObjectException) { if (isEnd()) throw NoObjectException(); else current=current->next; }

template bool LinkedStack::operator==(const Container& c) const { //LEFT AS EXERCICE TO THE READER }

Remarks Notice that the node keeps a pointer to the element, not the element itself. The reason for this is the same as in the ArrayStack case: the stack will be able to store objects of type T or descendants of T (see problem 6.10).

- 248 - CHAPTER 6: POLYMORPHISM

6.10.7. Comparison between both implementations Usually, different implementations of a container provide different features. These different features may make a particular implementation a good choice for a specific purpose and a bad choice for another one. In this case, the ArrayStack implementation is not as flexible as the LinkedStack in the sense that it reserves beforehand an amount of memory which, eventually, may not be used or be insufficient. On the other hand, the LinkedArray only reserves a new chunk of memory to store a new element whenever it is necessary. The system will not do such reservation only if it has run out of memory. However, the ArrayStack would be clearly superior if an operation to consult the ith element of the stack was to be added to the list of stack operations. Accessing the ith element of an array is very efficient ( O(1) ), but in the case of the linked representation, it would mean to traverse the elements until the ith (cost: O(n) ). This could be the implementation for this operation for the class ArrayStack: template T* ArrayStack::getElemPos(int i) { if (i>=getSize() || i<0) throw NoObjectException(); return v[i]; }

6.10.8. Using the stack implementations The ACME software development team may construct a library of functions called StackUsers that provide a collection of functions which work on the stacks that we have defined. One of them can be a function that inserts the n elements of an array into a stack and returns that stack, so that the user can do things on it. Such function may have the following header: template Stack* insertElemsIntoStack(T* x, int n)

Again, the specification of this function, deliberately, does not say a single word about the specific type of the returned object (we know that this specific type cannot be Stack, since this is an abstract class: it should be LinkedStack or ArrayStack). However, this is not a problem for the user, who can work with the returned object without having to know that. The following code converts an array of students (the class Student is defined by means of a name, an id and an age) into a stack and then, iterates on that stack and shows its components:

- 249 - CHAPTER 6: POLYMORPHISM

int main() { int i; Stack* pss; Student vs[4];

Student s0("john","1111",18); Student s1("ann","2222",19); Student s2("eve","3333",18); Student s3("joan","4444",20); Student* ps;

vs[0]=s0; vs[1]=s1; vs[2]=s2; vs[3]=s3;

pss= insertElemsIntoStack(vs,4);

pss->setFirst(); while (!pss->isEnd()){ ps=pss->getCurrent(); cout<<*ps<setNext(); } return 0; }

The actual implementation of insertElemsIntoStack(...) (developed by the ACME team) uses ArrayStack: template Stack* insertElemsIntoStack(T* x, int n) { ArrayStack* s; int i;

s=new ArrayStack;

for(i=0;i

s->insertTop(x[i]); }

return s; } but it could use LinkedStack too. The choice made by the implementers does not concern the user. Moreover, this choice may change in a later version of the library.

- 250 - CHAPTER 6: POLYMORPHISM 6.10.9. And a dynamic cast When we have compared the linked and the sequential (array) implementations we have mentioned that the ArrayStack class could add easily a new operation called getElemPos(i) which would return the ith element of the stack (this operation would also be possible in the linked representation, but with a higher cost). Let us imagine that the class ArrayStack does have the operation getElemPos(i) implemented. The ACME development team may take advantage of this operation in order to offer the following function within the library StackUsers: template T* getBottomElem(Stack& s)

This function returns the element in the bottom of the stack s (i.e., the first element that was inserted in the stack s). This function has been developed by the ACME team to work together with the other library functions (and, in particular, with insertElemsIntoStack). Therefore, the developers of this function know that the parameter s is actually, of type ArrayStack. Thus, they can provide the following implementation: template T* getBottomElem(Stack& s) { if (s.isEmpty()) throw EmptyStackException(); try{ return (dynamic_cast&>(s)).getElemPos(0); } catch (bad_cast) { //Stack of unexpected type!!!!! return 0; } }

6.11. Guided problem: Stack of stacks 6.11.1. The problem In this problem we will show how to use polymorphism in order to create indefinite levels of composite objects. Let us start by defining an abstract class Object with the following pattern: class Object { public: virtual bool operator==(Object& ob)=0; virtual void operator=(Object& ob)=0; virtual Object* clone()=0; };

- 251 - CHAPTER 6: POLYMORPHISM

The idea now is to use this class Object as the superclass of any other user defined class (i.e., everything will be an object). This class can be used in order to create a stack of objects in such a way that an element of that stack of objects can be itself a stack. That is, we should be able to have stacks of stacks of objects, stacks of stacks of stacks of objects (as many times as we want).

6.11.2. Solution hint The basic idea of the solution is to define a class Stack such that, each of its elements can be any kind of object. We can achieve this objective by defining the class Stack as an Object subclass. Since a stack is, itself, an object, a stack can be an element of another stack. The details are shown next.

#include using namespace std; const int N=100; class Object{

public: virtual bool operator==(Object& ob)=0; virtual void operator=(Object& ob)=0; virtual Object* clone()=0; };

/*********************************************** CLASS Stack **********************************************/ class Stack :public Object {

Object* v[N]; int top;

public: Stack(); void insertTop(Object& ob); void removeTop(Object& ob); bool isEmpty(); Object* clone(); void operator=(Object& cobj); bool operator==(Object& cobj); };

Stack::Stack() { top=0; }

- 252 - CHAPTER 6: POLYMORPHISM

void Stack::insertTop(Object& obj) { v[top]=obj.clone(); top++; } bool Stack::operator==(Object& cobj) { int i; bool equals; Stack* co;

co= (Stack*) &cobj;

equals=true; i=0; while(equals && iv[i]); i++; } return equals; } void Stack::operator=(Object& cobj) { //..... }

Object* Stack::clone() { Stack* aux; int i;

aux=new Stack;

for (i=0; iv[i]=v[i]->clone(); } aux->top=top; return aux; }

/*************************************************** class A ********************************************************/ class A :public Object { int attr; public: A(){} A(const A& a){ attr=a.attr;}

- 253 - CHAPTER 6: POLYMORPHISM

Object* clone(){ A* o; o=new A(*this); return o; } void setAttribute(int pattr) { attr=pattr;

} bool operator==(Object& o) { return attr==((A&)o).attr; } void operator=(Object& cobj) { //..... } }; int main() { int i; A x; Stack c, c2;

for (i=0;i<10;i++){ x.setAttribute(i); c.insertTop(x); } x.setAttribute(12);

c2.insertTop(x); c2.insertTop(c);

return 0; }

Notice in this solution that the Object abstract class acts as an interface in the sense that any object class that has to be an object element should conform to the Object abstract class.

- 254 -

Chapter 7 Exceptions

7.1. Exceptions in computer programs What is an exception in a computer program?

Definition (Exception) Exception is unexpected or prohibited situation that prevents successful completion of a function or a method. Unexpected or prohibited situation is not something that should almost never happen or some disastrous situation. It is an exceptional situation (hence the name) which prevents some part of system of doing what it was instructed to do.

These kinds of situations exist in any program, regardless of the programming language. However, not all the programming languages have mechanisms for handling these situations (exceptions). C++ programming language has such a mechanism which simplifies working with exceptions. The code written in C++ is more readable because it is not required to check for exceptions after each method or function call. Exception handling mechanism in C++ is fully object oriented.

Let us start explaining about the exception handling in OOP by first showing the traditional way of dealing with exceptions. One simple class "Calculator" is shown in the next example.

#include using namespace std; class Calculator { double result; int error; //error situation indicator public: Calculator() { result = 0; error = 0; }

int getError() { return error; } double getResult() { return result; }

double add(double value) { result += value; return result; }; double divide(double value); };

- 255 - CHAPTER 7: EXCEPTIONS double Calculator::divide(double value) { if (value != 0) result /= value; else error = 1; //if the divider is zero, //then indicate an exception

return result; } int main() { Calculator calc;

calc.add(9);

calc.divide(3);

//after each call to "divide" operation //we have to check for exceptions

if (calc.getError() != 0) { cout << "Error: You can not divide by zero!" << endl; return; }

calc.add(6);

calc.divide(0); if (calc.getError() != 0) { cout << "Error: You can not divide by zero!!" << endl; return; }

calc.add(2);

cout << "The result is " << calc.getResult() << endl; return 0; }

Our "Calculator" only supports two operations: addition and division. In the "Divide" method one prohibited situation may happen when we try to divide current result by zero. We handled this situation by setting one field defined in "Calculator" class to 1 (field "error"). Of course, by indicating this situation, we did not finish with handling the exception. We have to check "error" field after each call to "Divide()" method, and if it is set to 1 we need to notify the user about the situation. There are several problems with this exception handling approach. First, we use one value (of type "int") for indication of exception. The example shown is trivial and this one integer value is sufficient. But in programs that are more complex, different kinds of exceptions are possible and those exceptions can not be described by only one field.

- 256 - CHAPTER 7: EXCEPTIONS

The second problem is in "main()" function (in the part of program where we want to handle the exception). We unnecessary burdened the code by checking for exception after each method call. Also, this is very annoying for programmers - that is why they tend to simply ignore the fact that exceptions may happen and do not check for them at all. One might ask: "Why should we go through all this trouble? We could simply write the message about the error to the console and be done with it!" Simply writing the error message to the console would work only for some trivial examples. But if we are developing a class or a function that will be used in larger programs than we can not just write to the console for several reasons: • We do not know the type of the program that will be calling our function or using our class. It could be a program with graphic user interface or a web application. In that case writing to the console does not mean much to the user. • By only writing to the console the calling program is not informed about the error so it may continue execution normally as if the error had never occurred (and generally we do not want that, because we would like to inform the user about the error and/or possibly try something else).

7.2. Exception handling in C++ C++ exceptions handling mechanisms solve the problems that exist when working with exceptions in traditional way. Also, exception handling in C++ completely follows the principles of object oriented programming paradigm.

Exception handling mechanism in any programming language should separate error handling code from "ordinary" code and, by doing that, make the code more readable and easier to maintain and debug.

Two distinct parts can be identified in dealing with exceptional situations: • The reporting exceptional situations that cannot be resolved locally • The handling of exceptional situations detected elsewhere

Definition (throwing an exception) In C++ programming language, reporting an exceptional situation that cannot be handled locally is performed by throwing an exception. Throwing an exception simply means creating an object that holds the information about exceptional situation and transferring that object to the calling context.

- 257 - CHAPTER 7: EXCEPTIONS

To demonstrate what is formulated in this definition let us reuse the previous example. If "Divide" method is called with value = 0 we will report the situation by throwing an exception like this: double Calculator::divide(double value) { if (value == 0) throw DivByZeroException("Division by zero is not allowed!", result);

result /= value; return result; }

DivByZeroException is an ordinary class designed to hold information about the exception. The class could be defined like this: class DivByZeroException { char message[50]; double dividend; public: DivByZeroException(char* message, double dividend) { strcpy(this->message, message); this->dividend = dividend; } const char* getMessage() { return message; } double getDividend() { return dividend; } }

In the line where exception is thrown, the method ends and the program control is returned to the calling context (in our case into "main" function). Automatic objects in "Divide" method are destroyed as if the method ended normally. Throwing an exception is just one part of exception handling. After the exception is thrown it needs to be "caught" and processed somewhere in higher context. Let us modify the "main" function in order to catch the exception. int main() { /* try block contains the statements that we want to look out for exceptions */ try { Calculator calc; calc.add(9); calc.divide(3); calc.add(6); calc.divide(0); calc.add(2); cout << "The result is " << calc.getResult() << endl; }

- 258 - CHAPTER 7: EXCEPTIONS

catch (DivByZeroException &dbz) //this catch block handles the //DivByZeroException { cout << "Exception is thrown!" << endl; cout << dbz.getMessage() << endl; cout << "You tried to divide " << dbz.getDividend() << " by zero!" << endl; }

cout << "End of program!" << endl; //this statement //will always be executed return 0; }

Exception catching is done by using try and catch block. Try block holds the statements that could throw an exception. Note that this block does not contain any conditional statements for checking if exceptions are thrown or not. That was one of our goals: to make the code more readable by eliminating unnecessary code for exception checking. We write the code as if exceptions cannot happen. Exception handling code is isolated in one place (catch blocks). There can be one or more catch blocks following the try block. A catch block is like one small function that has only one argument. Based on the type of the argument exception handling mechanism decides which catch block to execute. The first catch block with an argument that matches the type of exception is executed.

If no match is made, the exception is transferred into even higher context, until the outermost context is reached. If the exception is not caught in the highest calling context the program terminates in an abnormal way. Let us improve our "Calculator" class by adding subtraction and square root extraction operations. Before we write any code, we must consider what kind of exceptional situations can arise while doing those operations. The most obvious one is the following: we can not extract square root from a negative number. We shall define new class that will hold the information about that exceptional situation (IllegalOpException).

#include #include #include using namespace std; class IllegalOpException { char message[50]; public: IllegalOpException(char* message) { strcpy(this->message, message); }

- 259 - CHAPTER 7: EXCEPTIONS

const char* getMessage() { return message; } }; class DivByZeroException: public IllegalOpException { double dividend; public: DivByZeroException(char* message, double dividend): IllegalOpException(message) { this->dividend = dividend; }

double getDividend() { return dividend; } }; class Calculator { double result; public: Calculator() { result = 0; } double getResult() { return result; } double add(double value) { result += value; return result; } double subtract(double value) { result -= value; return result; } double divide(double value); double squareRoot(); }; double Calculator::divide(double value) { if (value == 0) throw DivByZeroException("Division by zero not allowed", result); result /= value; return result; } double Calculator::squareRoot() { if (result < 0) throw IllegalOpException("Illegal Square root extraction!");

result = sqrt(result); return result; } int main() { try { Calculator calc;

calc.add(9); calc.divide(3); //exception may be thrown here calc.add(6); calc.subtract(11); calc.squareRoot(); //exception can also be thrown here

cout << "The result is " << calc.getResult() << endl; }

- 260 - CHAPTER 7: EXCEPTIONS

catch (DivByZeroException &dbz) { cout << "Exception is thrown!" << endl; cout << dbz.getMessage() << endl; cout << "You tried to divide " << dbz.getDividend() << " by zero!" << endl; } catch (IllegalOpException &iop) { cout << "Exception is thrown!" << endl; cout << iop.getMessage() << endl; }

cout << "End of program!" << endl; return 0; }

Notice that we have removed the attribute message from DivByZeroException since this attribute is now inherited from IllegalOpException.

Inside the "try" block there are two statements that could throw an exception. Depending on which exception is thrown, the corresponding "catch" block will be executed. If no exception is thrown all "catch" statements are skipped. There is one important thing to note in previous example. If the code inside "try" block throws DivByZeroException both catch blocks could handle this type of exception (DivByZeroException "is a special kind of" IllegalOpException). In that case exception handling mechanism executes the first matching catch block. If IllegalOpException is thrown only second catch block can handle it. Basically if a "catch" block can handle exceptions of some type "T" it can also handle the exceptions of any type derived from "T" (notice that this is an application of the substitution principle presented in Chapter 5).. If the type of the formal argument of a "catch" block is a reference or a pointer then virtual mechanism and dynamic binding may be activated. As you can see, the exception handling in C++ fully supports the principles of object oriented programming (data abstraction and polymorphism). Since DivByZeroException is derived from IllegalOpException our main function can also look like this: int main() { try { Calculator calc;

calc.add(9); calc.divide(3); //exception may be thrown here calc.add(6);

- 261 - CHAPTER 7: EXCEPTIONS

calc.subtract(11); calc.squareRoot(); //exception may also be thrown here

cout << "The result is " << calc.getResult() << endl; } catch (IllegalOpException &iop) //both exception types //can be handled by this block { cout << "Exception is thrown!" << endl; cout << iop.getMessage() << endl; }

cout << "End of program!" << endl; return 0; }

Although two types of exception can be thrown inside try block, only one catch is enough to handle both exception types. If we want to handle DivByZeroException differently we must put the catch block for that exception type before existing catch block. Consider what would happen if our "main()" function looked like this: int main() { try { Calculator calc;

calc.add(9); calc.divide(3); calc.add(6); calc.subtract(11); calc.squareRoot();

cout << "The result is " << calc.getResult() << endl; } catch (IllegalOpException &iop) { cout << "Exception is thrown!" << endl; cout << iop.getMessage() << endl; } catch (DivByZeroException &dbz) { /* This block will never execute because the block above catches both exception types: "IllegalOpException" and "DivByZeroException" */ }

cout << "End of program!" << endl; return 0; }

- 262 - CHAPTER 7: EXCEPTIONS

The second catch block will never execute because the first block will catch both exception types. Most compilers will issue a warning in such situation. It is possible to write a catch block that can catch any type of exception.

try { //... part of code ommited } catch (DivByZeroException &dbz) { //... part of code ommited } catch (IllegalOpException &iop) { //... part of code ommited } catch (...) //this block can catch any type of exception { //... part of code ommited }

The final catch block (with the ellipses instead of argument) catches any type of exception. Obviously, it only makes sense to put this block as a last catch block. Programmers often form a hierarchy of exception classes with common base class. Standard C++ library for example has one base class ("exception") for all exceptions that can be thrown by methods in the library. There is one more advantage in using C++ exception handling mechanism over traditional way of working with exceptions. In C++, if we do not handle exceptions in some function (for example we do not have enough information to handle them), and an exception is thrown inside that function it will still be propagated to the calling function where it can be handled. This is the default behaviour and we do not have to write any extra code to accomplish it (that is not the case with traditional exception handling). Look at the following example: void calculateIt() { //there is no exception handling in this function Calculator calc;

calc.add(9); calc.divide(3); //exception may be thrown here calc.add(6); calc.subtract(11); calc.squareRoot(); //exception may also be thrown here

cout << "The result is " << calc.getResult() << endl; }

- 263 - CHAPTER 7: EXCEPTIONS

int main() { try { calculateIt(); //exceptions will be propagated here } catch (DivByZeroException &dbz) { cout << "Exception is thrown!" << endl; cout << dbz.getMessage() << endl; cout << "You tried to divide " << dbz.getDividend() << " by zero!" << endl; } catch (IllegalOpException &iop) { cout << "Exception is thrown!" << endl; cout << iop.getMessage() << endl; }

cout << "End of program!" << endl; return 0; }

The function calculateIt does not handle any exceptions. If an exception is thrown in function calculateIt it will be transferred back to the calling function "main" where it can be handled. If the exception is not handled in the "main" function the program will terminate (more on this later). Sometimes we do not have enough information to completely handle some exception, but we need to catch it in order to release some resource. After releasing the resource we would like to re-throw the exception and propagate it to higher context. We can do that by putting "throw;" statement inside "catch" block. The following example demonstrates this. void write_to_db() { DbConnection cn = DbConnection("somedb"); try { //.. some sql statements executed } catch (DbException &e) { cn.Close(); throw; //re-throw the exception } } (DbConnection and DbException are fictional classes.)

- 264 - CHAPTER 7: EXCEPTIONS 7.3. Exceptions specification If some function can throw exceptions, for a user of that function it would be nice to know what different types of those exceptions can be. That way the programmer that uses the function can plan which exceptions to handle and in which way. It is possible to specify the types of exceptions that a function is supposed to throw. Although it is not required by C++, this exception specification is considered good and useful programming practice. The exceptions that a function is supposed to throw are specified in the function header. For example: class Calculator {

//... some code ommited

double divide(double value) throw (DivByZeroException); double squareRoot() throw (IllegalOpException); };

Generally the exceptions are specified in the following forms: void some_function() throw (Exception1, Exception2, Exception3);

The function declared this way should only throw the specified exceptions. void some_function() throw ();

The function declared with empty brackets after "throw" keyword should not throw any exceptions. If a function does not contain exception specification then it can throw any exception: void some_function();

If a function violates the specification during execution a special function, "unexpected()", is called. The behaviour of this function can be changed by calling "set_unexpected()" function. The function "unexpected()" is declared in the following way: void unexpected();

- 265 - CHAPTER 7: EXCEPTIONS

The function "set_unexpected" has one argument - pointer to a function. typedef void (*pf) (); pf set_unexpected(pf);

Note that the function "set_unexpected()" returns a pointer to a function. This pointer points to the previously installed function using "set_unexpected()" and we can use it for reinstalling the original function (or calling it) at some point in code. There is another special function relevant for exception handling in C++. That is the function "terminate()". This function is called when an exception is thrown, but there is no catch block that can handle the exception. We can install our own version of this function by calling "set_terminate()" function. Originally "terminate()" calls "abort()" function. The "terminate()" function is declared in the following way: void terminate();

The function "set_terminate()" has similar declaration like "set_unexpected()": typedef void (*pf) (); pf set_terminate(pf);

Consider the following example:

#include #include #include using namespace std;

class IllegalOpException { char message[50]; public: IllegalOpException(char* message) { strcpy(this->message, message); } const char* getMessage() { return message; } }; class DivByZeroException: public IllegalOpException {

double dividend;

- 266 - CHAPTER 7: EXCEPTIONS public:

DivByZeroException(char* message, double dividend): IllegalOpException(message) { this->dividend = dividend; } double getDividend() { return dividend; } }; class Calculator { double result; public: Calculator() { result = 0; } double getResult() { return result; }

double add(double value) { result += value; return result; } double subtract(double value) { result -= value; return result; }

double divide(double value) throw (DivByZeroException); double squareRoot() throw (); //this method should not //throw exceptions }; double Calculator::divide(double value) throw (DivByZeroException) { if (value == 0) throw DivByZeroException("Division by zero not allowed", result);

result /= value; return result; }

//the following function violates the exception specification //by throwing an exception while it is specified that it should //not throw any exceptions double Calculator::squareRoot() throw () { if (result < 0) throw IllegalOpException("Illegal Square root extraction!");

result = sqrt(result); return result; } void calculateIt() { Calculator calc;

calc.add(9); calc.divide(3); calc.add(6); calc.subtract(11); calc.squareRoot();

cout << "The result is " << calc.GetResult() << endl; }

- 267 - CHAPTER 7: EXCEPTIONS void my_unexpected() { cout << "Error: Program threw an unexpected exception!" << endl; abort(); } void my_terminate() { cout << "Error: Program did not catch the exception thrown!" << endl; abort(); } int main() { set_unexpected(my_unexpected); //installing our version //of "unexpected()"

set_terminate(my_terminate); //installing our version //of "terminate()"

calculateIt();

cout << "End of program!" << endl; return 0; }

When you run this program you should get the following output:

Error: Program threw an unexpected exception! Abnormal program termination

This is because in function calculate, method squareRoot() violates exception specification and throws IllegalOpException (it should not throw any exceptions). Change the declaration of squareRoot() method in such way that it is allowed to throw IllegalOpException, and try to run the program. Since no exception handling is done in the program the my_terminate() function should be called.

7.4. Exceptions in Java Most object oriented programming languages have a similar concept of exceptions. Of course there are differences and in the following paragraphs we will try to identify the similarities and differences between C++ and Java programming language in the context of exception handling. Exception handling in Java is very similar to C++. The difference is the fact that Java takes exception specifications in methods much more seriously then C++. If one function throws an exception, the calling function must either handle the exception or specify that it can also throw that exception. For example:

- 268 - CHAPTER 7: EXCEPTIONS

public class ExampleException extends Exception {

private int someData;

//constructor public ExampleException(int theData) { someData = theData; }

public String toString() { return "ExampleException: " + someData; } } public class Test {

//note that Java uses a different keyword (throws) for exceptio //n specification

public void functionOne() throws ExampleException { //.. some code throw new ExampleException(10); }

public void functionTwo() { functionOne(); } }

The code above will not compile in Java. Function "functionTwo()" must either catch the exception or specify that itself can throw "ExampleException". The following code shows the two valid versions of "functionTwo()". public void functionTwo() throws ExampleException { functionOne(); } Or public void functionTwo() { try { functionOne(); } catch (ExampleException e) { ..//some code that handles the exception } }

- 269 - CHAPTER 7: EXCEPTIONS

Actually, compiler will not check that the exception is caught or propagated if the exception thrown inherits from "RuntimeException" (which is Java built-in class) so what we said above is true only for custom exceptions (which usually inherit from "Exception" class). Java also has one more keyword for exception handling – the word "finally". Finally block may be used instead of catch block. This block contains statements that should execute even if exception is thrown in try block. For example:

Connection cn = ConnectionFactory.getConnection(); try { Statement st = cn.createStatement(); st.execute("UPDATE students SET graduated = 1"); } finally { cn.close(); }

If statements in try block throw an exception, finally block will execute (and connection will be closed) before the function exits. Finally block will also execute if no exception is thrown inside try block. As you can see finally keyword allows a programmer to specify which statements should execute regardless if the exception is thrown or not. The finally block is commonly used for releasing some allocated resources, similar to the example shown.

7.5. Guided problem: Array based stack class

7.5.1. The problem Create a class named ArrayStack that implements the virtual class BaseStack. Your class should implement stack-like structure (Last-In First-Out). The elements contained by the stack will be of a specific ItemType which has been defined to be double (no template classes are used in this problem). Abstract class BaseStack is given in basestack.h header file. basestack.h #ifndef _BASESTACK_H #define _BASESTACK_H typedef double ItemType;

- 270 - CHAPTER 7: EXCEPTIONS class BaseStack { public: virtual void push(const ItemType item) = 0; virtual ItemType pop(void) = 0; virtual bool isEmpty(void) = 0; virtual bool isFull(void) = 0; };

#endif

7.5.2. Simple solution On of the possible solutions is given below and consists of one class implemented in two files (header and implementation).

Solution: file arraystack.h #ifndef _ARRAYSTACK_H #define _ARRAYSTACK_H

#include "basestack.h" class ArrayStack : public BaseStack { private: ItemType* items; int maxSize; int top; public: ArrayStack(int maxSize); virtual ~ArrayStack(void);

void push(const ItemType item); ItemType pop(void); bool isEmpty(void); bool isFull(void); };

#endif

Solution: file arraystack.cpp #include "arraystack.h"

ArrayStack::ArrayStack(int maxSize) { this->maxSize = maxSize; this->top = -1; this->items = new ItemType[maxSize]; }

ArrayStack::~ArrayStack(void)

- 271 - CHAPTER 7: EXCEPTIONS

{ delete [] items; } void ArrayStack::push(const ItemType item) { items[++top] = item; //first increase top and then put the item } ItemType ArrayStack::pop(void) { return items[top--]; //first get the item then decrease top } bool ArrayStack::isEmpty(void) { return (top < 0); //stack is empty is top is negative } bool ArrayStack::isFull(void) { //stack is full is top+1 >= maxSize //for example if maxSize is 10 then //valid values for top are 0 – 9 //meaning if top = 9 then stack is full return (top + 1 >= maxSize); }

Figure 7.1 explains the details of how private member top is used.

Figure 7-1

Finally here is the example program that shows how ArrayStack class could be

- 272 - CHAPTER 7: EXCEPTIONS used.

Solution: file main.cpp #include #include "arraystack.h" using namespace std; int main() { ArrayStack st1(5);

st1.push(2.2); st1.push(2.4); st1.push(4.2); st1.push(4.4); st1.push(4.6);

while (!st1.isEmpty()) cout << st1.pop() << endl;

return 0; }

Remarks: Obviously, there are some difficulties with the solution that has just been presented: • Function ArrayStack::push does not check if the stack is full before pushing new item on it. • Function ArrayStack::pop does not check if the stack is empty before removing the item from top of the stack. The class relies on the user of the class to ensure that push() is not called on a full stack, and that pop() is not called for stack that is empty. User of the class could check for that using isEmpty() and isFull() member functions. Consequently, the class contract for the Push and Pop operations should include these assumptions as preconditions. If the operations were responsible for checking those conditions, those operations would be more robust since they would have void preconditions. Detailed discussion about class contracts is given in section 1.4.

7.5.3. More robust solution Make the ArrayStack class more robust by reporting exceptional situations to the user of the class in case adding items on the full stack is attempted or removing items from the empty stack.

- 273 - CHAPTER 7: EXCEPTIONS

Solution: file arraystack.h #ifndef _ARRAYSTACK_H #define _ARRAYSTACK_H

#include "basestack.h"

//two trivial classes are added that will only be used for //reporting the type of exception that occurred. class StackFullException {}; class StackEmptyException {};

//rest of the file is not changed class ArrayStack : public BaseStack { private: ItemType *items; int maxSize; int top; public: ArrayStack(int maxSize); virtual ~ArrayStack(void);

void push(const ItemType item); ItemType pop(void); bool isEmpty(void); bool isFull(void); };

#endif

Solution: file arraystack.cpp #include "arraystack.h"

ArrayStack::ArrayStack(int maxSize) { this->maxSize = maxSize; this->top = -1; this->items = new ItemType[maxSize]; }

ArrayStack::~ArrayStack(void) { delete [] items; } void ArrayStack::push(const ItemType item) { if (isFull()) throw StackFullException();

items[++top] = item; }

- 274 - CHAPTER 7: EXCEPTIONS

ItemType ArrayStack::pop(void) { if (isEmpty()) throw StackEmptyException();

return items[top--]; } bool ArrayStack::isEmpty(void) { return (top < 0); } bool ArrayStack::isFull(void) { return (top + 1 >= maxSize); }

Solution: file main.cpp #include #include "arraystack.h" using namespace std; int main() { ArrayStack st1(5);

try { st1.push(2.2); st1.push(2.4); st1.push(4.2); st1.push(4.4); st1.push(4.6);

//uncomment the next line to get StackFullException //st1.push(6.0);

while (!st1.isEmpty()) cout << st1.pop() << endl;

//uncoment the next line to get StackEmptyException //st1.pop(); } catch (StackFullException) { cout << "Can't push - stack is full!" << endl; } catch (StackEmptyException) { cout << "Can't pop - stack is empty!" << endl; }

return 0; }

- 275 - CHAPTER 7: EXCEPTIONS

Remarks • As it can be seen in file main.cpp, the object that describes the exception does not have to be used inside the catch block. The classes StackFullException and StackEmptyException might seem useless because they do not have any attributes or operations. They still serve the purpose. Sometimes, in order to properly handle the exceptional situation, it is enough to know the type of exception. In another situation that might not be the case.

7.6. Guided problem: Vector class

7.6.1. The problem Implement MyVector class using the interface given below. Vector is basically collection of items which are of the same type. Every item in a vector can be directly accessed (by using index). Vector should grow automatically when it reaches it's original capacity limit. • MyVector class should, at least, provide the following operations: - Constructor that takes initial capacity as parameter - Destructor - Append (to add elements at the end of vector) - GetLength (to get current length of the vector) - GetCapacity (to get current capacity of the vector) - operator [] overload to get the item with a specified index • MyVector class should be generic class which enables the use of the class with items of different types. • Test the class with items of int and double types.

7.6.2. The solution The following is one possible way of implementing MyVector class.

Solution file: myvector.h #ifndef _MYVECTOR_H #define _MYVECTOR_H template class MyVector { int capacity;

- 276 - CHAPTER 7: EXCEPTIONS

int growBy; int length; T* elements; public: MyVector(int initCap, int growBy = 10); ~MyVector();

void append(const T& item); int getLength() { return length; }; int getCapacity() { return capacity; };

T& operator[](int index); }; template MyVector::MyVector(int initCap, int growBy) { this->capacity = initCap; this->growBy = growBy; this->length = 0; this->elements = new T[this->capacity]; } template MyVector::~MyVector() { delete [] elements; } template void MyVector::append(const T& item) { if (length == capacity) //if vector reached it's capacity { capacity += growBy; //increase capacity by "growBy" value

T* newElements = new T[capacity]; //create new array in dynamic //memory

for (int i = 0; i < length; i++) //write elements from old newElements[i] = elements[i]; //array into new array

delete [] elements; //recycle old elements elements = newElements; //let elements point to new elements } elements[length] = item; length++; } template T& MyVector::operator[](int index) { return elements[index]; }

#endif

- 277 - CHAPTER 7: EXCEPTIONS

Test file: main.cpp #include #include "myvector.h" using namespace std; int main() { MyVector v1(2, 2); MyVector v2(2, 10);

cout << "Initial length, capacity of vector v1: " << v1.getLength() << ", " << v1.getCapacity() << endl; cout << "Initial length, capacity of vector v2: " << v2.getLength() << ", " << v2.getCapacity() << endl;

v1.append(1); v1.append(2); v1.append(3);

v2.append(0.1); v2.append(0.2); v2.append(0.3);

cout << "*******************" << endl; cout << "Vector v1 contents:" << endl; cout << "*******************" << endl;

for (int i = 0; i < v1.getLength(); i++) cout << v1[i] << endl;

cout << "*******************" << endl; cout << "Vector v2 contents:" << endl; cout << "*******************" << endl;

for (int i = 0; i < v2.getLength(); i++) cout << v2[i] << endl;

cout << "Length, capacity of vector v1: " << v1.getLength() << ", " << v1.getCapacity() << endl; cout << "Length, capacity of vector v2: " << v2.getLength() << ", " << v2.getCapacity() << endl;

return 0; }

Remarks • Similar to the generic Stack class presented in Chapter 3, there are some constraints concerning the type T (with which the class will be instantiated). If T is not a pointer type then it should overload operator = in order to avoid sharing of identity (since the objects of type T are copied into the elements array in append operation – see Chapter 4)

- 278 - CHAPTER 7: EXCEPTIONS

• One consequence of the fact that objects are simply being copied into the elements array is the lack of polymorphic behaviour of MyVector class. If polymorphic behaviour is required then T (the type that MyVector is instantiated with) should be pointer type. For example: int main() { MyVector v(50); //use default growBy value of 10

BaseClass* b1 = new BaseClass; v.append(b1);

DerivedClass* b2 = new DerivedClass; //DerivedClass inherits from //BaseClass v.append(b2); //... //do whatever with vector //...

for (int i = 0; i < v.getLength(); i++) delete v[i];

return 0; }

Since vector has no knowledge about the fact that T is actually a pointer type then we must explicitly call delete on dynamic objects added to the vector. • Overloaded operator [] does not check if index given as parameter is within valid range (0 <= i < length). It is more natural that this check is done by the operator []. • Also, when adding elements using append operation or when creating a vector we should handle bad_alloc exception. If there is not enough memory to allocate on the free store bad_alloc exception is thrown (this is the default behaviour which can be changed by calling set_new_handler() function).

7.6.3. More robust solution (1)

Solution file: myvector.h #ifndef _MYVECTOR_H #define _MYVECTOR_H

/* ***************** Exception classes ***************** */

- 279 - CHAPTER 7: EXCEPTIONS class VectorException { protected: char* message; public: VectorException(const char* message); virtual ~VectorException(); virtual const char* getMessage() const; }; class VectorOutOfBoundsException: public VectorException { public: VectorOutOfBoundsException(); }; class VectorOutOfMemoryException: public VectorException { public: VectorOutOfMemoryException(); };

/* ********************** Generic class MyVector ********************** */ template class MyVector { int capacity; int growBy;

int length; T* elements; public: MyVector(int initCap, int growBy = 10); ~MyVector();

void append(const T& item); int getLength() { return length; }; int getCapacity() { return capacity; };

T& operator[](int index); };

template MyVector::MyVector(int initCap, int growBy) {

this->capacity = initCap; this->growBy = growBy; this->length = 0;

- 280 - CHAPTER 7: EXCEPTIONS

try { this->elements = new T[this->capacity]; } catch (bad_alloc) { throw VectorOutOfMemoryException(); // throw exception } } template MyVector::~MyVector() { delete [] elements; } template void MyVector::append(const T& item) { f (length == capacity) { capacity += growBy; try { T* newElements = new T[capacity];

for (int i = 0; i < length; i++) newElements[i] = elements[i];

delete [] elements; elements = newElements; } catch (bad_alloc) { throw VectorOutOfMemoryException(); // throw exception } } elements[length] = item; length++; } template T& MyVector::operator[](int index) { if (index < 0 || index > length - 1) //if index is out of bounds throw VectorOutOfBoundsException(); //throw exception

return elements[index]; }

#endif

- 281 - CHAPTER 7: EXCEPTIONS

Solution file: myvector.cpp #include #include "myvector.h" using namespace std;

VectorException::VectorException(const char* msg) { message = new char[strlen(msg)+1]; strcpy(message, msg); } VectorException::~VectorException() { delete [] message; } const char* VectorException::getMessage() const { return message; } VectorOutOfBoundsException::VectorOutOfBoundsException(): VectorException("Index out of bounds!") { } VectorOutOfMemoryException::VectorOutOfMemoryException(): VectorException("Out of memory!") { }

Test file: main.cpp #include #include "myvector.h" using namespace std;

/********************************************** WARNING: If you run this program, you might get unexpected results ***********************************************/ int main() { try { MyVector v2(2) v2.append(0.1); v2.append(0.3); v2.append(0.3); cout << v2[10] << endl; } catch (VectorException& ex) { cout << "Exception: " << ex.getMessage() << endl; } return 0; }

- 282 - CHAPTER 7: EXCEPTIONS

Remarks: The code shown in the solution has a problem. When we try to access element with index 10 in vector v2 exception will be thrown. Object of class VectorOutOfBoundsException which is used to describe the exception has one member that is a pointer (message). When an exception is thrown, the expression after throw keyword (in our case a local object of class VectorOutOfBoundsException) is used for initialization of a temporary object in static memory. This has to be done because the object that we created locally will get out of scope when the function terminates. Since a copy of our local object is being created, copy constructor is called. We did not provide a copy constructor, so a default version is generated by the compiler. This will result in sharing of identity between our local object and the temporary object in static memory (message attributes of both objects will point to the same character array). When local object gets out of scope (function is terminated) the destructor will be called for that object, and it will deallocate the memory at the address stored in the message attribute. At that point, message member of the temporary object in static memory will point to invalid location. When getMessage() operation is called in catch block in main function our program might crash (this is because ex is a reference to the temporary object in static memory, and message attribute of that object points to invalid location).

The code given above might actually work on some compilers. That depends on the optimization done by them. Consider the following code:

template T& MyVector::operator[](int index) { if (index < 0 || index > length - 1) throw VectorOutOfBoundsException();

return elements[index]; }

Some compilers will try to optimize this code by creating the object directly in static memory using the constructor given after throw keyword. We should not rely on this behaviour since it is highly dependent on the specific compiler. Even if optimization is done, the semantics of exception throwing must be honoured. For example, if we declare copy constructor as private, the code will not compile (even if compiler never calls the copy constructor due to optimizations).

The solution is to provide copy constructor for the base exception class (VectorException).

- 283 - CHAPTER 7: EXCEPTIONS

7.6.4. More robust solution (2)

Solution file: myvector.h #ifndef _MYVECTOR_H #define _MYVECTOR_H

/********************************** Exceptions declarations **********************************/ class VectorException { protected: char* message; public: VectorException(const char* message); VectorException(const VectorException& e); //copy constructor virtual ~VectorException(); virtual const char* getMessage() const; };

//.. rest of the file is not changed

#endif

Solution file: myvector.cpp #include #include "myvector.h" using namespace std;

VectorException::VectorException(const VectorException& e) { message = new char[strlen(e.message)+1]; strcpy(message, e.message); }

//... rest of the file is not changed

- 284 -

Chapter 8 Standard C++ Library

8.1. What is the "Standard C++ Library" Since one of the principles of object oriented programming paradigm is code reuse, C++ compilers usually ship with a library of commonly used classes which can be used in our programs. This library is standardized, which means that it should contain the same components regardless of the compiler being used. All the elements of the standard library are defined in "std" namespace. A namespace is a mechanism for expressing logical grouping. That is, if some declarations logically belong together according to some criteria, they can be put in a common namespace to express that fact [Stroustrup1997]. A namespace is a scope and the usual scope rules hold for namespaces. A name declared in a namespace can be used when qualified by the name of its namespace. For example expression std::cout refers to the object cout inside std namespace. The Standard C++ library is presented as a set of header files. C++ library contains large number of facilities which a programmer can use for solving almost any common problem. Before standardization, different C++ compilers had different libraries which had similar functions, classes and other elements but those elements were not always compatible. Standard C++ library contains all those functions that existed before (and more), but they are standard now and should be compatible with different compilers. In order to maintain backward compatibility, header files in Standard C++ library follow different naming conventions then pre-standard libraries. The following line of code shows the "old" way of using libraries provided by the compiler:

#include

Corresponding header file in the standard library is named "iostream" (without ".h"), and since elements in it are defined in "std" namespace, we usually use it like this:

#include using namespace std;

Using-directive makes all names from a namespace available as if they had been declared outside their namespace.

- 285 - CHAPTER 8: STANDARD C++ LIBRARY

A standard header with a name starting with letter "c" is equivalent to a header in C standard library. For example "cstdlib" has the same functions like "stdlib.h" file in C standard library with one difference – the functions in "cstdlib" are defined in "std" namespace. Fully describing every class and function in the Standard C++ library is not the goal of this chapter since it would require a whole book or two. Introduction to most commonly used parts of the library will be made instead. String class and parts of the Standard Template Library (STL) are presented in this book. STL is a part of the Standard C++ library and contains commonly used data structures and algorithms realized as template classes and template functions. The core of the STL are the three elements: containers, iterators and algorithms.

8.2. Strings One of the commonly used things in C++ is manipulation with strings of characters. Traditional ("C" style) way of using strings is error prone. Creating "string" class in C++ which would simplify the use of strings of characters is often used as a good exercise for learning classes. Standard C++ library now contains powerful but simple to use "string" class. This class takes care of allocating, copying, merging and many other operations that can be performed on strings. Standard does not define the way this "string" class is realized, but defines the interface for using that class. As a consequence, strings do not necessary end with '\0' character like in "C" style libraries. The following example demonstrates the way objects of "string" class can be created and some of the member operations of the "string" class.

#include #include using namespace std; int main() { string s1 = "This is one way of string initialization!"; string s2("This is another way!");

//string "s3" is created and initialized with first //5 characters of string "s2" string s3(s2, 0, 5);

//string "s4" is initialized with 2 characters from //the middle of "s2" string s4(s2, 5, 2);

//string "s5" is initialized from the 5th character //to the end of string "s2" string s5(s2, 5);

- 286 - CHAPTER 8: STANDARD C++ LIBRARY

//calling substr operation which returns a substring and //"+" overloaded operator string s6 = s2.substr(0, 16) + "that we changed!";

cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s4 << endl; cout << s5 << endl; cout << s6 << endl;

/* size() operation gives us the current size of a string, and "capacity()" gives as the maximum size that a string can grow whithout allocating additional memory. */ cout << "The size of 's1' is: " << s1.size() << endl; cout << "The capacity of 's1' is: " << s1.capacity() << endl;

//inserting before 5th caracter and appending to the end of string s1.insert(5, "realy "); s1.append(" (appended)");

cout << s1 << endl; cout << "The new size of 's1' is: " << s1.size() << endl; cout << "The new capacity of 's1' is: " << s1.capacity() << endl;

return 0; }

There are many other operations in "string" class. For example, we can search for a character or a string inside another string using "find()" function. Here is the example:

#include #include using namespace std; int main() { string s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int position = s1.find("IJ"); if (position != string::npos) cout << "'IJ' is found in position: " << position << endl; else cout << "'IJ' is not found" << endl;

return 0; }

- 287 - CHAPTER 8: STANDARD C++ LIBRARY

8.3. STL Containers and Iterators When writing programs it is often required to make a collection of objects (collection of books in a library, collection of students enrolled in a course, collection of courses for a student, etc..). Now, you are probably saying: "So what?". We can make an array of books, array of students, array of courses and that's it – problem solved. Well, not exactly! An array would actually be a good solution when the number of objects inside the collection is known in advance. Things get a little trickier when that is not the case. Let us see an example. class Book { private: char* title; char* author; public: Book(char* theTitle, char* theAuthor) { title = new char[strlen(theTitle)+1]; strcpy(title, theTitle); author = new char[strlen(theAuthor)+1]; strcpy(author, theAuthor); }

const char* GetTitle() { return title; } const char* GetAuthor() { return author; } }; class Library { private: int numberOfBooks; Book* books[100]; //must be an array of pointers since //there is no default constructor for //the class "Book" public: Library() { numberOfBooks = 0; } void AddBook(Book* aBook) { books[numberOfBooks] = aBook; numberOfBooks++; } };

Consider what would happen if we try to add more then a hundred books to the library. The program would probably crash. Of course, we could modify the "AddBook()" method to check if the maximum number of books is reached and throw an exception if it is. But, what if storage of more then a hundred, or more then a thousand books in the library is needed (or unknown number of books)? We could make an array in dynamic memory (using operator "new"), and resize it when it reaches the limit. Now, another question arises: "How can we resize the array?".

- 288 - CHAPTER 8: STANDARD C++ LIBRARY

We can't! What we can do is allocate a new (bigger) array, copy the elements from the old one, and destroy the original. Tricky, isn't it? To make our life easier, Standard C++ Library includes special generic classes called "containers". Containers are objects that can contain other objects (which is similar to our "Library" class shown in previous example). These container classes enable us to create and use collections of objects without worrying about the resizing the collection, allocating enough space, etc. We simply add objects to the collection, and then later retrieve these objects from the collection. There are different container classes in the C++ library, and choosing the right one depends on the type of problem at hand. Different container classes have different underlying representation of the data. Some use sequential representation (like "vector"), others use linked representation (like "list"). However, objects inside the container can be accessed in similar way using "iterators". When accessing objects in a container using an iterator, the container is viewed as a simple sequence of objects. There are also other ways to access the objects in a container, depending on the container type, but iterators are a common way for all container types. Now that we know the basics, let us introduce some simple containers (or collections). The first one is the "vector" container. Here is the example of "vector" usage.

#include #include using namespace std;

//this is the same class from the previous example class Book { private: char* title; char* author; public: Book(char* theTitle, char* theAuthor) { title = new char[strlen(theTitle)+1]; strcpy(title, theTitle); author = new char[strlen(theAuthor)+1]; strcpy(author, theAuthor); }

const char* GetTitle() { return title; } const char* GetAuthor() { return author; } }; int main() { vector library; //the container vector::iterator libIter; //iterator for the container

//we add elements using "push_back" method library.push_back(new Book("Hamlet", "William Sheaksperae"));

- 289 - CHAPTER 8: STANDARD C++ LIBRARY

library.push_back(new Book("Thinking in C++", "Bruce Eckel")); library.push_back( new Book("Applying UML And Patterns", "Craig Larman") );

//accessing the elements using iterator for (libIter = library.begin(); libIter != library.end(); libIter++) { Book* currentBook = *libIter; cout << currentBook->GetTitle() << " by " << currentBook->GetAuthor() << endl; } return 0; }

Does it look complicated? You will see in a moment that it really isn't. The "Book" class should be clear. Let us look at the main function, line by line.

1) vector library;

If you remember generic classes from Chapter 3. this also should be clear enough. We create object "library" from the "vector" template. Parameter passed to the generic class means that the vector of pointers to books (objects of class Book) should be created. We can create a vector that can hold any type ("int" for example) in a similar way.

2) vector::iterator libIter;

Another object is created (libIter). This is the iterator that will be used for accessing the elements in a vector. Why is it created this way? Because the "iterator" class is nested inside the "vector" class. Again, by passing to the generic class we instruct the compiler that the iterator will iterate over pointers to books.

3) library.push_back(new Book("Hamlet", "William Sheaksperae")); 4) library.push_back(new Book("Lord of the Rings", "Meho Dzeger")); 5) library.push_back(new Book("Vlak u snijegu", "Mato Lovrak"));

Three books are added to the library (three pointers to books, actually). Method "push_back" of the vector class adds elements to the end of the collection.

6) for (libIter = library.begin(); libIter != library.end(); libIter++)

- 290 - CHAPTER 8: STANDARD C++ LIBRARY

Object "libIter" is initialized with "library.begin()". "Begin" method returns an iterator. This iterator points to the first element in the collection. Increment operator "++" moves the iterator to the next element. Using "for" statement we advance through the collection until we reach the final element. This is checked by using the "end" method which returns an iterator that points behind last element in collection (libIter != library.end()). It is important to use "!=" operator and not "<" or "<=". Iterators for all container types have "begin()" and "end()" methods.

7) { 8) Book* currentBook = *libIter; 9) cout << currentBook->GetTitle() << " by " << currentBook->GetAuthor() << endl; 10) }

By dereferencing the iterator (*libIter) we access the element the iterator currently points to (the element is of type "Book*"). Finally in line 9, we print the data about the book. This block is repeated for all elements in the "library" container. You might ask: "What is the size of this vector container?". We don't have to worry (much) about it. All the container classes will resize to accommodate new elements when needed. If you wish to know the number of elements currently stored in a vector you can use size() member function. As you can see vector class is not that complicated. Now, let us examine another container type: "list". We'll do it using the same example with Book class:

#include #include using namespace std; class Book { private: char* title; char* author; public: Book(char* theTitle, char* theAuthor) { title = new char[strlen(theTitle)+1]; strcpy(title, theTitle); author = new char[strlen(theAuthor)+1]; strcpy(author, theAuthor); }

const char* GetTitle() { return title; } const char* GetAuthor() { return author; } };

- 291 - CHAPTER 8: STANDARD C++ LIBRARY

int main() { list library; //the container list::iterator libIter; //iterator for the container

//we add elements using "push_back" method library.push_back(new Book("Hamlet", "William Sheaksperae")); library.push_back(new Book("Lord of the Rings", "Meho Dzeger")); library.push_back(new Book("Vlak u snijegu", "Mato Lovrak"));

//accessing the elements using iterator for (libIter = library.begin(); libIter != library.end(); libIter++) { Book* currentBook = *libIter; cout << currentBook->GetTitle() << " by " << currentBook->GetAuthor() << endl; } return 0; }

It is almost the same. That was the whole idea: no matter what container we choose, we use it the same way. So what is the difference between using vector and using list container? The difference is in the way those containers internally store elements. Based on that, certain operations with one container type can be less efficient then with another container type. Inserting an element in the middle of the collection with vector is not efficient (or is expensive) because vector internally stores elements using arrays. The same operation with "list" is efficient, because the elements are stored in a doubly linked list. On the other hand, randomly accessing elements in a vector (by index) is much more efficient then in a list. That is why vector has additional operations for accessing elements by index (which do not exist in list). The example shown above presents a common way (using iterators) for accessing the elements in vector or list.

8.3.1. Sequential containers The two container types presented so far (vector and list) are sequential containers. They are called sequential because the elements in these containers are stored in a sequence. Every element is preceded by one specific element and followed by another (except the first and the last element). There is another commonly used sequential container – deque (double-ended queue). Deque is similar to vector, but elements in a deque can be efficiently accessed at both ends (front and back). The following table summarizes the main characteristics of vector, list and deque containers.

- 292 - CHAPTER 8: STANDARD C++ LIBRARY vector - fast random access (by index) - slow insert or remove operations on elements in the middle of the container - fast insert or remove at end of the container list - fast insert or remove at any location - fast access to elements at both ends - slow random access deque - fast random access - slow insert or remove operations on elements in the middle of the container - fast insert or remove at the beginning or at the end of the container

8.3.1.1 Vectors Vectors are similar to arrays and they can even be used in a similar way using [] operator. Vector size is automatically increased when new elements are added to it. Vector has "size()" member function which returns the number of elements currently stored in the container. Here is a simple example.

#include #include using namespace std; int main() { vector numbers;

numbers.push_back(10); numbers.push_back(20); numbers.push_back(30); numbers.push_back(40); numbers.push_back(50);

for (unsigned int i = 0; i < numbers.size(); i++) cout << "Element " << (i+1) << " = " << numbers[i] << endl;

cout << endl; cout << "Maximum number of elements: " << numbers.max_size() << endl;

return 0; }

There is another member function shown in the example: max_size(). This function returns the maximum size to which a vector can grow. The result of the function is

- 293 - CHAPTER 8: STANDARD C++ LIBRARY dependent on the data being stored, the type of the container and the operating system.. The output produced should look something like this:

Element 1 = 10 Element 2 = 20 Element 3 = 30 Element 4 = 40 Element 5 = 50

Maximum number of elements: 1073741823

Elements are added to the end of the vector and also can be removed from the end. These two operations are fast with vectors. On the other hand, inserting and removing element in the middle of vector is slow. This is because when inserting in the middle, all the elements from the point of insertion to the end must be moved to make room for the new element. Next example shows push_back(), back(), pop_back(), insert() and remove() operations.

#include #include using namespace std;

//function which displays all the elements in a vector void print(char* desc, vector v) { cout << desc << ": "; for (unsigned int i = 0; i < v.size(); i++) cout << v[i] << " "; cout << endl; } int main() { vector numbers;

numbers.push_back(10); numbers.push_back(20); numbers.push_back(30); numbers.push_back(40); numbers.push_back(50);

print("Elements", numbers);

cout << "The last element is: " << numbers.back() << endl;

numbers.pop_back(); //does not return last element //only removes it //that's why we usually use "back()" //before "pop_back()"

- 294 - CHAPTER 8: STANDARD C++ LIBRARY

print("After pop_back()", numbers);

//insert 25 at before 3rd element numbers.insert(numbers.begin()+2, 25); print("After insert()", numbers);

//erase 3rd element numbers.erase(numbers.begin()+2); print("After erase()", numbers);

return 0; }

After running the example we get the following output.

Elements: 10 20 30 40 50 The last element is: 50 After pop_back(): 10 20 30 40 After insert(): 10 20 25 30 40 After erase(): 10 20 30 40

Insert and erase operation expect iterator to be passed as first argument. The iterator should point to the element before which the new element will be inserted (for insert) or to the element which will be deleted (for erase).

8.3.1.2 Lists Since lists are slow in accessing elements in arbitrary location, operator [] is not defined for lists. Elements can be added to both ends using "push_front()" and "push_back()" functions. There are also corresponding "pop" functions for removing elements from front and back end of the list. The following example shows the usage of those functions. Note that in the "print" function, elements are accessed using iterator and not [] operator.

#include #include using namespace std; void print(char* desc, list l) { cout << desc << ": "; for (list::iterator it = l.begin(); it != l.end(); it++) cout << (*it) << " "; cout << endl; } int main() { list numbers;

numbers.push_back("three");

- 295 - CHAPTER 8: STANDARD C++ LIBRARY

numbers.push_back("four"); numbers.push_back("five");

numbers.push_front("two"); numbers.push_front("one");

print("Elements", numbers);

cout << "The last element is: " << numbers.back() << endl;

numbers.pop_back(); print("After pop_back()", numbers);

cout << "The first element is: " << numbers.front() << endl;

numbers.pop_front(); print("After pop_front()", numbers);

return 0; }

List container uses doubly linked list to store elements. That means that "insert()" and "erase()" operation on a list are fast since only the pointers to next and previous elements must be modified.

8.3.1.3 Deque Elements can be efficiently added to back and front ends of deque container, and random access (using []) is fast. Inserting and removing the elements in the middle are still slow. Deque has the same benefits as a vector plus the possibility to add elements to the front end of the deque.

#include #include using namespace std; void print(char* desc, deque d) { cout << desc << ": "; for (unsigned int i = 0; i < d.size(); i++) cout << d[i] << " "; cout << endl; } int main() { deque numbers;

numbers.push_back("three"); numbers.push_back("four"); numbers.push_back("five");

- 296 - CHAPTER 8: STANDARD C++ LIBRARY

numbers.push_front("two"); numbers.push_front("one");

print("Elements", numbers);

cout << "The last element is: " << numbers.back() << endl;

numbers.pop_back(); print("After pop_back()", numbers);

cout << "The first element is: " << numbers.front() << endl;

numbers.pop_front(); print("After pop_front()", numbers);

return 0; } Note the random access in function print() using [] operator.

8.3.2. Associative containers Associative containers do not represent elements in a sequence like vector, list or deque do. Searching for a specific element in vector, list or deque involves stepping through all the elements from the beginning of the sequence until the element is found. Searching in associative containers is done using a key (some value like string or number). When adding an object into an associative container we must provide both the object that is to be stored and the key which will be used for a later retrieval of the object. In another words, a key represents the identifier of an object inside an associative container.

There are different associative containers in STL but they can all be thought of as variations of the "map" container. For example "set" is a special version of map where all objects are null (the key provided is the object that needs to be stored). "Multimap" container allows for several objects to be stored using the same key. There is also "multiset" container which is a "set" that can store multiple instances of the same key.

8.3.2.1 Map A map stores pairs of objects. One object represent the key, and the other object is the value. Both key and value can be strings, numbers or objects of any other class. The next example program shows a map where keys are integers and value objects are strings. Integer numbers represent student id numbers and strings are student names. Two students can not have the same id, so a map can be used.

#include

- 297 - CHAPTER 8: STANDARD C++ LIBRARY

#include #include using namespace std; void main() { map students; map::iterator it;

students[200] = "Handzic, Adel"; students[1200] = "Azemovic, Jasmin"; students[210] = "Skondric, Goran"; students[2500] = "Cehajic, Sinisa";

int key; cout << "Enter student id ->"; cin >> key;

it = students.find(key);

if (it != students.end()) cout << "Found: " << it->second << endl; else cout << "Not found!" << endl;

cout << endl; cout << "The list of students: " << endl;

for (it = students.begin(); it != students.end(); it++) cout << it->first << ": " << it->second << endl; }

When creating objects from map template at least two template parameters must be supplied. The first parameter is the type of key objects, and the second is the type of value objects that will be used. Elements can be added to the map using index operator []. Inside the angle brackets key object must be given. Key objects must be of the type specified in template parameters when map container is created. Finding the element in the map container is usually done using the find() function (actually all associative containers have a find() member function). This function returns an iterator which points to the element associated with the given key. If such element does not exist, function returns an iterator that points behind last element which can be tested using "end()" function. Note that iterators in a map container point to objects of type "map:.value_type". The attribute "first" of a "value_type" object returns the key, and the attribute "second" returns the value. For example if key "200" is entered in previous example "it->second" will return the value ("Hadzic, Adel"), and "it->first" would return the key (200).

- 298 - CHAPTER 8: STANDARD C++ LIBRARY

Elements in a map are ordered by key objects, which can be seen when the example above is run. The list of students ordered by student id numbers will be displayed. Another useful function that is available for all associative containers is the "count()" function. Count accepts a key and returns the number of objects associated with that key. For maps and sets count() can return 0 or 1, but for multimaps and multisets it can return higher integer values. This is because in multimap and multiset containers more then one value object can be associated with a single key.

8.3.2.2 Set As already said, the set container is a specialized version of the map container. It contains only keys and no values. The following example demonstrates the use of "set" containers.

#include #include #include using namespace std; int main() { set words; set::iterator it;

words.insert("Back"); words.insert("Agent"); words.insert("Agent"); words.insert("Duck"); words.insert("Cold");

cout << "The word list: " << endl;

for (it = words.begin(); it != words.end(); it++) cout << *it << endl;

return 0; } After running the example you should get something similar to this: The word list: Agent Back Cold Duck Two things can be noticed. First, the word "Agent" is displayed only once since set does not allow duplicate keys. Second, because objects in associative containers are stored using binary tree for easy searching, the words are sorted. If multiple occurrences of a single key are required multiset should be used instead.

- 299 - CHAPTER 8: STANDARD C++ LIBRARY 8.4. STL Algorithms Generic algorithms are another important part of STL. Algorithms operate on collections of data. Although they are used mostly with containers they can also be used with simple arrays. Algorithms in STL are realized through template functions (see Chapter 3). Some containers have member functions which replace certain algorithms because they offer a more optimized version for the container at hand. For example, set container has find() member function which should be used instead of "find()" algorithm template function. Three algorithms are presented here: find(), search() and sort().

8.4.1. find() The following is an example of find() algorithm that looks for the first element with specific value in a container or in an array.

#include #include //needed for alghoritms #include #include using namespace std; int main() { vector cities; vector::iterator it;

cities.push_back("Moscow"); cities.push_back("Berlin"); cities.push_back("London"); cities.push_back("Paris"); cities.push_back("Rome"); cities.push_back("Madrid");

it = find(cities.begin(), cities.end(), "Rome"); if (it != cities.end()) { int position = (int) (it - cities.begin()); cout << "Found at position: " << position << endl; } else cout << "Not found!" << endl;

return 0; }

Function find() accepts three parameters. The first two parameters specify the range of elements that will be searched. Since we passed "cities.begin()" and "cities.end()"

- 300 - CHAPTER 8: STANDARD C++ LIBRARY all elements in container "cities" will be examined. The third parameter is the value that we are searching for ("Rome"). Note that since we are searching inside vector of strings the search is case sensitive.

8.4.2. search() Search algorithm tries to find a specific sequence of values inside a container. The sequence that is being searched for is also specified by a container (we can say that search() operates on two containers). The sample code below uses ordinary arrays in order to demonstrate the use of STL algorithms with arrays.

#include #include #include //for strlen using namespace std; int main() { //two arrays of characters (source and pattern) char sentence[] = "The is an array of characters!"; //the source char word[] = "array"; //the pattern

int slen = strlen(sentence); int wlen = strlen(word);

char* result = search(sentence, sentence + slen, word, word + wlen);

if (result != (sentence + slen)) { int position = result - sentence; cout << "Found at position: " << position << endl; } else cout << "Not found!" << endl; }

Function search() accepts four parameters. The first two parameters specify the range of elements (using iterators or pointers) of the source container inside which the sequence of elements (the pattern) will be searched. The last two parameters identify the range of elements of the container which represent the sequence that is being searched for. The result is iterator (or pointer when arrays are used) which points to the location where the found sequence inside source container begins. If the sequence is not found "container.end()" is returned. How does this work for ordinary arrays? Almost the same as for STL containers. Member function end() in

- 301 - CHAPTER 8: STANDARD C++ LIBRARY every container returns the iterator which points one location after the last element. It is exactly the same with arrays. If sequence is not found, search() returns pointer to one element behind last element in the source array ("sentence" in previous example). Note that in previous example we ignored the existence of '\0' character at the end of character array. This works fine because strlen() function returns the size of character array not including the '\0' character.

8.4.3. sort() Before describing sort() we have to make a small introduction in function objects. Function objects are heavily used in STL. Function object is an object of a class that has only one member and that is overloaded operator () (see Chapter 2 and Chapter 3). Those classes are often generic (templates) so they can work with different types. As a result that object behaves similar to function pointer. Function objects are used in STL algorithms to customize the behaviour of some algorithms. There are several predefined function object classes located in "functional" header file. Here is the example of sort() algorithm

#include #include #include #include #include using namespace std; void main() { vector cities; vector::iterator it;

cities.push_back("Moscow"); cities.push_back("Berlin"); cities.push_back("London"); cities.push_back("Paris"); cities.push_back("Rome"); cities.push_back("Madrid");

//two function objects are created //"asc" is object of the generic class "less" //"desc" is object of the generic class "greater"

less asc; greater desc;

sort(cities.begin(), cities.end(), asc);

- 302 - CHAPTER 8: STANDARD C++ LIBRARY

//sort(cities.begin(), cities.end()) would produce the same //result

cout << "Sorted list od cities (ascending):" << endl; for (it = cities.begin(); it != cities.end(); it++) cout << *it << endl;

sort(cities.begin(), cities.end(), desc);

cout << "Sorted list od cities (descending):" << endl; for (it = cities.begin(); it != cities.end(); it++) cout << *it << endl; }

Sort() accepts range of elements (specified by iterators) as the first two parameters. The third parameter is optional and specifies the function object that will be used for comparison of elements. If this parameter is not supplied then the function object of generic class "less" is used. The standard library provides many useful function objects. Most of the template classes in "functional" header file are subclasses of "unary_function" or "binary_function" base template classes. The declaration of the two classes is given below. template struct unary_function { typedef Arg argument_type; typedef Res result_type; }; template struct binary_function { typedef Arg first_argument_type; typedef Arg2 second_argument_type; typedef Res result_type; };

The purpose of this classes is to provide standard names for the argument and return types. Template classes "less" and "greater" inherit from "binary_function". For example "less" template class is declared in the following way: template struct less: public binary_function { bool operator() (const T& x, const T& y) const { return x

- 303 - CHAPTER 8: STANDARD C++ LIBRARY

As it can be seen from the code above, overloaded operator() accepts two parameters of the same type and it returns true or false (as a result of the expression "x < y"). Template class "greater" is declared in a similar way: template struct greater: public binary_function { bool operator() (const T& x, const T& y) const { return x>y; } };

When an object is created from "less" or "greater" template class is created it can be used similar to a function. The following example shows one way how function objects may be used.

#include #include using namespace std;

/* The following template function uses function object to compare two elements in a array. */ template void bubbleSort(T* array, int size, FuncObj compare) {

for (int i = 0; i < size; i++) { for (int j = 0; j < size - i - 1; j++) {

//compare(...) calls overloaded operator() //of a function object pased as the third argument if (compare(array[j], array[j+1])) { T temp = array[j]; array[j] = array[j+1]; array[j+1] = temp; }

} //end for j

} //end for j

}

- 304 - CHAPTER 8: STANDARD C++ LIBRARY

int main() { double arr[] = {0, 4.5, 2.1, 3.3, 9.2, 7.8};

greater asc; less desc;

bubbleSort(arr, 6, asc);

for (int i = 0; i < 6; i++) cout << arr[i] << " "; cout<

bubbleSort(arr, 6, desc);

for (int i = 0; i < 6; i++) cout << arr[i] << " "; cout<

return 0; }

The STL algorithms use function objects in a similar way. Container classes are much more useful combined with algorithms which STL provides. Some of the basic algorithms presented here demonstrate the key principles and techniques for using those algorithms.

- 305 -

- 306 -

Appendix A Unified Modelling Language UML

“Modeling is a way of thinking and reasoning about systems. The goal of modeling is to come up with a representation that is easy to use in describing systems in a mathematically consistent manner.” Paul Fishwick, Simulation Model Design and Execution

A.1 UML evolution It is a fact that object-oriented software development has become more and more popular and that the development process of computer-supported information systems require suitable modelling process. During the last three decades software engineers have become aware of the fact that the development of object-oriented information systems requires special methodologies for modelling. An object- oriented approach promises to be very well suited to accomplish the goals commonly associated with conceptual modelling: Usually objects offer a more direct and natural correspondence to real world entities than data structures, while inheritance and encapsulation promote reusability of concepts and components. Numerous special approaches and methodologies have been proposed during the last years. Lot of reports give overviews of those methodologies and present a framework that helps to compare them. Some of those methodologies gained remarkable attention so far and are well documented at the same time. A methodology clarity or descriptiveness depends on the quality of the (graphical) presentations it provides. This is the case of both the basic modelling constructs and the rules for composing them. It is desirable that the constructs as well as the models composed from them should "correspond directly and naturally to our own conceptualizations ..." [Myopoulos1984]. Each methodology had its own notations. The problem was that if different people were using different notations, always, somebody had to do a translation. A lot of times, one symbol meant one thing in one notation, and something totally different in another notation. In 1991, Grady Booch came out with his first edition of the book which introduced OO methodology evaluated as very suitable in design and les suitable in analysis [Booch1991]. Ivar Jacobson came out with his book introducing Objectory which was very good with user experience [Jacobson1992], and Jim Rumbaugh came out

- 307 - APPENDIX A: UML with his OMT methodology really strong in analysis, but weaker in design [Rumbaugh1991]. Each methodology had its strengths as well as its weaknesses. For many years, Rumbaugh’s Object Modeling Technique (OMT), Booch’s O-O methodology, and Jacobsen’s Objectory methodology were the three primary, but competing, O-O methodologies. Unified Modelling Language „was born“ when James Rumbaugh joined Grady Booch at Rational Software. They both had object oriented syntaxes and needed to combine them. Semantically they were very similar, it was mainly the symbols that needed to be unified. The result was UML 1.0. It represents the evolutionary unification of their experience with other industry engineering best practices.

Fig. A-1 How UML came out The Object Management Group adopted the UML1.1 specification in November 1997 making it an independent industry standard. Some small changes were made in versions 1.3 and 1.4. Version 2.0 is now entering a use.

- 308 - APPENDIX A: UML

Figure. A-2: UML development

From the 1995 UML belongs to the Object Management Group (OMG). (On the web site www.omg.org, you can find the PDF version of the UML reference).

A.2 Some aspects of the UML

One reason UML has become a standard modelling language is that it is programming-language independent. Also, the UML notation set is a language and not a methodology. UML is defined as an easy-to-learn but semantically rich visual modelling language (with an associated textual language) which incorporate ideas from other modelling languages and industry best practices. One important fact is that the UML is based on experience in applying object-oriented methods to the development of systems. The main goal of using the UML is to create an application model that is complete, consistent, and accurate. The availability of different modelling tools enables the developer to find tools that emphasize the aspects of development that are important to them.

- 309 - APPENDIX A: UML

The UML is a graphical language for: specifying, visualizing, constructing and documenting. It is largely based on the object-oriented paradigm and is an essential tool for developing robust and maintainable software systems.

A.3 The Basic Building Blocks of UML The basic building blocks of UML are: - model elements (things.) - relationships - diagrams Simple building blocks (constructs) are used to create large, complex structures. Detailed overview of UML building blocks is shown on fig. A-3.

Figure A-3: UML building blocks

Although, in the book we used only a few model elements, in this appendix we presented some more of the most used model elements and diagrams.

A.3.1 Model elements (things)

A.3.1.1 Class We know that a class represents a set of objects with similar structure, behaviour, and relationships. The run-time execution provides extension of a class, that is, its instances. For class declaration and specification of their properties UML provides relatively simple and unique graphic and symbolic notation and enable us to represent using classes in various ways. Some modelling elements, similar in form to classes (such as interfaces, signals, or utilities), are notated using keywords on class symbols (these "keywords" are called stereotypes). Mainly, class diagrams is a place where to declare classes but they are used in most other diagrams.

- 310 - APPENDIX A: UML

Within the system being modelled, each concept is represented by a class. Classes have data structure and behaviour and relationships to other elements. A graphical representation of class is drawn as a solid-outline rectangle with three rectangles separated by horizontal lines. The top rectangle holds the class name and other general properties of the class (including stereotype); the middle rectangle contains a list of attributes; the bottom rectangle holds a list of operations. Graphical notation for classes (declaring and using), and textual notation for referencing classes are shown in next few figures.

Class Name

attribute[[ :Type] =initial value] Class Name Operation[[ arg list] :return type]

Time - hour : int - minute : int - secunde : int Time Time + Time() + setTime(int, int, int) : void setTime() + printMilitary() : void + printStandard() : void

Point

Point # x : int Point # y : int x : int y : int ostream &operator<< : friend + Point(int=0, int=0) + setPoint(int, int) : void + getX() const : int + getY() const : int

Figure A- 4: UML class notation and example of the class Time and Point

An attribute is shown as a text string.. The default syntax is: visibility name : type-expression [ multiplicity ordering ] = initial-value { property-string } where visibility is one of:

- 311 - APPENDIX A: UML

+ public visibility # protected visibility - private visibility ~ package visibility

An operation (function) is shown as a text string. Behaviour of object depends on its class (remember each object “knows” its class). Operations take parameters of certain type, and return result of certain type. The default syntax is: visibility name ( parameter-list ) : return-type-expression { property-string } where visibility is one of: + public visibility # protected visibility - private visibility ~ package visibility

Notation for abstract class is shown on the fig. A-5.

0r Class Name {abstract} Class Name

Figure A-5: UML abstract class notation

A nested class is a class that is fully enclosed within another class declaration. If class is attached to another class by a line with the “anchor” symbol then it is declared within the Namespace of that class. They are useful for providing objects that are required by a particular class to function, but have no stand-alone functionality.

- 312 - APPENDIX A: UML

DeclaringClass

NestedClass

Figure A-6: UML nested class notation

A.3.1.2 Object As we know, an object represents a particular instance of a class. The object notation is derived from the class notation by underlining a name of object (and its class) in the top rectangle of the graphics representation. The syntax: objectname : classname Object is shown with object name, class name (optional) and attribute value (optional). The presence of an object in a particular state of a class is shown using the syntax: objectname : classname ‘[‘ statename-list ‘]’ The list are comma-separated list of names of states that can occur concurrently. The second part of the graphical representation contains the attributes for the object and their values as a list. Each value line has the syntax: attributename : type = value

dinnerTime : Time hour = 19 minute = 30 second = 0

stratPoint : Point endPoint : Point x = 0.18 x = 4.28 : Point y = 1.23 y = 7.27

Figure A-7 UML notation for objects dinnerTime of class Time and startPoint of class Point

- 313 - APPENDIX A: UML

A.3.1.3 Interface An interface is a collection of operations that are used to specify a service of a class or a component (Booch, 1999). It is a contract of related services and a set of conditions that must be true for the contract to be faithfully executed

Figure A-8: UML interface notation

A.3.1.4 Component A component is a physical and replaceable part of a system that conforms to and provides the realization of a set of interfaces (Booch, 1999). Component may be: source code component (shell scripts, data files, *.cpp), run time component (Java Beans, ActiveC controls, COM objects, CORBA objects, DLL’s and OCX’s from VB), Executable component (*.exe files) etc.

Figure A-9: UML a component notation

A.3.1.5 Package A package is a general purpose mechanism for organizing elements into groups (Booch,1999). It is a model element which can contain other model elements. That is a grouping mechanism and do not have any semantics defined. An element can belong to only one package. A package could be very suitable to present Modularity.

- 314 - APPENDIX A: UML

Figure A-10 UML package notation

Some of the other numerous model elements (things) and their notation will be presented through UML diagrams.

A.4. Relationships The UML defines a number of different kinds of relations. A relationship is a connection among things. The most important relations in UML are association, generalization, and dependency.

A.4.1. Association An association is one of a basic relation defined in UML. UML notation of association is a solid line connecting two elements (both ends may be connected to the same element, but the two ends are distinct). Associations denote a relationship between concepts (classes). Association link has two ends which are called roles. A role, that identifies one end of an association, has a name, a multiplicity, a navigability and a type .

1..* Company * Person employer employee

Person Manager Name Company Works for Person Name Address Name ID Address employer employee Address Supervises Salesperson ID

Figure A-11 Examples of UML notation of association

Classes play a specific role in an association. A multiplicity indicates number of objects of one of the classes linked by the association that participate in the relationship (a faculty has many students and each student attends only one faculty) . Specifications of multiplicity may be given for

- 315 - APPENDIX A: UML roles within associations, parts within composites, repetitions, and other purposes. Its specification is represent with subset of the open set of positive integers. It is shown as a text string with a comma-separated sequence of integer intervals, where an interval represents a (possibly infinite) range of integers, in the format: lower- bound .. upper-bound

Class unspecified

1 exactly one Class * –any number, also written as 0..*, or 0..n

* many n – an exact number e.g., 1, 3, … Class (zero or more) 0..1 – zero or one

0..1 optional Class (zero or one)

m..n numerically Class specified

Figure A-12 Examples of UML notation of multiplicity

A role can have an arrow indicating its navigability that shows which class has the responsibility for maintaining the relationship between the classes (the school class has a responsibility for knowing which students attend).

Figure A-13 Examples of navigability

- 316 - APPENDIX A: UML

An association class is an element that has both association and class properties. That is frequent case in many-to-many association because it is difficult to position the properties at any end of the association.

User authorized on Workstation

Authorization Access right Priority startSession()

1..* Company * Person employer employee

Contract salari : int boss startData : Data 0..1 worker *

manages

Figure A-14 Examples of association classes

A.4.1.1. Aggregation An aggregation is a association denoting a „part of“ (or „has-a“) relationship between the objects of the respective classes. The term aggregation is frequently used to describe the structure of a class or object which includes instances (objects) of other user defined classes. Aggregation can be of two types: shared aggregation and composition.

Note: A student can be in more 1 * * * University Faculty PlannedCourses than one department and

* courses can be planned in more * than one department Students

Figure A-15 A shared aggregation

- 317 - APPENDIX A: UML

A.4.1.2 Composition Composition indicates that one class belongs to the other. Composition is a type of aggregation which links the lifetimes of the aggregate and its parts. That means that the parts cannot exist if the aggregate no longer exists and an entity can exist as part of only one aggregate (i.e. Destroying a database destroys its tables. But destroying the tables of a database does not destroy the database) Composite aggregation is a strong form of aggregation, which requires that a part instance be included in at most one composite at a time and that the composite object has sole responsibility for the disposition of its parts. Composition is shown by a solid filled diamond as an association end adornment.

under composition EmailMessage an element can be part of at most one aggregate

* 0..n 1 Header Body Attachment

Figure A-16: example for composition

Alternately, UML provides a graphically-nested form that is more convenient for showing composition in many cases.

Window

2 scrollbar : Slider

1 title : Header

1 body : Panel

Figure A-17: Graphically-nested form of composition

- 318 - APPENDIX A: UML

A.4.1.3 Generalization Generalization, as relationship, is another name for inheritance or an "is a" relationship. It is a relationship between two classes where one class is a specialized version of another. We can say that it is a relationship between a more general element (the parent) and a more specific element (the child). This specific element is fully consistent with the more general element and adds additional necessary information. For example, Player is a kind of Person. So the class Player would have a generalization relationship with the class Person.

Figure A-18: UML notation for generalization, example

Generalization relationship is very important in modelling, because it captures similarities, and for support of software reusability. It is used for classes, packages, use cases, and other elements. A notation for Generalization is a solid-line path from the child (the more specific element, such as a subclass) to the parent (the more general element, such as a superclass), with a large triangle at the general element side end.

- 319 - APPENDIX A: UML

When generalization is applied to associations, arrow goes from a child association path to a parent association path.. An more suitable notation is to represent each association as an association class.

A.4.1.4 Dependency Dependency relation shows that a change to one element's (package’s) definition may cause changes to the other. It is a relationship between two model elements that relates the model elements themselves and does not require a set of instances for its meaning. It shows that a change made on the target element, usually, require a change to the source element in the dependency.

Figure A-19: Various dependencies among classes

A notation: dashed arrow between two model elements. The element at the arrow back side (the client) depends on the element at the arrowhead (the server). Optional stereotype and an optional individual name may stay as a label.. Some of many dependency stereotypes (keyword) are shown in the next table:

Keyword Name Description access Access The granting of permission for one package to reference the public elements owned by another package (subject to appropriate visibility). Maps into a Permission with the stereotype access. bind Binding A binding of template parameters to actual values to create a nonparameterized element.

- 320 - APPENDIX A: UML import Import The granting of permission for one package to reference the public elements of another package, together with adding the names of the public elements of the supplier package to the client package. use Usage A situation in which one element requires the presence of another element for its correct implementation or functioning. May be stereotyped further to indicate the exact nature of the dependency, such as calling an operation of another class, granting permission for access, instantiating an object of another class, etc. If the keyword is one of the stereotypes of Usage (call, create, instantiate, send), then it maps into a Usage with the given stereotype. Figure A-20: Some dependency stereotypes

A.5 The UML Diagrams

UML diagrams are basic and very powerful modelling tool, used in every phase of system building. Although, each of nine UML diagrams provides developer or development team with a different perspective, we can group them into five groups from the system modelling aspects. Those five groups are: use-case model diagram, static structure diagrams, interaction diagrams, state diagrams and implementation diagrams.

- 321 - APPENDIX A: UML

Figure A-21: UML diagrams

A.5.1 Use-Case Model Diagrams Use-Case Model Diagrams as tools necessary to document system requirements, as a critical factor in the outcome of successful information systems development project. The main tool is use case diagram. In a few words, we can say that use-case diagrams graphically describe who will use the system and in what ways the user expects to interact with the system. The pieces of interactive functionality are called use cases. To construct a use-case model diagram we have to be able to define actors and use cases, to identify them from context diagrams and other sources and to describe the relationships that can appear on a use-case model diagram. A use case is a description of a set of sequences of actions that a system performs to yield an observable result of value to an actor An actor represents a coherent set of rules that users of use cases play when interacting with these use cases A use case model is composed of a set of use cases describing what the system should do (intended to represent all the functionalities the system should provide). A use case models a dialogue between the actors and the system and a use case is triggered by an actor to invoke a certain function in the system.

- 322 - APPENDIX A: UML

Figure A-22: Use case diagram for a simple e-learning

Actor: Role that a user plays with respect to the system (student, registrar, teacher). Actors carry out use cases, look for actors, then their use cases. Actors do not need to be humans. Actors can get value from the use case or participate in it.

It is necessary to make use-case description: generic, step-by-step written description of a use case’s event flow includes interactions between the actor(s) and a use case. The actors are outside of the boundary, whereas the use cases are inside the boundary of the system. We describe a use case (written in natural language) as follows: 1. Use case name 2. Participating actors 3. Entry condition 4. Flow of events 5. Exit condition 6. Special requirements

When describing a complex system, its use case model can become quite complex and can contain redundancy. We reduce the complexity of the model by identifying commonalities in different use cases. In the process of object-oriented analysis, each previously defined use case has to be refined to include more and more detail based on the facts we discovered (i.e. defining user interface requirements). Figure A-23 shows one use case diagram for the simple catalogue-sale system, and figure A-24 shows some use case relationship in this system.

- 323 - APPENDIX A: UML

Figure A-23: Use case diagram for a catalogue-sale system

Figure A-24: relationships in a catalogue-sale system

A.5.2 Static Structure Diagrams Two diagrams that model static structure of a system are class diagrams and object diagrams.

A.5.2.1 Class diagrams Class diagrams are the central point of almost every object oriented method, including UML. They show object classes of the system and relationship between those object classes. Classes and objects are depicted in UML by boxes including

- 324 - APPENDIX A: UML three compartments, name, attributes and operations. Object names are underlined to indicate that they are instances. We stated some basic relationships earlier in this chapter. Generalization is the relationship between a general class and one or more specialized classes. Generalization enables us to describe all the attributes and operations that are common to a set of classes. Abstract classes are distinguished from concrete classes by italicizing the name of abstract classes. Figure A-25 and A-26 shows two examples of class diagrams.

Figure A-25: An example of class diagram

- 325 - APPENDIX A: UML

Figure A-26: An example of class diagram

A.5.2.2 Object diagrams Object diagrams is closely linked to class diagrams and describe the static structure of a system at a particular time. They can be used to test class diagrams for accuracy. As an object is an instance of a class, an object diagram could be considered as an instance of a class diagram. It model actual object instances and show current values of object’s attribute. Using object diagrams, developer could “see the picture ”of the system’s objects at one point in time.

- 326 - APPENDIX A: UML

Figure A-27: An example of object diagram

A.5.3 Interaction diagrams To model interaction of a system means to consider set of objects, relationships between objects and messages changed between them. In this group of UML diagrams we use sequence diagrams and collaboration diagrams. Those diagrams are used to model dynamic behaviour of the system.

A.5.3.1 Sequence diagrams Sequence diagrams describe interactions among classes in terms of an exchange of messages in time. The way of an object behaviour, in some particular context, is described by class roles. Use the UML object symbol to illustrate class roles, but don't list object attributes. Activation boxes represent the time an object takes to complete a task. Arrows represent messages in communication between objects. Lifelines of objects are presented by vertical dashed lines. Sequence diagrams are used to formalize the behaviour of the system and to visualize the communication. They are useful for identifying additional objects that participate in the use cases. We call objects involved in a use case participating objects.

- 327 - APPENDIX A: UML

Shortly, we can say that a sequence diagram represents the interactions that take place among objects. Sequence diagrams depict services as a connection among the use case behaviour and objects. Actors are shown as the leftmost column.

Figure A-28: An example of sequence diagram

A.5.3.2 Collaboration diagrams As we saw, sequence diagrams focus on the timing of messages. Collaboration diagrams are similar to sequence diagrams but they focus on collaboration among objects. They represent interactions between objects as a series of sequenced messages. Collaboration diagrams describe both the static structure and the dynamic behaviour of a system. Objects behaviour is described by a class roles. Using UML object symbol to illustrate class roles it does not list object attributes. Unlike sequence diagrams, collaboration diagrams do not have an explicit way to denote time and instead number messages in order of execution.

- 328 - APPENDIX A: UML

Figure A-29: An example of collaboration diagram

A.5.4 State Diagrams

A.5.4.1 Statechart Diagrams Statechart diagrams describe the dynamic behaviour of a system in response to external stimuli. Statechart diagrams are especially useful in modelling reactive objects whose states are triggered by specific events. States represent situations during the life of an object. You can easily illustrate a state in SmartDraw by using a rectangle with rounded corners.

A.5.4.2 Activity Diagrams Activity diagrams illustrate the dynamic nature of a system by modelling the flow of control from activity to activity. An activity represents an operation on some class in the system that results in a change in the state of the system. Typically, activity diagrams are used to model workflow or business processes and internal operation.

A.5.5 Implementation diagrams In this last group of UML diagrams we have component diagram and deployment diagram

A.5.5.1 Component Diagrams Component diagrams describe the organization of physical software components, including source code, run-time (binary) code, and executable modules. As we said, a component is a physical building block of the system and it is represented as a rectangle with tabs.

- 329 - APPENDIX A: UML

An interface describes a group of operations used or created by components.

Figure A-30: An example of component diagram

A.5.5.2 Deployment Diagram Deployment diagrams show the physical resources in a system, in nodes, components, and connections and depict the relationship among run-time components and hardware nodes. Components are self-contained entities that provide services to other components or actors (i.e. A Web server is a component that provides services to Web browsers and browser such as Netscape is a component that provides services to a user). A distributed system can consist of many interacting run-time components.

Figure A-31: An example of deployment diagram

- 330 - BIBLIOGRAPHY

Bibliography

[Allison1999] Thinking in C: Foundations for Java & C++, by Chuck Allison (a MindView, 1999). [Stroustrup1997] The C++ Programming Language, 3rd edition, by Bjarne Stroustrup (Addison-Wesley, 1997) [Lippman1998] C++ Primer, 3rd Edition, by Stanley Lippman and Josee Lajoie (Addison-Wesley 1998) [Allison1998] C & C++ Code Capsules, by Chuck Allison (Prentice-Hall, 1998). [ANSI/ISO] The C++ ANSI/ISO Standard. [Eckel1993] C++ Inside & Out, by Bruce Eckel, (Osborne/McGraw-Hill 1993). [Eckel2000a] Thinking in C++, Volume 1, Second Edition, by Bruce Eckel, (MindView Inc 2000) [Eckel2000b] Thinking in C++, Volume 2, Second Edition, by Bruce Eckel, (MindView Inc 2000) [Vandev2002] C++ Templates: The Complete Guide, By David Vandevoorde, Nicolai M. Josuttis, (Addison Wesley, 2002) [Lafore1998] Waite Group's Object-Oriented Programming in C++, Third Edition, by Robert Lafore, (Macmillan Computer Publishing, 1998) [Josuttis] The C++ Standard Library, A Tutorial And Reference, by Nicolai M. Josuttis, (Addison-Wesley) [Gamma1995] Design Patterns, Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Addison-Wesley, 1995) [Larman2002] Applying UML And Patterns: An Introduction to Object- Oriented Analysis And Design And Iterative Development (3rd Edition), by Craig Larman (Prentice-Hall, Inc, 2002) [Budd2002] An Introduction to Object-Oriented Programming (3rd Edition), by Timothy A. Budd (Upper Saddle River, N.J. Pearson Addison Wesley cop. 2002) [Booch1999] The Unified Modeling Language Users Guide, by Grady Booch, James Rumbaugh, Ivar Jacobson (Reading, Mass. Addison-Wesley cop. 1999) [Booch1991] Object-Oriented Design With Applications, by Grady Booch (Redwood City [etc.] The Benjamin/Cummings Publishing cop. 1991)

- 331 - BIBLIOGRAPHY

[Meyer1997] Object-Oriented Software Construction (2nd Edition), by Bertrand Meyer (Upper Saddle River, NJ Prentice Hall, 1997) [Liskov] Abstraction And Specification in Program Development, by Barbara Liskov, John Guttag (Cambridge (Mass.) [etc.] MIT Press New York [etc.] McGraw-Hill) [Rumbaugh1991] Object-Oriented Modeling and Design, James E. Rumbaugh, Michael R. Blaha, William J. Premerlani, Frederick Eddy, William E. Lorensen (Prentice-Hall, 1991) [Jacobson1992] Object-Oriented Software Engineering – A Use Case Driven Aproach, by I. Jacobson, M. Christerson, P. Jonsson (Addison- Wesley, 1992) [Myopoulos1984] An Overview of Knowledge Representation, by John Myopoulos, Hector J. Levesque (Springer-Verlag, 1984)

- 332 - INDEX

Index

- 333 -