基类调用派生类的方法?

4
class BaseClass {
 public:
  virtual void method1(){
   method2();
  };
  virtual void method2(){
   std::cout << "Base Method" << std::endl;
  };
}

class DerivedClass : public BaseClass {
 virtual void method2(){
   std::cout << "Derived Method" << std::endl;
  };
}

int main() {
  DerivedClass derived;
  derived.method1();
}

在上面的例子中,我得到了“Derived Method”作为输出结果——这是为什么呢?
我知道DerivedClass继承自BaseClass,因此derived可以调用method1。但我不明白,为什么当从BaseClass中调用它时,DerivedClassmethod2会隐藏BassClassmethod2
对于任何糟糕的代码/错误感到抱歉——我还是C++的新手。

1
这就是 C++ 的设计目的。 - n. m.
1
因为BaseClass::method2()被声明为虚函数。 - Oktalist
这是面向对象编程的一个原则,你听说过多态性吗? - ichramm
4个回答

3
因为method2virtual的。
当你将一个函数声明为virtual时,实际上做的是使得当通过指针或引用调用方法(换句话说,以一种正常的方式)时,被调用的函数是最终的重载函数。
这是一件好事情,通常也是你想要的。请注意,无论从什么上下文中进行调用都没有关系。你正在从基类的上下文中调用method2,该基类有一个可用的method2实现。假定你正在做的是从基类中调用,因此可以调用基类的实现。
virtual并不是这样工作的——这也是一件好事情。
如果你愿意,可以通过明确地使用它来调用基类中的版本:
class BaseClass {
 public:
  virtual void method1(){
   BaseCLass::method2();
  };

但这通常是不可取的,在我的书中,这是一种代码异味。

2

derived.method1() 静态绑定了函数调用 BaseClass::method1(),然而这个函数调用了虚函数 method2()。这导致动态绑定到 DerivedClass::method2() 而不是调用 BaseClass::method2()

如果你想在 BaseClass::method1() 中静态绑定 BaseClass::method2(),你必须这样做:

class BaseClass {
 public:
  virtual void method1(){
   BaseClass::method2(); //explicit class scope prevents dynamic binding
  }
  virtual void method2(){
   std::cout << "Base Method" << std::endl;
  }
};

从技术上讲,即使 derived.method1() 是一次动态调用,因为 method1virtual 的,但是它可以通过某些编译器优化在编译时轻松解决。不过,并不是所有的编译器都这样做。 - pmr
不,我们知道derived是一个DerivedClass,而不是对其的引用或指针。如果derived是从继承自DerivedClass的某个东西中切片出来的,则虚表指针将指向假定为DerivedDerivedClass的某个位置,并尝试使用它没有的数据成员。因此,它必须静态绑定。 - Suedocode
@pmr 实际上,我对切片的理解是错误的;gcc 4.8.2似乎会将虚表指针重新分配给您要切片的类。例如,如果A继承自B,并且将B切片到A,则新对象的虚表指针将被重新分配给A的内容,而不是B的内容。因此,是的,method1()也可以动态绑定。我的错! - Suedocode

0
因为你声明了它是虚函数。如果不想要这种行为,请移除虚函数的声明。虚函数对于继承和面向对象编程的整个概念都至关重要。请参见《C++程序设计语言(特别版)》第12章进行深入讨论。

声明“method2()”为虚函数。而“method1()”仍然可以是虚函数。 - Suedocode

0
尝试从BaseClass的method2()中删除virtual关键字。这将使输出为"Base Method"。这是静态绑定,因为调用了BaseClass的method1(),所以也会调用BaseClass的method2()。
相反,虚拟成员函数在运行时动态解析。也就是说,根据对象的类型,在运行时动态选择成员函数。因此,DerivedClass的对象总是只调用派生类的method2(),输出将为"Derived class"。

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