c ++中的虚拟乘法运算符

时间:2011-11-15 21:45:02

标签: c++ virtual multiplication

我有一个名为Figure

的祖先abstact类
class Figure {
public:
   string style, color, name;


   virtual void printInfo() = 0;
   /*
   different methods here
   */
   virtual Figure operator * (int prod) = 0;

};

我有继承图的Line类(和其他几个)。

class Line : public Figure {
     /* .... */
     Line operator*(int prod);
};

Line Line::operator *(int prod) {
    Line temp = *this ;
    Point p = getPoint2();
    p.setXYZ(p.getX() * prod, p.getY() * prod, p.getZ() * prod);
    temp.setPoint2(p);
    return temp;
}

关键是我想拥有这样的虚拟运算符,但如果我像上面那样编写代码,我就会遇到很多错误。 我做错了什么?

4 个答案:

答案 0 :(得分:2)

Figure是一个抽象类,因此您无法按值返回它。它是抽象的,因为它具有纯虚方法。抽象类无法实例化。

答案 1 :(得分:0)

Figure::operator*按值返回Figure Line::operator*按值返回Line 如果指针或引用返回派生类型,则只允许返回派生类型,但不是值。

我的代码现在处于同一点,但我还没有弄清楚如何解决这个问题,除了删除按值返回的虚函数。

答案 2 :(得分:0)

重载运算符,例如产品运算符,应该非常小心,因为(很遗憾)很容易创建一种违背常识的语法。使这样的重载运算符虚拟化并没有帮助。 (当然,你的代码显然是错误的,如同已经指出的那样 - 图和线都不能用作返回类型,因为它们都是抽象的。)

答案 3 :(得分:0)

要添加到之前的答案:如果您确实需要操作员*重载,您必须意识到您需要想出一些方法来创建您将要返回的新Figure

然而,在你这样做之前,最好指出二元运算符*通常应该在类之外重载,如下所示:

T operator*(T const& lhs, T const& rhs);

将它变成朋友可能很有用,但它通常也是不必要的。如果您希望此运算符的行为不同,具体取决于是否传入了T或派生的T,请让它在其中一侧调用虚拟T multiply(T const& rhs) const;函数(无论如何这都是个好主意)。

现在,至于回归,有几种方法可以做到这一点:

制作 Figure 不抽象并按值返回

这是一个相当简单的解决方案,但不幸的是,它没什么用处。如果您使用多态,则切片是不可接受的。

new Figure 并按引用方式返回

有可能,这看起来像这样:

T& operator*(T const& lhs, int rhs) {
    T* t = lhs.Clone(); // Clone returns a new derived-from-T.
    t->multiply(rhs);
    return *t;
}

这样可行,但它会使内存管理变得困难:例如,如果multiply()抛出,则会导致内存泄漏。一般来说,虽然可以做到这一点,但这似乎并不明智。

使用PImpl并按值返回包装

说实话,我没有PImpl的实际经验,但我认为它(或类似的东西)可以在这里应用。您可以将当前Figure*重命名为Figure,而不是传递FigureImpl,并创建一个新的Figure类,其内容为:

class Figure {
    // We don't want direct instantiation.
    Figure();
    Figure(FigureImpl* fi) : p_(fi) {}

  public:
    Figure(Figure const& f)
        : p_(f.p_->Clone())
    {}
    void PrintInfo(); // Hm, why not overload operator<<?
    Figure Multiply(int rhs) const {
      Figure f(p_.Clone());
      f.p_->Multiply(rhs);
      return f;
      // Or even:  return Figure(p_->Multiply(rhs.p_));
      // with Multiply returning a new FigureImpl*.
    }

  private:
    unique_ptr<FigureImpl*> p_;
};

Figure operator*(Figure const& lhs, int rhs) {
    return lhs.Multiply(rhs);
}

然后,您可以拥有FigureImpl班级和LineImpl班级:

struct FigureImpl {
    virtual void printInfo() = 0;
    virtual void Multiply(int) = 0; // or make it const and return a FigureImpl*
};

struct LineImpl {
    // implement functions
};

还有两个问题,主要问题是Figure的实例化。我建议创建一些像MakeLineFigure这样的函数来返回相应类型的新Figure。您还可以向最终用户公开FigureImpl,但这似乎不太有用。

另一个问题是你不能再做正常的LineImpl了。这是否真的是一个问题因您打算如何使用该课程而异。你可以创建一个名为Line的类似包装器并给它一个operator Figure(这可能很好;你不需要实例化函数),但它最终会成为更多的样板(也许你可以使用继承来做,所有新类都没有获得任何新的成员变量,只是扩展函数 - 我没有尝试过。)

我看到的最后一个问题是,你不允许一条线乘以一个平面。这可能是正确的行为,但看起来有问题。

我没有注意到乘法是用标量,所以不要介意最后一部分。