【问题标题】:Can I implement an override of a virtual function which gets as parameter a father class?我可以实现一个以父类为参数的虚函数的覆盖吗?
【发布时间】:2018-02-12 14:49:03
【问题描述】:

我正在尝试在以下代码中覆盖equal 方法:

class object{
  int a;
public:
  virtual bool equal(const object& o) const{
    return this->a==o.a; // not sure if i can access private field (o.a) from one object function to the object o so it's another question
  }
};

class point: public object{
  double x;
  double y;
public:
  bool equal(const point& o) const override{
    return (this->x==o.x && this->y==o.y);
  }
};

我曾想过重载operator== 并将其用作friend 函数,但如果我能在这种情况下以某种方式实现它会对我有所帮助...

【问题讨论】:

  • 如果您在point::equal 函数之后添加特殊标识符override(如bool equal(const point& o) const override { ... }),那么编译器会告诉您您没有覆盖任何内容。
  • 在任何实施之前,您想要做的事情在设计层面上都会失败。假设我持有一个object &,它指的是point,我用另一个object 来称呼它——也许是square。发生什么了? -- 在一般情况下,派生函数可以接受比其父函数更宽的参数集,但不能接受更窄的参数集。
  • 如果它采用不同的参数类型,则不是重载
  • 哦,好的...谢谢,一些程序员老兄
  • 你可以简单地尝试一下,然后得到一个类似 error: bool point::equal(const point&) const 标记为override,但不会覆盖的错误。你有吗?

标签: c++ overriding virtual-inheritance


【解决方案1】:

不,你不能,尝试添加override

bool equal(const point& o) const override;

...你得到一个编译错误:

错误:'bool point::equal(const point&) const' 标记为'覆盖',但没有覆盖

这是面向对象语言的正常设计,想象一下:

object *p = new point();
object p2;

p->equal(p2);

p->equal 会通过虚拟查找找到point::equal(point const&),但p2 不是point,而是object,那么编译器应该怎么做?

如果您想覆盖equal,您可以执行以下操作:

bool equal(const object& o) const override {
    auto *p = dynamic_cast<const point*>(&o);
    if (!p) {
        return false;
    }
    return this->x == p->x && this->y == p->y;
}

...但也许您应该重新考虑您的设计而不是这样做?

【讨论】:

  • @GuySadoun 您可以测试o 是否为point,如果不是则返回false,然后进行正确的测试,但这看起来不像C++ 的好设计.
【解决方案2】:

如果更改 equal 方法的签名,它会隐藏父级而不是覆盖它。你可以这样做(提高效率的空间):

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::boolalpha;

class object{
  int a;
public:
  object(int value) : a{value} {}
  virtual bool equal(object const& o) const {
    return typeid(*this) == typeid(o)
      && this->a == o.a;
  }
};

class point : public object {
  double x;
  double y;
public:
  point(int value, double xval, double yval) : object{value}, x{xval}, y{yval} {}
  bool equal(object const& o) const {
    return object::equal(o)
      && this->x == dynamic_cast<point const&>(o).x
      && this->y == dynamic_cast<point const&>(o).y;
  }
};

int main() {
  auto o1 = object{7};
  auto o2 = object{7};
  auto p3 = point{7, 8.0, 9.0};
  auto p4 = point{7, 8.0, 9.0};
  auto p5 = point{7, 8.1, 9.1};

  cout << boolalpha;
  cout << "o1 equal o2? " << o1.equal(o2) << "\n";
  cout << "o1 equal p3? " << o1.equal(p3) << "\n";
  cout << "p3 equal p4? " << p3.equal(p4) << "\n";
  cout << "p3 equal p5? " << p3.equal(p5) << "\n";
  cout << "o2 equal o1? " << o2.equal(o1) << "\n";
  cout << "p3 equal o1? " << p3.equal(o1) << "\n";
  cout << "p4 equal p3? " << p4.equal(p3) << "\n";
  cout << "p5 equal p3? " << p5.equal(p3) << "\n";
  cout << "---done---" << endl;
}

【讨论】:

  • 如果 rhs 不是真正的 point,我特别喜欢未捕获的异常。
  • 是的。但如果抛出异常,则 typeid 失败。如果你不能信任 typeid...好吧,那么事情就很糟糕了。
  • 我同意 Holt 的评论“......但这看起来不像 C++ 的好设计”。仅仅因为可以做某事与是否应该做某事无关。
  • 如果object 参数不是真正的point,它将抛出std::bad_cast。这并不意味着您有一般的 RTTI 问题,只是您对参数的动态类型做出了毫无根据的假设。
  • 我错过了基类中的 typeid 检查。我不确定我对此有何感受,但我们都只是在猜测所需的语义并没有帮助。
【解决方案3】:

这在几个方面是有问题的。

首先,你现有的代码肯定不会工作,如果你试过编译器会告诉你的。

让我们看看原因

object a{42};
point b{1.2, 3.4};
bool ab = a.equal(b);
bool ba = b.equal(a);

所以,问题:

  1. 我们应该如何计算ab

    好的,这很简单:point b object,所以我们称之为a-&gt;object::equal(object const&amp;)

  2. 我们应该如何计算ba

    这个更难。

    我们可以使用b-&gt;object::equal(object const &amp;),它的好处是对称(如果a.equal(b) == b.equal(a) 通常更好。

    或者我们可以写一个覆盖,所以我们调用b-&gt;point::equal(object const &amp;),并让它决定它的参数对象是否真的point。 (这是 Holt 的 dynamic_cast 解决方案)。

对于最后一种情况,我们可以有一个重载(而不是覆盖),所以point 有两个equal 方法,一个采用object(这实际上可以是基类方法)和一个采用point。但是,这只适用于静态类型。

class point : public object{
  double x;
  double y;
public:
  bool equal(const point& o) const {
    return (this->x==o.x && this->y==o.y);
  }
  using object::equal; // stop this being hidden
};

现在我们有一个重载而不是覆盖(所以在这个阶段使用virtual 毫无意义),我们可以写

point c{5.6, 7.8};
bool ba = b.equal(a); // uses b->object::equal(object const&)
bool bc = b.equal(c); // uses b->point::equal(point const&)

但是,一旦我们开始存储基类指针或引用(这是首先使用virtual 的正常原因),我们就会丢失动态类型信息:

object &r = c;
bool br = b.equal(r); // uses object::equal(object const&)

如果您需要这种情况下使用动态类型(即,找出r 确实指的是point),您需要使用dynamic_cast 之类的东西。随着子类数量的增加,代码的扩展性不是很好,但是对于小案例来说,它比成熟的访问者实现起来要简单得多。

【讨论】:

  • 非常感谢,对我的理解很有帮助
猜你喜欢
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 2021-05-21
  • 1970-01-01
  • 2016-07-12
  • 1970-01-01
相关资源
最近更新 更多