C++从入门到实战(六)类和对象(第二部分)C++成员对象及其实例化,对象大小与this详解
- 前言
- 一、类和对象里面成员变量,成员函数是什么
- 二、类的实例化
-
- 2.1什么是实例化,实例化的概念
- 2.2类的实例化过程
-
- [1. 类的定义](#1. 类的定义)
- [2. 实例化对象](#2. 实例化对象)
- 3.初始化对象
- [4. 访问对象的成员函数](#4. 访问对象的成员函数)
- 三、对象大小
- 四、this指针
-
- [4.1 this的原理](#4.1 this的原理)
- [4.2 this指针的作用](#4.2 this指针的作用)
- [4.3 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 字节?
- 成员函数如何区分不同对象的数据?
一、类和对象里面成员变量,成员函数是什么
- 首先我们来看一段代码
cpp
#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
- 例如上面代码里面的
cpp
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
1.2成员函数
- 成员函数是类的行为,用于对成员变量进行操作。成员函数可以访问和修改类的成员变量,也可以调用其他成员函数。成员函数同样可以有不同的访问权限,其调用规则与成员变量的访问规则类似。
- 例如上面代码里面的
cpp
// 成员函数,用于初始化对象的成员变量
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 成员函数,用于打印对象的日期信息
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
- 代码中成员变量和成员函数的详细信息
类型 | 名称 | 访问权限 | 作用 |
---|---|---|---|
成员变量 | _year |
private |
存储日期的年份 |
成员变量 | _month |
private |
存储日期的月份 |
成员变量 | _day |
private |
存储日期的日 |
成员函数 | Init(int year, int month, int day) |
public |
初始化对象的 _year 、_month 和 _day 成员变量 |
成员函数 | Print() |
public |
输出对象的日期信息 |
1.3成员变量、成员函数与局部变量的对比
对比项 | 成员变量 | 成员函数 | 局部变量 |
---|---|---|---|
定义位置 | 类的定义内部 | 类的定义内部 | 函数或代码块内部 |
作用域 | 整个类的对象 | 整个类的对象 | 定义它的函数或代码块内部 |
生命周期 | 与对象的生命周期相同 | 与对象的生命周期相同 | 从定义处开始,到函数或代码块结束 |
内存分配 | 在对象创建时分配内存 | 存储在代码段,不随对象分配内存 | 在栈上分配内存 |
访问权限 | 可以有 private 、protected 、public 等访问权限 |
可以有 private 、protected 、public 等访问权限 |
无访问权限概念,只能在其作用域内访问 |
使用目的 | 存储对象的状态信息 | 实现对象的行为和操作 | 临时存储函数或代码块内的数据 |
二、类的实例化
- 实例化是面向对象编程的核心操作,它将抽象的类转化为可操作的实体,支持封装、复用和多态等特性。
2.1什么是实例化,实例化的概念
实例化概念 :
- 实例化是⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
- 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间
咱们来通俗地讲讲实例化是啥。
- 你可以把类想象成一张建筑设计图。
- 这张设计图上详细规划好了房子有几个房间,每个房间的大小、功能等情况,但是它仅仅是一张图,还没有实实在在的房子建起来,也不能让人住进去。

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

- 总而言之
- 类 ≈ 房子设计图
- 实例化 ≈ 按图纸建造出真实房子
- 对象 ≈ 建好的真实房子
2.2类的实例化过程
- 我们以下面的这段代码为例,来讲一下类的实例化过程
cpp
#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. 类的定义
- 首先我们需要对类进行定义
cpp
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 分配空间,并且每个对象都有自己独立的成员变量副本。
cpp
Data d1;
Data d2;
3.初始化对象
- 实例化对象之后,需要对对象的成员变量进行初始化。
- 在代码中,通过调用 Init 成员函数,为 d1 和 d2 的成员变量赋予具体的值。
cpp
d1.Init(2025, 3, 21);
d2.Init(2024, 3, 21);
4. 访问对象的成员函数
- 对象初始化完成后,就可以调用对象的成员函数来执行特定的操作。在代码中,调用了 Print 成员函数,用于输出对象的日期信息。
三、对象大小
C++ 里类实例化的对象要遵循内存对齐规则,目的是提升内存访问效率,具体规则如下:
- 首个成员:首个成员存于结构体偏移量为 0 的地址处。
- 其他成员:其他成员变量要对齐到某个对齐数的整数倍地址处。对齐数是编译器默认对齐数与该成员大小的较小值,像 VS 中默认对齐数是 8
- 结构体总大小:结构体总大小需是最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
- 嵌套结构体:若嵌套了结构体,嵌套的结构体要对齐到自身最大对齐数的整数倍处,结构体整体大小是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
类对象大小计算示例
cpp
#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指针
cpp
#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函数,其真实的原型其实是
cpp
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指针隐式传递
一、成员变量与成员函数
对比项 | 成员变量 | 成员函数 | 局部变量 |
---|---|---|---|
定义位置 | 类体内 | 类体内 | 函数/代码块内 |
内存分配 | 随对象实例化分配 | 存储于代码段(共享) | 栈上分配 |
作用域 | 类作用域 | 类作用域 | 局部作用域 |
生命周期 | 与对象共存亡 | 程序运行期间始终存在 | 随函数调用结束销毁 |
二、类的实例化与对象大小
-
实例化本质:
- 类 → 设计图,对象 → 实体房屋
- 每个对象独立拥有成员变量副本,成员函数共享代码段
-
内存对齐规则(VS 编译器):
- 第⼀个成员起始地址为 0
- 其他成员对齐到
min(成员大小, 默认对齐数8)
的整数倍 - 总大小为 最大对齐数的整数倍
- 空类大小为 1 字节(占位符,标识对象存在)
三、this
指针的作用
-
核心作用 :区分成员函数操作的对象
-
隐式传递 :编译器自动为成员函数添加
this
形参cpp// 代码写法(隐式) 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
|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |
