Base class constructors run even without a constructor for the derived class
Constructors run in the order the members appear in the class
#include <iostream>
using namespace std;
class Base
{
public:
int j;
Base( ) : j( 3 )
{
}
};
class Derived : public Base
{
public:
int k;
};
int main( )
{
Derived d;
cout << d.j << endl; // j = 3
cout << d.k << endl; // k = junk
}
% derived.exe
3
-858993460
#include <iostream>
using namespace std;
class Base2
{
public:
int i;
Base2( int i ) : i( i )
{
cout << “i = " << i << endl;
}
};
class Base3
{
public:
int j;
Base3( int j ) : j( j )
{
cout << "j = " << j << endl;
}
};
class Derived4 : public Base2, Base3
{
public:
Derived4( int j, int i ) :
Base3( j ), Base2( i )
{
cout << "Derived4" << endl;
}
};
int main( )
{
Derived4 d4( 18, 22 );
}
% order.exe
i = 22
j = 18
Derived4
A derived type is used to represent an is a relationship
A derived type inherits the member functions and member variables of its parent
Permits an object of a subclass type to be treated as an object of any supercalss type
When a variable of the base calss has a value of the derived class, downcasting is possible, which casts a reference of a base class to one of its derived classes
Save implementation effort by sharing functions provided by the base class
We can replace any Triangle
object in a program with an Isosceles
object and the program will still work
If S is a subtype of T, then objects of type T may be replaced with objects of type S
Functions that use pointers to base classes can use objects of derived classes without knowing it
This is upcasting
Allow different derived classes to be used interchangeably thorugh the interface provided by a common base class
Often, the compiler can tell which derived type is needed
This is called the static type
Triangle t( 3, 4, 5 );
t.print( );
Triangle *t_ptr = &t;
t_ptr->print( );
Isosceles i( 1, 12 );
i.print( );
Isosceles *i_ptr = &i;
i_ptr->print( );
./a.out
Triangle: a=3, b=4, c=5
Triangle: a=3, b=4, c=5
Isosceles: base=1, leg=12
Isosceles: base=1, leg=12
Other times, the type is not known until run time
This is called the dynamic type
Triangle * ask_user( );
int main( )
{
Triangle *t = ask_user( ); //enters "Isosceles"
t->print( );
}
The static type of t
is Triangle *
The dynamic type of t
is Isosceles *
To create an object that won't go out of scope when the function returns, we use new
Triangle *ask_user( )
{
cout << "Triangle, Isosceles "
"or Equilateral?" << endl;
string s;
cin >> s;
if ( s == "Triangle" )
return new Triangle( 3,4,5 );
if ( s == "Isosceles" )
return new Isosceles( 1,12 );
if ( s == "Equilateral" )
return new Equilateral( 5 );
cout << "Unrecognized shape '"
<< s << "'\n";
exit( 1 ); //crash
}
Always safe to upcast, so returning Isosceles
or Equilateral
is safe
Always goes together with new
int main( )
{
Triangle *t = ask_user( );
//enters "Isosceles"
t->print( );
delete t;
}
./a.out
Triangle, Isosceles or Equilateral?
Isosceles
Triangle: a=1 b=12 c=12
t
can change types at runtime, in other words it is polymorphic- We can use the virtual function mechanism in C++ to check the dynamic type of
t
at runtime and call the correction version ofprint()
The ability to associate many behaviors with one function name
A polymorphic type is any type with a virtual function
Virtual functions are the C++ mechanism used to implement polymorphism
virtual
means "check the dynamic type at runtime, then select the correct print( )
member funciton
virtual
is inherited so the overridden print( )
in Isosceles
and Equilateral
will automatically become virtual
class Triangle
{
virtual void print( ) const
{
:
}
:
};
Now the orignal program works correctly
./a.out
Triangle, Isosceles or Equilateral? Isosceles
Isosceles: base=1 leg=12
Rectangle
is a Shape
class Shape
{
public:
virtual double area( ) const {/*...*/}
virtual void print( ) const {/*...*/}
};
class Triangle : public Shape {/*...*/};
class Rectangle : public Shape {/*...*/};
Although our code is perfectly legal C++, Shape
has no member variables, and something like
Shape s;
s.print( );
makes no sense and will not compile!
A shape is an abstract idea, and our shape should only be an interface to ensure that all shapes behave the same way
You cannot create an instance of an abstract class, which is what we want for a Shape
An abstract class will force derived types to all behave the same way
Each method is declared a virtual
function and "Assigns" a 0 to each of these virtual
functions
class Shape
{
public:
virtual double area( ) const = 0;
void print( ) const = 0;
};
- You can create a pointer to an abstract class, and then assign the pointer to a concrete class derived from the base class
Rectangle r(2,4); //concrete derived type
Shape *s = &r; //OK, Rectangle is a Shape
s->print(); //virtual, so correct version
//of print() is called
./a.out
Rectangle: a=2 b=4
Creates objects for another programmer who doesn't need to know their actual types
//EFFECTS: asks user to select a shape
// returns a pointer to correct object
Shape * ask_user( );
ask_user( )
is an example of a factory function
Conversion from Isosceles
to Shape
is called an upcast
In ask_user( )
, the type conversion is automatic, an implicit cast
A cast from one type in the class hierarchy to a lower one
Since Shape
might not be Isosceles
, this cas cannot happen automatically
dynamic_cast<T*>(ptr)
downcasts ptr
to type T*
, if possible. Otherwise, it returns 0.
This is usually a symptom of a poor design