Subclass unable to access parent class C++ Inheritance, Virtuality and Polymorphism

Im making a program whereby i have a Square class which is a subclass of Shape class

But my program crashes when the subclass tries to access variable from parent class.

Here are my codes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Shape2D
{
public:
    string shape;
    void setShape(string newShape);
    string getShape();

    string specialType;
    void setSpecialType(string newSpecialType);

    vector <int> shapeXCoordi;
    void setshapeXCoordi(int newshapeXCoordi);

    vector <int> shapeYCoordi;
    void setshapeYCoordi(int newshapeYCoordi);
    void toString();

    virtual int computeArea()
    {
        return 0;
    }
    void displayArea();
};


For testing purpose, i only make this computeArea() return the x-coordinate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Square : public Shape2D
{
public:
    int computeArea()
    {
        return shapeXCoordi[0];
    }
};

int main(){
    if (aShape2D[0].getShape() == "Square")
    {
        Shape2D *pShape2D;
        pShape2D = &aSquare;

        cout << "Area: " << pShape2D -> computeArea()  << endl;
    } 
}


I did i couple of test, if i were to change "return shapeXCoordi[0];" to "return 123;", it works fine. I also tried changing it to "return shape" but it will not display anything, although this time, it doesnt crash

So im guessing there is something wrong when SquareClass is trying to access shapeXCoordi[0] from ShapeClass

Can anyone enlighten me on this situation?
Have you ever pushed anything back into the vector? The vector defaults to empty so there exist no elements to access if you try to access them immediately.
Yes, in my main it will ask user for inputs for vertices of the Square, so there are 4 x-coordinates.
I suspect you have object slicing and bad casting going on.

How is aShape2D defined? And how are you adding elements to it?
I have this in my main

Shape2D aShape2D[100];

And
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
for (int i=0;i<12;i++)
{
									
	cout << "Please enter x-coordinate for point " << i+1 << ": ";
	cin >> inputXCoordi;
	if (cin.good())
	{
		aShape2D[shapeNum].setshapeXCoordi(inputXCoordi);
		cout << "Please enter y-coordinate for point " << i+1 << ": ";
		cin >> inputYCoordi;
		if (cin.good())
		{
			aShape2D[shapeNum].setshapeYCoordi(inputYCoordi);
		}else{
											
			cin.clear();
			cin.ignore(1000, '\n');
			cout << "Invalid input, please x-coordinate: "<< endl; 
		}	   
	}else{
										
		cin.clear();
	        cin.ignore(1000, '\n');
		cout << "Invalid input, please x-coordinate: "<<endl;
        }
										  
									
}	 	 	 	  	 	 	  	 
Last edited on
Okay this is jiving with what I was thinking... but I misread one line of code here:

 
pShape2D = &aSquare;


How is 'aSquare' defined and initialized?
I only declare it in my main

Square aSquare;

and its used only in this part

1
2
3
4
5
6
7
    if (aShape2D[0].getShape() == "Square")
    {
        Shape2D *pShape2D;
        pShape2D = &aSquare;

        cout << "Area: " << pShape2D -> computeArea()  << endl;
    } 
Last edited on
What exactly is variable aSquare?

Shouldn't you just call aShape2D[0].computeArea() ?

The biggest issue is you have an array of Shape2D objects of only type Shape2D, not any of its derived classes. First, you need an object that is a derived type, such as Square or Circle.

1
2
3
4
5
6
7
8
9
10
11
// Declare objects of a derived type
Circle myCircle;
Square mySquare;
// Create a pointer to base class
Shape2D* pShape;

pShape = &myCircle;
pShape->computeArea();    // calls Circle::computeArea()

pShape = &mySquare;
pShape->computeArea();    // calls Square::computeArea() 
Last edited on
Okay... so I'm confused. If you are computing aSquare's area... then what purpose does the aShape2D array serve?

EDIT:

And if that is the only time you are using aSquare, then you must not have properly initialized shapeXCoordi and shapeYCoordi... which is why they have zero elements, which is why it's crashing when you try to access them.


EDIT2:

To clarify what I suspect is the problem:

You are putting all your shape data in your aShape2D array.
You then try to access it through your aSquare object.

But the problem is your aSquare and aShape2D objects are completely different things. You're writing to one variable and then expecting to be able to read it from an entirely separate variable.
Last edited on
aShape2D is an array class which stores the x and y coordinate of a shape. And the x and y coordinates are store into vector shapeXCoordi and shapeYCoordi.

And aSquare is a subclass of Shape2D with a computeArea function. Im suppose to have subclasses for other shapes and each class has diff algorithm for calculating the area but im doing one at a time.

I'll post my entire code so u guys could have a better view of what im doing

Sorry for making u guys confused cause i dont really know how to explain my situation well =/
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#include <iostream>
#include <string>
#include <vector>
using namespace std;

//----------------------------------------------------------
class Shape2D
{
	private:
		
	public:
		string shape;
		void setShape(string newShape);
		string getShape();
		
		string specialType;
		void setSpecialType(string newSpecialType);
		
		vector <int> shapeXCoordi;
		void setshapeXCoordi(int newshapeXCoordi);	  

		
		vector <int> shapeYCoordi;
		void setshapeYCoordi(int newshapeYCoordi);
		void toString();
		
		virtual int computeArea()
		{
			return (0);
		}	 
		
};

void Shape2D::setShape(string newShape)
{
	shape = newShape;
}

string Shape2D::getShape()
{
	return shape;
}

void Shape2D::setSpecialType(string newSpecialType)
{
	specialType = newSpecialType;
}

void Shape2D::setshapeXCoordi(int newshapeXCoordi)
{
	shapeXCoordi.push_back(newshapeXCoordi);
}


void Shape2D::setshapeYCoordi(int newshapeYCoordi)
{
	shapeYCoordi.push_back(newshapeYCoordi);
}



void Shape2D::toString()
{
	cout << "Shape: " << shape << endl;
	cout << "Special type: " << specialType << endl;
	cout << "Vertices: " << endl;
	for (int i=0; i<shapeXCoordi.size(); i++)
	{
		cout << "Point [" << i+1 << "] " << "(" << shapeXCoordi[i] << "," << shapeYCoordi[i] << ")" << endl;
	}
}
//----------------------------------------------------------

class Cross: public Shape2D
{
	public:

		
};
//----------------------------------------------------------

//----------------------------------------------------------

class Square : public Shape2D
{
	
	public:
		int computeArea()
		{
			return shapeXCoordi[0];	    		
		}
	
};



//----------------------------------------------------------
class Assn2
{
	private:
		
	public:
		int shapeNum;
		string inputShape;
		string inputSpecialType;
		int inputXCoordi;
		int inputYCoordi;
		
		void printMainMenu();
		int main();
};


void Assn2::printMainMenu()
{
	cout << "\nWelcome to Assn2 Program!\n" << endl;
	cout << "1) Input sensor data" << endl;
	cout << "2) Compute area (of all records)" << endl;
	cout << "3) Print shapes report" << endl;
	cout << "4) Sort shapes data\n" << endl;
	cout << "Please enter your choice: ";
}

int Assn2::main()
{

	bool exitProgram=false;		//Boolean for checking if choice is correct
	bool hasRecords=false;		//Boolean for checking if there are any records to prevent error when selecting choice 2,3,4 before 1
	
 	Shape2D aShape2D[100];
	Square aSquare;
	
	
	shapeNum = 0;
	// Choice 0 for main menu
	int choice=0;
	do{
		printMainMenu();
		cin>>choice;
		do
		{
			if (choice==1)
			{
				bool validInput = false;
				hasRecords = true;
				cout << "\n[Input Sensor Data]" << endl;
				cout << "\nPlease enter name of shape: ";
				cin >> inputShape;
				do
				{
					// Check for input of shape
					if (inputShape=="Cross" || inputShape=="Square" || inputShape=="Rectangle" )
					{
						aShape2D[shapeNum].setShape(inputShape);
						cout << "Please enter special type: ";
						cin >> inputSpecialType;
						
						// Check for input for special type
						if (inputSpecialType=="WS" || inputSpecialType=="NS")
						{
							aShape2D[shapeNum].setSpecialType(inputSpecialType);
							
							// Check for shape to determine the number of vertices for input
							if (inputShape=="Cross")
							{
								for (int i=0;i<12;i++)
								{
									
									cout << "Please enter x-coordinate for point " << i+1 << ": ";
									cin >> inputXCoordi;
									if (cin.good())
									{
										aShape2D[shapeNum].setshapeXCoordi(inputXCoordi);
										cout << "Please enter y-coordinate for point " << i+1 << ": ";
										cin >> inputYCoordi;
										if (cin.good())
										{
											aShape2D[shapeNum].setshapeYCoordi(inputYCoordi);
										}else{
											
											cin.clear();
											cin.ignore(1000, '\n');
											cout << "Invalid input, please x-coordinate: "<< endl; 
										}	   
									}else{
										
										cin.clear();
										cin.ignore(1000, '\n');
										cout << "Invalid input, please x-coordinate: "<<endl;
									}	   
									//This code is for checking if data is stored correctly
									//cout << aCross.crossXCoordi[i] << endl;	 	 	 
								}	 	 	 	 
								validInput = true;
								
							}else if(inputShape=="Square" || inputShape=="Rectangle"){
								for (int i=0;i<4;i++)
								{
									
									cout << "Please enter x-coordinate for point " << i+1 << ": ";
									cin >> inputXCoordi;
									if (cin.good())
									{
										aShape2D[shapeNum].setshapeXCoordi(inputXCoordi);
										cout << "Please enter y-coordinate for point " << i+1 << ": ";
										cin >> inputYCoordi;
										if (cin.good())
										{
											aShape2D[shapeNum].setshapeYCoordi(inputYCoordi);
										}else{
											
											cin.clear();
											cin.ignore(1000, '\n');
											cout << "Invalid input, please x-coordinate: "<< endl;
										}	   
									}else{
										
										cin.clear();
										cin.ignore(1000, '\n');
										cout << "Invalid input, please x-coordinate: "<<endl;
									}
										  
									//This code is for checking if data is stored correctly
									//cout << aCross.crossXCoordi[i] << endl;	 	 	 
								}
								
								
								validInput = true;
								
							}else{
								cin.clear();
								cin.ignore(1000, '\n');
								cout << "Invalid input, please re-enter all data" << endl;
								cout << "Please enter name of shape: ";
								cin >> inputShape;
							}
						}else{
						cin.clear();
						cin.ignore(1000, '\n');
						cout << "Invalid input, please enter WS or NS" << endl;
						cout << "Please enter name of shape: ";
						cin >> inputShape;
						}
						
					}else{
						cin.clear();
						cin.ignore(1000, '\n');
						cout << "Invalid input, please enter Square, Rectangle or Cross" << endl;
						cout << "Please enter name of shape: ";
						cin >> inputShape;
					}
				}while(!validInput);
				shapeNum++;
				cout << "\nRecord successfully stored, returning to main menu..." << endl;
				choice=0;
				
			}else if(choice==2){
				if (hasRecords==true)
				{
					
					if (aShape2D[0].getShape() == "Square")
					{
						Shape2D *pShape2D;
						pShape2D = &aSquare;						
						
						cout << "Area: " << pShape2D -> computeArea()  << endl;
					} 
					choice=0;
				
				}else{
					cout << "\nNo records found. Please enter records by choosing 1st option." << endl;
					choice=0;
				}
			}else if(choice==3){
				if (hasRecords==true)
				{	
					for (int i=0;i<shapeNum;i++)
					{	
						cout << "\nShape " << i+1 << endl;
						aShape2D[i].toString();
					} 	   	   	   
					choice=0;
				
				}else{
					cout << "\nNo records found. Please enter records by choosing 1st option." << endl;
					choice=0;
				}
			}else if(choice==4){
				if (hasRecords==true)
				{	 	 	 	 
					choice=0;
				
				}else{
					cout << "\nNo records found. Please enter records by choosing 1st option." << endl;
					choice=0;
				}
			}else if(choice==999){
				exitProgram=true;
			}else if(choice==0 && cin.good()){
				printMainMenu();
				cin>>choice;	   
			}else{
				// Catch exceptions other then 1,2,3 and 4
				exitProgram = false;
				cin.clear();
				cin.ignore(1000, '\n');
				cout << "Invalid choice, please enter 1,2,3 or 4." << endl;
				cout << "Please re-enter your choice: ";
				cin >> choice;
				
			}
		}while(!exitProgram);
	}while (choice==0);
}

int main()
{
	Assn2 Assignment2;
	Assignment2.main();


}
@Disch (EDIT2)

im using inheritance so im guessing i could get attributes from class Shape2D?
1
2
3
4
5
6
7
8
9
10
class Square : public Shape2D
{
	
	public:
		int computeArea()
		{
			return shapeXCoordi[0];	    		
		}
	
};

Shape2D aShape2D[100];

This array is problematic. You want an array of different shapes, but this is not the way to do it.

To use polymorphism and virtual functions, you need a base pointer/reference of type Shape2D that will point to a derived type (ie. Square, Rectangle, or Cross). In your case, you want an array of pointers/references of type Shape2D that each will point to any derived type you want.

1
2
3
4
5
6
7
8
9
10
11
// Declare an array of 3 base pointers to type Shape
Shape* shapeArray[3];
// Have each one point to a derived type
shapeArray[0] = new Square();
shapeArray[1] = new Rectangle();
shapeArray[2] = new Cross();

// Now I can simply loop through the array and do something useful with them, like printing the area of the shape.
for (int i = 0; i < 3; i++) {
    cout << "The area of this " << shapeArray[i]->getShape() << " is " << shapeArray[i]->computeArea() << endl;
}
Okay you have a fundamental misunderstanding here.

Polymorphism allows you to access a child object through a parent pointer. It does not allow you to transform a parent object to a child object.

You cannot ever transform a variable from one type to another. It simply is not possible in C++. Once you define a variable as a certain type... it is that type forever (or at least until the variable goes out of scope).

(You can, however, convert one type to another by transferring it to a DIFFERENT variable... but that's not what you want to do here, so nevermind that -- I only mention it for completeness).

With that in mind... when you do this:

 
Shape2D aShape;


This creates a Shape2D object. A Shape2D object is not a Square object. It will never be a Square object.





Now... inheritance forms an "is a" relationship between two classes. When you derive Square from Shape2D like you're doing, this implies a few things:

1) Square objects contain everything that Shape2D objects do
2) Square objects may contain more that Shape2D objects, but cannot contain less.
3) Since Squares contain everything Shape2Ds do, Squares can be treated as if they were Shape2Ds. IE: a Square "is a" Shape2D.
4) The "is a" relationship only goes one way. A child is a parent, but a parent is not a child. An easy to visualize example of this is child class Poodle deriving from parent class Dog. All Poodles are also Dogs, but that doesn't mean all Dogs are Poodles.

In your example... all Squares are also Shape2Ds. But that does not mean that a Shape2D is a Square.



So understanding the "is a" relationship, and realizing that you can't transform the type of an object after it's declared... how can you "dynamically" create an object based on user input?

You're trying to do something like this, which is wrong:

1
2
3
4
5
6
7
8
9
10
Shape2D shape;  // creating a Shape2D object

if( user_wants_a_square )
{
  ... code that treats 'shape' like a square...
}
else if( user_wants_a_cross )
{
  ... code that treats 'shape' like a cross...
}


This of course is wrong because you don't have a Square or a Cross. You only have a Shape2D. A Shape2D is neither a Square nor a Cross.


What you need to do in order to get this to work is to create the object dynamically. The above code is simply creating a Shape2D all the time. We want to create a Square or a Cross depending on user input. We can do this with pointers and the new operator:

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
Shape2D* ptr;  // 'ptr' is a pointer (NOT an object).  It is not a shape
  // itself, but rather it "points to" a shape that exists somewhere else in memory.


if( user_wants_a_square )
{
  Square* sq = new Square;  // create a Square object dynamically with 'new'
   // this is a little tricky, conceptually.  'new' creates an UNNAMED object that is a Square.
   //  'sq' is just a pointer, it is not a Square itself.. it just "points to" this unnamed Square
   //  object that we've created.

  // you can use the 'sq' pointer to initialize the unnamed square here

  ptr = sq;  // now that the unnamed square is initialized, we can assign 'ptr' so that it
   // ALSO points to this unnamed square object.
   // This is where inheritance and the "is a" relationship comes in.  Even though we have
   // a Square object and not a Shape2D object, we can still point to it with a Shape2D
   //   pointer because a Square "is a" Shape2D.
}
else if( user_wants_a_cross )
{
  Cross* cr = new Cross;  // create a cross dynamically

  // .. initialize the cross here

  ptr = cr;
}

// at this point, 'ptr' could be pointing to either a Square object or a Cross object.
//   you could then add this pointer to an array, or push_back it into a vector... or
//   however you want to keep track of it. 




The last thing I'll mention is that to prevent memory leaks, anything you new must also be deleted. When you are done using all these objects, you have to go back and delete any one pointer that points to the unnamed object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Square* foo = new Square;  // allocates an unnamed object.  'foo' points to it
delete foo;  // deletes whatever object 'foo' points to.  Since 'foo' points
   // to our unnamed object, this correctly cleans up.


// another example:

Square* foo = new Square;  // allocates an unnamed object.  'foo' points to it
Square* bar = foo;  // now 'bar' also points to the unnamed object

delete bar;  // since bar points to the unnamed object, this cleans it up correctly.
  // however, now that it's been deleted, the unnamed object no longer exists...
  //   so foo and bar now point to nothing (they're bad pointers)

// at this stage... you do NOT want to delete foo, because foo does not point to 
//  a valid object, so there is nothing for it to delete.

// remember that you aren't deleting the pointer... you are deleting the object. 





Hopefully that clarifies.
My comment is to do with the unnecessary complexity of the Assn2::main function. It has 10 levels of nesting as a start. I am very sure this could be done much more efficiently.

You seem to have combined a menu with all the options code rolled into one big nightmare function that has 190 lines of code. The code to show a menu should be it's own function, the code to process the menu should be a function (with a switch inside). Each option on the menu should call it's own function.

There is a bit of a rule that functions should not exceed 80 lines. Other candidates for functions are the code in the body of a loop or in else if clauses. Getting user input and validating it, should be a function.

Sometimes it is worth putting something into a function to clarify the meaning, even it is only a few lines long.

This can all explained as the "Divide & Conquer" approach. Divide the big problem into smaller bits (the menu) Divide each of these into more manageable parts (A function for each). Keep this concept going until you have something easy to deal with.

The other concept is to do with classes and it is called encapsulation. Basically the code do with a particular type of object should be defined in that class. On top of that you can have an interface with virtual or pure virtual functions declared in a base class.

Given the other advice posted already, I am guessing you are going to start again (Don't worry - it's not the first time it has happened to any one). One idea you might find handy, is designing your code by writing psuedocode first. Before doing any thing else, write comments describing your methodology. This can be done iteratively - start with very general ideas, then keep go back and fill in more detail, until you are happy to write code.

I know this sounds boring, but it is good because it helps to organise your thoughts logically, identify functions, data structures to store data, data types for variables. With any luck it will lead to more elegant & efficient code.

Class design is not as simple as what people think it is. You have to consider how they are all are going to interoperate - and that raises a number of questions. Keep in mind the three types of relationships - "IS A", "HAS A" and "USES A".

Hope all goes well, interested to see how you get on.
Alright thanks everyone for ur advice, I'm currently still working on it. I'll update u guys again once I havessome results
Hi guys, just a little update.

I finally got it working by making updating with this code
1
2
3
4
5
6
7
8
9
10
11
if(inputShape=="Cross")
{ 
	aShape2D[shapeNum] = new Cross();
}else if(inputShape=="Square")
{
	aShape2D[shapeNum] = new Square();
}else
{
	aShape2D[shapeNum] = new Rectangle();
}
	aShape2D[shapeNum]->setShape(inputShape);


Thank you Elementary,Disch and TheIdeasMan for your advice!

Topic archived. No new replies allowed.