C++ Class Hierarchy Pointer Assigments

Implicit Conversion of Classes: The address of an object of a derived class can be assigned to a pointer declared to the base class, but the base class pointer can be used only to invoke member functions of the base class

A function in a derived class overrides a function in the base class only if its signature is identical to that of the base class except for some minor differences in the return type.

class Shape 
{
private:
 Point Centroid;
public:
 void Move(Point newCentroid); // move Shape to new centroid
 Point getCentroid() const;
 void print() const; // prints just Centroid coordinates
 /* more stuff here */
};
class Rectangle : public Shape
{
private:
 float length, width;
public:
 /* some stuff here too */
 float LengthDiagonal() const;// functionality not in Shape class
 void print() const 
 { /* print stuff that Rectangle class has here */ }
};

Shape* pShape;
pShape = new Rectangle;
pShape->print();

The address of the dynamically allocated, anonymous Rectangle object is assigned to a Shape pointer. The dereferenced pShape pointer will point to the print() member function in the Shape class, since the compiler binds a pointer to the member functions of its own class. Even though it points to a Rectangle object, pShape cannot invoke the Rectangle’s print() function. This problem will be overcome when virtual functions are introduced.

What is needed is a way to allow the pointer to the base class to access the members of the derived class. This is the purpose of a virtual function. Declaring a function to be virtual in the base class means that if a function in a derived class overrides it and a base class pointer is dereferenced, that pointer will access the member function in the derived class

class CBase 
{
public:
virtual void print() 
{ cout<< "Base class print() called. " << endl; }
};
class CDerived : public CBase 
{
public:
void print() 
{ cout<< "Derived class print function called." << endl; }
};
void main()
{
CBase* baseptr;
baseptr = new CDerived;
baseptr->print();
}

When this program is run, even though the type of baseptr is CBase*, the function invoked by the dereference “baseptr >print()” will be the print function in the derived class, CDerived, because the run time environment bound baseptr->print() to the derived object when it was assigned a pointer of type CDerived and it knew that print() was virtual in the base class.

Constructors cannot be virtual. Virtual destructors are possible:


class CBase {
public:
CBase()
{ cout << "Constructor for CBase called." << endl;}
~CBase()
{ cout << "Destructor for CBase called." << endl;}
};

class CDerived: public Cbase {
public:
CDerived()
{ cout << "Constructor for CDerived called." << endl;}
~CDerived()
{ cout << "Destructor for CDerived called." << endl;}
};
void main() {
CBase *ptr = new CDerived();
delete ptr;
}

Constructor for CBase called.
Constructor for CDerived called.
Destructor for CBase called.

The destructor for the CDerived class was not called because the destructor was not declared to be a virtual function, so the call "delete ptr" will invoke the destructor of the class of the pointer itself, i.e., the CBase destructor. This is exactly how non-virtual functions work.

If we make the destructor in the base class virtual. Even though we cannot actually override a destructor, we still need to use the virtual function specifier to force the pointer to be bound to the destructor of the most-derived type,

class CvirtualBase {
public:
CVirtualBase()
{ cout << "Constructor for CVirtualBase called." << endl; }
virtual ~CVirtualBase() // THIS IS A VIRTUAL DESTRUCTOR!!!
{ cout << "Destructor for CVirtualBase called." << endl; }
};
class CDerived: public CvirtualBase {
public:
CDerived()
{ cout << "Constructor for CDerived called." << endl; }
~CDerived()
{ cout << "Destructor for CDerived called." << endl; }
};
void main() {
CVirtualBase *ptr = new CDerived();
delete ptr;
}

Constructor for CVirtualBase called.
Constructor for CDerived called.
Destructor for CDerived called.
Destructor for CVirtualBase called.

In summary, a class C must have a virtual destructor if both of the following conditions are true:
• A pointer p of type C* may be used as the argument to a delete call, and
• It is possible that this pointer may point to an object of a derived class.

A pure virtual function is one that has no possible implementation in its own class. To declare that a virtual function is pure, use the following syntax:

virtual return-type function-name( parameter-list) = 0;

A class with at least one pure virtual function cannot have any objects that are instances of it, because at least one function has no implementation. Such a class is called an abstract class. In contrast, a class that can have objects is called a concrete class. A

Abstract Classes as Interfaces

template 
class Stack // an abstract stack base class
{
public:
Stack() {}
virtual void pop() = 0; // all pure
virtual T top() const = 0;
virtual void push(const T& t) = 0;
virtual unsigned int size() const = 0;
};
template 
class VStack : public Stack // a vector-based implementation
{
private:
vector vec;
public:
VStack() {}
void pop() { vec.pop_back(); }
T top() const { return vec[vec.size() - 1];}
void push (const T& t) { vec.push_back(t);}
unsigned int size() const { return vec.size(); }
};

When a class is derived from an abstract class and it does not redefine all of the pure virtual functions, it too is an abstract class, because it cannot have objects that represent it