【问题标题】:C++ Parent class with virtual methods implemented in 2 different child classes具有在 2 个不同子类中实现的虚拟方法的 C++ 父类
【发布时间】:2014-03-28 16:34:31
【问题描述】:

标题很难清楚地说明主题,但我会尝试解释上下文(下面有一些代码)。注意:我已经看到回答了类似的问题,但他们只处理了 1 个子类的案例。所以他们对我的情况并没有真正的帮助,因为我有 2 个子类。

背景: 我有一个父类 Shape 有 2 个孩子:圆形和方形。 我将有一个 Shape 对象的向量,但这些 Shape 对象实际上只是 Circle 对象或 Square 对象。我需要 Circle 和 Square 类具有相同的父类,以便我可以将它们存储在同一个向量中。

诀窍是我需要使用向量中的 Shape 对象来调用在 Circle 类或 Square 类中实现的方法,因此,我需要在父类中拥有这些方法的“虚拟”版本形状。

这是我的类的代码的简化部分:

形状.h:

class Shape{
public:
    std::string getColor();

    virtual int getRadius() = 0; //Method implemented in Circle
    virtual int getHeight() = 0; //Method implemented in Square
    virtual int getWidth() = 0;  //Method implemented in Square

protected:
    std::string color;
};

class Circle : public Shape{
public:
    int getRadius();

private:
    int radius;
};

class Square : public Shape{
public:
    int getHeight();
    int getWidth();

private:
    int height;
    int width;
};

在 Shape.cpp 我有这样的东西:

std::string Shape::getColor(){
    return color;
}

int Circle::getRadius(){
    return radius;
}

int Square::getHeight(){
    return height;
}

int Square::getWidth(){
    return width;
}

当我想创建 Circle 和 Square 对象时,main.cpp 中出现错误

Circle *c = new Circle(...);//Error: cannot instantiate abstract class
                            //pure virtual function "Shape::getHeight" has no overrider
                            //pure virtual function "Shape::getWidth" has no overrider


Square *s = new Square(...);//Error: cannot instantiate abstract class
                            //pure virtual function "Shape::getRadius" has no overrider

看来我需要在 Square 类中声明“getRadius”,在 Circle 类中声明“getHeight”和“getWidth”...

我尝试使用虚拟添加它们,但这会使 Circle 和 Square 成为抽象类,因此我无法使用它们创建任何对象。

有没有办法让它工作?

这是我在 stackoverflow 上发布的第一个问题。我希望一切都清楚。感谢您的帮助!

【问题讨论】:

  • 您必须至少为您在Shape 中声明的每个派生类中的纯虚函数定义一个主体,否则这些类将成为抽象基类并且无法实例化。
  • 另外,你应该继承自 Shape 而不是 Shapes
  • 哎呀,我修好了。谢谢
  • 不,这种设计尝试毫无意义,也没有办法添加任何东西。父类必须只具有所有子类通用的接口,句号,没有 ifs 或 buts 或但是。查找“is-a relationship”。
  • 如上所述,您应该重新考虑您的设计,而不是试图让设计不佳的类进行编译。形状类应该只包含所有形状通用的成员。即使是一个空的基类也会比你现在的更好。

标签: c++ class inheritance polymorphism virtual


【解决方案1】:

您的虚拟方法并不是真正适合虚拟方法的候选对象,因为它们对一个类具有特定功能,但对另一个类没有用处。

虚拟方法的好例子是由每个类实现但具有不同功能或结果的东西,如virtual int area()virtual bool intersects( Shape * otherShape ) 等。

无论如何,这就是编译代码的方式(带有一些附加功能):

形状:

class Shape{
public:
    std::string getColor();

    Shape() {}
    virtual ~Shape() {}

    virtual int getRadius() { return 0; }  // no pure virtual
    virtual int getHeight() { return 0; }  // no pure virtual
    virtual int getWidth() { return 0; }   // no pure virtual

protected:
    std::string color;
};


class Circle : public Shape {
public:
    Circle( int r )
        : Shape()
        , radius( r )
    {}  

    Circle() : Circle( 0 ) {}
    ~Circle() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    int getRadius() override { return radius; }; 

private:
    int radius;
};

正方形:

class Square : public Shape {
public:
    Square( int h, int w )
        : Shape()
        , height( h )
        , width( w )
    {}  

    Square() : Square( 0, 0 ) {}
    ~Square() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    int getHeight() override { return height; }
    int getWidth() override { return width; }

private:
    int height;
    int width;
};  

测试:

int main() {
    using shapes = std::vector< Shape * >;

    shapes s;
    s.push_back( new Circle( 10 ) );
    s.push_back( new Square() );
    s.push_back( new Square( 1, 3 ) );
    s.push_back( new Circle() );

    for ( Shape * sh : s ) {
        std::cout
            << " r " << sh->getRadius()
            << " h " << sh->getHeight()
            << " w " << sh->getWidth()
            << std::endl;
    }       

    for ( Shape * sh : s ) { delete sh; } s.clear();
}

输出:

r 10 h 0 w 0
r 0 h 0 w 0
r 0 h 1 w 3
r 0 h 0 w 0
virtual Circle::~Circle()
virtual Square::~Square()
virtual Square::~Square()
virtual Circle::~Circle()

下面是一个更好地使用虚方法的区域示例:

#include <iostream>
#include <vector>

struct Shape {
    Shape() {}
    virtual ~Shape() {}

    virtual double area() = 0;
};

用不同的区域实现扩展:

struct Circle : public Shape {
    Circle( int r )
        : Shape()
        , radius( r )
    {}

    Circle() : Circle( 0 ) {}
    ~Circle() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    virtual double area() override { return radius * radius * 3.14; }

    int radius;
};

struct Square : public Shape {
    Square( int h, int w )
        : Shape()
        , height( h )
        , width( w )
    {}

    Square() : Square( 0, 0 ) {}
    ~Square() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    virtual double area() override { return height * width; }

    int height;
    int width;
};

测试

int main() {
    using shapes = std::vector< Shape * >;

    shapes s;
    s.push_back( new Circle( 1 ) );
    s.push_back( new Square( 1, 1 ) );
    s.push_back( new Square( 2, 3 ) );
    s.push_back( new Circle( 2 ) );

    for ( Shape * sh : s ) {
        std::cout << sh->area() << std::endl;
    }

    for ( Shape * sh : s ) { delete sh; } s.clear();
}

输出:

3.14
1
6
12.56
virtual Circle::~Circle()
virtual Square::~Square()
virtual Square::~Square()
virtual Circle::~Circle()

【讨论】:

  • 这听起来很有趣。明天我会在我的代码中测试它。我有一个简短的问题要问你。 "for (Shape * sh : s)" 是否与使用迭代器相同:"for(vector::iterator sh = objects.begin(); sh != objects.end(); + +sh){ (*sh)->area()}。非常感谢!
  • 这将编译代码,但不会向其中添加一点 sense。你可以得到各种垃圾来编译。我们的目标不是拥有它,而是拥有一个可以使用的合理设计。
  • 这不是家庭作业,我试图用 OpenFrameworks 绘制东西,我需要一个包含我所有形状的矢量。这就是为什么我需要父类。我想我可以有 2 个向量,1 个用于 Circles,1 个用于 Squares,但这不是很好;)
  • @n.m.好吧,如果您不喜欢关于隐藏每个班级需要做的细节的虚函数的建议,或者您没有阅读完整的答案,我很抱歉? ...我一直在向下滚动手指,但看不到您的答案-如果您提供有趣的观点,我以及我确定的其他任何人都会立即投票...
  • @n.m.如果你真的看过我的回答,你会发现在我建议的类结构中没有虚拟 getRadius,在我建议的解决方案类 Shape 中只有一个示例虚拟 area() 方法来展示如何做得更好......完全按照您在 cmets 中的建议
【解决方案2】:

这应该会让你朝着正确的方向前进。通过使您的成员不是纯虚拟的,您可以在派生类中实现它们,但您不必这样做。

class Shape{
    public:
    std::string getColor();

    virtual int getRadius(); //Method implemented in Circle
    virtual int getHeight(); //Method implemented in Square
    virtual int getWidth();  //Method implemented in Square

protected:
    std::string color;
};

class Circle : public Shape{
public:
    int getRadius();

private:
    int radius;
};

class Square : public Shape{
public:
    int getHeight();
    int getWidth();

private:
    int height;
    int width;
};

【讨论】:

  • 我试过这样做,但是,这意味着我必须给“Shape”中的那些虚拟方法一个主体,对吗?我可以让它成为一个空体,但是每当我从向量中的 Shape 对象调用 getRadius() 时,它都会使用那个空体而不是 Circle 类中的那个,不是吗?这可能很奇怪,但我需要能够在我的向量中使用 Shape 对象调用 getRadius()(该对象最初将创建为 Circle)并使其执行定义的操作在 Circle::getRadius(){...}
  • @JLuc5 如果你的意思是形状对象,Shape myShape 那么是的,它会调用空体。但是,Shape *myShape = new Circle() 不会。
  • 形状 *myShape = new Circle(); ...我可能需要尝试一下。我不知道你能做到这一点。谢谢
【解决方案3】:

我希望你知道pure virtualvirtual 函数之间的区别。 Pure virtual 函数本质上是具有 no 主体的占位符函数。您的 shape 基类更适合普通的 virtual 函数,这些函数可以但不必在子类中更改。去掉函数声明中的= 0部分,使函数只是普通的virtual函数。

【讨论】:

  • 这是我之前所做的,但是,我不需要在我的 Shape.cpp 文件中给它们一个空的主体吗?如果我这样做,那么当我从向量中的 Shape 对象调用方法时,它不会使用空体定义的函数吗?而不是使用 Circle 或 Square 类中的正确的?我可以添加一些代码来解释我想如何使用 Shape 对象的向量。
猜你喜欢
  • 2011-05-22
  • 2018-06-12
  • 1970-01-01
  • 2013-03-08
  • 1970-01-01
  • 2018-06-02
  • 1970-01-01
  • 2012-02-04
  • 1970-01-01
相关资源
最近更新 更多