Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
For example, the programmer could model a car object this way:
class car {
engine motor;
public: //please ignore for the moment
void on();
void off();
This means that a car object is something that has an engine, represented by the motor data member, and for which two operations are permitted: on() (which presumably turns the engine on) and off() (which turns the engine off). After having given these lines, the user could create more instances of the car class, with statements like:
car renault_megane, fiat_punto,opel_astra;
and operate on them with the allowed member functions:
Please be aware that, while every instance of the car class shown permits the same operations on it (namely, on() and off()) they have each one their own copy of the data members, i.e. the renault_megane and the fiat_punto objects for example have their own motor inside!
The C++ member access specifiers permits to the class author to decide which data members and member functions are visible to whom. This mechanism provides several advantages and improves overall the quality of design: If the class user, who uses the class to create a more articulated design, can only see what is in the public section, then he/she doesn’t have to care of the implementation details that are not visible to him/her; this means that he/she can concentrate on how to use the class, and how this reacts to invocations of its member functions, instead of fiddling around with the internals of the data type; On the other hand, for the class creator, this separation of interface and implementation is also beneficial, since he/she can change the internal representation of the storage of the data type, and even the algorithms embodied in the member functions, as long as he/she doesn’t change the interface of the class (the prototypes of the member functions); this ensures that such modifications will not break existing codes written by users of the class.
Aggregation or composition is one of the two most important ways to reuse code in object oriented programming languages (the other is inheritance). Aggregation is an operation which models a so called “has a” relationship, like in a car has an engine, which is exactly the example we shown two slides ago: this shows to you that the data members of a class do not have to be necessarily C++ basic data types (char, int, float, and so on…) but can be also instances of the data types that the user has defined by mean of the class keyword. Actually, one of the major effort in the design of the C++ language was to let the user defined data types be as indistinguishable as possible from the “native” basic data types.
Inheritance  is the other mechanism peculiar to object oriented programming languages to improve code reuse. Inheritance models an “is a” relationship because the Derived class inherits all the data members and member functions of the Base class, and is therefore itself one object of the Base data type too; The Derived class can override the implementation of some or all of the Base member functions, and can also add some additional member functions or data members. The way inheritance is used in object oriented programming is to create a more peculiar, more specialized data type starting from an existing one, of which it reuses some characteristics, for example: An opel astra is an auto, a fiat punto is an auto, an opel astra turbo diesel is a specialized version of an opel astra:
opel astra
fiat punto
opel astra td
Polymorphism is a concept peculiar to object oriented programming, and represents the capability of using an object of a derived class where one object of its base class is syntactically required. This enforces again the is a relationship that we mentioned before: if an opel astra is a car, in C++ you can pass an opel astra object to every function which expects a car (it sound logical, hopefully!):
void start(car& c); // turns on a car...
opel_astra my_opel_astra;
Please note that the other assignment directions are not legal, though: if you have a function that expects an opel you can not pass to it a generic car, since not every car must necessarily be an opel! (You could pass to the function an opel astra turbodiesel, though, as this is a more specialized class) The advantages of polymorphism are that you can refactor code which must act on related- data types in a single function, which makes the design more concise and less error prone.
In C++ object oriented programs, it is common to have some of the member functions of the base class tagged with the virtual keyword. If a member function is tagged as virtual, when the program must invoke it starting from a pointer or a reference to the base class , it detects at run-time if the pointer or reference is actually connected to an object of a derived class (which, as we have seen in the previous slide, is permitted), and if this is the case, it calls the proper version of the member function (and not the one declared in the base class). This mechanism, though, doesn’t happen if the virtual keyword is not present, in which case the compiler takes the decision of which version of the function must be invoked based on the information that it has at disposal at compile time, when it examines the line
which, therefore, forces the invocation of the base class version.
Consider for example the following scenario:
If the BirdController reLocate() member function performs something like:
void reLocate() {
and provided that move() is virtual inside of Bird, if the BirdReference_inside... data member of BirdController is once connected to a Goose and once connected to a Penguin, their corresponding versions of move() get called, which probably is differently implemented in the two classes (a Penguin can not fly, for instance).
In the C++ programming language, instances of the native data types, as of the user defined data types, can be created on the stack automatically when their visibility scope is entered, and are automatically destroyed when the scope is left (i.e. when the } closing brace of the block is reached):
if(...) {
int i; //i created on the stack
car c1;//c1 created on the stack
} //i and c1 both go out of scope and are destroyed
alternatively, objects (and instances of the native data types) can be allocated dynamically  using new, in which case it is responsibility of the programmer to destroy them when they are no longer needed using delete:
int *ip;
car *cp;
ip = new int(3);//mem allocated for an int and initialized to 3
cp = new car;//mem allocated for a car object
...//ip now stores the address of the initialized area where
...//the “3” integer is stored
delete ip; delete cp;//this frees up the memory
Please note that memory is not freed up for objects allocated with new until the end of the program unless you explicitly call delete.