views:

139

answers:

3

Hi folks!

Actually I'm new to C++. I tried something out (actually the map container) but it doesn't work the way I assumed it will... Before posting my code, I will explain it shortly.

I created 3 classes:

ClassA ClassDerivedA ClassAnotherDerivedA

The two last ones are derived from "ClassA".

Further I created a map:

  map<string,ClassA> test_map;

I put some objects (from Type ClassDerivedA and ClassAnotherDerivedA) into the map. Keep in mind: the mapped value is from type "ClassA". This will only work because of Polymorphism. Finally I created an iterator which runs over my map and compares the user input with my keys in the map. If they match, it will call a specific method called "printOutput".

And there is the Problem: Although i declared "printOutput" as "virtual" the only method called is the one from my base class, but why? and here is the code:

#include <iostream>
#include <map>

using namespace std;

class ClassA
{
    public:
        virtual void printOutput() { cout << "ClassA" << endl;      }
};

class ClassDerivedA : public ClassA
{
    public:
        void printOutput() { cout << "ClassDerivedA" << endl;       }
};

class ClassAnotherDerivedA: public ClassA
{
    public:
        void printOutput() { cout << "ClassAnotherDerivedA" << endl;        }
};

int main()
{
    ClassDerivedA class_derived_a;
    ClassAnotherDerivedA class_another_a;


  map<string,ClassA> test_map;
    test_map.insert(pair<string,ClassA>("deriveda", class_derived_a));
    test_map.insert(pair<string,ClassA>("anothera", class_another_a));

    string s;

    while( cin >> s )
    {
    if( s != "quit" )
    {
        map<string,ClassA>::iterator it = test_map.find(s);
      if(it != test_map.end())
        it->second.printOutput();
    }
    else
      break;
    }

}
+4  A: 

C++ is not Java. You cannot store a derived type in a variable of a base type. For example:

Base b = Derived();

will only store the Base part of Derived in the variable b. In order to get polymorphic behaviour, you would need to use pointers, and create the derived class dynamically:

Base * b = new Derived();

The same goes for C++ containers - you need:

map <string, Base *> m;

All of this should be covered in every introductory C++ text book - which one are you using?

anon
Keep in mind that you'll also need to delete the objects pointed to by your map entries when you're done with the map. If the map is destroyed before the Derived objects, those Derived objects are memory leaks!
dash-tom-bang
Thank you! The book I'm reading (german one) doesn't go that deep into STL, but tells all basic things. I missunderstood something, but know it's clear to me!
Mike Dooley
+13  A: 

The problem is slicing. You are storing ClassA values in your map. When you store derived class instances into the map, the get sliced into ClassA objects. You'll need to store pointers in your map instead of values.

See this for more info on slicing: http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c

Fred Larson
Works now! I misunderstood something: Polymorphism (in C++) does only work with pointers (like Neil Butterworth stated). Thanks to all!
Mike Dooley
Polymorphism works with pointers or references, but you can't use references in a container. So in this case you must use pointers of some sort. Using smart pointers, as Dima suggested, would be a great idea. A good choice would probably be shared_ptr from Boost or TR1.
Fred Larson
+4  A: 

You are experiencing "slicing". To get the virtual functions to work properly, you need to call them using a pointer or a reference. In other words, your map should contain pointers to ClassA:

map<string, ClassA *> test_map

Please remember to delete them when you are done, or use smart pointers.

Here's more on slicing: here, here, and here

Dima