Class Design: Which Member Functions Must be Virtual
Clearly virtual functions provide the ability to write code
GENERICALLY, i.e., independent of class hierarchy. This means
regardless of which classes are defined in the future, the code
continues to work. The alternative would require you to decide which
function to use by writing conditional code:
With virtual functions:
Car *p;
...
p->display(); // would automatically use the right display
// for the car pointed to by p at run time
Notice that this code works EVEN IF YOU ADD CLASSES IN THE FUTURE
Remember that object-oriented programs evolve through the addition of
new derived classes. So, it is a big advantage to have a mechanism to
accommodate class addition gracefully.
**************
Without dynamic binding:
Car *p;
// we have to encode the type somehow, and use it:
switch (p->typeOf())
{
case CAR: p->display(); break;
case TRUCK: ((TRUCK *) p)-> display(); break;
}
You need one case per class. If you add classes, this code has to be
maintained to deal with that case.
Constructor Protocol for Derived Classes
class Car { ... };
class Truck :public Car {...};
When a Truck is instantiated:
Truck X;
A new Truck X is created. But, every Truck is also a Car. Creating a
new Truck is also creting a new Car. As a result, creating a Truck
will result in calling the Truck constructor (as you might expect) AND
the Car constructor. This raises the question:
In what order are the constructors called when you instantiate a
derived class object?
The rule is always: BASE class constructor first, then DERIVED class
constructor. In other words:
Truck X;
will result in Car constructor call first, then Truck constructor
A second important question is "What if the Car constructor requires
parameters?"
class Car
{
public:
// assume that this is the ONLY constructor for Car
Car (string ser, string man, string mod, int pri);
...
};
Now, supposing we define:
class Truck :public Car
{
public:
// Truck constructor has to find a way to provide
// parameters to the Car constructor
// Truck usually accepts Car parameters in addition
// to Truck parameters, and should pass them along to Car
// Truck prototype below
Truck (int len, int wid, int ht, string ser,
string man, string mod, int pri);
...
};
Truck::Truck (int len, int wid, int ht, string ser,
string man, string mod, int pr)
: Car (ser, man, mod pr)
// Above syntax is called "member initialization list"
// names the base class constructor and provides actual
// parameters to that constructor -- in this case Car
{
height = ht;
width = wid;
length = len;
}
One way to avoid the problem of providing parameters to the base class
constructor is to define a DEFAULT CONSTRUCTOR for the base class.
So, if Car class provides a default constructor, then that constructor
will be used automatically whenever a derived class object is
instantiated.
Example:
Car10.h
Car10.cpp
Truck10.h
Truck10.cpp
main10.cpp
Types of Derivation
We will now describe the rules for different types of derivation. Up
to this point, we have only discussed "public" derivation, which meets
the normal idea of inheritance. It is used most of the time in C++
derived classes. We now describe the rules for public, protected, and
private derivation.
Under ALL derivations, the derived class members can directly access
the public and protected members of the base class. In other words,
members of Truck can ALWAYS access public and protected members of the
Car class. The private members of Car are visible ONLY inside Car.
class Car
{
public:
void get_serial ();
void set_serial (string);
protected:
string comment;
private:
string serial, manufacturer, model;
int price, seats;
};
Rules for public Derivation
class Truck : public Car { ... };
Under public derivation, the inherited public and protected members of
the base class RETAIN THE SAME ACCESS LEVEL in the derived class.
Inside Car class members:
ONLY members of Car are visible
Inside Truck class members:
ALL Member of Truck
Public and Protected members of Car
In non-member functions like main() { ... }
Through objects of type Truck:
All public functions of Truck
All public functions of Car
Through objects of type Car
All public functions of Car
Rules for protected Derivation
class Truck : protected Car { ... };
Under public derivation, the inherited public and protected members of
the base class APPEAR TO BE PROTECTED in the derived class.
Inside Car class members:
ONLY members of Car are visible
Inside Truck class members:
ALL Member of Truck
Public and Protected members of Car
In non-member functions like main() { ... }
Through objects of type Truck:
All public functions of Truck
Through objects of type Car
All public functions of Car
Rules for private Derivation
class Truck : private Car { ... };
Under public derivation, the inherited public and protected members of
the base class APPEAR TO BE PRIVATE in the derived class.
Inside Car class members:
ONLY members of Car are visible
Inside Truck class members:
ALL Member of Truck are visible
Public and Protected members of Car are visible
In non-member functions like main() { ... }
Through objects of type Truck:
All public functions of Truck
Through objects of type Car
All public functions of Car
With both protected and private derivation, the INTERFACE of the
derived class changes because public members of the base class will no
longer be visible. So, in this respect they are very similar. The
difference will be seen in subsequent derived classes of Truck.
class Eighteen_Wheeler : public Truck { ... };
Can members of Eighteen_Wheeler see members of Car?
Under protected derivation of Truck from Car, they can -- because Car
members appear protected in Truck.
Under private derivation of Truck from Car, they cannot -- because Car
members appear private in Truck, and will not be available to new
derived classes.
types-of-derivation.cpp [Example of different types of derivation]
Programming Aggregation in C++
Think of a Computer. It consists of other objects like a CPU, RAM,
and DISK. These are themselves instances of other classes. When an
objects consists of other objects inside, then you have the concept of
aggregation.
class CPU
{
public:
CPU (int speed, string model);
...
};
class DISK
{
public:
DISK (int capacity);
...
};
class RAM
{
public:
RAM (int maxsize);
...
};
// You can recognize aggregation because a computer object
// consists of other objects -- in other words each computer
// consists of unique instances of DISK, RAM, CPU.
class Computer
{
public:
private:
DISK d;
RAM r;
CPU c;
};
When a composite object like Computer is created, you also end up
creating internal objects corresponding to the DISK, RAM, CPU.
Creating a Computer object, will create the internal objects and
therefore constructors for those objects must be called.
2 questions:
1. What is the order of the constructors when you have aggregation?
The order of the constructors is the same as the order of the data
members in the class:
So, first DISK to initialize d
then RAM to initialize r
then CPU to initialize c
then Computer constructor
2. How are parameters passed to the internal constructors for DISK,
RAM, and CPU -- given that all require parameters?
We must design the Computer constructor so that it can accept
additional parameters that are intended to be passed into the
constructors for internal objects.
class Computer
{
public:
Computer (string ser, int ramsize, int disksize,
int cpuspeed, string cpumodel);
// Computer constructor accepts parameters for internal objects
...
private:
string serial;
};
Computer::Computer (string ser, int ramsize, int disksize,
int cpuspeed, string cpumodel)
// member initialization list specifies actual parameters
// for each data member
c(cpuspeed, cpumodel), r(ramsize), d(disksize)
{
// initalize the Computer attributes
serial = ser;
}
It is important to remember that the member initialization list
determines ONLY the parameters and NOT the order of the execution of
the constructors. That depends on the order in which the data members
appear in the class Computer.
Computer.cpp [example of aggregation in C++]
The University of Southern California does not screen or control the content on this website and thus does not guarantee the accuracy, integrity, or quality of such content. All content on this website is provided by and is the sole responsibility of the person from which such content originated, and such content does not necessarily reflect the opinions of the University administration or the Board of Trustees