2015-07-11 63 views
1
#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; 
} 

在上面的代码示例中,输出是“测试基地”。我想到的方式是,由于派生函数没有函数调用者它可以调用基类函数,但由于对象的实际类型是dissectionDerived它将能够调用测试函数解剖派生类。这是因为重载解析在基类范围内找到最接近的测试函数后就停止了吗?当通过使用派生对象的另一个基本函数调用基本和派生调用基时的常用函数?

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

我已经使用-cg克++编译器标志,然后做了objectfile的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给出解剖::测试()

谢谢!

回答

0

要打印“测试导出”,声明dissection::test()virtual

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

如果你为此解释了为什么是 – JustSid

+0

,那么我将upvote它是'test()'必须是虚拟的,而不是'caller()'。 – juanchopanza

+0

谢谢,juanchopanza。我一定失去了理智。 – Oswald

2

当你想“这实际的类是参与”,以确定哪些函数被调用,该功能需要被打上virtual

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

换句话说,使virtual void test();basevirtual属性将“继承”,因此您的派生类也将为virtual - 对于“安全性”,您可以将其标记为override(例如void test() override { ... },以确保编译器在尝试“覆盖”不是基类的一部分。

1

由于历史的原因在C默认调度++是逻辑即依赖于静态类型的参考/指针,而不是实际的对象的类型。

C++中的“静态类型”意味着引用或指针的类型......即const BaseObj& x;静态类型x的pe是BaseObj

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

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

这种逻辑错误默认的官方原因是效率,因此调用的地址可以在链接时计算并且是固定的。

要根据对象类型进行调度,需要声明成员函数为virtual。 请注意,virtual关键字是每个方法所必需的,但仅在基类中是必需的,因为在这种情况下,它也假定在所有派生类中......无论如何,它是IMO良好的文档,可以在派生类中重复它。

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

通常在一个C++程序中,一个类不是被派生出来的(然后virtual关键字不存在,你可以从每个实例中删除几个字节),或者它是用来派生的,最好是声明析构函数和其他方法是virtual。在想要派生的类中使用非虚拟方法是一种例外,IMO应该只是为了证明正确的理由才会发生。

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

+1

为什么偏见?它本身不是“好的”或“错误的”。对于你想要的,这只是一个不同的事情。 –

+0

@EmilioGaravaglia:错在于它是默认的...从语言的角度来看,出于性能原因而进行“非虚拟”调度会更好。 – 6502

+0

可能会受到限制,但我完全无法看到任何概念上的差异。 –

相关问题