我目前正在编写一个程序来模拟各种类型的数字,并在Set对象(已经编写和测试)中以多态方式管理它们。我的继承关系是这样的: 多号码(所有虚拟功能,虚拟类) 由Pairs,Complex,Rational继承 子类都具有大致相同的函数和不同的参数。
我遇到的问题是这样的函数:
Multinumber& Complex::operator=(const Multinumber &rhs)
{
imag=rhs.imag;
real=rhs.real;
return *this;
}
因为我正在多态地处理这个,所以我的返回类型和参数都必须是Multinumber类型,以便覆盖基类的参数。但是,我有一个很糟糕的时间来编译。我遇到了很多错误:
error: 'const class Multinumber' has no member named 'imag'
这是真的。多号码没有图像属性或功能。但是我怎样才能让编译器意识到Multinumber& rhs永远是复杂的,或者像它一样对待它?谢谢你的帮助。
这就是我的超类看起来的样子:
class Multinumber
{
public:
virtual Multinumber& operator+(Multinumber&);
virtual Multinumber& operator=(Multinumber&);
virtual bool operator==(Multinumber&);
virtual string object2string();
virtual Multinumber& makeobject(double, double)=0;
};
答案 0 :(得分:1)
我认为你必须投。试试这个:
Multinumber& Complex::operator=(const Multinumber &rhs){
const Complex & _rhs = dynamic_cast<const Complex &>(rhs);
imag=_rhs.imag;
real=_rhs.real;
return *this;
}
答案 1 :(得分:1)
签名如:
Multinumber& Complex::operator=(const Multinumber &rhs)
表示可以将任何类型的Multinumber
分配给Complex
。那真的你想要的东西吗?你有两个选择:
Complex
(例如通过dynamic_cast
)。如果不是,你会怎么做?你最终可能会抛出异常。Multinumber& Complex::operator=(const Complex &rhs)
,禁止将其作为一个整体:尝试将Rational
分配给Complex
将无法编译。最后,您是唯一一个能够真正决定哪种更适合您需求的人,但从我的观点来看,编译时错误比运行时错误更可取。
另一方面,我认为您通过询问“如何让编译器意识到多编号&amp; rhs 始终是复杂的”来给出答案:将其设为{{1}它永远不会是其他任何东西。
编辑现在我们在Complex
看到operator=
是虚拟的,看来您确实被迫坚持使用初始签名并验证参数的动态类型在Multinumber
中(见史蒂夫的回答)。
答案 2 :(得分:0)
如果你确定Multinumber总是很复杂,你可以简单地将你的rhs投射到Complex &
。
然而,这听起来像是错误的做法。相反,你为什么不简单地写Complex& Complex::operator=(const Complex &rhs)
?您的运算符不是虚拟的,因此没有必要使用与基类相同的类型。
答案 3 :(得分:0)
这里缺少的短语是covariant return types。在C ++中,允许重写的方法返回派生(因此协变)引用或指针类型。于是
Complex& Complex::operator=(const Multinumber &rhs)
是Multinumber& Multinumber::operator=(const Multinumber&)
不幸的是,我认为您还希望在参数类型上具有协方差。这是C ++标准IFAIK所不允许的。此时你应该考虑是否真的想要这种多态性。这意味着您必须具有在类型之间进行转换的方法。您是否希望支持Pair和Complex类型之间的转换?如果是这样,史蒂夫的dynamic_cast
方法似乎就要走了。否则,您应该从Multinumber的可重写方法中删除诸如operator =之类的方法,并且只允许声明有意义的方法在它们属于派生类型的位置。
<强>更新强>
作为对进一步评论的回应:返回类型协方差仅适用于引用和指针。按值返回不起作用,因为每种类型的存储要求都不同(与指针不同)。所以Multinumber& operator+(Multinumber&)
是一个可以被覆盖的有效方法,但它可能无法正常工作,因为你不能返回对新创建类型的引用,因为它会在函数完成时被销毁。解决这个问题的一种方法是用Multinumber& operator+=(const Multinumber&)
替换该方法。对于Complex
,您可以将其实现为:
Complex& Complex::operator+=(const Multinumber &rhs){
const Complex & _rhs = dynamic_cast<const Complex &>(rhs);
imag+=_rhs.imag;
real+=_rhs.real;
return *this;
}
另一种方法是完全处理指针,并使operator+
返回new
指针的副本。但这简直太糟糕了,我强烈建议你远离这些恐怖事件 - 就像在C ++出现之前的结构C一样。你可以用某种形式的多态pimpl方法改进一些东西(注意我没有检查过以下内容,它只是为了给你一个想法,它肯定可以改进):
class Multinumber
{
public:
virtual Multinumber* operator+(const Multinumber&);
virtual Multinumber& operator=(const Multinumber&);
virtual bool operator==(const Multinumber&) const;
// etc.
};
class MultinumberOuter
{
std::unique_ptr<Multinumber> impl_;
public:
explicit MultinumberOuter(Multinumber* pimpl) : impl_(pimpl) {}
MultinumberOuter operator+(const MultinumberOuter& src) const {
return MultinumberOuter(impl_->operator+(*(src.impl_)));
}
MultinumberOuter& operator=(const MultinumberOuter& src) {
impl_->operator=(*(src.impl_));
return *this;
}
bool operator==(const MultinumberOuter& src) const {
return impl_->operator==(*(src.impl_));
}
// etc.
};
但是,在走这条路之前,要长时间考虑这种复杂性是否合理。也许您所设定的问题不能保证您所追求的多态性水平。
答案 4 :(得分:0)
放弃。它无法完成。 @beldaz是正确的,C ++中不允许使用协变参数,但忽略了真正的观点:即使它们是类型安全的也无济于事。
请参阅我的回答:
C++ Abstract class can't have a method with a parameter of that class