C++中方法和重载中的const关键字

4

因此,这个程序:

#include <iostream>
using namespace std;
class Base {
public:
  Base() { cout << "Costruttore Base" << endl; }
  virtual void foo(int) { cout << "Base::foo(int)" << endl; }
  virtual void bar(int) { cout << "Base::bar(int)" << endl; }
  virtual void bar(double) { cout << "Base::bar(double)" << endl; }
  virtual ~Base() { cout << "Distruttore Base" << endl; }
};
class Derived : public Base {
public:
  Derived() { cout << "Costruttore Derived" << endl; }
  void foo(int) { cout << "Derived::foo(int)" << endl; }
  void bar(int) const { cout << "Derived::bar(int)" << endl; }
  void bar(double) const { cout << "Derived::bar(double) const" << endl; }
  ~Derived() { cout << "Distruttore Derived" << endl; }
};
int main() {
  Derived derived;
  Base base;
  Base& base_ref = base;
  Base* base_ptr = &derived;
  Derived* derived_ptr = &derived;
  cout << "=== 1 ===" << endl;
  base_ptr->foo(12.0);
  base_ref.foo(7);
  base_ptr->bar(1.0);
  derived_ptr->bar(1.0);
  derived.bar(2);
  return 0;
}

在调用 base_ptr->bar(1.0); 时,会调用 Base::bar(double)。相反,在调用 derived_ptr->bar(1.0); 时,会调用 Derived::bar(double) const。我理解这与 const 关键字有关,但我不明白为什么编译器会���择不同的重载函数。如果我去掉 const 关键字,那么两种情况下都将调用 Derived::bar,并且一切都按预期工作。

5
дҪҝз”Ёoverrideе…ій”®еӯ—пјҢзј–иҜ‘еҷЁдјҡе‘ҠиҜүдҪ жІЎжңүйҮҚеҶҷеҹәзұ»еҮҪж•°гҖӮ - WhiZTiM
3个回答

3

这是因为 const 改变了函数的签名,所以它们不同。要么将基类和派生类都设为 const,要么都不设,否则其中一个就无法覆盖另一个。


2

这是因为你并没有真正重写函数bar()。在派生类中定义了一个具有不同签名的函数bar()。

所以,要么在派生类中删除const,要么在上层类中的bar()签名中添加const。(或者保持原样但知道你现在在派生类中有两个名为bar()的函数,其中一个被隐藏)


1
@kopiro:因为这是唯一匹配的候选项,由于名称隐藏,Base中定义的bar方法被隐藏了。 - Sander De Dycker
在我看来,base_ptr和derived_ptr是类似的东西,因为它们都指向一个Derived*,对吧? - kopiro
不,它们不是。 - RoaaGharra
@RoaaGharra: 好的,那么为什么 base_ptr->foo(12.0) 会调用 Derived::foo(int) ? - kopiro
因为这里我们有一个指向Derived实例的Base指针。所以,编译器会在Base中搜索foo。它找到了它,但是它是虚拟的,所以它会检查实例的真实类型,发现它是Derived,因此它会从Derived中获取函数foo。 - RoaaGharra
显示剩余6条评论

2
C++11增加了override修饰符,以防止这种意外发生。使用override关键字,编译器只会在您的代码确实进行重写时才编译它。
对于bar(int)(这也适用于bar(double)),如果它被认为是重写函数:
class Base {
public:
  ....
  virtual void bar(int) { cout << "Base::bar(int)" << endl; }
};

class Derived : public Base {
public:
  ...
  //This isn't an override, program is well formed
  void bar(int) const { cout << "Derived::bar(int)" << endl; }
};

在派生类中,bar 成员函数没有覆盖基类的同名函数。由于 const 限定符不同,因此成员函数签名也不同。在派生类中,你所做的只是添加了一个新的重载函数并隐藏了基类的函数。

在派生类中加入 override 关键字,如果它没有覆盖基类函数,编译器会给出一个诊断信息。

class Base {
public:
  ....
  virtual void bar(int) { cout << "Base::bar(int)" << endl; }
};

class Derived : public Base {
public:
  ...
  //This isn't an override, program is ill-formed, diagnostic required
  void bar(int) const override { cout << "Derived::bar(int)" << endl; }
};

要进行覆盖,成员函数的签名必须相同。
class Base {
public:
  ....
  virtual void bar(int) { cout << "Base::bar(int)" << endl; }
};

class Derived : public Base {
public:
  ...
  //This overrides
  void bar(int) override { cout << "Derived::bar(int)" << endl; }
};

因此,当您想要覆盖时,请学会使用override关键字,它将为您节省一些麻烦。

我不明白为什么调用derived_ptr->bar(1.0);会调用Derived::bar(double) const? Base::bar应该被调用,对吗? - kopiro
@kopiro:如其他地方所述:由于名称隐藏,因此在“Base”中定义的“bar”方法被隐藏,因此它们不是重载解析的候选项。 - Sander De Dycker
@kopiro 这就是它的工作原理。派生指针指向类Derived的一个实例。因此,当调用任何方法时,编译器会在类Derived中搜索具有相同名称的方法。找到了吗?是的!bar(double) const。所以这就是被调用的函数。 - RoaaGharra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接