An acquaintance of mine has shared some sample code that exhibits somewhat strange behavior. Basically the sample code has a base class and a derived class, which overrides a virtual method. There are two sets of STL lists, each containing two lists (i.e. four STL lists in all).
Set-A
- List of instances of base class, containing derived instances
- List of instances of derived class, containing derived instances
Set-B
- List of pointers to base class, containing pointers to derived instances
- List of pointers to derived class, containing pointers to derived instances
In Set-A the overridden method is not executed in the case where we have a list of instances of base class, containing derived instances.
The sample code follows:
#include <iostream>
#include <list>
using namespace std;
//***********************************************************************
//
//***********************************************************************
class Base {
protected:
int i;
public:
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
Base(int m)
{
i = m;
}
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
int get_i()
{
return i;
}
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
virtual int xyz()
{
return i;
}
// Returns the value of the base
// class attribute
};
//***********************************************************************
//
//***********************************************************************
class Derived : public Base {
protected:
int j;
public:
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
Derived(int m, int n) : Base(m)
{
j = n;
}
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
int get_j()
{
return j;
}
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
int xyz()
{
return j;
}
// Returns the value of the derived
// class attribute
};
//***********************************************************************
//
//***********************************************************************
typedef list<Base> BaseList;
typedef list<Base>::iterator BaseIterator;
typedef list<Derived> DerivedList;
typedef list<Derived>::iterator DerivedIterator;
typedef list<Base*> BasePtrList;
typedef list<Base*>::iterator BasePtrIterator;
typedef list<Derived*> DerivedPtrList;
typedef list<Derived*>::iterator DerivedPtrIterator;
//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
main()
{
Derived* d[5];
for (int k1 = 0; k1 < 5; k1++)
{
d[k1] = new Derived(k1, 2 * k1);
// The base attribute ('i') has value 0 through 4
// The derived attribute value ('j') is double that
}
// Instance collection declarations
BaseList bcollection;
BaseIterator biter, beol;
DerivedList dcollection;
DerivedIterator diter, deol;
// Pointer collection declarations
BasePtrList bpcollection;
BasePtrIterator bpiter, bpeol;
DerivedPtrList dpcollection;
DerivedPtrIterator dpiter, dpeol;
for (int k2 = 0; k2 < 5; k2++)
{
//Insert elements in base collection
bcollection.insert(bcollection.begin(), *d[k2]);
//Insert the SAME elements in the derived collection
dcollection.insert(dcollection.begin(), *d[k2]);
//Insert elements in base-ptr collection
bpcollection.insert(bpcollection.begin(), d[k2]);
//Insert the SAME elements in the derived-ptr collection
dpcollection.insert(dpcollection.begin(), d[k2]);
}
cout << "** Instance-collection behavior **\n";
// Iterate through the base collection and execute the
// virtual method "xyz()" on each element
cout << "Base collection:" << endl;
beol = bcollection.end();
for (biter = bcollection.begin(); biter != beol; biter++)
cout << " get_i()=" << (*biter).get_i() << ", xyz()="
<< (*biter).xyz() << endl;
// Iterate through the derived collection and execute the
// virtual method "xyz()" on each element. Since we entered
// the exact same elements in both lists, the EXPECTED output
// is the same as before.
//
// Check out for yourself ;-(
//
cout << "Derived collection:" << endl;
deol = dcollection.end();
for (diter = dcollection.begin(); diter != deol; diter++)
cout << " get_i()=" << (*diter).get_i() << ", xyz()="
<< (*diter).xyz() << endl;
cout << "The exact same elements were entered in both collections.\n"
<< "Is the output the same in both the cases?\n";
cout << "\n\n** Pointer-collection behavior **\n";
// Iterate through the base-pointer collection and execute the
// virtual method "xyz()" on each element
cout << "Base-pointer collection:" << endl;
bpeol = bpcollection.end();
for (bpiter = bpcollection.begin(); bpiter != bpeol; bpiter++)
cout << " get_i()=" << (*bpiter)->get_i() << ", xyz()="
<< (*bpiter)->xyz() << endl;
// Iterate through the derived-pointer collection and execute the
// virtual method "xyz()" on each element. Since we entered
// the exact same elements in both lists, the EXPECTED output
// is the same as before.
//
// No surprises this time around :-(
cout << "Derived-pointer collection:" << endl;
dpeol = dpcollection.end();
for (dpiter = dpcollection.begin(); dpiter != dpeol; dpiter++)
cout << " get_i()=" << (*dpiter)->get_i() << ", xyz()="
<< (*dpiter)->xyz() << endl;
cout << "The exact same elements were entered in both collections.\n"
<< "Is the output the same in both the cases?\n";
}