C++中类的大小

前面我们提到过C中的struct结构体的大小,什么内存对齐,偏移量,然后Linux和Window下的还不一样,

搞懂了上面的这些,那就明白了C++中的了吗??

这里的结构体可是还有函数的,还有虚函数,友员函数,继承,多重继承,虚继承,等等一大堆东西,那这里面又是咋样的呢??


1,class空类

     C++的空类是指这个类不带任何数据,即类中没有非静态数据成员变量,没有虚函数,也没有虚基类,也就是说:空类对象不使用任何空间,因为没有任何对象需要存储,但是,但是,但是,:C++标准规定,凡是一个对立的对象都必须具有非零大小:::::

所以,也就是说:空类或者空类的对象的大小也不为0。。。

#include <iostream>                                                             
using namespace std;

class test
{
};

int main()
{
        cout<<sizeof(test)<<endl;
        return 0;
}
运行结果:

所以我们可以看到空类test的大小为1

其实在C++早起的编译器中,这个值为0(也就是说空类的大小),然而当我们创建这样的对象是,它与紧接着后面的对象有相同的地址:

test  tt;

int    a;(这里tt和变量a有相同的地址),这样的话我们对对象tt的操作就会直接影响a,所以对与如今的编译器来说,空类的大小为1、

类的实例化就是在内存中分配一块地址,每一个实例在内存中都是独一无二的地址。所以空类实例化后也就有了自己独一无二的地址了


2,当含有虚成员函数时:

#include <iostream>
using namespace std;


class A
{
	public:
		virtual void fun();
};


int main()
{
	cout<<sizeof(A)<<endl;
	return 0;
} 

运行结果(window,dev-c++):


此时,类的大小为4,因为在类中隐藏了一个指针,该指针指向虚函数表(具有虚函数表的地址),正是因为如此,使得C++能够支持多态,即在运行时绑定函数的地址。

运行结果(Linux):

所以可以看出,虚函数在linux系统里面占了8个字节


#include <iostream>
using namespace std;

class A
{
	public:
		virtual void aa()
		{
		}
	private:
		char k[3];
};

class B:public A
{
	public:
		virtual void bb()
		{
		}
};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
	cout<<"B is size:"<<sizeof(B)<<endl;
	
	return 0;
}

Linux中给出的结果是:

16,16

window中(vc,dev-c++)

8,8

说明:有虚函数的类有个virtual table(虚函数表),里面包含了类的所有虚函数,类中有个virtual table pointers,通常是:vptr指向这个virtual table,占有4个字节,成员类B   public继承于A,类B的虚函数表里实际上有两个虚函数

A::aa()和B::bb()都在虚表里面,虚表的每一个表项保存着一个虚函数的入口地址。当调用虚函数时,我们是先找到虚表中的表项,找到入口地址再执行。

然而对于含有虚函数的类的大小而言,大小等于成员变量+指向虚表的指针

类B的大小等于char  k【3】的大小加上一个指向虚函数表指针vptr的大小,并且还必须考虑内存对齐,故结果为:8


所以,觉得应该是在linux下:在类中涉及到虚函数的指针(指向虚表的指针+指向父类的指针)都应该占用8个字节,下面的程序都可以这样理解!!!


#include <iostream>
using namespace std;

class A
{
	public:
		virtual void aa()
		{
		}
	private:
		char k[3];
};

class B:public A
{
};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
	cout<<"B is size:"<<sizeof(B)<<endl;
	
	return 0;
}
window下(vc和dev-c++):

A is size:8

B is size:8

linux下:

A is size:16

B is size:16

类B看上去没有虚函数,实际上类B也是有的,它是通过继承A来的,所以说,类A和类B的虚函数表里面都是A::aa()



#include <iostream>
using namespace std;

class A
{
public:
	virtual void aa()
	{}
	virtual void aa1()
	{}
private:
	char k[3];
};

class B:public A
{
	virtual void bb()
	{}
	virtual void bb1()
	{}
};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
	cout<<"B is size:"<<sizeof(B)<<endl;

	return 0;
}

window下(vc和dev-c++)

A is size:8

B is size:8

linux下:

A is size:16

B is size:16

可以看出:一个类中若存在多个虚函数,无论有多少个虚函数都只是存在一个指向虚表的指针,虚表的每一个表项保存着一个虚函数的入口地址。当调用虚函数时,我们是先找到虚表中它对应的入口地址,找到入口地址再执行。

对于上面这种情况:

类A的虚表中存在的是:A:aa() ,A:aa1()

类B的虚表中存在的是:A:aa() ,A:aa1(),B:bb(),B:bb1()           





#include <iostream>
using namespace std;

class A
{
public:
	virtual void aa()
	{}
	virtual void aa1()
	{}
private:
	char k[3];
};

class B
{
	virtual void bb()
	{}
	virtual void bb1()
	{}
};


class C:public A,public B
{
public:
	virtual void aa(){}        //重写虚函数
	virtual void cc()
	{}
};


int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
	cout<<"B is size:"<<sizeof(B)<<endl;
        cout<<"C is size:"<<sizeof(C)<<endl;
	return 0;
}

window下(vc和linux)

A is size:8

B is size:4

C is size:12

linux下:

A is size:16

B is size:8

C is size:24

类A和B的大小就不用解释了,参照上面我们所讲的,类C(有虚函数的覆盖)多重继承于A和B

首先我们来看成员变量,有一个继承A的char[3].

在来看虚函数,那么类C中的虚函数是如何分布的,它可是继承了两个基类啊!!!(有两个虚函数表)

1,第一个虚函数表,继承于类A的虚函数和C自己的虚函数(C::aa(),A::aa2(),C::cc()),当然如果我们没有重写虚函数的话,那么第一个虚函数表就是:(A::aa(),A::aa2(),C::cc())

2,第二个虚函数表,继承于类B的虚函数和C自己的虚函数(B::bb(),B::bb2(),C::aa(),C::cc())嗯嗯,应该就是这样了

所以说:总的大小就是指向两张虚表的大小在加上继承过来的成员变量,加上内存对齐的因素就是12字节


#include <iostream>
using namespace std;

class A
{
public:
	virtual void aa()
	{}
private:
	char k[3];
};

class B:virtual public A
{

};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
	cout<<"B is size:"<<sizeof(B)<<endl;
 
	return 0;
}

window下(vc和linux)

A  is  size:8

B  is  size:12

linux下:

A is size:16

B is size:24


类B里包含继承过来的char k【3】,继承的虚函数

类B的虚函数表里有A::aa(),所以有一个指向虚表的指针,又因为是虚继承,所以还有一个指向父类的指针,加上成员变量,考虑内存对齐,所以总大小为12


友元函数:

#include <iostream>
using namespace std;

class A
{
public:
	friend void kk();
};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
 
	return 0;
}

运行结果:

A  is  size  :1

所以我们可以知道友元函数如同其他函数就是一样的,也是不影响类的大小,如上,类A也相当于空类!


#include <iostream>
using namespace std;

class A
{
public:
	static int a;
};

int main()
{
	cout<<"A is size:"<<sizeof(A)<<endl;
 
	return 0;
}

运行结果:

A is size :1

所以此刻A类还是空类!!!

空类是指这个类不带任何数据,即类中没有非静态数据成员变量,没有虚函数,也没有虚基类,也没有函数(包括友元函数)




所以:每个对象里有虚表指针,指向虚表,虚表里面存放了虚函数的地址。虚函数是顺序存放虚函数地址的。不需要

   用到链表(link list)

如下程序:

#include <iostream>
#include <memory.h>
#include <assert.h>

using namespace std;

class A
{
        char k[3];
        public:
                virtual void aa()  {};
};

class B:public virtual A   //我知道了,这里的virtual是虚继承的意思
{
        char j[3];
        //加入一个变量是为了看清楚class中vfptr放在什嘛位置
        public:
                virtual void bb()  {};
};

class C:public virtual B
{
        char i[3];
        public:
                virtual void cc()  {};
};

int main()
{
        cout<<"sizeof(A):"<<sizeof(A)<<endl;
        cout<<"sizeof(B):"<<sizeof(B)<<endl;
        cout<<"sizeof(C):"<<sizeof(C)<<endl;
        return 0;
}

运行结果:

    


关于虚拟继承:

虚拟继承是多重继承中特有的概念。虚拟继承是为了解决多重继承而出现的。


类D继承自类B和类C,而类B和类C都继承自类A,因此出现如下图所示这种情况


在类D中会再次出现A。为了节省空间。可以将B,C对A的继承定义为虚拟继承,而A就成了虚拟基类。如下图:


class A;
class B:public virtual A;
class C:public virtual A;
class D:public B, public C;

虚函数继承和虚继承是完全不同的概念。。。


基类和派生类的地址和内存布局的问题

#include <iostream>
using namespace std;

class A
{
        int a;
};
class B
{
        int b;
};
class C:public A, public B
{
        int c;
};

int main()
{
        C *pc = new C;
        B *pb = dynamic_cast<B*>(pc);

        cout<<"pc :"<<pc<<endl;
        cout<<"pb :"<<pb<<endl;
        cout<<"(c*)pb :"<<(C *)pb<<endl;

        if(pc == pb)
                cout<<"equal"<<endl;
        else
                cout<<"not equal"<<endl;

        cout<<"int(pc):"<<(long)pc<<endl;
        cout<<"int(pb):"<<(long)pb<<endl;

        if((long)pc == (long)pb)
                cout<<"equal"<<endl;
        else
                cout<<"not equal"<<endl;
        return 0;
}

运行结果:

如上:if(pc == pb)

两端的数据类型不同,比较时需要进行隐式类型转换。(通常情况下,我们都是往高类型上转换(int char

会转成int))

(pc == pb)相当于:pc == (C *)pb;

pb实际上指向的地址是对象C中的子类B的部分,从地址上跟pc不一样,所以直接比较是不想等的。

但是:(C *)隐式转换pb后的pb指向的对象(pc指向的对象)的部分,是同一部分,也就显示相等了



而第二处:两端转换为int,指针pc和pb的值不同,转换后的int值不同。。。。






本文关于虚函数的类的介绍:

摘自(谢谢啦):http://www.cnblogs.com/luxiaoxun/archive/2012/09/01/2666395.html

关于C++虚函数,可以看《C++虚函数表解析》:http://blog.csdn.net/haoel/article/details/1948051

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值