向下转换多态指针

Casting down polymorphic pointers

我有一个 Shape(抽象)基础 class,TriangleSquare 继承自该基础。 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

第一行应该:

  1. 在数组的第一个元素中取Shape*指针
  2. 向下转换为 Triangle*

double p = shapeArray[0]-> 3. 分配回数组的第一个元素。这是 Shape* 类型,因此需要向上转换,将其返回到您开始的位置。

第二行应该:

  1. 在数组的第一个元素中取Shape*指针
  2. 向下转换为 Triangle*,或 return nullptr 如果它实际上不是“三角形”。
  3. 重新分配给数组的第一个元素。这是 Shape* 类型,因此需要向上转换,将其返回到您开始的位置。

简而言之,第一行什么都不做,如果元素不是 Triangle,第二行会将元素归零。

要查看数组中是否真的有Triangle*,添加一行如:

double perimeter = shapeArray[0]->Perimeter();

您可以在 TrianglePerimeter 方法中设置断点或记录消息以检查它是否被调用。

或者写成:

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 中的哪一种