面向对象编程基础:类的实例化与对象内存模型详解
目录
一、类的实例化详解
1、什么是类的实例化?
2、关键概念
类如同模型或蓝图
实例化创建实际对象
现实类比
2、代码示例
示例说明
3、重要结论(重要!!!)
二、类对象模型与内存布局详解
1、类对象的内存结构(重要!!!)
成员变量存储
成员函数存储
存储效率考量
2、类对象存储模型图示
3、类大小计算规则
基本规则
特殊类大小说明
4、验证示例
类Person的对象内存布局:
5、结构体内存对齐规则详解
基本规则
VS编译器默认值
嵌套结构体规则
对齐示例
6、重要结论
一、类的实例化详解
1、什么是类的实例化?
-  
类实例化是指用类类型在物理内存中创建对象的过程
 -  
类是对对象的一种抽象描述,是一种模型或蓝图
 -  
类限定了成员变量和成员函数,但这些只是声明,不会分配实际内存空间
 
2、关键概念
类如同模型或蓝图
-  
类定义仅规定了类包含哪些成员(属性和方法)
 -  
定义类时并不会分配实际的内存空间
 -  
类似于C语言中定义结构体类型但不创建变量
 
实例化创建实际对象
-  
一个类可以实例化出多个对象,每个对象都是独立的实体,互不干扰
 -  
只有在实例化对象时,系统才会为成员变量分配实际的物理内存空间
 -  
每个对象占用独立的物理内存空间存储其成员变量
 -  
类似于用结构体类型创建变量时分配实际内存
 
现实类比

类实例化对象就像使用建筑设计图建造房子:
-  
类相当于设计图:规划了房间数量、大小和功能,图纸本身(类)没有实体存在
 -  
实例化就是根据图纸建造实际的房子
 -  
对象相当于实际建造的房子,只有建造出的房子(对象)才占用物理空间:可以实际使用和居住
 -  
设计图(类)本身不能住人,只有建好的房子(对象)才能住人
 
2、代码示例
#include<iostream>
using namespace std;class Date {
public:// 初始化日期void Init(int year, int month, int day) {_year = year;_month = month;_day = day;}// 打印日期void Print() {cout << _year << "/" << _month << "/" << _day << endl;}private:// 这里只是声明成员变量,没有实际分配空间int _year;int _month;int _day;
};int main() {// Date类实例化出对象d1和d2Date d1;  // 第一个Date对象Date d2;  // 第二个Date对象// 初始化并打印d1d1.Init(2024, 3, 31);d1.Print();// 初始化并打印d2d2.Init(2024, 7, 5);d2.Print();return 0;
} 
示例说明
-  
类定义阶段:
-  
Date类定义了三个私有成员变量(_year,_month,_day) -  
此时只是声明,没有分配内存空间
 
 -  
 -  
实例化阶段:
-  
Date d1和Date d2语句实际创建了两个Date对象 -  
此时系统为每个对象分配独立的内存空间来存储其成员变量
 
 -  
 -  
对象使用:
-  
每个对象可以独立调用成员函数(
Init()和Print()) -  
对象之间的数据互不干扰(d1和d2存储不同的日期值)
 
 -  
 
3、重要结论(重要!!!)
-  
类定义只是描述了对象的结构和行为
 -  
只有实例化后才会为对象分配内存,才能实际使用类的功能
 -  
每个对象都有自己独立的成员变量存储空间,每个对象都有自己独立的状态(成员变量值)
 -  
类的方法(成员函数)被所有对象共享,不占用对象的内存空间
 -  
类是抽象的模板,对象是具体的实例
 
理解类的实例化是面向对象编程的基础,它体现了"抽象"与"具体"的关系。
二、类对象模型与内存布局详解
1、类对象的内存结构(重要!!!)
类对象在内存中的存储方式遵循以下原则:
成员变量存储
-  
每个对象独立存储自己的成员变量
 -  
不同对象的成员变量值互不影响
 -  
成员变量按照结构体内存对齐规则排列
 
成员函数存储
-  
所有对象共享同一份成员函数代码
 -  
成员函数存储在公共代码区,不占用对象的内存空间
 -  
成员函数编译后成为指令代码,存储在代码段(公共代码区)
 -  
对象中不存储成员函数指针(避免重复存储浪费空间)
 -  
函数调用地址在编译链接时确定,运行时直接通过固定地址调用
 
存储效率考量
-  
若存储函数指针,100个对象将重复存储100次相同指针
 -  
实际只需在代码段保存一份函数实现,所有对象共享
 
2、类对象存储模型图示

3、类大小计算规则
基本规则
-  
类大小等于其所有非静态成员变量大小之和(考虑内存对齐)
 -  
静态成员变量不占用类对象空间(存储在全局区)
 -  
成员函数不占用类对象空间
 
特殊类大小说明
-  
空类大小:
-  
大小为1字节(编译器分配的占位标识,确保对象有唯一地址)
 -  
示例:
class C{};→ 1字节 
 -  
 -  
仅有成员函数的类:
-  
大小同样为1字节
 -  
示例:
class B{void Print(){}};→ 1字节 
 -  
 -  
含成员变量的类:
-  
大小为成员变量总和(考虑对齐)
 -  
示例:
class A{char _ch; int _i;};→ 8字节(1+3填充+4) 
 -  
 
4、验证示例
#include <iostream>
using namespace std;class Person
{
public:void ShowInfo(){cout << _name << "-" << _sex << "-" << _age << endl;}public:char *_name; // 姓名 (指针类型,通常4/8字节)char *_sex;  // 性别 (指针类型,通常4/8字节)int _age;    // 年龄 (通常4字节)
};// 测试类
class A1
{
public:void f1() {}private:int _a;
};
class A2
{
public:void f2() {}
};
class A3
{
};int main()
{cout << "Person size: " << sizeof(Person) << endl; // 通常12/24字节(取决于指针大小)cout << "A1 size: " << sizeof(A1) << endl;         // 4 (int)cout << "A2 size: " << sizeof(A2) << endl;         // 1 (只有成员函数)cout << "A3 size: " << sizeof(A3) << endl;         // 1 (空类)return 0;
} 

类Person的对象内存布局:

5、结构体内存对齐规则详解
基本规则
- 第一个成员变量位于偏移量0处
 - 后续成员对齐到min(成员大小, 编译器默认对齐数)的整数倍地址
 - 结构体总大小为最大对齐数的整数倍
 
VS编译器默认值
- 默认对齐数为8字节
 - 实际对齐数取成员大小和默认对齐数的较小值
 
嵌套结构体规则
- 嵌套结构体对齐到其自身最大对齐数的整数倍处
 - 整体大小是所有最大对齐数(含嵌套结构体)的整数倍
 
对齐示例
struct Example {char a;     // 1字节,偏移0// 3字节填充(因为int要对齐到4)int b;      // 4字节,偏移4short c;    // 2字节,偏移8// 2字节填充(总大小需为4的整数倍)
};              // 总大小:12字节 
6、重要结论
-  
对象只存储成员变量,成员函数存储在公共代码区(代码段)
 -  
类大小计算只考虑成员变量,需遵循内存对齐规则,可能包含填充字节
 -  
空类有1字节占位符,保证每个对象有唯一地址,仅有成员函数的类大小也为1字节(占位标识)
 -  
内存对齐能提高CPU访问效率,但可能增加空间开销(内存对齐牺牲部分空间换取CPU访问效率提升)
 -  
实际开发中应注意类成员排列顺序以优化内存使用
 
