向下转换多态指针
Casting down polymorphic pointers
我有一个 Shape
(抽象)基础 class,Triangle
和 Square
继承自该基础。 Square 有一个方法 Split
,其中 returns 一个形状数组:
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;
}
在我的主要方法中我有
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
我假设这是 object 切片的示例?但我不明白为什么我不能将数组中的第一个元素(这是一个三角形)转换为三角形,但最后一行允许我进入 Triangle
class。
header Square 如果需要
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];
};
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?
不,不是。您正在处理形状的 pointers - 当您尝试将派生类型的 value 分配给其中一个实例时,就会发生切片base classes,首先 - base class 可能不够大(即数据成员较少,内存较少 reserved/allocated,其次,赋值代码可能或可能不会将派生 class 的指针复制到派生 class 虚拟调度 Table - 如果这是您的编译器支持多态性的方式)。当您只是手动处理指向对象的指针时,切片不会发生(尽管如果您随后使用指针来协调复制,您可以切片)。
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.
您尝试的转换没有做任何有用的事情。他们告诉编译器暂时将 Shape*
视为 Triangle*
,但随后您将该值重新分配给 Shape*
变量,暂时丢失 Triangle*
静态类型信息注入。
最后的 dynamic_cast<>
不同之处在于您直接使用转换为值 ->GetType()
- 而编译器仍然认为静态类型是 Triangle
- 而不是分配它到 Shape*
并将静态类型 "decay" 返回到 Shape*
.
由于篇幅原因,我将其放在了一个答案中,但这并不是一个完整的答案。
您对这段代码有何期待?
shapeArray[0] = (Triangle*)shapeArray[0]; //contains no Triangle members
shapeArray[0]= dynamic_cast<Triangle*>(shapeArray[0]); //still no Triangle members
第一行应该:
- 在数组的第一个元素中取
Shape*
指针
- 向下转换为
Triangle*
double p = shapeArray[0]->
3. 分配回数组的第一个元素。这是 Shape*
类型,因此需要向上转换,将其返回到您开始的位置。
第二行应该:
- 在数组的第一个元素中取
Shape*
指针
- 向下转换为
Triangle*
,或 return nullptr
如果它实际上不是“三角形”。
- 重新分配给数组的第一个元素。这是
Shape*
类型,因此需要向上转换,将其返回到您开始的位置。
简而言之,第一行什么都不做,如果元素不是 Triangle
,第二行会将元素归零。
要查看数组中是否真的有Triangle*
,添加一行如:
double perimeter = shapeArray[0]->Perimeter();
您可以在 Triangle
的 Perimeter
方法中设置断点或记录消息以检查它是否被调用。
或者写成:
Triangle* triangle = dynamic_cast<Triangle*>(shapeArray[0]);
您似乎期望指针以某种方式指向"remember"其中存储了何种类型的指针。强类型语言不是那样工作的。
一旦声明了 Shape ** shapeArray;
,它就是,并且永远是指向 Shape
的指针。
表达式 shapeArray[0]
的类型为 Shape *
。您可以在其中存储一个指向 Shape
的指针,它恰好也是一个 Triangle
,但是编译器和运行时以后唯一可以假设指向的对象是它是一个 Shape
某种形式。
表达式 (Triangle*)shapeArray[0]
具有类型 Triangle *
,表达式 dynamic_cast<Triangle*>(shapeArray[0])
.
也是如此
如您所见,您可以从类型为 Triangle*
的任何表达式访问 Triangle
的成员,但不能从 Shape*
访问,因为无法告诉 它可能是 Shape
中的哪一种。
我有一个 Shape
(抽象)基础 class,Triangle
和 Square
继承自该基础。 Square 有一个方法 Split
,其中 returns 一个形状数组:
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;
}
在我的主要方法中我有
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
我假设这是 object 切片的示例?但我不明白为什么我不能将数组中的第一个元素(这是一个三角形)转换为三角形,但最后一行允许我进入 Triangle
class。
header Square 如果需要
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];
};
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?
不,不是。您正在处理形状的 pointers - 当您尝试将派生类型的 value 分配给其中一个实例时,就会发生切片base classes,首先 - base class 可能不够大(即数据成员较少,内存较少 reserved/allocated,其次,赋值代码可能或可能不会将派生 class 的指针复制到派生 class 虚拟调度 Table - 如果这是您的编译器支持多态性的方式)。当您只是手动处理指向对象的指针时,切片不会发生(尽管如果您随后使用指针来协调复制,您可以切片)。
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.
您尝试的转换没有做任何有用的事情。他们告诉编译器暂时将 Shape*
视为 Triangle*
,但随后您将该值重新分配给 Shape*
变量,暂时丢失 Triangle*
静态类型信息注入。
最后的 dynamic_cast<>
不同之处在于您直接使用转换为值 ->GetType()
- 而编译器仍然认为静态类型是 Triangle
- 而不是分配它到 Shape*
并将静态类型 "decay" 返回到 Shape*
.
由于篇幅原因,我将其放在了一个答案中,但这并不是一个完整的答案。
您对这段代码有何期待?
shapeArray[0] = (Triangle*)shapeArray[0]; //contains no Triangle members
shapeArray[0]= dynamic_cast<Triangle*>(shapeArray[0]); //still no Triangle members
第一行应该:
- 在数组的第一个元素中取
Shape*
指针 - 向下转换为
Triangle*
double p = shapeArray[0]->
3. 分配回数组的第一个元素。这是 Shape*
类型,因此需要向上转换,将其返回到您开始的位置。
第二行应该:
- 在数组的第一个元素中取
Shape*
指针 - 向下转换为
Triangle*
,或 returnnullptr
如果它实际上不是“三角形”。 - 重新分配给数组的第一个元素。这是
Shape*
类型,因此需要向上转换,将其返回到您开始的位置。
简而言之,第一行什么都不做,如果元素不是 Triangle
,第二行会将元素归零。
要查看数组中是否真的有Triangle*
,添加一行如:
double perimeter = shapeArray[0]->Perimeter();
您可以在 Triangle
的 Perimeter
方法中设置断点或记录消息以检查它是否被调用。
或者写成:
Triangle* triangle = dynamic_cast<Triangle*>(shapeArray[0]);
您似乎期望指针以某种方式指向"remember"其中存储了何种类型的指针。强类型语言不是那样工作的。
一旦声明了 Shape ** shapeArray;
,它就是,并且永远是指向 Shape
的指针。
表达式 shapeArray[0]
的类型为 Shape *
。您可以在其中存储一个指向 Shape
的指针,它恰好也是一个 Triangle
,但是编译器和运行时以后唯一可以假设指向的对象是它是一个 Shape
某种形式。
表达式 (Triangle*)shapeArray[0]
具有类型 Triangle *
,表达式 dynamic_cast<Triangle*>(shapeArray[0])
.
如您所见,您可以从类型为 Triangle*
的任何表达式访问 Triangle
的成员,但不能从 Shape*
访问,因为无法告诉 它可能是 Shape
中的哪一种。