我有一个名为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;
}
关键是我想拥有这样的虚拟运算符,但如果我像上面那样编写代码,我就会遇到很多错误。 我做错了什么?
答案 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
(这可能很好;你不需要实例化函数),但它最终会成为更多的样板(也许你可以使用继承来做,所有新类都没有获得任何新的成员变量,只是扩展函数 - 我没有尝试过。)
我看到的最后一个问题是,你不允许一条线乘以一个平面。这可能是正确的行为,但看起来有问题。
我没有注意到乘法是用标量,所以不要介意最后一部分。