父类指针转换为子类的指针问题

C++居然能用父类指针(里面存的也是父类)调用子类(子类独有的)函数?

 

昨天问另外一个问题的时候, 今天早上有人回复给出了这样一段代码, 我试着运行了一下, 结果却大大出乎我的意料 :

class Father
{
public:
    virtual void func1() { std::cout << "Father" << std::endl; }
    virtual ~Father(){}
};
class Son :public Father
{
public:
    virtual void func1(){ std::cout << "Son1" << std::endl; }
    void func2(){ std::cout << "Son2" << std::endl; }
    virtual ~Son(){}
};
class Factory
{
private:
    Father* myFather;
public:
    void setFun(Father* m_Father) { myFather = m_Father; }
    Father* getFun() { return myFather;};
};

int main()
{
    Father *m_Father = new Father();
    Factory* m_Factory = new Factory();
    m_Factory->setFun(m_Father );
    m_Factory->getFun()->func1();
    dynamic_cast<Son*>(m_Factory->getFun())->func2();
    ///
    return 0;
}

运行结果是 :

Father
Son2

这让我感觉特别的奇怪 :
首先对于main中的变量m_Father, 他的静态类型和动态类型都是Father指针, 之后使用dynamic_cast<Son*>进行强制转换, 按照我的设想, 只有当m_Father的动态类型, 也就是实际指向一个Son的时候, 转换才能够成功, 然而这里不但成功转换, 同时还调用了Son独有的函数fun2, 这不是太奇怪了吗?

 
 

你试试在子类里加个数据成员然后在调用func时打印它。

    • 张智敏: 

      试了一下, 直接segmentfault, 对啊, 确实应该段错误啊, 问题就在于为什么能够转换, 照理来说在转换那一行就应该出错的..

    • mkwz: 

      如果是不安全的转型,dynamic_cast会返回一个null_ptr (usually 0),所以你应当检查返回值再调用。之前调用成功是因为func里没有涉及到数据成员,不代表转型成功。

    • 张智敏: 

      果然是这样, 非常感谢!

       

你要理解在C++里,类的成员函数跟C里面的函数没什么本质区别,只不过是内存里的一段代码。你这里的fun2也一样,当你把指针变成Son*类型时,你调用(Son*)->func2(),C++只会把它拼成Son$func2...这样的一个函数签名,然后到符号表里面去查这个函数签名对应的内存位置,然后执行函数

在程序中dynamic_cast<Son*>(m_Factory->getFun())并没有转换成功,是个空指针,但空指针上依然可以调用成员函数,因为能寻址成功

    • 张智敏: 

      问题是转换为什么能够成功, dynamic_cast不是带有运行时类型检查吗, 当他发现实际要转换的指针根本不指向son而是指向father就应该报错啊..

    • manong: 

      没注意到你关注的点是这个。其实这时转换并没有成功,你可以在后面加一行std::cout << dynamic_cast<Son*>(m_Factory->getFun()) << endl;试试,打印出来是空指针的。如果你是转换引用的话,会跑bad_cast异常。但是对空指针调用函数依然会成功,下面有兄弟说了的

    • 张智敏: 

      恩, 发现了, 多谢了!

       

类的对象实例中,内存布局中只存放成员变量,有virtual会再加一个vptr,类的成员函数是单独存放的。
如果一段代码里的类没有任何成员变量,也不涉及虚函数,即使你用一个指向NULL的类指针来调用,同样会成功。这就类似于c语言调用函数。

一看就是被c++的语法糖毒害的患者,哈哈。对于这两个类其实本质上都继承了father类。本质都是包含了指向father的指针而已。强制转换是取转换对象为基准的并集。所以只取了son中的属性作为转换的属性。而他俩都继承了father所以转换是没问题的

  • 张智敏: 

    问题是转换为什么能够成功, dynamic_cast不是带有运行时类型检查吗, 当他发现实际要转换的指针根本不指向son而是指向father就应该报错啊..

  • 载昊君: 

    它俩同时指向了father 本质上都是继承了father。father担任了接口类的角色。它俩都实现了接口类,factory中的setfun方法看似不实现接口但是它内部用给接口类father添加了setfun方法,于是便成立。自然可以转换

  • 载昊君: 

    不对是我眼花看错了是factory通过setfun 和getfun实现了fun1方法,所以son和factory都算实现了father接口,于是可以相互转换

  • 载昊君: 

    这个应该是没错了,我是go开发者。所以看这个问题和c++不一样

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class A {
    public:
        A(int a)
        {
            this->a = a;
        }
        virtual void display() 
        {
            cout << "A class" <<"    "<<"a = "<<a<< endl; 
        }
    int a;
    };
    
    class B :public A {
    public:
        B(int a,int b):A(a)
        {
            this->b = b;
        }
        void displayB()//子类新增的函数
        {
            cout << "B_class"<<"    "<<"a = "<<a<<" b = "<<b<< endl; 
        }
    private:
        int b;
    };
    
    class Test {
    public:
        Test(A* pObj):m_pObj(pObj){}
        A* m_pObj;
        int GetData()
        {
            ((B*)m_pObj)->displayB();//强转访问子类的函数(不是继承基类的函数,是子类新增的函数)。
            m_pObj->display();
            return 0;
        }
    };
    
    int main() {
        A *pObj = new B(13,12);
        Test* t = new Test(pObj);
        t->GetData();
    
        A* pObj2 = new A(11);
        //((B*)pObj2)->displayB();//基类的强转子类的,然后访问子类的函数。(这种转换方式A可以不设置为虚基类,不用加virtual)
        dynamic_cast<B*>(pObj2)->displayB();//dynamic_cast的操作数必须是多态类型,所以我们把A虚基类。用c++的转换方式程序会崩溃,提示this指针的为NULL,但是用c语言强转的方式可以运行通过。
        system("pause");
        return 0;
    }
    
    
    class Father
    {
    public:
        virtual void func1() { std::cout << "Father" << std::endl; }
        virtual ~Father() {}
    };
    class Son :public Father
    {
    public:
        virtual void func1() { std::cout << "Son1" << std::endl; }
        void func2() { std::cout << "Son2  " << a<< std::endl; }//不访问数据成员的话可以运行成功的。有数据成员就不行了(c++转换方式)
        virtual ~Son() {}
        int a;
    };
    class Factory
    {
    private:
        Father* myFather;
    public:
        void setFun(Father* m_Father) { myFather = m_Father; }
        Father* getFun() { return myFather; };
    };
    
    int main()
    {
        Father *m_Father = new Father();
        Factory* m_Factory = new Factory();
        m_Factory->setFun(m_Father);
        m_Factory->getFun()->func1();
        //dynamic_cast<Son*>(m_Factory->getFun())->func2();//c++强制转换方式,更加安全
        ((Son*)(m_Factory->getFun()))->func2();//c语言强制转换方式
        cout<<((Son*)(m_Factory->getFun()))<<endl;
        cout<<dynamic_cast<Son*>(m_Factory->getFun())<<endl;
        system("pause");
        ///
        return 0;
    }

    https://segmentfault.com/q/1010000006169708

posted @ 2014-11-20 16:13  南哥的天下  阅读(1041)  评论(0编辑  收藏  举报