12.你知道空类的大小是多少吗?
12.你知道空类的大小是多少吗?
1.C++空类的大小不为0,不同编译器设置不一样,vs设置为1
2.C++标准指出,不允许一个对象(当然包括类对象)的大小为0,不同的对象不能具有相同的地址
3.带有虚函数的C++类大小不为1,因为每一个对象会有一个vptr指向虚函数表,具体大小根据指针大小确定
4.C++中要求对于类的每个实例都必须有独一无二的地址,那么编译器自动为空类分配一个字节大小,这样便保证了每个实例均有独一无二的内存地址
类的对象存储空间?
- 非静态成员的数据类型大小之和。
- 编译器加入的额外成员变量(如指向虚函数表的指针)。
- 为了边缘对齐优化加入的padding。
空类(无非静态数据成员)的对象的size为1, 当作为基类时, size为0。
C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的。
首先,类计算大小与C语言中struct计算大小的规则是一样的,都遵循内存对齐原则
类的属性与方法是分开存储的,内存给类实例化出的对象开辟空间时只开辟成员变量所占用的空间,类中的所有成员函数全部都会被放入公共代码区,并且会被此类域修饰。也就是说成员函数是不占空间的,在计算类实例化对象的空间时,只计算成员变量的大小。
这样的好处:可以避免每个对象都保存着相同的代码,造成空间浪费。参考:[(8条消息) 【C++】之类和对象 - 概念与存储空间_类的对象存储空间_Hello_World_213的博客-CSDN博客]
一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一个成员变量的地址。(在定义类对象的同时这些成员变量也就被定义了),举个例子:
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
this->age = 23;
}
void printAge()
{
cout << this->age <<endl;
}
~Person(){}
public:
int age;
};
int main()
{
Person p;
cout << "对象地址:"<< &p <<endl;
cout << "age地址:"<< &(p.age) <<endl;
cout << "对象大小:"<< sizeof(p) <<endl;
cout << "age大小:"<< sizeof(p.age) <<endl;
return 0;
}
//输出结果
//对象地址:0x7fffec0f15a8
//age地址:0x7fffec0f15a8
//对象大小:4
//age大小:4
1.空类占用1个字节的存储空间
问题:C++类的成员函数在定义对象前分配了存储空间吗?[(8条消息) c++类的成员函数在定义对象前分配了存储空间吗?_爱吃甜食_的博客-CSDN博客]
对于一般的类(非静态)来说,在定义类但还未创建对象的时候,类的所有成员(包括变量和函数)都占用着内存空间(准确地说占用着指令代码区),但不占用堆栈空间
而创建对象的时候,会根据对象的类型占用堆栈的空间(用传统模式创建对象会占用栈空间,用引用+new模式创建对象会占用堆空间,同时引用会保存在栈里)
对于静态(static)类来说,静态类是不能实例化创建对象的,所有的成员都是静态成员,也需要占用内存空间,但不在堆栈里,而是在内存的静态/全局区(这个区域用于存放所有的全局成员和静态成员)。
声明一个空类做测试:
#include <iostream>
using namespace std;
class A
{
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
输出:
1
原因:类中没有任何成员变量,占用的存储大小本该为0,但是如果是0,类实例化出的对象就不会在内存上占用空间,没有地址,也就无法区分这些对象。为了解决这个问题,编译器会给空类隐含加一个字节,保证用此类定义的对象都有一个独一无二的地址。
2.类的成员函数(非虚函数)不占用类的存储空间
在1的基础上增加类的成员函数做测试:
#include <iostream>
using namespace std;
class A
{
public:
A() {}
~A() {}
int func1() { return 0; }
int func2() { return 0; }
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
输出:
1
原因:成员函数(包括构造和析构函数)编译后存放在代码区,不占用类的存储空间。
3.类的静态成员变量不占用类的存储空间
在2的基础上定义一个静态成员变量做测试:
#include <iostream>
using namespace std;
class A
{
private:
static int n;
public:
A() {}
~A() {}
int func1() { return 0; }
int func2() { return 0; }
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
输出:
1
原因:类里的静态成员变量在全局数据区中分配空间,不占用类的存储空间,全局只有一份,不会随着类的实例化存储在每个对象里。
静态成员函数的存放问题:静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。
就像我前面提到的,所有函数都存放在代码区,静态函数也不例外。所有有人一看到 static 这个单词就主观的认为是存放在全局数据区,那是不对的。
4.类中的虚函数占用类的存储空间,但所占的空间不会随着虚函数的个数增长
在3的基础上定义3个虚成员函数做测试:
#include <iostream>
using namespace std;
class A
{
private:
static int n;
public:
A() {}
~A() {}
int func1() { return 0; }
int func2() { return 0; }
virtual int func3() { return 0; }
virtual int func4() { return 0; }
virtual int func5() { return 0; }
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
输出:
1
原因:有虚成员函数的类实例化的时候,会有一个指针vptr指向虚函数表vtbl,而虚函数表里就存储着类中定义的所有虚函数。所以虚函数引起的额外内存占用就是指针vptr占用内存的大小,对于64位系统来讲,指针占用的内存空间为8,所以这里的结果是8。而且不管定义几个虚函数,额外的内存占用都只是vptr指针所占空间的大小。
总结:类对象所占用的空间只由以下3部分组成:
(1)类的非静态成员变量
(2)编译器所做的数据对齐处理
(3)虚函数带来的额外开销
其他类内定义的成员函数,静态成员变量等,均不占用类对象的存储空间。
参考资料来源于:[(8条消息) C++类对象到底占多大存储空间呢_c++对象占用内存_haowunanhai的博客-CSDN博客]