Virtual functions

I'm not sure how to progress on this program. I need to store user-entered shapes into an array. New shapes should be created like this:

Shape * rect = new Rectangle(width, height);

And deleted in a similar way. How do I manage this array and store shapes? I've hopefully commented the program enough so you can get a good picture of what needs to happen. I don't have the draw() functions filled in for each shape yet. Everything helps, thanks!

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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <iostream>
#include <string>
using namespace std;

///////////////////////////////////////
class Shape	//parent class
{
protected:
	static int numShapes;	//keeps track of number of shapes that exist and are available to be rendered, class scope
public:
	static int getNumShapes()	//retrieves value of the numShapes member	
	{
		return numShapes;
	};			
	virtual void print() = 0;	//display textual description of the shape object
	virtual void draw() = 0;	//renders shape on the console window
};
//////////////////////////////////////////////////////////////////////////////
//four concrete subclasses provide customized implementations for print and draw methods
//Shape class cannot be instantiated, objects can only be created from the subclasses
//////////////////////////////////////////////////////////////////////////////
class Triangle: public Shape
{
private: 
	int base;
public:
	void getData()	//gets information from the user
	{		
		cout << "Enter triangle base: ";	
		cin >> base;	//store information

		++numShapes;	//increase the number of shapes by 1

	}
	void print()	//textual description of the shape
	{
		int height = (base-1)/2;	//set height
		cout << "Triangle with base " << base <<", and height " << height;	//display description
	}

	void draw()
	{
	}
};

class Rectangle: public Shape
{
private:
	int width;
	int height;
public:
	void getData()	//gets information from the user
	{		
		cout << "Enter rectangle width: ";	
		cin >> width;
		cout << "Enter rectangle height: ";
		cin >> height;	//store information

		++numShapes;	//increment number of shapes by 1
	}

	void print()	//textual description of the shape
	{
		cout << "Rectangle with dimensions (" << width <<", " << height << ")";	//display description
	}

	void draw()	//draws shape
	{
	// Draw row of 'w' asterisks for top side
    cout << string(width, '+' ) << endl;
    
    // Draw sides: asterisk, width-2 spaces, asterisk
    for( int j = 0; j < height - 2; ++j )
    cout<< '+'<< string(width - 2, ' ')<< '+' << endl;
        
    // Draw row of 'w' asterisks for bottom side
    cout << string( width, '+' ) << endl;
	}
};

class Square: public Rectangle
{
private:
	int width;
	int height;
public:
	void getData()	
	{		
		cout << "Enter square width: ";
		cin >> width;
		cout << "Enter square height: ";
		cin >> height;
		++numShapes;		
	}

	void print()
	{
		cout << "Square with demensions (" << width <<", " << height << ")";
	}

	void draw()	//draws shape
	{
	// Draw row of 'w' asterisks for top side
    cout << string(width, '+' ) << endl;
    
    // Draw sides: asterisk, width-2 spaces, asterisk
    for( int j = 0; j < height - 2; ++j )
    cout<< '+'<< string(width - 2, ' ')<< '+' << endl;
        
    // Draw row of 'w' asterisks for bottom side
    cout << string( width, '+' ) << endl;
	}
};

class Diamond: public Shape
{
private:
	int width;
public:
	void getData()
	{
		cout << "Enter diamond width: "; 
		cin >> width;
		++numShapes;
		
	}
	void print()
	{
		int height = (width - 1);
		cout << "Diamond with width " << width << " and height " << height <<endl;
	}

	void draw()
	{
	}
};

////////////////////////////////////

int main()
{
	Shape * shapeArr[20];

	int ch, ch2;	//choice variables for menu navigation

	Rectangle rect;	//class instances
	Square sq;
	Triangle tri;
	Diamond di;
	

	while(ch!=5)
	{
		cout << "Main menu:\n\n"	//main menu listing
			<< "1. Add a Shape\n"	//user will enter in data about a shape of their choice
			<< "2. Delete a Shape\n"	//user will be presented with list of shapes entered so far, can choose one to delete
			<< "3. Display total number of Shapes\n"	//displays number of shapes (getNumShapes)
			<< "4. Display all Shapes\n"	//displays textual/graphic description (print() and draw()) for all shapes entered so far
			<< "5. Quit\n\n"
			<< "Enter your choice: "; cin >> ch; cout << "\n\n";

		switch(ch)	//main menu switch statement
		{
		case 1: 
			cout << "1. Add a Rectangle\n"
			<<"2. Add a Square\n"
			<<"3. Add a Triangle\n"
			<<"4. Add a diamond\n"
			<<"5. Cancel\n\n"
			<<"Enter your choice: "; cin >> ch2; cout << "\n\n";

			switch(ch)	//shape menu switch statement
			{
			case 1: rect.getData(); break;
			case 2: sq.getData(); break;
			case 3: tri.getData(); break;
			case 4:	di.getData(); break;
			case 5: break;
			}//end shape menu switch statement
			break;

		case 2:	break;
		case 3: break;
		case 4: break;
		case 5: break;

		}//end main menu switch statement
	}

}
Here is your main function that I have changed to create the shapes dynamically:
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
int main()
{
	// I recommend using std::vector<Shape*> shapes;
	Shape * shapeArr[20];

	int ch, ch2;	//choice variables for menu navigation

	// No need to do this if you want to create them
	// dynamically as the user askes for them.

	//Rectangle rect;	//class instances
	//Square sq;
	//Triangle tri;
	//Diamond di;


	while(ch!=5)
	{
		cout << "Main menu:\n\n"	//main menu listing
			<< "1. Add a Shape\n"	//user will enter in data about a shape of their choice
			<< "2. Delete a Shape\n"	//user will be presented with list of shapes entered so far, can choose one to delete
			<< "3. Display total number of Shapes\n"	//displays number of shapes (getNumShapes)
			<< "4. Display all Shapes\n"	//displays textual/graphic description (print() and draw()) for all shapes entered so far
			<< "5. Quit\n\n"
			<< "Enter your choice: "; cin >> ch; cout << "\n\n";

		switch(ch)	//main menu switch statement
		{
		case 1:
			cout << "1. Add a Rectangle\n"
			<<"2. Add a Square\n"
			<<"3. Add a Triangle\n"
			<<"4. Add a diamond\n"
			<<"5. Cancel\n\n"
			<<"Enter your choice: "; cin >> ch2; cout << "\n\n";

			switch(ch)	//shape menu switch statement
			{

			case 1:
				Rectangle* rect = new Rectangle;
				rect->getData();
				// Add rect to shapeArr[] here
			break;

			case 2:
				Square* sq = new Square;
				sq->getData();
				// Add sq to shapeArr[] here
			break;

			case 3:
				Triangle* tri = new Triangle;
				tri->getData();
				// Add tri to shapeArr[] here
			break;

			case 4:
				Diamond* di = new Diamond;
				di->getData();
				// Add di to shapeArr[] here
			break;

			// This was not dynamic.
//			case 1: rect.getData(); break;
//			case 2: sq.getData(); break;
//			case 3: tri.getData(); break;
//			case 4:	di.getData(); break;

			case 5: break;
			}//end shape menu switch statement
			break;

		case 2:	break;
		case 3: break;
		case 4: break;
		case 5: break;

		}//end main menu switch statement
	}

}

You need to work out how to add them to your Shape* shapeArr[]. You could use an incrementing int to record the correct place for a new shape to be inserted. Personally I recommend you don't use an array but use a std::vector<Shap*> instead:

http://cplusplus.com/reference/stl/vector/

http://cplusplus.com/reference/stl/vector/operator%5B%5D/

EDIT: To fix some errors
Last edited on
Thanks! This should help me a lot. I would use vectors but I have to stick to a certain set of requirements for my assignment, the array being one of them.
There is something I find it "trouble-some" for C++ classes. If I intend for a member function to be over-ridden by subclasses, I put a virtual keyword in front. This gets on my nerves. Why can't the C++ compiler figure out themselves? Afterall in most sensible situations, when I have a subclass whose member function signature is SAME as the parent class member function, common sense would say I want to override isn't it ?

This is where I feel Java is more "sensible". I don't have to put in a virtual keyword in front. As long as the subclass method signature is same as the parent class, it overrides.

This is just one of those quirks in C+ I find not "sensible". I believe the Java creator share the same "sense" as me :P
Can anyone help me with the constructor and destructor for this class as well? I think only numShapes needs to be updated (incremented and decremented) but I don't quite know what that would look like.
@pastamaker

You said you wanted to be able to create your objects like this:
 
Shape* rect = new Rectangle(width, height);

For that example your constructor needs two parameters. You would write it like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Rectangle: public Shape
{
private:
	int width;
	int height;
public:

	// constructor
	Rectangle(int width, int height) // no return type, same name as class
	: width(width)                   // initialise member width with parameter width
	, height(height)                 // initialise member height with parameter height
	{
	}

	// ... etc. ...

};

Now you have to supply two parameters to your Rectangle when you create it:
1
2
3
4
5
			case 1:
				// ask user for width and height here...
				Rectangle* rect = new Rectangle(width, height);
				// Add rect to shapeArr[] here..
			break;

Of course, now you have to get your with and height from the user *before* you construct your rectangle so your member function rect->getData(); is no longer useful. However, it is actually better design to ask the user for values outside of class Rectangle.

Also, when you have virtual functions it is important to also have a virtual destructor in your base class:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Shape	//parent class
{
protected:
	static int numShapes;	
	
public:
	virtual ~Shape() // always add a virtual destructor in a class with virtual functions
	{
	}

	virtual void print() = 0;
	virtual void draw() = 0;
};
So should I scrap my getData() functions for each class and simply include the prompts to enter data right before the "Rectangle* rect = new Rectangle(width, height);"?

Also, thanks for the great input guys.
Updated program:

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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// -----------------------------------------------------------------
// This program allows the user to create and delete shapes with dimensions
// of their choosing. They can then view the total number of shapes or view
// a textual and graphic rendering of all shapes created.
// -----------------------------------------------------------------
 

#include <iostream>
#include <string>
using namespace std;

///////////////////////////////////////
class Shape	//parent class
{
protected:
	static int numShapes;	//keeps track of number of shapes that exist and are available to be rendered, class scope
public:
	Shape()	//constructor
	{
		++numShapes;	//increment number of shapes when user adds a new one
	}
	virtual ~Shape() // always add a virtual destructor in a class with virtual functions
	{
		--numShapes;	//decrement number of shapes when user deletes an existing one
	}
	static int getNumShapes()	//retrieves value of the numShapes member, number of shapes entered by user	
	{
		if(numShapes == 0)
			{cout << "No shapes to display";}
		else 
			return numShapes;
	};			
	virtual void print() = 0;	//display textual description of the shape object
	virtual void draw() = 0;	//renders shape on the console window
};
//////////////////////////////////////////////////////////////////////////////
//four concrete subclasses provide customized implementations for print and draw methods
//Shape class cannot be instantiated, objects can only be created from the subclasses
//////////////////////////////////////////////////////////////////////////////
class Triangle: public Shape
{
private: 
	int base;
public:
	Triangle(int base):	//constructor 2-args
	  base(base)	//initialize member width and height
	  {
	  }

	  ~Triangle();	//deconstructor

	
	void print()	//textual description of the shape
	{
		int height = (base-1)/2;	//set height
		cout << "Triangle with base " << base <<", and height " << height;	//display description
	}

	void draw()
	{
	}
};

class Rectangle: public Shape
{
private:
	int width;
	int height;
public:
	Rectangle(int width, int height):	//constructor 2-args
	  width(width), height(height)	//initialize member width and height
	  {
	  }

	  ~Rectangle();	//deconstructor

	void print()	//textual description of the shape
	{
		cout << "Rectangle with dimensions (" << width <<", " << height << ")";	//display description
	}

	void draw()	//draws shape
	{
    cout << string(width, '+' ) << endl;
    
    for( int j = 0; j < height - 2; ++j )
    cout<< '+'<< string(width - 2, ' ')<< '+' << endl;
        
    cout << string( width, '+' ) << endl;
	}
};

class Square: public Rectangle
{
private:
	int width;
	int height;
public:
	~Square();	//deconstructor

	void print()
	{
		cout << "Square with demensions (" << width <<", " << height << ")";
	}

	void draw()	//draws shape
	{	
    cout << string(width, '+' ) << endl;   
    
    for( int j = 0; j < height - 2; ++j )
    cout<< '+'<< string(width - 2, ' ')<< '+' << endl;        
    
    cout << string( width, '+' ) << endl;
	}
};

class Diamond: public Shape
{
private:
	int width;
public:
	Diamond(int width):	//constructor 1-arg
	  width(width)	//initialize member width
	  {
	  }

	~Diamond();
		
	void print()
	{
		int height = (width - 1);
		cout << "Diamond with width " << width << " and height " << height <<endl;
	}

	void draw()
	{
	}
};

////////////////////////////////////
int main()
{
	cout << "This program allows the user to create and delete shapes with dimensions\n"
	<< "of their choosing. They can then view the total number of shapes or view\n"
	<< "a textual and graphic rendering of all shapes created.\n\n";

	int j;
	Shape * shapeArr[20];
	
	int ch, ch2, ch3;	//choice variables for menu navigation

	while(ch!=5)
	{
		cout << "Main menu:\n\n"	//main menu listing
			<< "1. Add a Shape\n"	//user will enter in data about a shape of their choice
			<< "2. Delete a Shape\n"	//user will be presented with list of shapes entered so far, can choose one to delete
			<< "3. Display total number of Shapes\n"	//displays number of shapes (getNumShapes)
			<< "4. Display all Shapes\n"	//displays textual/graphic description (print() and draw()) for all shapes entered so far
			<< "5. Quit\n\n"
			<< "Enter your choice: "; cin >> ch; cout << "\n\n";

		switch(ch)	//main menu switch statement
		{
		case 1:
			cout << "1. Add a Rectangle\n"
			<<"2. Add a Square\n"
			<<"3. Add a Triangle\n"
			<<"4. Add a diamond\n"
			<<"5. Cancel\n\n"
			<<"Enter your choice: "; cin >> ch2; cout << "\n\n";

			switch(ch2)	//shape menu switch statement
			{

			case 1:
				int width, height;
				cout << "Enter rectangle width: ";
				cin >> width;
				cout << "Enter rectangle height: ";
				cin >> height;
				Rectangle* rect = new Rectangle(width, height); //make new rectangle
				shapeArr[j] = rect; //add to shapeArr
			break;

			case 2:
                int width; int height;
                cout << "Enter square width: ";
				cin >> width;
				cout << "Enter square height: ";
				cin >> height;
				Square* sq = new Square; //make new square				
				shapeArr[j] = sq; //add to shapeArr
			break;

			case 3:
				int base;
				cout << "Enter triangle base: ";
				cin >> base;
				Triangle* tri = new Triangle(base); //make new triangle				
				shapeArr[j] = tri; //add to shapeArr
			break;

			case 4:
               int width;
               cout << "Enter diamond width: ";
               cin >> width;
			   Diamond* di = new Diamond(width); //make new diamond			
				shapeArr[j] = di; //add to shapeArr
			break;

			case 5: break;
			}//end shape menu switch statement
			break;

		case 2:	
			switch(ch3)
				//display list of shapes entered so far using print()
				//delete the one the user chooses

			cout << "Choose shape to delete: ";

			for(j = 0; j<20; j++)	//print descriptions of shape array
			{cout << j << "	"; shapeArr[j] ->print(); cout << endl;}

			//user chooses one to delete and program deletes it			
			break;
		case 3: 
			//getNumShapes();
			break;
		case 4: 
			//textual and graphic displays of the shape array
			for(j = 0; j <20; j++)	//print shapes
				shapeArr[j] -> print();
			for(j = 0; j <20; j++)	//draw shapes
				shapeArr[j] -> draw();
			break;
		case 5: 
			//terminates program
			break;

		}//end main menu switch statement
	}

}


What am I doing wrong regarding the getNumShapes function? I can't call it in my main program. All it needs to do is show the number of shapes the user has entered. The error says "identifier getNumShapes is undefined."
Last edited on
Topic archived. No new replies allowed.