C++类详解

类详解:

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

<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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值