想法很奇葩,运行结果更是奇葩,大家看应该怎么解释一下,运行环境是vs2012 release Win32平台
代码:
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("A类中:实foo()\n");
}
virtual void fun()
{
printf("A类中:虚fun()\n");
}
};
class B : public A
{
public:
virtual void foo()
{
printf("B类中:虚foo()\n");
}
void fun()
{
printf("B类中:实fun()\n");
}
};
class C : public B
{
public:
void foo()
{
printf("C类中:实foo()\n");
}
void fun()
{
printf("C类中:实fun()\n");
}
};
int main(void)
{
A a;
B b;
C oc;
A *ap = &a;
ap->foo(); //父实子虚
ap->fun(); //父虚子实
cout<<endl;
ap = &b;
ap->foo();
ap->fun();
cout<<endl;
cout<<"???"<<endl;
B *ptr = (B *)&a;
ptr->foo(); //为什么是c
ptr->fun(); //为什么是c
cout<<endl;
B *bp = &b;
bp->foo();
bp->fun();
cout<<endl;
bp = &oc;
bp->foo();
bp->fun(); //为什么不是B中
cout<<endl;
return 0;
}
运行结果:
派生类的虚函数只是一个同名的完全不同的函数,它会隐藏基类的函数,而不是覆盖。
它再有派生类,则可以重写它。
楼主的想法很独特,只是没有深入了解c++的多态的实现。
首先父类已经定义的virtual 函数,假设 是 void fun(),如果子类没有显示的重写(override),则子类直接继承父类的虚函数实现,
因为该子类的虚表slot的值直接复制来之父类的虚表,如果有显示的重写(无需写关键字virtual)则都会被当做对父函数的改写,编译器会
把改子类函数的地址放到该子类的虚表对应的slot中
如果父实子虚,则父类的指针即使指向子类对象,也会抑制多态特性,父类指针的虚表指针虽然指向子类的虚表,但是由于编译器在编译期间直接调用了父类函数,根本没给实现运行时多态的机会。
建议可以看下c++对象模型,便会了解的更清楚!
都是设计错误
//父实子虚,父类函数为实函数,
表明这个函数是个模板,对所有子类,都是意义相同的,子类不可更改父类的函数。
子类定义同名虚函数则是推翻了这个协议,始终设计错误。
//父虚子实
也同样是设计错误,
1)子类非虚函数,会屏蔽任何父类同名函数
2)虚函数重定义为非虚函数,这会造成继承体系混乱,
3) 实际上 C++父类的虚函数,子类中即便省略 关键字 virtual, 依然是虚函数
不存在变成非虚函数的问题,只是在子类的定义中,看不出来哪个函数 是虚函数,哪个是非虚函数而已
另外,单纯的父类对象地址,不可转换为子类指针,因为那就不是一个子类对象。
C++ 同名同参,同签名的函数不存在,父虚子实的问题
父类的虚函数,即便子类函数, 没有使用关键字 virtual, 也还是虚函数
class A
{
public:
void foo()
{
printf("A类中:实foo()\n");
}
virtual void fun()
{
printf("A类中:虚fun()\n");
}
};
class B : public A
{
public:
virtual void foo()
{
printf("B类中:虚foo()\n");
}
void fun() //这个函数的写法,等价于 写 virtual void fun() 二者没有区别,因为父类的函数 fun() 是虚函数,所以他也是虚函数
{
printf("B类中:实fun()\n");
}
};
cout<<"???"<<endl;
B *ptr = (B *)&a; //这是一个错误,a 并不是一个 B类对象,结果 未定义
ptr->foo(); //为什么是c,因为结果未定义,编译器可以给出任何结果,
ptr->fun(); //为什么是c,因为结果未定义,编译器可以给出任何结果,
cout<<endl;
想研究发了什么,编译成汇编文件,看汇编代码
调试的时候,看反汇编代码
C++中,继承关系是这样的
派生类对象,也是一个基类对象(其实是内部含有基类子对象)。
所以基类指针指向派生类是没有问题的
基类对象并不是一个派生类对象,所以用 派生类指针,指向基类对象,结果未定义。
cout<<"???"<<endl;
B *ptr = (B *)&a; //这是一个错误,a 并不是一个 B类对象,结果 未定义
ptr->foo(); //为什么是c,因为结果未定义,编译器可以给出任何结果,
ptr->fun(); //为什么是c,因为结果未定义,编译器可以给出任何结果,
cout<<endl;
父指针转为子类指针用dynamic_cast转换,如果父类指针确实为子类指针,则转换成功,如果不是指向子类指针,则返回NULL。
正确的写法应该这样。像你得例子那样,应该是会出现段错误。