父类非虚函数,子类继承变成虚函数,会发生什么

想法很奇葩,运行结果更是奇葩,大家看应该怎么解释一下,运行环境是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。
正确的写法应该这样。像你得例子那样,应该是会出现段错误。