如何从多态层次结构中的基类引用调用子类的成员

时间:2010-11-25 20:35:54

标签: c++ inheritance polymorphism

我目前正在编写一个程序来模拟各种类型的数字,并在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;
};

5 个答案:

答案 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