zoukankan      html  css  js  c++  java
  • 内存角度探寻C++面向对象 之 继承、多态

    一,简单继承:

    #include <iostream>
    
    class TableTennisPlayer
    {
    private:
        int id;
    public:
        TableTennisPlayer(int id);
        void sayHi();
    };
    
    TableTennisPlayer::TableTennisPlayer(int n)
    {
        id = n;
    }
    
    void TableTennisPlayer::sayHi()
    {
        std::cout << "Hi,I am player" << id << std::endl;
    }
    
    class MasterPlayer :public TableTennisPlayer
    {
    private:
        int id;
    public:
        MasterPlayer(int id);
        void fuck();
    };
    
    MasterPlayer::MasterPlayer(int n) :TableTennisPlayer(n)
    {
        id = n;
    }
    
    void MasterPlayer::fuck()
    {
        std::cout << "fuck you"  << std::endl;
    }
    
    
    
    int main(void)
    {
        using std::cout;
        using std::cin;
        TableTennisPlayer player(1);
        MasterPlayer master(2);
        player.sayHi();
        master.sayHi();
        master.fuck();
        cout << sizeof(player) << std::endl;
        cout << sizeof(master) << std::endl;
        cin.get();
    }

    父类成员变量id在构造函数初始化。

    子类也有成员变量id,在子类构造函数初始化。

    反汇编代码看出,子类构造函数初始化子类时首先调用父类构造函数初始化父类--具体是初始化父类成员变量。

    所以:

    这是父类在内存中的存储 00 00 00 01 占4字节

    这是子类在父类的存储:

    00 00 00 02 00 00 00 02  占8字节,前四字节是父类成员变量,后四字节是子类成员变量 

    由此看出,子类对父类是包含关系。具体是指包含父类成员变量。另外,构造函数没什么特殊的。其实就是类建立时自动调用的函数。去初始化类成员,也就是为成员变量申请内存。子类初始化,先调用父类构造方法初始化,父类成员变量。然后调用自己的,初始化自己成员变量。

    二,虚函数

    由于C++中存在指针的概念。所以,必须引出虚函数。这一点你如果不从内存角度思考,去真正理解C++中对象、以及指向对象的指针的概念是什么,你是无法真正理解虚函数的!!!

    如果一个指针指向子类。那么:

    #include<iostream>
    using namespace std;
    class A
    {
        public:
            void print()
            {
                cout<<"This is A"<<endl;
            }
    };
     
    class B : public A
    {
        public:
            void print()
            {
                cout<<"This is B"<<endl;
            }
    };
     
    int main()
    {
        A a;
        B b;
        a.print();
        b.print();
        return 0;
    }

    运行结果:指向子类的指针,调用的却是父类的print函数。

    这便是引入虚函数概念的原因:

    #include<iostream>
    using namespace std;
    class A
    {
    private:
        int a = 1;
    public:
        virtual void print()
        {
            cout << "This is A" << endl;
        }
    };
    
    class B : public A
    {
    private:
        int b = 2;
    public:
        virtual void print()
        {
            cout << "This is B" << endl;
        }
    };
    
    class C : public B
    {
    private:
        int c = 3;
    public:
        virtual void print()
        {
            cout << "This is C" << endl;
        }
    };
    
    int main()
    {
        A a;
        B b;
        C c;
        A *p1 = &a;
        A *p2 = &b;
        A *p3 = &c;
        p1->print();
        p2->print();
        p3->print();
        cin.get();
        return 0;
    }

    所以利用指针来操作的话,虽然调用的是指向子对象的指针,其实那里存储的是子对象里包含着的父对象的空间。

    加上virtual修饰符后,则是完全另一种结果。

    带有虚函数的对象是另外一种初始化方式:在构造函数中会将this指针指向虚函数表(对象内存地址里首先保存的是虚函数表)。

    也就是说:带有虚函数的对象,对象地址首先保存的是,指向虚函数表的指针(地址),然后才是类成员变量。

    子类继承了,带有虚函数的父类。对象地址首先保存的是,指向虚函数表的指针(地址),然后是父类成员变量,然后才是子类成员变量。

    三、多重继承

    继续上面的例子,C继承B,B继承A(A为虚函数)。那么C在内存中是什么样子呢?前四个字节是虚函数表指针,然后是A类成员变量,然后是B类成员变量,最后才是自己的成员变量。

    四、多继承

    如下代码所示,类C继承类B、类A

    #include<iostream>
    using namespace std;
    class A
    {
    private:
        int a = 1;
    public:
        virtual void print()
        {
            cout << "This is A" << endl;
        }
    };
    
    class B 
    {
    private:
        int b = 2;
    public:
        virtual void print()
        {
            cout << "This is B" << endl;
        }
    };
    
    class C : public B,public A 
    {
    private:
        int c = 3;
    public:
        virtual void print()
        {
            cout << "This is C" << endl;
        }
    };
    
    int main()
    {
        C c;
        A *p = &c;
        p->print();
        cin.get();
        return 0;
    }

    由反汇编结果可知,内存中存储结果为:指向虚函数表的指针、类B成员变量、指向虚函数表的指针、类A成员变量、最后是自己的成员变量。

    五,总结

    由上面几个例子可以知道。

    1 C++为了实现面向对象语言中的“向上转型”,特别引入了虚函数的概念。为何如此?因为C++有指针!!!!!!

      Java是不需要的,因为java没有指针。

    2 虚函数表具体是什么样的?具体可以看这篇博客:http://blog.csdn.net/haoel/article/details/1948051/

    类似这张图,虚函数表中,父类的虚函数在子类虚函数前面。

  • 相关阅读:
    php中curl类常用方法封装和详解
    一个简单的PHP的CURL类
    PHP的curl常用的5个例子
    PHP封装CURL扩展
    马老师的WoTou生产消费线程讲解例子
    URL路径设置----第二章:创建和管理内容
    浅谈js设计模式之迭代器模式
    浅谈js设计模式之代理模式
    浅谈js设计模式之策略模式
    浅谈js设计模式之单例模式
  • 原文地址:https://www.cnblogs.com/rixiang/p/5503518.html
Copyright © 2011-2022 走看看