制作基类指针的向量并将派生类对象传递给它(多态)

Posted

技术标签:

【中文标题】制作基类指针的向量并将派生类对象传递给它(多态)【英文标题】:Making a vector of base class pointers and pass Derived class objects to it (Polymorphism) 【发布时间】:2019-12-06 17:33:18 【问题描述】:

我正在尝试为我的 Shape 程序实现一个菜单。我已经实现了所有的形状类。两个直接派生自抽象类“Shape”,另外两个派生自一个名为“Polygon”的类,该类派生自“Shape”,如下所示:

Shape -> Polygon -> Rectangle, Triangle
  `-> Circle, Arrow

在我的菜单类中,我想创建某种数组,它可以包含指向对象的指针以及基类“Shape”的类型。但我不确定如何正确地做到这一点,并且以一种适用于我所有形状的方式,因为我的 2 个类不是直接从“形状”派生的。

这是我的菜单类:

class Menu

protected:
    //array of derived objects 
public:

    Menu();
    ~Menu();

    // more functions..
    void addShape(Shape& shape);
    void deleteAllShapes();
    void deleteShape(Shape& shape);
    void printDetails(Shape& shape);
private:
    Canvas _canvas; //Ignore, I use this program to eventually draw this objects to a cool GUI
;

在函数“addShape(Shape& shape);”中,我想用它来将每个给定的形状添加到我的数组中。如何实现向其中添加新对象?而且,我如何检查给定的对象是否来自“多边形”?因为如果是这样,那么据我所知,我需要以不同的方式调用成员函数。

【问题讨论】:

无论它们是直接还是间接地从Shape 派生,都无关紧要 - 它的工作方式相同。您不必以不同的方式调用成员函数 - 所有形状都应该具有相同的接口(并通过不同地实现接口的 virtual 方法来做不同的事情)。但是您不必根据类型更改有关如何调用这些成员函数的任何内容-这将违反en.wikipedia.org/wiki/Liskov_substitution_principle 您应该考虑谁应该拥有这些形状。如果Menu 是所有者,则使用std::unique_ptr<Shape> 的数组。如果没有,在Menu 中,您应该使用Shape*std::shared_ptr<Shape>,这取决于您是否确定形状的生命周期会比Menu 的生命周期长。 std::vector<std::unique_ptr<Shape>>std::vector<std::shared_ptr<Shape>> 应该是您的 goto 存储选择。您选择的将基于您的所有权要求。 但是如果我想使用像“printDetails”这样的函数,它假设打印给定形状的详细信息,我不能以相同的方式为所有对象调用打印函数,因为有些其中不直接派生 @NathanOliver-ReinstateMonica 两者有什么不同?我的菜单应该一直工作到我将在之后实现的主程序结束 【参考方案1】:

我看到您在菜单中有一个数组,假设:

Shape* myshapes[10];

形状可以是矩形、三角形、圆形等。 你想要的是能够像这样使用菜单的 printDetails() 方法:

    void printDetails()
    
        for(int i = 0; i < size; i++)
        
            cout << "Index " << i << " has " << myshapes[i]->getShapeName() << endl;
        
    

getShapeName() 将返回一个字符串,例如如果是矩形,则为“矩形”。 您将能够在纯虚函数的帮助下做到这一点。纯虚函数必须在抽象类Shape中,它有:

virtual string getShapeName() = 0; //pure virtual

这意味着我们期望在派生类中定义这个函数。这样您就可以使用形状数组中的形状指针来使用 getShapeName() 方法,它会告诉您形状是矩形、三角形还是圆形等。

class Shape

    public:
    virtual string getShapeName() = 0;
;

class Circle : public Shape

    private:
    int radius;

    public:
    Circle(int r)  radius = r; cout << "Circle created!\n"; 
    string getShapeName()  return "Circle"; 
;

class Arrow : public Shape

    private:
    int length;

    public:
    Arrow(int l)  length = l; cout << "Arrow created!\n"; 
    string getShapeName()  return "Arrow"; 
;

class Polygon : public Shape

    public:
    virtual string getShapeName() = 0;
;

class Triangle : public Polygon

    private:
    int x, y, z;

    public:
    Triangle(int a, int b, int c)  x = a; y = b; z = c; cout << "Triangle created!\n"; 
    string getShapeName()  return "Triangle"; 
;

class Rectangle : public Polygon

    private:
    int length;
    int width;

    public:
    Rectangle(int l, int w) length = l; width = w; cout << "Rectangle created!\n"; 
    string getShapeName()  return "Rectangle"; 
;

要实现 addShape() 方法,您可以这样做:

void addShape(Shape &shape)

    myshapes[count] = &shape;
    count++;

另外,请记住在 addShape() 方法中通过引用或使用指针传递 Shape。 我希望这会有所帮助......祝你好运:-)

【讨论】:

关于addShape 的注释:注意提供的Shape 的生命周期足够长,可以超过它的所有用途。像这样的函数很容易被传入局部变量的人误用。 哇!感谢您提供的信息丰富的评论。我还想知道通过引用或指针传递形状有什么区别:o 谢谢!你完全正确,我应该提到它。当您从一个过早结束的函数调用 addShape() 时会出现问题,因此 Shape 不再存在,并且形状指针数组有一个指向一些垃圾的条目。您能否还提及此问题的最佳解决方法?嗨@Kidsm!按值传递和按引用传递具有相同的效果。不同的是,当你通过指针传递时,变量被当作Shape*,而当你通过引用传递时,变量被当作Shape,得到你写&shape的地址。

以上是关于制作基类指针的向量并将派生类对象传递给它(多态)的主要内容,如果未能解决你的问题,请参考以下文章

如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?

将基类的对象传递给派生类的引用函数

如何使用多态性从基类访问派生类向量成员?

试图将派生类对象地址分配给基类指针向量

无法将参数 1 从派生指针转换为基类指针引用

创建基类对象的向量并将派生类对象存储在其中