C++知识点43——解引用运算符和箭头运算符的重载及智能指针类的实现

【C++11】三大神器之——智能指针 在Cpp11新标准下学习C++三大神器之一的智能指针 阅读详情

一、概念、

在自定义行为类似指针的类时,需要重载*和->。C++中的智能指针就重载了这两个运算符。->必须是成员函数,*也应该是成员函数。与内置类型保持一致,这两个函数通常都是const的。以为*和->通常不会也不应该改变对象的状态

*运算符的返回值通常都是一个类型的引用(因为类内部的数据不一定非得指向T的对象,也可能指向一个T的派生类对象。如果是这样并且operator*返回的是个T对象,而非一个引用(代表真正的派生类对象),那么,operator*函数便是返回一个错误类型的对象!,也就是导致子类对象转基类对象的切割问题)

->运算符的返回值必须是一个类的指针。因为当自定义类型的operator->被调用时,编译器会将operator->转化为

(obejct.operator->())->member

所以,operator->的返回值必须是一个指针

 

二、示例

通过实现一个智能指针来熟悉这两个运算符的重载,类的整体实现如下

#include <iostream>
#include <memory>

using namespace std;

template <typename T>
class mysmartpointer
{
public:
	mysmartpointer(T *ptr=nullptr):
	 ptr_(ptr),
	 pcount_(new size_t(0))
	{
		cout<<__func__<<endl;
		if (ptr_==nullptr) {//空指针不增加引用计数
			*(this->pcount_)=0;
		}
		else {
			*(this->pcount_)=1;
		}
	}

	mysmartpointer(const mysmartpointer &rval):
	ptr_(rval.ptr_),
	pcount_(rval.pcount_)
	{
		if (ptr_) {//拷贝初始化,如果指针不为空,增加引用计数
			++*pcount_;
		}
		cout<<"mysmartpointer(const mysmartpointer &rval)"<<endl;
	}
	
	mysmartpointer &operator=(const mysmartpointer &rval)
	{
		cout<<__func__<<endl;
		mysmartpointer t(rval);
		this->swap(t);
		--*pcount_;//减少左值的引用计数
		if (*pcount_==0) {//此时如果引用计数为0,那么释放指向的对象和引用计数
			cout<<"delete object when operator="<<endl;
			delete ptr_;
			delete pcount_;
		}
		++*rval.pcount_;//增加右值的引用计数
		return *this;
	}

	T &operator*() const
	{
		cout<<__func__<<endl;
		return *ptr_;//返回指向对象本身
	}

	T *operator->() const
	{
		cout<<__func__<<endl;
		return &this->operator*();//通过调用operator*来实现operator->
	}

	explicit operator bool()
	{
		cout<<__func__<<endl;
		return !(ptr_==nullptr && *pcount_==0);
	}

	void swap(mysmartpointer &t)//交换指针和引用计数
	{
		cout<<__func__<<endl;
		std::swap(this->ptr_, t.ptr_);
		std::swap(this->pcount_, t.pcount_);
	}

	size_t use_count()
	{
		cout<<__func__<<endl;
		return *pcount_;
	}

	~mysmartpointer()
	{
		cout<<__func__<<endl;
		if (--*pcount_==0) {//析构时,--引用计数
			cout<<"delete object"<<endl;
			delete ptr_;
			delete pcount_;
		}
	}
	
private:
	T *ptr_;
	size_t *pcount_;
};

上述代码中,operator->的实现使用过operator*来实现。这两个函数一般只实现一个,另一个直接调用即可

该类中引用计数是采用指针的方式实现的,当对智能指针进行赋值、拷贝时,可以利用指针的特性让两个对象的引用计数同时指向一块内存,共享引用计数

operator=的实现也是采用swap方式,避免重新分配内存,而且可以处理自赋值的情况

 

测试

int main(int argc, char const *argv[])
{
	mysmartpointer<string> spstr1(new string("1234"));
	mysmartpointer<string> spstr2=spstr1;
	cout<<spstr1.use_count()<<endl;
	cout<<spstr2.use_count()<<endl;
	cout<<"------"<<endl;
	mysmartpointer<string> spstr3;
	spstr3=spstr1;
	spstr1=spstr1;
	cout<<spstr3.use_count()<<endl;
	cout<<spstr1.use_count()<<endl;
	cout<<spstr2.use_count()<<endl;
	cout<<spstr1->size()<<endl;
	if (spstr1) {
		cout<<"spstr1 is not nullptr"<<endl;
	}
	return 0;
}

 

参考

《C++ Primer》

《More Effective C++》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

【25届秋招备战C++】最强C++学习教程:Cherno C++笔记 纵览各种CPP教学视频,这是最接近底层逻辑的,生动有趣,举例精准贴切,希望所有在C++学习之路“踽踽独行”又迷茫困顿的初学者可以通过Cherno的视频理解C++,编程或许可以很有趣。 阅读详情

相关推荐

C++智能指针之AutoPtr

在学习异常的时候,异常有一个很大的缺陷,就是异常会导致执行流乱跳.从而可能会导致某些资源没有被及时得到释放等等问题. 例如这样的问题: 1 #include &amp;amp;amp;lt;iostream&amp;amp;amp;gt; ...

Yinghuhu333333的博客 410

C++ 解引用运算符

在C++编程中,指针是一种非常重要的数据类型,它可以指向内存中的地址,让程序可以访问到变量或对象。指针的解引用运算符(*)是一个常见的操作符,在操作指针时非常常用。除此之外,C++中还有三种指向成员的指针,分别是::*.*和->*。本文将对这三种指针进行介绍,并提供相应的例子。::*是指向成员声明符的指针,可以用来绑定对象和成员函数。在这个例子中,我们定义了一个名为Rectangle的类,其中包含了成员变量w_和h_,以及一个计算面积的成员函数area()。

qq_42896106的博客 1662

C++(03):operator=赋值运算符的返回值

C++中赋值运算符的返回值通常为this对象的引用: 之所以这样做,是因为原生类型,比如int型变量允许做这样的操作: 虽然我不太理解,( i = 8 ) = 0;这样操作的意义是什么,不过原生类型确实允许这样的操作。为了让自定义的类型也具备这样的能力,因此要让赋值运算符的返回值为this对象引用: 如果赋值运算符的返回值类型为this对象的常量引用,是无法完成(a1 = 8) = 0;这样的操作的: 注:Effective C++条款10,对赋值运算符的返回值类型之所以为this对象的引用

风静如云的博客 2351

【C++】引用&详细解析

关于C++经常使用的引用——详细解析。

weixin_63449996的博客 1974

【16】linux命令/工具,systemd,文件操作,动静态库,信号,socket

apt install python3-pip --fix-missing(断开vpn,cp /etc/apt/sources.list /etc/apt/sources.list.bak,python3 -m pip install --upgrade pip,apt-get update,pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com)设置时间,联网,apt-get install vim。

持续更新ing... 6788

pyecharts可视化图表-tree:从入门到精通

本文系统介绍了树图(Tree Chart)的可视化技巧,包含基础实现、方向定制和环形布局等实例。树图能清晰展示层次结构数据,适用于组织架构、文件系统等场景。教程详细解析了pyecharts树图的核心配置项,包括数据准备、方向设置(下上、右左)和样式定制,并提供了完整代码示例。通过本指南,读者可快速掌握专业树图的构建方法,实现复杂层次数据的可视化呈现。

大数据内容个人总结与分享 1970

c++ 引用

C++引用详解解 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。 引用的声明方法:类型标识符 &引用名=目标变量名;   如下:定义引用ra,它是变量a的引用,即别名。     int a;     int &ra=a;   (1)&在此不是求地址运算符,而是起标识作用。   (2)类型标识符是指目标变量的类型。   (3

beibei_hao_csdn的博客 740

AD2S1210与DSP28335 SPI通信中的GPIO配置陷阱与解决

本文深入剖析了AD2S1210与DSP28335进行SPI通信时,因GPIO输入限定配置不当导致数据整体右移的典型问题。文章指出,错误地将MISO引脚配置为带采样周期的同步模式是根本原因,并提供了将输入选择限制正确设置为异步模式的解决方案。同时,总结了不同应用场景下GPIO输入限定的配置原则,为高速同步通信的稳定实现提供了关键指导。

weixin_29163857的博客 146

C++入门基础知识155—【关于C++类成员访问运算符 -> 重载】

关于【C++类成员访问运算符 -> 重载】 一、什么是类成员访问运算符( ->)‌ 二、类成员访问运算符( ->)的语法规则 三、类成员访问运算符( ->)的应用场景 四、类成员访问运算符( ->)示例代码 五、什么是类成员访问运算符( ->)重载 六、类成员访问运算符( ->)重载示例代码

zhaoylzy的博客 1010

杂记 | 使用keycloak实现SSO单点登录(新手向,概念、原理、逻辑、详细步骤、难点解释)

使用keycloak实现SSO单点登录(概念、原理、逻辑、详细步骤、难点解释)

野生猿林仔的博客 2万+

从时频谱图到智能感知:雷达信号处理的AI进化之路

本文探讨了人工智能在雷达信号处理中的革命性应用,特别是深度学习在时频谱图分析中的突破性进展。通过对比传统信号处理方法与AI驱动的解决方案,展示了CNN、3D CNN等模型在目标检测、行为识别等场景中的优势,并分析了数据获取、模型优化等关键技术挑战。AI赋能的雷达技术已广泛应用于智能交通、工业检测和医疗健康领域,展现出巨大的发展潜力。

weixin_29284201的博客 507

[Cherno C++ 笔记 P41~P50]运算符重载、this、生存期、智能指针、复制与拷贝构造函数、箭头操作符、动态数组、std::vector、静态链接、动态库

[Cherno C++ 笔记 P21~P30]static,枚举,构造函数,析构函数,继承,虚函数,接口,可见性系列博客前言视频链接P41 C++运算符及其重载P42 C++的this关键字P43 C++的对象生存期(栈作用域生存期)P44 C++的智能指针P45 C++的复制与拷贝构造函数P46 C++的箭头操作符P47 C++的动态数组(std::vector)P48 C++的stdvector使用优化P49 C++中使用库(静态链接)P50 C++中使用动态库 系列博客 [Cherno C++ 笔记

qq_44525568的博客 709

CMOS Sensor的调试经验分享

我这里要介绍的就是CMOS摄像头的一些调试经验。 首先,要认识CMOS摄像头的结构。我们通常拿到的是集成封装好的模组,一般由三个部分组成:镜头、感应器和图像信号处理器构成。一般情况下,集成好的模组我们只看到外面的镜头、接口和封装壳,这种一般是固定焦距的。有些厂商只提供芯片,需要自己安装镜头,镜头要选择合适大小的镜头,如果没有夜视要求的话,最好选择带有红外滤光的镜头,因为一般的sensor都...

camerall 3764

C++学习笔记-3-Cherno系列视频

原因在于,第一次pb(push_back)时,会在main函数的栈上创建一次vertex,然后copy到vertices中(1次),第二次pb时,由于vertices初始容量为1,需要先扩容到2,再复制,而扩容需要把前面的复制到新内存地址(2次),第三次同理(3次)。unique_ptr是想要一个作用域指针时的唯一选择,这个是不能复制的(原因在于,若是复制了,则两个指针指向同一个内存地址,若其中一个消失了,会释放那部分内存,以至于另一个指针出现不可预料的错误)(比如下面的复制,为什么会复制?

ww666662的博客 1725

基于Simulink的光储系统黑启动能力仿真

本文基于Simulink平台构建了光储系统黑启动仿真模型,详细阐述了储能VF控制、光伏孤岛MPPT和同步并网等核心控制策略。通过分阶段协调控制(储能启动→关键负载→光伏接入→非关键负载→同步并网),实现了60秒内恢复供电、母线电压波动<5%的性能指标。仿真结果表明,协调控制策略相比无序启动可显著提升系统稳定性,避免电压崩溃和负载跳闸。文章还提供了完整的建模步骤、核心代码及进阶优化方向,为微电网应急供电设计提供了实用参考。该模型适用于光伏储能系统的黑启动能力验证和优化研究。

amy_mhd的博客 106

【01】总目录——软件设计师50讲通关地图|从零基础到工程师职称

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏的01篇总目录,系统梳理了50篇博文的完整架构与核心逻辑。专栏覆盖软考软件设计师(中级)全部考点,分为导学规划、基础知识、设计模式、下午题专项、算法实战、冲刺模拟、收官进阶七大模块,既兼顾上午题75道单选题的理论攻坚,也聚焦下午题案例分析的解题能力提升。每篇博文均包含考点精讲、真题解析、实战技巧与速记方法,配套可运行代码、解题模板及备考资源。无论你是零基础转行、在校学生还是在职程序员,都能通过这份学习地图明确进度、精准定位薄弱环节,高效搭建知识体系

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1280

Cherno C++【41-45】

运算符时我们使用给的一种符号,通常代替一个函数来执行一些事情。比如加减乘除、dereference运算符、箭头运算符、+=运算符、&运算符、左移运算符、new和delete、逗号、圆括号、方括号等等等等。重载这个术语本质上是给运算符重载赋予新的含义,或者添加参数,或者创建,允许在程序中定义或更改运算符的行为。运算符就是函数。运算符重载是一个非常有用的特性,但在Java等语言中不受支持,它在C#等语言中得到部分支持,C++给了我们完全的控制权。不用运算符实现加、乘加入运算符后。

Rootzzy1216的博客 204

创建 Fiori Space 时看不到传输请求的根因与排查路径

摘要: 在创建Fiori Space时,若Transport字段下拉列表为空,通常是因为系统未找到符合要求的传输请求。主要原因是: 请求类型不匹配:需使用Customizing request而非Workbench request。 用户权限问题:当前用户需在请求下有可用的task。 客户端或系统不一致:请求需在当前客户端和正确系统(如后端)中创建。 解决方法: 在当前客户端和系统中新建Customizing request,确保包含用户task。 检查是否为前后端分离架构,确保请求创建在正确的系统。 验证

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 107

c++知识总结

工作到现在学到的一些c++经验

qq_43617638的博客 1950

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

c++&visualStudio学习笔记

所有cpp文件都会被编译,而头文件不会,只有cpp,头文件在预处理被include到头文件里,那是他们被编译的时候,所以我们有一堆cpp文件被编译,而且它们是一个个被单独被编译的。在这里还有一个log.cpp中使用了Log函数,我们拿到一个错误,log函数不再该文件中,该文件不知道log函数的存在。这些向左的带角的括号,看起来像左移位符号(bit shift),其实是被重载的符号,你得把它想成一个函数,我知道他看起来像运算符,但实际上,运算符就是函数,所以这种情况下和cout.print一模一样。

roymustang的博客 1280

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

cherno c++学习笔记(更新中) (最好的C++教程)B站转载的视频名称叫这个(1 - 46)

1. 预处理就是将里面的内容拷贝到目前文件中。 2.预处理里面的#define A b 就是把A改成b显示在编译后。 3.函数申明不一定在本文件里,也可能在其他文件,但必须在某处。 4.

xu1009的博客 4219

TheChernoCppTutorial

文章目录1. 欢迎来到C++ 1. 欢迎来到C++ 使用C++的最大原因是直接控制硬件。用C++写的代码,这些代码被送去编译器去编译,这些编译器将代码输出为目标平台的机器码。机器码是你的设备在CPU上实际执行的指令。使用C++我们完全可以控制CPU执行的每一条指令。 C#和JAVA与cpp不同,是因为它们运行在虚拟机上,这意味着代码首先被编译成一种中间语言,当在目标平台运行应用程序时,虚拟机在程序运行时再转换成机器码。 C++是本地语言(native language)。比如x64编译器将输出x64机器代码

weixin_47652005的博客 4618

c++ primer复习(3)那些太容易被忽略的细节(持续更新)

1.std::endl 有一个作用是来冲洗buffer 2.声明语句时理解方式 typedef char* pstring; //这两看似一样,但是实际不同 const pstring cstr = 0; //char*为基本数据类型 const char* cstr = 0; //char 为基本数据类型,*变成声明 3.auto声明 多条声明语句类型必须一致 auto i=0,*p=&i; 4.std::size_t 是一种std::string里内置的变量类型,是无符号的,所以要小心跟

普雷的博客 1091

C++类型系统深度解析:从底层机制到工程实践,理解类型即世界

在编程语言中,类型系统是构建可靠软件的基石,它定义了数据的边界与行为,决定了程序的安全性与可维护性。理解类型不仅仅是记住语法,更是深入掌握语言的底层原理。C++作为一门强类型语言,其类型机制尤为复杂,涉及隐式转换、窄化、整型提升、显式转换等核心概念。例如,char类型既能表示字符也能参与整数运算,而long类型相加可能遭遇溢出,这些现象背后都源于类型边界的物理限制。同时,智能指针unique_ptr与裸指针的差异,以及强类型枚举enum class的引入,都展现出类型系统在所有权与状态空间管理中的关键作用。

weixin_33901641的博客 315

侯捷-C++面向对象高级开发-下

个人学习笔记

weixin_56148345的博客 353

Qt开发入门:从零到跨平台GUI低手

Qt开发入门:从零到跨平台GUI低手

Yusei_0523的博客 585
上一篇: C++知识点42——下标运算符[]的重载及string类的实现
下一篇: 7.git提交的前进与后退
Master Cui
博客等级 码龄9年 1408粉丝 224原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值