#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()
谢谢!
答案 0 :(得分:2)
当您想要“确定涉及哪个实际类”时,需要将该函数标记为virtual
。
这使得编译器将“虚函数表”作为其中的一部分和任何派生类。
换句话说,在virtual void test();
中设置base
。 virtual
属性将“继承”,因此您的派生类也将为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"; }