C++类详解

来源:互联网 发布:淘宝运费险什么意思 编辑:程序博客网 时间:2024/05/17 01:54

类详解:

一,类在内存中的存放方式

<1>不含成员变量的类

class A{        //sizeof(A) == 1        //空类具有1个字节的唯一标识,用于区别不同的空类};class B{    public:        //sizeof(B) == 1,其他同上        //成员函数不占用任何对象内存空间        B(){}        ~B(){}};class C{    public:        //sizeof(C) == 4        //拥有虚函数的类则会创建一个虚表指针        C(){}        virtual ~C(){}};

<2>静态变量&常量。

class cA{public:    int a;    int b;    static int z;    //const int e = 0;这样初始化时错误的,类常量只能使用构造函数    const int e;public:    cA(int pe):e(pe)    {    }    static void function1()    {       }};

内存结构如下
这里写图片描述
由此可以知道:
(1)类常量只能在构造函数中赋值。
(2)静态函数和静态变量不占用类内存空间。

<3>含成员变量的类。
(1)无虚函数类

class A{    public:        int a;        int b;    public:        void function1(){}};

内存排列输出如下:
这里写图片描述
由此可以知道,可以看见类的成员函数并不会占内存空间,而类的成员变量则会按顺序储存。
(PS,假如类的成员变量涉及多种类型,则需要进行字节对齐)。

(2)含虚函数类

class A{    public:        int a;        int b;    public:        void function1(){}        virtual void fuction2(){}};

内存排列输出如下:
这里写图片描述
由此可以知道:
(1),当存在虚函数时,在类内存的开始处(偏移值为0时),会保存一个虚表指针,然后再按顺序存放成员变量。
(2),接下来是虚表,它记录了一张虚表,该虚表表示对应的虚指针在内存中的分布,左边的0表示第一个虚函数的位置。
编译器是在构造函数创建此虚表指针以及虚表的。
:那么编译器是如何利用虚表指针和虚表实现多态的呢?
:当创建一个含有虚函数的父类的对象时,编译器在对象构造时将虚表指针指向父类的虚函数;同样,当创建子类的对象时,编译器在构造函数里将虚表指针(子类只有一个虚表指针,它来自父类)指向子类的虚表(这个虚表里面的虚函数入口地址是子类的)。

(3)继承后的类

class A{    public:        int a;        int b;    public:        void function1(){}        virtual void fuction2(){}};class B:public A{    public:        int c;    public:        virtual void function2(){}//重写        void function3(){}        //属于派生类的成员函数}

内存排列输出如下:
这里写图片描述
由此可以知道:
(1)内存排列继续沿用cA的内存结构,然后在末尾增加新的成员变量内存空间。
(2)虚函数表使用的是cB自己的虚表。

(4)带有自己虚函数及没重载父类虚函数的的派生类。

class B:public A{    public:        int c;    public:        virtual void function2(){}//重写        virtual void function3(){}        //属于派生类的成员函数}

内存排列输出如下:
这里写图片描述
由此可知道:
(1)没有重载父类虚函数,则虚函数表使用的是父类的虚函数。
(2)新增的派生类虚函数,会加进派生类的虚表。

(5)多继承派生类的内存结构。

class C:public A{    public:        int c;    public:        virtual void function2(){}//重写        virtual void function3(){}        //属于派生类的成员函数}class D:public C,public B{}

类D的内存结构如下:
这里写图片描述
由此可知道:
(1)多重继承会按照继承的顺序将虚表指针及成员变量按顺序排列。
(2)将会记录所有直接父类的虚表,因此我们调用function2和function3时必须使用对应的父类标记调用。

    cD *tmp = new cD();    tmp->function2();//这样编辑器会提示函数不明确,不能编译    tmp->B::function2();//这样写能正确调用

假如我修改类D重载B,C的function3,将会将屏蔽B,C的虚函数:

class D:public C,public B{    //属于派生类的成员函数    virtual void function3()    {    }  }

则在虚表部分会有相应的修改
这里写图片描述
(6)虚继承。

class D:virtual public C,virtual public B{    //属于派生类的成员函数    virtual void function3()    {    }  }

内存结构如下:
这里写图片描述

二,类引申出来的问题

(1)指向类的指针在不实例化的情况下,调用类的成员函数有什么结果,虚函数呢?
答:由类的内存结构我们可以知道,类的成员函数(非虚函数,也包含构造函数与析构函数)是储存在一个固定的位置,和类的实例化无关(类的实例化只申请类成员变量空间和绑定虚函数表)。
因此类没实例化之前,类的成员变量和虚函数表没有初始化,但是类的成员函数因为与类的实例化无关,所以可以正常调用。但是此时请注意,在类的成员函数中假如使用到类的成员变量,程序将会运行时出错,同理,调用虚函数时也会出错。

(2)在类的构造函数中能调用delete this吗,会出现什么结果?
答:此问题主要考察类调用函数的原理,由问题(1)我们可以知道,类不实例化时还是能调用自己的成员函数的(但函数不能操作成员变量),其实类在调用成员函数时就是将自己的this指针作为参数传进成员函数,因此成员函数能随时使用this中指向的已被创建出来的成员变量,因此,由于构造函数也是成员函数,所以在构造函数是可以使用delete this的,但是delete后该变量的实例化将会被消除,后续就不能使用了。

参考文档:

c++ 类成员函数内删除this指针
http://blog.csdn.net/fridayzhu/article/details/32396205
C++类内存分布
http://www.cnblogs.com/jerry19880126/p/3616999.html

声明:
该文仅作学习与记录之用,欢迎技术纠错和讨论;
非技术性言论皆为一家之谈,如有不同意见请坚持己见;
如有雷同可能为学习汝之所得,请各位巨人的肩膀还请继续空出位置。

0 0