吃透C++类和对象(中):const成员函数与取地址运算符重载深度解析

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》

《C++入门到进阶&自我学习过程记录》

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

前言

[一、const 成员函数](#一、const 成员函数)

1、本质与语法逻辑

2、使用场景与意义

二、取地址运算符重载

结束语


前言

上篇文章吃透C++类和对象(中):详解 Date 类的设计与实现我们通过利用之前所学的几个默认成员函数实现了 Date 类的许多功能,最重要的默认成员函数我们已经基本讲解完了,剩下的两个成员函数就是本篇文章所要讲解的const成员函数和取地址运算符重载。

一、const 成员函数

1、本质与语法逻辑

之前我们学习类的时候讲过:在类中所写的成员函数第一个形参是隐含的 this 指针,而这个 this 的类型是指向对象的指针。其实这句话并不严谨,我们用上次 Date 类里面一个函数作为例子:

cpp 复制代码
//Date.h
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print();
private:
	int _year;
	int _month;
	int _day;
};

//Date.cpp
void Date::Print()
{
	Date d1;
	this = &d1;
	cout << _year << "-" << _month << "-" << _day << endl;
}

我们在Print 函数 中多加了一条代码将 this 指针所指向的对象进行修改,就发生了下面问题:

所以通过这个报错我们就能发现这个隐含的 this 指针的类型其实是指向 Date 对象的常量指针(Date* const this),所以才会出现这种报错。

但是我们想一下之前学习关键字 const 时讲过,const 直接修饰指针本身时,虽然不能改变指针所指向的对象 ,但是能够修改指向对象的内容,这其实是很不好的,原因就是其他人能够随意去修改你指针指向对象的内容导致最后结果出错:

cpp 复制代码
void Date::Print()
{
	_year += 1;
	cout << _year << "-" << _month << "-" << _day << endl;
}

例如上面这种情况,原本只是打印日期,但由于 const 只是对指针本身进行修饰,别人仍然可以对你指针所指向对象的内容进行修改,则最后打印的日期结果就会不一样。

那怎么样才能让别人不能随意去修改你指针指向对象的内容呢?

那就是让 const 去修饰指针所指向对象的内容:将 const 修饰的成员函数称之为 const 成员函数,const 修饰成员函数放到成员函数参数列表的后面

cpp 复制代码
void Print() const;

2、使用场景与意义

在成员函数的后面直接加上一个 const 就能达到我们想要的目的:

cpp 复制代码
//Date.h
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print()const;
private:
	int _year;
	int _month;
	int _day;
};

//Date.cpp
void Date::Print()const
{
	_year += 1;
	cout << _year << "-" << _month << "-" << _day << endl;
}

这样别人就既不能直接修改指针所指向的对象,也不能修改指向对象的内容,从而保证了数据的可维护性。此时 this 的类型就变成了指向常量 Date 对象的常量指针(const Date* const this)

而且通过 const 修饰成员函数,对于被 const 修饰的对象我们也能够调用函数;但是成员函数如果没有被 const 修饰,则就会导致下面的问题:

cpp 复制代码
//Date.h
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print();
private:
	int _year;
	int _month;
	int _day;
};

//Date.cpp
void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

//Test.cpp
int main()
{
	const Date d1;
	d1.Print();
	return 0;
}

原因就是原本对象d1的指针类型是const Date* ,而成员函数第一个隐含的形参 this 的类型为 Date* ,当传给形参时就相当于从只可读变成了既可读也可写 ,也就是前面C++入门基础指南:引用全解析(从入门到精通)所学的权限放大,这就是导致报错的原因。

总结就是:**只要一个成员函数没有对指向对象的内容进行修改的都可以用 const 进行修饰。**而且用 const 修饰后还有一个好处是如果在写代码时比如判断的 == 误打成了赋值的 =,也会导致报错进行提醒。

二、取地址运算符重载

对于一个类,编译器会自动 生成两个取地址运算符重载函数 ,分别应对普通对象const 对象的地址获取:

普通取地址重载:Date* operator&() ,作用是返回普通 Date 对象的地址 ,默认实现就是简单返回 this 指针, this 指针指向当前对象的起始地址
const取地址重载:const Date* operator&() const; ,针对 const 修饰的 Date 对象,返回 const 指针 ,保障 const 对象地址获取的**"只读" 特性**(获取的地址不能用于修改对象 ),默认实现同样是返回 this 指针。

大多数时候,编译器生成的这两个函数就能满足我们日常需求,所以我们无需手动编写。除非一些很特殊的场景,比如我们不想让别人获取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址,以此来提高数据的安全性。

cpp 复制代码
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print()const;
	Date* operator&()
	{
		return this;
		// return nullptr;//特殊的场景:为了保障地址数据的安全不让其他人获取,可返回虚假地址
	}

	const Date* operator&()const
	{
		return this;
		// return nullptr;
	}

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

但是这里为什么同时写应对普通对象和 const 对象两个地址的取地址运算符重载呢?

虽然我们知道普通对象也能调用 const 成员函数,只是会权限缩小但不会报错,但是为了保证对象权限不发生变化 ,我们就让普通对象去调用对应的普通成员函数,让 const 对象去调用对应的 const 成员函数,因为编译器会自动识别与对象最相似的函数,这样就不会导致对象的权限放大和缩小了。

结束语

到此,默认成员函数的最后两个也就讲解完了,内容是比较少的,主要还是因为所涉及的知识比较少,主要是保证我们所写代码的安全性和可靠性。希望这篇文章对大家学习C++能有所帮助!

C++参考文档:
https://legacy.cplusplus.com/reference/
https://zh.cppreference.com/w/cpp
https://en.cppreference.com/w/

相关推荐
CoderCodingNo2 小时前
【GESP】C++五级/六级练习题(前缀和/动态规划考点) luogu-P1719 最大加权矩形
开发语言·c++·动态规划
学嵌入式的小杨同学2 小时前
循环队列(顺序存储)完整解析与实现(数据结构专栏版)
c语言·开发语言·数据结构·c++·算法
Yu_Lijing2 小时前
基于C++的《Head First设计模式》笔记——适配器模式
c++·笔记·设计模式
txinyu的博客2 小时前
C++ 单例模式
c++·单例模式
点云SLAM2 小时前
C++ 设计模式之工厂模式(Factory)和面试问题
开发语言·c++·设计模式·面试·c++11·工厂模式
玖釉-2 小时前
[Vulkan 学习之路] 05 - 缔结契约:创建逻辑设备 (Logical Device)
c++·windows·图形渲染
彩妙不是菜喵2 小时前
c++:初阶/初始模版
开发语言·c++
想唱rap2 小时前
MySQL表得内外连接
服务器·数据库·c++·mysql·ubuntu
A7bert7772 小时前
【DeepSeek R1部署至RK3588】RKLLM转换→板端部署→局域网web浏览
c++·人工智能·深度学习·ubuntu·自然语言处理·nlp