类与对象简介
我们知道C
语言中,数据
与处理数据的操作(函数
)是分开进行声明。C++
中通过抽象数据类型(Abstact Data Type, ADT
)来将数据与函数绑定在一起。这种封装方式并不会给C++
的布局带来成本,与C
语言的一致。C++
在内存布局以及存取时间上主要受virtual
带来的影响:
- 虚函数机制:主要用来支持运行期间动态绑定;
- 虚基类:主要用来解决多重重复继承中的基类,要被单一继承,去除子类导致二义性问题;
在C++
封装的类中,主要分为两大块:成员变量、成员函数。下面我们会对这两大块进行分析。
- 成员变量:静态(static)成员变量、非静态(nonstatic)成员变量;
- 成员函数:静态(static)成员函数、非静态(nonstatic)成员函数、虚(virtual)函数;
C++
类对象模型的内存布局:
从C++
类对象内存分布模型可以看到,影响类对象的大小或者说内存分布,主要是在非静态成员变量与虚函数模块。其它的都是类统一分布,不会对对象的大小有所改变。
类与对象的关联与区分
关于类与对象的区别:我想是不是可以这么解释,对象是类的实例化。对于单个类的实例化对象的内存分布大小只与非静态成员变量与虚函数有关(注:这里不包含继承关系,只讨论单个类情况)。
静态变量、成员变量属于类还是对象?
在这里我为什么没有把静态的成员变量与成员函数叫做静态成员函数或者静态成员变量,主要是因为静态的与非静态的初始化有着不同的意义。对于静态的说,类就可以当做与作用域空间,类似于namespace
的概念。
静态函数、成员函数、虚函数属于类还是对象?
静态成员变量
、静态成员函数
对于类
与对象
来说,都不会在占据对象
的内存空间,类
相当于作用域namespace
一致。成员函数
也不占据对象
的内存空间,成员函数
只属于类。
相关测试案例类与对象
- 空类:一个空类的sizeof()大小为1个字节;
- 含有成员函数(静态、非静态):一个空类含有静态与非静态成员函数;该类的sizeof()大小为1个字节;
- 含有成员变量(静态):一个空类含有静态成员变量,该类的sizeof()大小为1个字节;
#include <iostream>
using namespace std;
class Base
{
public:
static void static_memberFunction()
{
cout << "static_memberFunction()." << endl;
}
void memberFunction()
{
cout << "class memberFunction()." << endl;
}
public:
static int static_member_variable;
};
int main(void)
{
Base b;
cout << "A sizeof is: " << sizeof(Base) << endl;
cout << "A object sizeof is: " << sizeof(b) << endl;
return 0;
}
- 含有成员函数(虚函数、纯虚函数):含有虚函数时候,会生成一个虚函数指针,按照指针所占的字节大小;多个虚函数依然是一个虚函数指针所占的字节大小;
- 含有成员变量(非静态):成员变量所占字节大小的总和(多个非静态成员变量的话,必须考虑内存字节对齐);
#include <iostream>
using namespace std;
class Base
{
public:
virtual void virtual_memberFunction() // 虚函数存在该类,会产生虚函数指针,指针所占的字节数;
{
cout << "virtual_memberFunction()." << endl;
}
int member_variable; // 只有一个int成员变量的对象模型,所占4个字节
};
int main(void)
{
Base b;
cout << "A sizeof is: " << sizeof(Base) << endl;
cout << "A object sizeof is: " << sizeof(b) << endl;
return 0;
}
- 单继承:基类所占字节的大小,子类所占的字节大小,同时要考虑内存对齐的原则;
- 多继承:子类多个继承基类的大小,同时也需要考虑内存对齐的原则;
- 多个不同类型成员变量,内存对齐:依据成员变量占据的字节大小与在类中声明的顺序,进行内存对齐后所占据的冗余总和;
#include <iostream>
using namespace std;
class Base
{
public:
virtual void virtual_memberFunction() // 虚函数存在该类,会产生虚函数指针,指针所占的字节数;
{
cout << "virtual_memberFunction()." << endl;
}
int static_member_variable; // 只有一个int成员变量的对象模型,所占4个字节
};
class Derive : public Base // 单继承
{
public:
int member_variable;
};
class Base2
{
public:
char mem_variable;
};
class MultiDerive : public Base, public Base2
{
public:
int member_var;
};
int main(void)
{
Base b;
cout << "A sizeof is: " << sizeof(Base) << endl;
cout << "A object sizeof is: " << sizeof(b) << endl; // 8个字节
Derive d;
cout << "Derive sizeof is: " << sizeof(Derive) << endl;
cout << "Derive object sizeof is: " << sizeof(d) << endl; // 12个字节 = Base的8个字节 + Derive的4个字节
MultiDerive md;
cout << "MultiDerive sizeof is: " << sizeof(MultiDerive) << endl;
cout << "MultiDerive object sizeof is: " << sizeof(md) << endl;
// 16个字节 = Base的8个字节 + Base2的1个字节(但是内存对齐原则,对齐至4个字节) + MultiDerive字节的4个字节
return 0;
}
- 虚继承:子类虚继承基类,会生成虚基类表指针,这样会增加一个指针所占据的字节;
#include <iostream>
using namespace std;
class Base
{
public:
int member_variable;
};
class Derive : virtual public Base
{
public:
};
int main(void)
{
Base b;
cout << "Base sizeof is: " << sizeof(Base) << endl;
cout << "Base object sizeof is: " << sizeof(b) << endl; // 4个字节
Derive d;
cout << "Derive sizeof is: " << sizeof(Derive) << endl;
cout << "Derive object sizeof is: " << sizeof(d) << endl; // 8个字节
return 0;
}
上述的几种情况,分别介绍类与对象在各个状态下的所占据的内存空间布局。通过验证,我们可以得出:类对象模型所占的内存空间布局主要依靠非静态成员变量与虚函数,继承体系下主要是虚继承。类中成员函数、静态成员函数、静态成员变量都不会占用对象的内存布局。
小结
关于C++
类对象模型中,简单介绍几种对于类对象模型内存布局所占的影响。类中成员函数与成员变量是主要分布内容。而对象模型中内存布局只与非静态成员变量与虚函数有关。