Reputation: 8705
I have a Shape
(abstract) base class that Triangle
and Square
inherit from. Square has a method Split
which returns an array of Shapes:
Shape** Square::Split(string direction, int times)
{
if (direction == "diagonal" && times == 1)
{
numSplits = times;
for (int i = 0; i < times +1; i++)
{
shapes[i] = new Triangle(side, side, sqrt(2) * side);
}
}
else if (direction == "horizontal" || direction == "vertical")
{
double newSide = (double)side / 2;
numSplits = times;
for (int i = 0; i < times + 1; i++)
{
shapes[i] = new Rectangle(newSide, side);
}
}
else
{
//to do
}
return shapes;
}
in my main method I have
Square* square= new Square(3);
Shape** shapeArray = square->Split("diagonal", 1);
shapeArray[0] = (Triangle*)shapeArray[0]; //contains no Triangle members
shapeArray[0]= dynamic_cast<Triangle*>(shapeArray[0]); //still no Triangle members
int triangleType = dynamic_cast<Triangle*>(shapeArray[0])->GetType(); //contains Triangle members
I assume this is an example of object slicing? But I don't understand why I can't cast the first element in the array (which is a triangle) to a Triangle, but the last line allows me to reach into the Triangle
class.
header for Square if needed
class Square :
public Shape
{
public:
Square();
Square(string);
Square(string, string);
~Square();
Square(int);
Square(string, int);
virtual double Area();
virtual void Save(string);
virtual double Perimeter();
Shape** Split(string direction,int);
private:
string sName;
string filePath;
int side;
double diagonal;
int numSplits;
Shape** shapes = new Shape*[numSplits];
};
Upvotes: 0
Views: 477
Reputation: 106236
Shape** shapeArray = square->Split("diagonal", 1);
shapeArray[0] = (Triangle*)shapeArray[0]; //contains no Triangle members
shapeArray[0]= dynamic_cast<Triangle*>(shapeArray[0]); //still no Triangle members
int triangleType = dynamic_cast<Triangle*>(shapeArray[0])->GetType();
I assume this is an example of object slicing?
No it's not. You're dealing with pointers to your shapes - slicing happens when you try to assign the value of a derived type into an instance of one of its base classes, for a start - the base class may not be big enough (i.e. had fewer data members, had less memory reserved/allocated for it, and secondly, the assignment code may or may not copy the derived class's pointer to the derived class Virtual Dispatch Table - if that's how your compiler's supporting polymorphism). Slicing doesn't happen when you just manhandle pointers to objects (though if you then use the pointers to coordinate copying you can slice).
But I don't understand why I can't cast the first element in the array (which is a triangle) to a Triangle, but the last line allows me to reach into the Triangle class.
Your attempted casts aren't doing anything useful. They tell the compiler to treat the Shape*
as a Triangle*
momentarily, but then you assign that value back into a Shape*
variable anyway, losing the Triangle*
static type information you momentarily injected.
The final dynamic_cast<>
differs in that you use the cast-to value directly ala ->GetType()
- while the compiler still considers the static type to be Triangle
- instead of assigning it to the Shape*
and having the static type "decay" back to Shape*
.
Upvotes: 1
Reputation: 1283
You seem to be expecting pointers to somehow "remember" what type of pointer has been stored in them. Strongly typed languages don't work like that.
Once Shape ** shapeArray;
is declared, it is, and will always be, a pointer to a pointer to a Shape
.
The expression shapeArray[0]
has the type Shape *
. You can store a pointer to a Shape
that happens to also be a Triangle
in it, but the only thing the compiler and runtime can assume later about the object pointed to is that it's a Shape
of some kind.
The expression (Triangle*)shapeArray[0]
has the type Triangle *
, as does the expression dynamic_cast<Triangle*>(shapeArray[0])
.
As you've found, you can access Triangle
's members from any expression with the type Triangle*
, but not from a Shape*
because there's no way of telling what kind of Shape
it might be.
Upvotes: 1
Reputation: 6834
I've put this in an answer due to length, but is not a full answer.
What do you expect from this code?
shapeArray[0] = (Triangle*)shapeArray[0]; //contains no Triangle members
shapeArray[0]= dynamic_cast<Triangle*>(shapeArray[0]); //still no Triangle members
The first line should:
Shape*
pointer in the first element of the arrayTriangle*
double p = shapeArray[0]->
3. Assign back into to the first element of the array. This is of type Shape*
, so requires an upcast, bringing it back to where you started.
The second line should:
Shape*
pointer in the first element of the arrayTriangle*
, or return nullptr
if it is not actually a `Triangle'.Shape*
, so requires an upcast, bringing it back to where you started.So in short, the first line does nothing and the seconds zeros the element if it is not a Triangle
.
To see whether you really have Triangle*
in the array, add a line such as:
double perimeter = shapeArray[0]->Perimeter();
You can set a breakpoint or log message in the Perimeter
method of Triangle
to check it is being called.
Alternatively write:
Triangle* triangle = dynamic_cast<Triangle*>(shapeArray[0]);
Upvotes: 1