当前位置: 首页 > article >正文

C++从入门到实战(六)类和对象(第二部分)C++成员对象及其实例化,对象大小与this详解

C++从入门到实战(六)类和对象(第二部分)C++成员对象及其实例化,对象大小与this详解

  • 前言
  • 一、类和对象里面成员变量,成员函数是什么
    • 1.1成员变量
    • 1.2成员函数
    • 1.3成员变量、成员函数与局部变量的对比
  • 二、类的实例化
    • 2.1什么是实例化,实例化的概念
    • 2.2类的实例化过程
      • 1. 类的定义
      • 2. 实例化对象
      • 3.初始化对象
      • 4. 访问对象的成员函数
  • 三、对象大小
    • 类对象大小计算示例
  • 四、this指针
    • 4.1 this的原理
    • 4.2 this指针的作用
    • 4.3 this指针的使用规则
  • 总结核心概念速记
        • **知识图谱**
        • **常见问题与误区**
        • **技术对比表**


前言

  • 在上一篇博客中,我们初步认识了 C++ 类的核心概念:类是数据与操作的封装体,通过访问限定符(public/private/protected)实现数据保护,并利用类域(::)明确成员归属

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

本篇我们将深入探索类的实例化过程、对象内存布局以及 this指针的奥秘,解决以下关键问题:

  • 类的成员变量和成员函数在内存中如何存储?
  • 为什么空类的对象大小是 1 字节?
  • 成员函数如何区分不同对象的数据?

一、类和对象里面成员变量,成员函数是什么

  • 首先我们来看一段代码
#include<iostream>
using namespace std;

class Data
{
private:

	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	// Date类实例化出对象d1和d2
	Data d1;
	Data d2;
	d1.Init(2025,3,21);
	d1.Print();
	d2.Init(2024, 3, 21);
	d2.Print();

}
  • 根据上次博客的内容,我们知道class Data 定义了一个名为 Data 的类,private:为私有的,public为共有的
  • 在C++里,什么是成员变量,什么是成员函数呢

1.1成员变量

  • 在 C++ 中,类是一种用户自定义的数据类型,它将数据和操作这些数据的函数封装在一起
  • 类中的数据被称为成员变量,而操作这些数据的函数则被称为成员函数
  • 成员变量是类的属性,用于存储对象的状态信息。
  • 在类定义中声明成员变量,但只有在创建类的对象时,才会为这些成员变量分配内存空间成员变量可以有不同的访问权限,如 private、protected 和 public
  • 例如上面代码里面的
private:

	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;

1.2成员函数

  • 成员函数是类的行为,用于对成员变量进行操作成员函数可以访问和修改类的成员变量,也可以调用其他成员函数。成员函数同样可以有不同的访问权限,其调用规则与成员变量的访问规则类似。
  • 例如上面代码里面的
 // 成员函数,用于初始化对象的成员变量
    void Init(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // 成员函数,用于打印对象的日期信息
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
  • 代码中成员变量和成员函数的详细信息
类型名称访问权限作用
成员变量_yearprivate存储日期的年份
成员变量_monthprivate存储日期的月份
成员变量_dayprivate存储日期的日
成员函数Init(int year, int month, int day)public初始化对象的 _year_month_day 成员变量
成员函数Print()public输出对象的日期信息

1.3成员变量、成员函数与局部变量的对比

对比项成员变量成员函数局部变量
定义位置类的定义内部类的定义内部函数或代码块内部
作用域整个类的对象整个类的对象定义它的函数或代码块内部
生命周期与对象的生命周期相同与对象的生命周期相同从定义处开始,到函数或代码块结束
内存分配在对象创建时分配内存存储在代码段,不随对象分配内存在栈上分配内存
访问权限可以有 privateprotectedpublic 等访问权限可以有 privateprotectedpublic 等访问权限无访问权限概念,只能在其作用域内访问
使用目的存储对象的状态信息实现对象的行为和操作临时存储函数或代码块内的数据

二、类的实例化

  • 实例化是面向对象编程的核心操作,它将抽象的类转化为可操作的实体,支持封装、复用和多态等特性。

2.1什么是实例化,实例化的概念

实例化概念

  • 实例化是⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
  • 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间

咱们来通俗地讲讲实例化是啥

  • 你可以把想象成一张建筑设计图
  • 这张设计图上详细规划好了房子有几个房间,每个房间的大小、功能等情况,但是它仅仅是一张图,还没有实实在在的房子建起来,也不能让人住进去。

在这里插入图片描述

  • 在编程里,就是这样一种抽象的描述,它规定了有哪些成员变量,不过这些成员变量只是声明出来了,并没有给它们分配实际的存储空间。
  • 实例化呢,就好比按照这张设计图去建造房子。用类型在物理内存里创建对象的这个过程,就是实例化。当我们实例化出对象时,就相当于房子建好了,这时候就会给成员变量分配实际的空间,对象就可以存储数据啦。

在这里插入图片描述

  • 总而言之
  • 类 ≈ 房子设计图
  • 实例化 ≈ 按图纸建造出真实房子
  • 对象 ≈ 建好的真实房子

2.2类的实例化过程

  • 我们以下面的这段代码为例,来讲一下类的实例化过程
#include<iostream>
using namespace std;

class Data
{
private:

	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	// Date类实例化出对象d1和d2
	Data d1;
	Data d2;
	d1.Init(2025,3,21);
	d1.Print();
	d2.Init(2024, 3, 21);
	d2.Print();

}

1. 类的定义

  • 首先我们需要对类进行定义
class Data
{
private:
    // 这里只是声明,没有开空间
    int _year;
    int _month;
    int _day;

public:
    void Init(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

2. 实例化对象

  • 接着在main函数里面进行实例化对象
  • 在 main 函数中,使用 Data 类实例化出了两个对象 d1 和 d2。实例化的过程实际上就是根据 Data 类这个模板
  • 在内存中为对象 d1 和 d2 分配空间,并且每个对象都有自己独立的成员变量副本。
Data d1;
Data d2;

3.初始化对象

  • 实例化对象之后,需要对对象的成员变量进行初始化。
  • 在代码中,通过调用 Init 成员函数,为 d1 和 d2 的成员变量赋予具体的值。
d1.Init(2025, 3, 21);
d2.Init(2024, 3, 21);

4. 访问对象的成员函数

  • 对象初始化完成后,就可以调用对象的成员函数来执行特定的操作。在代码中,调用了 Print 成员函数,用于输出对象的日期信息。
    在这里插入图片描述

三、对象大小

C++ 里类实例化的对象要遵循内存对齐规则,目的是提升内存访问效率,具体规则如下:

  • 首个成员:首个成员存于结构体偏移量为 0 的地址处
  • 其他成员:其他成员变量要对齐到某个对齐数的整数倍地址处。对齐数是编译器默认对齐数与该成员大小的较小值,像 VS 中默认对齐数是 8
  • 结构体总大小:结构体总大小需是最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
  • 嵌套结构体:若嵌套了结构体,嵌套的结构体要对齐到自身最大对齐数的整数倍处,结构体整体大小是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

类对象大小计算示例

#include<iostream>
using namespace std;

class A
{
public:
	void Print()
	{
		cout << _ch << endl;
	}
private:
	char _ch;
	int _i;
};
class B
{
public:
	void Print()
	{
		//...
	}
};

class C
{
};

int main()
{
	A a;
	B b;
	C c;
	cout << sizeof(a) << endl; 
	cout << sizeof(b) << endl;
	cout << sizeof(c) << endl;
}

在这里插入图片描述

  • 可以发现:
    • A类有成员变量char _ch和int _i,其对象大小按内存对齐规则计算
    • B类和C类没有成员变量,但它们的对象大小是 1 字节,这是为了占位,以此标识对象存在

四、this指针

#include<iostream>
using namespace std;

class Data
{
private:

	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	// Date类实例化出对象d1和d2
	Data d1;
	Data d2;
	d1.Init(2025,3,21);
	d1.Print();
	d2.Init(2024, 3, 21);
	d2.Print();

}
  • 在Date类里有Init和Print这两个成员函数,在函数体中并没有对不同对象加以区分。
  • 但当对象d1调用Init和Print函数时,函数是怎么知道要访问的是d1对象的数据,而不是d2对象的数据呢?C++ 引入了隐含的this指针来解决这个问题

4.1 this的原理

  • 编译器在编译类的成员函数时,会默认在形参的第一个位置添加一个当前类类型的指针,也就是this指针。
  • 例如,Date类的Init函数,其真实的原型其实是
void Init(Date* const this, int year, int month, int day) 

4.2 this指针的作用

  • 在类的成员函数里访问成员变量,本质上都是通过this指针来访问的。
  • 比如在Init函数里给_year赋值,实际上是this->_year = year,只是通常可以省略this->,直接写成_year = year

4.3 this指针的使用规则

实参和形参位置

  • C++ 规定不能在实参和形参的位置显式地写this指针,编译器在编译时会自动处理。例如在main函数里调用d1.Init(2024, 3, 31),这里不需要写成d1.Init(&d1, 2024, 3, 31) 。
    函数体内
  • 可以在函数体内显式地使用this指针。比如在Init函数里,可以写成this->_month = month; 。不过需要注意的是,this指针是一个常量指针,不能对其进行赋值操作,像this = nullptr; 这样的代码会编译报错。

总结核心概念速记

类对象的内存本质 = 成员变量集合 + 内存对齐 + this指针隐式传递

一、成员变量与成员函数

对比项成员变量成员函数局部变量
定义位置类体内类体内函数/代码块内
内存分配随对象实例化分配存储于代码段(共享)栈上分配
作用域类作用域类作用域局部作用域
生命周期与对象共存亡程序运行期间始终存在随函数调用结束销毁

二、类的实例化与对象大小

  1. 实例化本质

    • 类 → 设计图,对象 → 实体房屋
    • 每个对象独立拥有成员变量副本,成员函数共享代码段
  2. 内存对齐规则(VS 编译器):

    • 第⼀个成员起始地址为 0
    • 其他成员对齐到 min(成员大小, 默认对齐数8) 的整数倍
    • 总大小为 最大对齐数的整数倍
    • 空类大小为 1 字节(占位符,标识对象存在)

三、this指针的作用

  • 核心作用区分成员函数操作的对象
  • 隐式传递:编译器自动为成员函数添加 this 形参
    // 代码写法(隐式)
    void Init(int year) { _year = year; }
    
    // 实际原型(显式)
    void Init(Date* const this, int year) { this->_year = year; }
    
  • 使用规则
    • 不能显式修改 this 指针(this = nullptr 编译错误)
    • 可显式使用 this->成员 访问变量
知识图谱
类和对象(下)  
├─ 成员构成  
│  ├─ 成员变量(数据)  
│  └─ 成员函数(操作)→ 共享代码段  
├─ 实例化  
│  ├─ 对象 = 成员变量集合 + 对齐填充  
│  └─ 空类大小 = 1 字节(占位)  
├─ 内存对齐  
│  ├─ 规则:偏移量、对齐数、总大小  
│  └─ 目的:提升 CPU 访问效率  
└─ this 指针  
   ├─ 隐式参数:指向调用对象  
   └─ 作用:区分多对象调用时的数据归属  
常见问题与误区

Q1:成员函数可以不定义吗?

  • A:可以声明为 = delete(禁止调用),或完全不定义(链接错误)。

Q2:为什么空类对象大小不是 0?

  • A:C++ 要求每个对象必须有唯一地址,1 字节用于占位。

Q3:this 指针是存储在对象里吗?

  • A:不是!this 是编译器隐含的形参,通过寄存器传递(如 ecx),不占用对象内存。
技术对比表

面向过程 vs 面向对象(类的优势)

维度面向过程(C)面向对象(C++类)
数据管理全局变量,易冲突封装为类成员,数据安全
代码复用函数复制粘贴继承/多态,高内聚低耦合
可维护性牵一发而动全身类接口隔离,修改局部化

以上就是这篇博客的全部内容,下一篇我们将继续探索更多精彩内容。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述


http://www.kler.cn/a/595366.html

相关文章:

  • 腾讯位置服务点标记
  • 【jQuery】常用API
  • idea中ctrl + shift +f失效的问题
  • LeetCode热题100JS(59/100)第十一天|46|78|17|39|22
  • Linux驱动开发-①中断②阻塞、非阻塞IO和异步通知
  • Python 爬取 1688 商品详情接口数据全攻略
  • iStoreOS软路由对硬盘格式化分区(转化ext4)
  • Java实现十大经典排序算法详解
  • Linux--软硬链接、动静态库
  • 内核ICMP协议分析
  • 使用excel.EasyExcel实现导出有自定义样式模板的excel数据文件,粘贴即用!!!
  • C# 项目06-计算程序运行时间
  • mysql 对json的处理?
  • deepseek使用记录25——当反思失效了
  • AI工具如何改变编程学习?Trae IDE与Claude 3.5的实践案例
  • 使用AI一步一步实现若依(18)
  • SpringBoot整合MQTT最详细版(亲测有效)
  • 基于springboot的教师工作量管理系统(031)
  • 同旺科技USB to I2C 适配器 ---- 指令循环发送功能
  • Linux系统——keepalived安装与部署