类和对象(二)

在上一篇文章中,我们已经初步认识了类和对象的基本概念。今天,我们将深入探讨 C++ 中一个非常关键且容易混淆的概念------this 指针。理解 this 指针,是掌握类成员函数内部工作机制的重要一步。本文将通过一个具体的 Date 类示例,带你直观地理解 this 指针的来龙去脉。

this指针

我们先来看一个类:

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;
};

在类Date中有两个函数Init和Print,我们创建Date类的两个对象,比如d1和d2,这两个对象都要访问这些成员函数,那么它们是如何准确地找到并访问到自己对象中的内容的呢?

cpp 复制代码
int main()
{
	Date d1, d2;
	d1.Init(2026, 9, 11);
	d2.Init(2026, 10, 1);
	d1.Print();//打印d1中的日期
	d2.Print();//打印d2中的日期
	return 0;
}

在编译时,编译器会为每个成员函数的参数部隐式地添加一个 this 指针,该指针在参数列表第一个位置,为类类型,且const修饰放在 * 右边,以防修改指针指向,所以成员函数的参数列表实际上是这样的:

cpp 复制代码
void Init(int year, int month, int day,Date* const this)

而我们传过去的参数会隐式地多一个,是对象的地址。

cpp 复制代码
	d1.Init(&d1);

该 this 指针用于接收对象地址,然后去访问对象内部的成员变量,所以成员函数实际上是这样的:

cpp 复制代码
void Init(int year, int month, int day,Date* const this)
{
	this->_year = year;
	this->_month = month;
	this->_day = day;
}

编译器不允许我们显示地在形参和实参部分写this指针,但允许在成员函数内部使用。

cpp 复制代码
void Print()
{
	cout << this->_year << '/' << _month << '/' << this->_day << endl;
}

类的默认成员函数

用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。

一个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解一下即可。其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个我们后面再讲解。默认成员函数很重要,也比较复杂,我们要从两个方面去学习:

  • 第一:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求。
  • 第二:编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现?

构造函数

构造函数是类的一种特殊的成员函数,它的名字与类名相同,没有返回值(连 void 都不写),在对象创建时由编译器自动调用,用于完成对象的初始化工作。

以我们前面定义的 Date 类为例,如果不写构造函数,那么创建对象后成员变量 _year、_month、_day 都是未初始化的随机值,这显然是不安全的。因此我们需要自己实现一个构造函数来初始化这些成员:

cpp 复制代码
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

有了构造函数之后,创建对象时就可以直接传入初始值:

cpp 复制代码
int main()
{
	Date d1(2026, 9, 11);  // 调用构造函数,初始化 d1
	Date d2(2026, 10, 1);  // 调用构造函数,初始化 d2
	return 0;
}

关于构造函数,有几点需要特别注意:

  • 构造函数支持重载,即一个类可以有多个构造函数,只要参数列表不同即可。
  • 构造函数可以带默认参数,方便在部分场景下省略某些实参。
  • 如果用户没有显式定义任何构造函数,编译器会自动生成一个无参的默认构造函数;但一旦用户定义了构造函数,编译器就不会再自动生成了。
  • 构造函数没有返回值 ,也不能写 void。
  • 构造函数在对象创建时自动调用,不需要我们手动调用。

构造函数是类中最重要、最常用的默认成员函数之一,掌握好它,是写出健壮 C++ 类的基础。

我们自己写的无参构造函数、全缺省的构造函数还有编译器自动生成的都叫做默认构造函数。简单来说就是不用传参数就可以调用的构造函数。

编译器自动生成的构造函数,对于内置类型来说,一般会生成随机值;对于自定义类型则会调用对应的构造函数来初始化。

析构函数

构造函数是对实例化对象进行初始化的函数,析构函数就是用于完成对象销毁时的清理工作,在对象销毁时编译器自动调用。

析构函数与构造函数对应,它也不具有返回值(不用写void),函数名为类名前加~。

cpp 复制代码
class Date
{
public:
  Date(int year = 1,int month = 1,int day = 1)
  {
  _year =year;
  _month = month;
  _day = day;
  }
  //析构函数
  ~Date()
  {
  _year = 0;
  _month = 0;
  _day = 0;
  }
private:
  int _year;
  int _month;
  int _day;
};

关于析构函数需要注意的有以下几点:

  • 一个类只能有一个析构函数 ,因为析构函数的作用相当于Destory函数,完成资源清理。
  • 当我们没有显示地定义析构函数时,编译器会自动生成析构函数。对于内置类型,不会做任何处理;对于自定义类型,则会调用对应的析构函数。
  • 编译器自动生成的析构函数通常情况下可以满足我们的需求,除非我们手动申请开辟了一块空间,这时需要显示地定义析构函数去释放该空间。
  • 后定义的对象先析构。
相关推荐
sunshine22 girl23 分钟前
Java学习六 Java常见API-1-Radom随机数
java·开发语言·学习
可乐鸡翅yeah_26 分钟前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
潼心1412o42 分钟前
C++初阶(长期更新)第6讲:类和对象(下)
开发语言·c++·算法
wangbing11251 小时前
使用jps jstack排查系统问题
java
夜晚回家1 小时前
Python 零基础入门 | 输入的数字为什么不能直接算
开发语言·python
时间的拾荒人1 小时前
Qt 事件机制全解析:从事件处理到事件过滤器
开发语言·qt·面试
于指尖飞舞1 小时前
java面试题总结
java·开发语言
ZealSinger2 小时前
LangChain4j用AiServices拼Tool
java·rag·ai智能体·langchain4j
vx_Biye_Design2 小时前
springboot高校选课系统82776-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express
zhangzeyuaaa3 小时前
深入理解 Ruby 运算符:本质、分类、坑点与重载实战
开发语言·前端·ruby