面向对象编程基础:类的实例化与对象内存模型详解

目录

一、类的实例化详解

1、什么是类的实例化?

2、关键概念

类如同模型或蓝图

实例化创建实际对象

现实类比

2、代码示例

示例说明

3、重要结论(重要!!!)

二、类对象模型与内存布局详解

1、类对象的内存结构(重要!!!)

成员变量存储

成员函数存储

存储效率考量

2、类对象存储模型图示

3、类大小计算规则

基本规则

特殊类大小说明

4、验证示例

类Person的对象内存布局:

5、结构体内存对齐规则详解

基本规则

VS编译器默认值

嵌套结构体规则

对齐示例

6、重要结论


一、类的实例化详解

1、什么是类的实例化?

  • 类实例化是指用类类型在物理内存中创建对象的过程

  • 类是对对象的一种抽象描述,是一种模型或蓝图

  • 类限定了成员变量和成员函数,但这些只是声明,不会分配实际内存空间

2、关键概念

类如同模型或蓝图

  • 类定义仅规定了类包含哪些成员(属性和方法)

  • 定义类时并不会分配实际的内存空间

  • 类似于C语言中定义结构体类型但不创建变量

实例化创建实际对象

  • 一个类可以实例化出多个对象,每个对象都是独立的实体,互不干扰

  • 只有在实例化对象时,系统才会为成员变量分配实际的物理内存空间

  • 每个对象占用独立的物理内存空间存储其成员变量

  • 类似于用结构体类型创建变量时分配实际内存

现实类比

类实例化对象就像使用建筑设计图建造房子:

  • 相当于设计图:规划了房间数量、大小和功能,图纸本身(类)没有实体存在

  • 实例化就是根据图纸建造实际的房子

  • 对象相当于实际建造的房子,只有建造出的房子(对象)才占用物理空间:可以实际使用和居住

  • 设计图(类)本身不能住人,只有建好的房子(对象)才能住人

2、代码示例

cpp 复制代码
#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和d2
    Date d1;  // 第一个Date对象
    Date d2;  // 第二个Date对象
    
    // 初始化并打印d1
    d1.Init(2024, 3, 31);
    d1.Print();
    
    // 初始化并打印d2
    d2.Init(2024, 7, 5);
    d2.Print();
    
    return 0;
}

示例说明

  1. 类定义阶段

    • Date类定义了三个私有成员变量(_year, _month, _day

    • 此时只是声明,没有分配内存空间

  2. 实例化阶段

    • Date d1Date d2语句实际创建了两个Date对象

    • 此时系统为每个对象分配独立的内存空间来存储其成员变量

  3. 对象使用

    • 每个对象可以独立调用成员函数(Init()Print()

    • 对象之间的数据互不干扰(d1和d2存储不同的日期值)

3、重要结论(重要!!!)

  • 类定义只是描述了对象的结构和行为

  • 只有实例化后才会为对象分配内存,才能实际使用类的功能

  • 每个对象都有自己独立的成员变量存储空间,每个对象都有自己独立的状态(成员变量值)

  • 类的方法(成员函数)被所有对象共享,不占用对象的内存空间

  • 类是抽象的模板,对象是具体的实例

理解类的实例化是面向对象编程的基础,它体现了"抽象"与"具体"的关系。


二、类对象模型与内存布局详解

1、类对象的内存结构(重要!!!)

类对象在内存中的存储方式遵循以下原则:

成员变量存储

  • 每个对象独立存储自己的成员变量

  • 不同对象的成员变量值互不影响

  • 成员变量按照结构体内存对齐规则排列

成员函数存储

  • 所有对象共享同一份成员函数代码

  • 成员函数存储在公共代码区,不占用对象的内存空间

  • 成员函数编译后成为指令代码,存储在代码段(公共代码区)

  • 对象中不存储成员函数指针(避免重复存储浪费空间)

  • 函数调用地址在编译链接时确定,运行时直接通过固定地址调用

存储效率考量

  • 若存储函数指针,100个对象将重复存储100次相同指针

  • 实际只需在代码段保存一份函数实现,所有对象共享

2、类对象存储模型图示

3、类大小计算规则

基本规则

  • 类大小等于其所有非静态成员变量大小之和(考虑内存对齐)

  • 静态成员变量不占用类对象空间(存储在全局区)

  • 成员函数不占用类对象空间

特殊类大小说明

  1. 空类大小

    • 大小为1字节(编译器分配的占位标识,确保对象有唯一地址)

    • 示例:class C{}; → 1字节

  2. 仅有成员函数的类

    • 大小同样为1字节

    • 示例:class B{void Print(){}}; → 1字节

  3. 含成员变量的类

    • 大小为成员变量总和(考虑对齐)

    • 示例:class A{char _ch; int _i;}; → 8字节(1+3填充+4)

4、验证示例

cpp 复制代码
#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字节
  • 实际对齐数取成员大小和默认对齐数的较小值

嵌套结构体规则

  • 嵌套结构体对齐到其自身最大对齐数的整数倍处
  • 整体大小是所有最大对齐数(含嵌套结构体)的整数倍

对齐示例

cpp 复制代码
struct Example {
    char a;     // 1字节,偏移0
                // 3字节填充(因为int要对齐到4)
    int b;      // 4字节,偏移4
    short c;    // 2字节,偏移8
                // 2字节填充(总大小需为4的整数倍)
};              // 总大小:12字节

6、重要结论

  1. 对象只存储成员变量,成员函数存储在公共代码区(代码段)

  2. 类大小计算只考虑成员变量,需遵循内存对齐规则,可能包含填充字节

  3. 空类有1字节占位符,保证每个对象有唯一地址,仅有成员函数的类大小也为1字节(占位标识)

  4. 内存对齐能提高CPU访问效率,但可能增加空间开销(内存对齐牺牲部分空间换取CPU访问效率提升)

  5. 实际开发中应注意类成员排列顺序以优化内存使用

相关推荐
跟着珅聪学java1 分钟前
Java 发送 HTTP POST请求教程
开发语言·lua
重生之我是Java开发战士2 分钟前
【C语言】动态内存管理详解
c语言·开发语言·算法
Hello.Reader43 分钟前
Rust ⽣成 .wasm 的极致瘦⾝之道
开发语言·rust·wasm
稚肩43 分钟前
DHCP 握手原理
开发语言·网络协议
赵_|大人44 分钟前
Qt 自动无法加载数据库为空
开发语言·数据库·qt
fbbqt1 小时前
Go语言 逃 逸 分 析
开发语言·golang
啊阿狸不会拉杆1 小时前
《算法导论》第 2 章 - 算法基础
数据结构·c++·算法·排序算法
q567315231 小时前
C语言编写轻量爬虫工具
c语言·开发语言·爬虫
啊阿狸不会拉杆1 小时前
《算法导论》第 4 章 - 分治策略
开发语言·数据结构·c++·算法·排序算法