A note on derived classes and assignment/parameter passing

Suppose you have a class Parent, and a class Child derived from the Parent:
class Parent {
   public:
      int i;
      Parent();
      void print();
};

Parent::Parent() 
{ 
   i = 1; 
}

void Parent::print() 
{ 
   cout << i << endl; 
}

class Child: public Parent {
   public:
      string s;
      Child();
      void print();
};

Child::Child() 
{ 
   s = "s"; 
}

void Child::print() 
{ 
   cout << s << ";";   
   Parent::print(); 
} 

Then the following is perfectly legal, and simply copies any
inherited fields (in this case just i) from c into p.
(Note that trying the reverse, c = p;, won't compile.)
Parent p; 
Child c; 
p = c;

Similarly, the following is perfectly legal, but the swap operates only using the Parent class fields/methods:
void swap(Parent &x, Parent &y)
{
   Parent tmp = x;
   x = y;
   y = tmp;
   x.print();
   y.print();
}

int main()
{
   Parent p;
   Child c;
   swap(p, c);
} 

Polymorphism:

For our purposes, polymorphism is allowing an item to assume multiple forms (data types).

There are a number of practical forms of polymorphism in C++:

Dynamic Binding

This is a form of polymorphism, in which identification of the correct class type (and hence the correct method to be used in a call) is delayed until run time rather than specified at compile time.

Ordinarily if you call a method through an object the object type determines the method type, e.g.
triangle t;
t.print(); // calls triangle::print on t

The same principle applies for dynamically allocated objects:
triangle *ptr = new triangle();
ptr->print(); // calls triangle::print on ptr's triangle

Suppose we want to have a generic Shape variable, but during the run of a program choose a specific flavour of shape (e.g. from derived class Triangle or Circle), and call the correct method for the derived class, instead of the base class.

This is done through abstract classes and virtual methods, as shown below (and discussed in depth in the lecture).

Example:
#include <iostream>
using namespace std;

// create the base class, with a pure virtual method, print
// note that such methods are assigned a null pointer (the 0)
//    rather than being given an actual implementation
class shape {
   public:
      virtual void print() = 0;
};

// create a derived class, this time providing
//    an implementation for the virtual method
class triangle: public shape {
   public:
      virtual void print() {
        cout << "I am a triangle" << endl;
      }
};

// create another derived class, again providing
//    an implementation for the virtual method
class circle: public shape {
   public:
      virtual void print() {
        cout << "I am a circle" << endl;
      }
};

int main()
{
   // create a pointer for an instance of the base class,
   //    it is ok to make this point at instances of
   //    any classes derived from the base class!
   shape *s;

   // let the user pick a specific kind of shape,
   //     allocate one, and set s to point to it
   cout << "Pick a shape: C for circle or T for triangle" << endl;
   char c;
   cin >> c;
   if (c == 'C') s = new circle;
   else s = new triangle;

   // call the print method through the pointer:
   //    at run time, since print for s is a pure virtual method,
   //    the program will identify which kind of shape s actually
   //    points at and will call either triangle::print or
   //    circle::print, whichever is appropriate!
   s->print();
}
Important note: you cannot create objects directly from an abstract base class, since the class has unimplemented methods. i.e. the following will not compile:
Shape s; // a pure shape object cannot exist!

In essence the abstract base class, Shape, exists only as a placeholder, indicating which methods are shared across all its descendants and which ones will be dynamically bound (those with "method() = 0;" implementations).