当使用派生对象通过另一个基函数调用时,基本和派生调用库中的公共函数?

时间:2015-07-11 06:36:31

标签: c++

#include <iostream>

using namespace std;

class dissection {
  int x;
  public:
    void test() {
     cout<<"Test base";
   }
    void caller(){ test(); }
};

class dissectionDerived: public dissection {
  int x;
  public:
    void  test(){ 
    cout<< "Test derived";  
    }
};

int main( int argc, char ** argv ) {
  dissectionDerived derived;
  derived.caller();
  return 0;
}

在上面的代码示例中,输出为&#34; 测试基础&#34;。我想到它的方式是,因为派生没有名为调用者的函数,它可以调用基类函数,但是因为对象的实际类型是 dissectionDerived 它将能够调用dissectionDerived类的测试功能。这是因为重载决策在基类范围内找到最接近的测试函数后会停止吗?

如果可以从派生函数调用调用者函数,为什么它也不能成为派生类中重载决策的一部分?

我使用了-cg g ++编译器标志然后对目标文件进行了obj转储,输出如下所示:

SYMBOL TABLE:

0000000000000000 l    d  .text._ZN10dissection4testEv   0000000000000000 .text._ZN10dissection4testEv
0000000000000000 l    d  .text._ZN10dissection6callerEv 0000000000000000 .text._ZN10dissection6callerEv
0000000000000000  w    F .text._ZN10dissection4testEv   000000000000001d _ZN10dissection4testEv
0000000000000000         *UND*  0000000000000000 __gxx_personality_v0
0000000000000000         *UND*  0000000000000000 _ZSt4cout
0000000000000000         *UND*  0000000000000000 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000000000000000  w    F .text._ZN10dissection6callerEv 000000000000001a _ZN10dissection6callerEv

c ++ filt _ZN10dissection4testEv给出解剖:: test()

谢谢!

3 个答案:

答案 0 :(得分:2)

当您想要“确定涉及哪个实际类”时,需要将该函数标记为virtual

这使得编译器将“虚函数表”作为其中的一部分和任何派生类。

换句话说,在virtual void test();中设置basevirtual属性将“继承”,因此您的派生类也将为virtual - 对于“安全”,您可以将其标记为override(例如void test() override { ... },以确保如果你试图“覆盖”不属于基类的东西,编译器会给出错误。

答案 1 :(得分:1)

由于历史原因,C ++中的默认调度逻辑上是错误,即取决于引用/指针的静态类型,而不取决于对象的实际类型。

  

C ++中的“静态类型”是指引用或指针的类型......即在const BaseObj& x; x的静态类型是BaseObj。< / p>      

对于“动态类型”,它意味着指向或引用的对象实例的实际类型,可以与静态类型或从它派生的类型相同。例如,在BaseObj *p = new DerivedObj;中,静态和动态类型是不同的。

     

使用非虚方法(不幸的是默认方法),名为的代码取决于静态类型,而不取决于实例的实际类型

     

这种逻辑上错误的默认值的官方原因是效率,因此可以在链接时计算要调用的地址并将其修复。

要根据需要将成员函数声明为virtual的对象类型进行调度。 请注意,每个方法都需要virtual关键字,但只在基类中,因为在所有派生类中也假设它...无论如何IMO都是在派生类中重复它的好文档。

还要记住,在C ++中,为多态声明派生和销毁的类声明析构函数是非常重要的,因为即使是它,否则如果使用删除派生实例,你将遇到麻烦(未定义的行为)指向基地的指针上的delete

通常在C ++程序中,一个类不是要派生的(那么virtual关键字不存在,你可以从每个实例中删除几个字节)或者它意味着派生而且它更好声明析构函数和其他方法为virtual。在一个意图派生的类中使用非虚方法是一种例外,IMO应该只是出于有充分理由才会发生。

In the words of C++ original author默认情况下非虚拟调度只是因为类不能用作基类,而是因为内存优化和字节级与C和Fortran结构的兼容性。

答案 2 :(得分:0)

打印&#34;测试派生&#34;,将dissection::test()声明为virtual

virtual void test(){ cout<<"Test base"; }