C ++如何返回未知的派生类?

时间:2016-10-03 04:59:24

标签: c++ inheritance polymorphism

我有一个基类Shape,它有EllipseRectangle等派生类。

在一个函数中,我有一个变量:

Shape activeShape(black, black, {0,0}, {0,0}, false);

以及后来的那个功能:

activeShape = updateShape(isButton, activeShape, true);

updateShape看起来像这样:

Shape updateShape(int button, Shape active, bool leftClick)
{
    switch(button)
    {
        case 1:
            return active;
        case 2:
            return Line(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false);
            break;
        case 3:
            return Rectangle(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false);
            break;
        case 4:
            return FilledRectangle(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false);
            break;
        case 5:
            return Ellipse(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false);
            break;
        case 6:
            return FilledEllipse(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false);
            break;
        default:
            if(leftClick)
            {
                active.setColor(getEnumColor(button), active.getBorderColor());
            }
            else
                active.setColor(active.getFillColor(), getEnumColor(button));
            break;
    };
    return active;
}

因此,当我返回Rectangle之类的内容时,它们会被归为Shape。这不是我想要的。

如何让activeShape成为Shape派生类之一,我需要做些什么?

3 个答案:

答案 0 :(得分:5)

创建对象后,无法变换其类型(例如,基于有关在运行时获得的按钮的信息)。将Rectangle作为Shape返回会产生切片对象的效果,因此调用者只会收到Shape Rectangle部分的副本,但不会收到剩余部分的副本

假设Shape是一个多态基类(即它提供了可能由派生类专门化的虚函数),Shape *(指向形状的指针)可以指向Rectangle。但是,有必要正确管理对象的生命周期。

您可以使用智能指针处理所有这些,例如在C ++ 11及更高版本中std::unique_pointer<Shape>

std::unique_pointer<Shape> updateShape(int button,
        std::unique_pointer<Shape> active, bool leftClick)
{
    switch(button)
    {
        case 1:
             break;    // don't change active
        case 2:
            active = new Line(active->getFillColor(), active->getBorderColor(), active->getP1(), active->getP2(),false);
            break;
        case 3:
            active = new Rectangle(active->getFillColor(), active->getBorderColor(), active->getP1(), active->getP2(),false);
            break;
        case 4:
            active = new FilledRectangle(active->getFillColor(), active->getBorderColor(), active->getP1(), active->getP2(),false);
            break;
        case 5:
            active = new Ellipse(active->getFillColor(), active->getBorderColor(), active->getP1(), active->getP2(),false);
            break;
        case 6:
            active = new FilledEllipse(active->getFillColor(), active->getBorderColor(), active->getP1(), active->getP2(),false);
            break;
        default:
            if(leftClick)
            {
                active->setColor(getEnumColor(button), active->getBorderColor());
            }
            else
            {
                active->setColor(active->getFillColor(), getEnumColor(button));
            }
            break;
     }
     return active;
}

这样做的原因是std::unique_pointer管理动态分配(使用运算符new)对象的生命周期。它通过存储指向对象的指针,并指定std::unique_pointer<Shape>更改指针(使其指向不同的对象)来完成此操作。重要的是,分配给智能指针也会释放它先前管理的对象。

请注意,由于std::unique_pointer将使用运算符delete在其生命周期结束时销毁所包含的对象,因此Shape必须具有虚拟析构函数。如果不这样做,将导致使用运算符delete时出现未定义的行为。

这种用法类似于

 std::unique_pointer<Shape> activeShape(new Rectangle( whatever_parameters));

 activeShape = updateShape(button, activeShape, leftClick);

请记住activeShape是一个智能指针。因此,使用包含的Shape对象需要指针语法(activeShape->whatever)而不是成员语法(activeShape.whatever)。

由于active参数(通过值)传递给函数并返回,因此分配返回值是不可能的。如果,而不是

activeShape = updateShape(button, activeShape, leftClick);

你只是

updateShape(button, activeShape, leftClick);   // returned object is discarded

(即不要将返回值赋给任何东西),净效果是activeShape所持有的对象将被销毁,任何使用它的尝试都会给出未定义的行为。

答案 1 :(得分:2)

类型是在编译时确定的,因此您无法根据运行时信息更改变量的类型,例如dynamic_cast<Rectangle&>(active_shape)的值。

相反,您需要使用return std::make_unique<Ellipse>(active.getFillColor(), active.getBorderColor(), active.getP1(), active.getP2(),false); 之类的东西来测试它是否是一个矩形然后如果该测试通过则使用它(如果形状不是矩形则会抛出异常)

编辑:您必须将函数更改为不按值返回Shape。我建议返回std :: unique_ptr,返回语句如下:

require 'prime'

def gap(g, m, n)
  range = (m..n).to_a
  primes = []
  range.each { |num| primes.push(num) if Prime.prime?(num)}


  primes.each_index do |count|
    prv , nxt = primes[count], primes[count+1]
    if !nxt.is_a? Integer
      return nil
    end

    if nxt - prv == g
      return [prv, nxt]
    end
  end
end

答案 2 :(得分:0)

在你的代码对象的下面一行会发生切片,所以你的代码无论如何都会有问题。

activeShape = updateShape(isButton, activeShape, true);

这里,如果updateShape返回除shape之外的任何类型,那么在返回之前,您的对象将被切成形状类型。因此,activeShape可能包含错误的切片对象。

要改进它,您应该返回指向基类(形状)的指针并使用它。