Need help in using a STL list and polymorphism

Pages: 12
Suppose I have a class A
Then I create a class B derived from class A
I also create a class C derived from class A

I then create a Class X that will store a STL list of objects of classes A, B and C(all of these objects will be stored inside the same list) within a method of class X.

I can't really figure out how to do this.

I found 2 sources that kinda give me some help & suggestions, but I don't really understand and manage to combine them. Here they are:

http://www.cs.bu.edu/teaching/cpp/polymorphism/intro/

Code that helps me:

1
2
3
4
5
6
7
Employee *emplP;

if (condition1) {
  emplP = new Employee(...);
} else if (condition2) {
  emplP = new Manager(...);
}


And also : http://stackoverflow.com/questions/10183609/list-of-inherited-objects-c

I would be really grateful if someone could show me how to do such a thing: Create a STL list of objects of the classes A, B , C within a method in class X.
A list of pointers to the base class (A in this case) can hold pointers to any of the derived classes as well. So why not make a list of pointers to A and insert addresses of your objects?
But If i also have setter methods for each of the classes A, B, C like this

1
2
3
4
5
6
7
8
9
10
11
class A{
private:
     int a;
public:
     void set( int x )
    {
      cout << "Enter number: ";
      cin >> x;
      a = x;
    }
};


where do I call these functions to set some values for the objects ?

before or after I add them in the STL list?
Last edited on
You would probably have to call specialized methods before, since after it is in the list you can only treat it as an A object.

But that makes me ask; if you need to keep track of which classes are B's and C's to adjust their unique members/methods, why are you trying to put them in a list together? The point of the list of A's is that since all B's and C's are A's, you can simply treat them all as A's and get some things done with them.
Maybe make the setter pure virtual, or use dynamic_cast<>?
@Bardikus
where do I call these functions to set some values for the objects ?

before or after I add them in the STL list?


@Zhuge
You would probably have to call specialized methods before, since after it is in the list you can only treat it as an A object.


@naraku9333
Maybe make the setter pure virtual, or use dynamic_cast<>?



If the list is declared as holding dynamic pointers (created with new) of the base class type, and if each class has it's own virtual set function (doesn't have to be pure, but virtual anywhere in the inheritance tree), and the list is populated with pointers of derived classes, then the compiler will execute the correct function associated with that class pointer.

This is something I have done numerous times.

The essence of it is that the compiler will use the nearest available virtual function. It starts with the derived class, if not, it travels up the inheritance tree until it finds one that it can execute.

So there is no need for dynamic casting. Pure virtual functions could be used if the function must be redefined. Ordinary virtual functions allow redefinition of function only if required. Specialised methods can still be called after the list is populated.

Having a list of similar objects and applying specialised methods to all of them is a very handy thing.

Imagine you have an inheritance tree of Enemies in a RPG. Each Enemy has it's own weapon or it's own way of attacking something.

The list is declared as a list pointers to the base class Enemy, and is populated with dynamic pointers(must be created with new) to these derived Enemies. You iterate through the list, calling the Attack function for each one. Each Enemy attacks using it's particular Weapon or it's own Attack method (eg A T-Rex bites, while others might use a sword or gun).

Hope this helps :)
1
2
3
class enemy{
   attack *the_attack;
};
Now you can teach a velociraptor to use firearms.
Now you can teach a velociraptor to use firearms.


HaHa - I guess it is up to the game designer as to what various game objects can / cannot do.
A correction if I may.
Objects can be declared statically, or even as local variables and a pointer to them can still be used to invoke polymorphic behavior.
The objects don't have to be created dynamically using new.
The following example should work fine:
1
2
3
4
5
6
7
8
9
int main()
{
    B b;// local to main(), declared statically
    C c;
    A* pA = &b;
    pA->virtualMethod();// gives a "B" result
    pA = &c;
     pA->virtualMethod();// gives a "C" result
}

ne555 is suggesting (I think) use of what's called the "strategy pattern" and it is powerful.
@fun2code

The objects don't have to be created dynamically using new.


I said to use new because, I can only get my code to work when I create objects with new. Using the & operator didn't work for me. I am not sure why that is. Your example does not have inheritance, I wonder if that has anything to do with it? I am guessing there is something about the operation of the new operator and the _vptr being inherited?

http://www.cplusplus.com/forum/general/86094/2/#msg463678


I hope L B doesn't mind - we had a lengthy debate last time - I don't wish to rehash the entire debate again. It's all about learning, I guess.

Since posting that code in that thread, I have thought about it a bit more. The problem is that all the objects have to have a PollinateMe function, which isn't ideal, even though they might only be defined once virtually, high up in the inheritance tree for some objects like Animals.

Maybe this could be solved by removing the PollinateMe function from everything except flowers and making use of exceptions. An exception is thrown because the Animal or whatever does not have a PollinateMe function.

It is almost the same though, because we now have an exception handling function. It does sound better form a design point of view.

I have also been thinking about the design of a RPG using the mediator pattern, and I think I have it sorted so that plugins can be used, and the other requirement mentioned by L B are met. I will investigate the strategy pattern as well.

Hope your respective silly seasons are going well so far. :)

> Your example does not have inheritance, I wonder if that has anything to do with it?
> I am guessing there is something about the operation of the new operator and the _vptr being inherited?

No. With new, the storage duration is different, the object representation is the same.

@JLBorges

I don't mean to derail the OP's thread, but I am wondering why my code above only works if I use new?

It is not a big deal for me, my code works & I am happy with it - just an educational thing really. Hopefully the OP might learn something as well.
but I am wondering why my code above only works if I use new?


To know that, one would have to know what your code looked like when you didn't use new.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <list>
#include <iostream>

struct Base
{
    virtual char const* name() const = 0 ;
};

struct A : public Base
{
    char const* name() const { return "Class A" ; }
};

struct B : public Base
{
    
    char const* name() const { return "Class B" ; }
};

int main()
{
    A bcde ;
    B cdef ;
    A efgh ;
    B wxyz ;

    std::list<Base*> theList ;

    theList.push_back(&bcde) ;
    theList.push_back(&cdef) ;
    theList.push_back(&efgh) ;
    theList.push_back(&wxyz) ;

    for ( auto & element : theList )
        std::cout << element->name() << '\n' ;
}
Class A
Class B
Class A
Class B
@cire

I had something similar to yours I think.

I have to go out for a bit now, but I will change my code & post the errors, when I get back in about 2 hours.

Thanks for your time in writing some code. :)
All right I have been busted again !!

It all works fine with the & operator.

I definitely did have errors when I was writing the code, and using new fixed the problem. Must have got discombobulated somewhere!!
Thank you for the information you gave me. I managed to make some progress with my problem, but I'm still kinda stuck. I have written a simplified version of my problem to exemplify my problem :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include<iostream>
#include<list>
using namespace std;

class A
{
private:
	int x;
public:
	void setInfoA( int _x ) { x = _x;}

	int getInfoA() { return x; }

	virtual void display() { cout << getInfoA() << endl; }	

	virtual void read() 
	{
		int _x;
		cout << "Enter x: ";
		cin >> _x;
		setInfoA( _x );
	}

	void readInfo()	{ read(); }			// I will use these two methods
										// to read and display info for
	void displayInfo()	{ display(); }	// all the classes in the inheritance tree
};

class B : public A {
private:
	int y;
public:
	void setInfoB( int _y ) { y = _y; } 

	int getInfoB() { return y; }

	virtual void read()
	{
		int _y;
		cout << "Enter y: ";
		cin >> _y;
		setInfoB(_y);

		A::read();
	}

	virtual void display() 
	{
		A::display();
		cout << getInfoB() << endl; 
	}

};

class C : public A{
private:
	int z;
public:
	void setInfoC( int _z ) { z = _z; }

	int getInfoC() { return z; }

	virtual void read()
	{
		int _z;
		cout << "Enter z: ";
		cin >> _z;
		setInfoC(_z);
		A::read();
	}

	virtual void display() 
	{ 
		A::display();
		cout << getInfoC() << endl; 
	}
};

class X{
private:
	list <A*> myList;
public:
	void menu()
	{
		int choice = -1;

		while( choice != 0)
		{
			cout <<"1.Add Element" << endl;
			cout <<"0.EXIT" << endl;
			switch( choice)
			{
			case 1:
				int type = -1;

				while ( type != 0)
				{
					cout <<"1.Type A" << endl;
					cout <<"2.Type B" << endl;
					cout <<"3.Type C" << endl;
					cout <<"0.EXIT" << endl;
                                        cin >> type;
					switch( type )
					{
					case 1:
						A obj;
						obj.readInfo();
						myList.push_back(&obj);
					case 2:
						B obj;
						obj.readInfo();
						myList.push_back(&obj);
					case 3:
						C obj;
						obj.readInfo();
						myList.push_back(&obj);
					case 4:
						exit(1);
					default:
						cout << "Wrong type. Enter again" << endl;
						break;
					}
				}
				break;
			case 0:
				exit(1);
				break;
			default:
				cout << "Wrong choice. Enter again" << endl;
				break;
			}
		}
	}

	void displayList()
	{
		list <int> ::iterator it;

		for( it = myList.begin(); it != myList.end(); it++ )
		{
			it -> displayInfo();
		}
	}
};

int main()
{
	X obj;

	obj.menu();

	obj.displayList();

	return 0;
}


I know this may look kind of complex, but it's only to explain my problem. This problem I have is in the menu() and displayList() methods of class X, the class that holds, creates and displays the STL list. I hope you understand what I am trying to make and be able to help me. Thanks again in advance!
Last edited on
1
2
3
4
					case 1:
						A obj;
						obj.readInfo();
						myList.push_back(&obj);


This won't work for several reasons.
One, you cannot define an object there - a case label can't cross the initialization of an object.

This would compile, but then we encounter the second problem:
1
2
3
4
5
6
					case 1:
                                               {
						    A obj;
						    obj.readInfo();
						    myList.push_back(&obj);
                                                }

obj which has an automatic storage duration would be destroyed immediately after the push_back() and the list now holds a pointer to an object that no longer exists.

So, create the object with a dynamic storage duration:
1
2
3
4
5
6
					case 1:
                                               {
						    A* obj = new A ;
						    obj->readInfo();
						    myList.push_back(obj);
                                                }


And finally,

a. you need to add a break for each case label

b. At the end, you need to delete all the dynamically allocated objects (iterate through the list and delete them one by one). This means that class A must have a virtual destructor.
Thanks alot everyone, especially JLBorges, I managed to make it work. Thanks!
Here is the working code for my problem. Maybe it will help someone someday:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include<iostream>
#include<list>
using namespace std;

class A
{
private:
	int x;
public:
	void setInfoA( int _x ) { x = _x;}

	int getInfoA() { return x; }

	virtual void display() { cout << getInfoA() << " "; }	

	virtual void read() 
	{
		int _x;
		cout << "Enter x: ";
		cin >> _x;
		setInfoA( _x );
	}

	void readInfo()	{ read(); }			// I will use these two methods
										// to read and display info for
	void displayInfo()	{ display(); }	// all the classes in the inheritance tree

	//virtual ~A() { delete A;}
};

class B : public A {
private:
	int y;
public:
	void setInfoB( int _y ) { y = _y; } 

	int getInfoB() { return y; }

	virtual void read()
	{
		A::read();

		int _y;
		cout << "Enter y: ";
		cin >> _y;
		setInfoB(_y);
	}

	virtual void display() 
	{
		A::display();
		cout << getInfoB() << " "; 
	}

};

class C : public A{
private:
	int z;
public:
	void setInfoC( int _z ) { z = _z; }

	int getInfoC() { return z; }

	virtual void read()
	{
		A::read();
		int _z;
		cout << "Enter z: ";
		cin >> _z;
		setInfoC(_z);	
	}

	virtual void display() 
	{ 
		A::display();
		cout << getInfoC() << " "; 
	}
};

class X{
private:
	list <A*> myList;
public:
	void menu()
	{
		int choice = -1;
		int type = -1;

		while( choice != 0)
		{
			cout <<"1.Add Element" << endl;
			cout <<"0.EXIT" << endl;
			cin >> choice;
			switch( choice)
			{
			case 1:
				while ( type != 0)
				{
					cout <<"1.Type A" << endl;
					cout <<"2.Type B" << endl;
					cout <<"3.Type C" << endl;
					cout <<"0.EXIT" << endl;

					cin >> type;

					switch( type )
					{
					case 1:
						{
						A* obj1 = new A;
						obj1 -> readInfo();
						myList.push_back(obj1);
						break;
						}
					case 2:
						{
						B* obj2 = new B;
						obj2 -> readInfo();
						myList.push_back(obj2);
						break;
						}
					case 3:
						{
						C* obj3 = new C;
						obj3 -> readInfo();
						myList.push_back(obj3);
						break;
						}
					case 0:
						//exit(1);
						break;
					default:
						cout << "Wrong type. Enter again" << endl;
						break;
					}
				}
				break;
			case 0:
				//exit(1);
				break;
			default:
				cout << "Wrong choice. Enter again" << endl;
				break;
			}
		}
	}

	void displayList()
	{
		list <A*>::iterator it;

		for( it = myList.begin(); it != myList.end(); it++ )
		{
			(*it) -> displayInfo();
			cout << endl;
		}
	}
};

int main()
{
	X obj;

	obj.menu();

	obj.displayList();

	return 0;
}
Hi Bardikus,

It might work but there is still room for improvement I think.

For example you have get & set functions for each class, that all take an int as a parameter, but they all have different names. Would it be better for them to be declared as a virtual function in the base class, then redefined in each derived class. This is the purpose of a virtual function.

The display & read function seem to be doing the same thing as the get / set functions.

You can make use of polymorphism, instead of calling base class functions directly, as you have done on line 41 & 51 plus others.

HTH

Pages: 12