本章介绍一下C++类的内存方面的知识
一个类包含成员对象时,该类成为封闭类。
成员对象意思是一个类的成员变量是另一个类的对象
封闭类对象的生成过程:
成员对象的构造函数调用顺序与其在封闭类中的声明顺序有关,与初始值列表无关
封闭类对象的消亡过程:
参考:c++——成员对象和封闭类
类中的普通成员只能通过对象访问;
类中的静态成员可以通过对象、也可以通过类访问;
类中typedef
定义的类型只能通过类进行访问。
//c++作用域的研究
#include
#include
using namespace std;
class A{
public:
typedef int INT;
static void show(){cout<<"show()"<<endl;}
void work(){cout<<"work()"<<endl;}
};
int main(){
A a;
a.work();
a.show();
A::show();
A::INT n=10;
cout<<n<<endl;
//a.INT m=10;报错
return 0;
}
类的成员函数定义在外部,一旦遇到类名,剩余部分都在类的作用域之内,如果该函数的返回值类型定义在类的内部则需要指明那个类定义过他。例如上面的代码修改为:
class A{
private:
int m;
public:
typedef int INT;
void show(INT N);
INT work();
};
void A::show(INT N){//A作用域内的INT
cout<<"show()"<<endl;
m=100;//A作用域内的m
}
A::INT A::work(INI P){
cout<<"work()"<<endl;
return p;
}
参考:类其实也是一种作用域
我们首先要明确C++类存在的部分:
对于对象而言,其内存模型不包含函数(在上一章中介绍过C++ 成员函数的编译过程),函数单独存放,通过地址调用对象,其内存模型和占用的空间大小受以下几个因素影响:
派生类如果有和基类重名的成员,调用时会覆盖掉基类的成员,也可以显示声明出基类成员,在派生类对象的内存中,基类的成员变量仍然会占有内存。派生类对象继承基类时,会把自身的虚函数加入到第一个基类的虚函数表中;如果重写了基类的虚函数,就会把对应的虚函数表中的地址替换成派生类的虚函数地址。虚函数表位于只读数据段(.rodata),也就是C++内存模型的常量区,虚表指针在对象的内存中。
主要参考这篇C++ 对象内存模型文章,但是我发现这篇文章有些地方说法是不对的,下面的实验没有改变,我从新组织了一下讲解。推荐看看下面四篇博客。
C++ 继承详解
C++ 虚函数详解
虚析构函数详解
C++中的纯虚函数
C++中的虚函数表和虚函数在内存中的位置
对于没有任何函数成员和数据成员的类,编译器给予一个字节的标志位
//无成员的类
class A
{
};
int main()
{
cout << sizeof(A) << endl;
A a;
cout << sizeof(a) << endl;
}
//输出
1
1
仅包含虚函数的类,其对象增加一个虚表指针(32位机器是4,64位机器是8),指向虚函数表。
class A{
public:
virtual void base(){
cout << 1 << endl;
}
};
int main(){
cout << sizeof(A) << endl;
A a;
cout << sizeof(a) << endl;
}
//输出结果
8
8
包含普通成员的类按成员变量顺序以及字节对齐存放在内存中,涉及继承则基类在前,派生类在后。下面例子为单一继承,可以看到基类首先会包含一个虚表指针,成员变量按字节对齐的方式最终为16,派生类继承了基本的成员(派生类的虚函数放在了基本的虚函数表中,所有派生类对象只有一个虚表指针),加上自身的成员变量,按字节对齐(基类在前,派生类在后)最后为24
class A{
private:
char c;
int i;
public:
virtual void base()
{
cout << 1 << endl;
}
};
class B :public A{
private:
char c;
int i;
public:
virtual void drive()
{
cout << 2 << endl;
}
};
int main(){
cout << sizeof(A) << endl;
A a;
cout << sizeof(a) << endl;
B b;
cout << sizeof(B) << endl;
cout << sizeof(b) << endl;
}
//输出
16
16
24
24
如果类不包含虚函数,则按常规的字节对齐方式进行对齐。double c[0]虽然不占空间,但通知编译器以8字节对齐
class C{
private:
char cr;
short b;
double c[0];
};
int main(){
cout<<sizeof(C)<<endl;
C c;
cout<<sizeof(c)<<endl;
}
//输出
8
8
存在多个基类时,派生类对象将包含多个虚函数表(两个基类就有两个虚函数表),每一张存放自身虚函数与从对应基类继承过来的虚函数。其派生类内存占用为从两个基类继承而来的数据成员+自身数据成员+2个虚表指针:
(4+4)x2+4+4+(2x8)=40;
class A{
private:
char c;
int i;
public:
virtual void base(){
cout << 1 << endl;
}
};
class B{
private:
char c;
int i;
public:
virtual void base(){
cout << 1 << endl;
}
};
class C :public A,public B{
private:
char c;
int i;
public:
virtual void drive(){
cout << 2 << endl;
}
};
int main(){
cout << sizeof(C) << endl;
C c;
cout << sizeof(c) << endl;
}
//输出
40
40
本来还想写写继承、虚函数之类的,上面推荐的博客已经写的特别完善了,接下来我就看看书,没什么补充就接着看其他内容了。