【C++】智能指针的原理和实现
⽬录
不要⽤auto_ptr
回顾:
C++11 4种智能指针种,C++STL auto_ptr 不提倡⽤的原因是:
auto_ptr<TYPE> A,B;
A = B;
第⼆⾏执⾏完毕后,B的地址为0;
这就是智能指针auto_ptr是转移语义造成的,容易让⼈⽤错的地⽅。
智能指针的原理和实现
智能指针的实现主要依赖于两个技术
C++中智能指针的实现主要依赖于两个技术概念:
1、析构函数,对象被销毁时会被调⽤的⼀个函数,对于基于栈的对象⽽⾔,如果对象离开其作⽤域则对象会被⾃动销毁,⽽此时析构函数也⾃动会被调⽤。
2、引⽤计数技术,维护⼀个计数器⽤于追踪资源(如内存)的被引⽤数,当资源被引⽤时,计数器值加1,当资源被解引⽤时,计算器值减1。
3、操作符重载。
智能指针的⼤致实现原理就是在析构函数中,检查所引⽤对象的引⽤计数,如果引⽤计数为0,则真正释放该对象内存。
我们是怎么创建原⽣指针的:
// 创建⼀个不知所指的指针
char *punknown ;
// 创建空指针
char *pnull = NULL;
// 创建字符串指针
const char *pstr = "Hello world!";
// 创建指向具体类的指针
SomeClass *pclass = new SomeClass();
// 从同类型指针创建指针
SomeClass *pclassother = pclass;
求我们的智能指针类,也能满⾜对应的⽤法:
// 创建⼀个不知所指的智能指针
SmartPointer spunknown;
// 创建空智能指针
SmartPointer spnull = NULL;
// 创建字符串的智能指针
SmartPointer spstr = "Hello world!";
// 创建指向具体类的智能指针
SmartPointer spclass = new SomeClass();
实现智能指针版本1
/*
* file name : smartpointer.h
* desp : 智能指针版本v1
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__
template <typename T> // 将智能指针类定义成模板类
class SmartPointer
{
public:
// 默认构造函数
SmartPointer(): mPointer(NULL)
{
std::cout << "create unknown smart pointer." << std::endl;
}
// 接收不同指针类型的构造函数
SmartPointer(T *p): mPointer(p)
{
std::cout << "create smart pointer at " << static_cast<const void *>(p) << std::endl;
}
// 析构函数
~SmartPointer()
{
std::cout << "release smart pointer at " << static_cast<const void *>(mPointer) << std::endl;
// 实现内存资源⾃动销毁机制
if (mPointer) delete mPointer;
}
private:
T *mPointer; // 指向智能指针实际对应的内存资源,根据参数⾃动推导规则,定义内部资源指针类型
};
#endif // __SMARTPOINTER_H__
上⾯的智能指针没有实现赋值和拷贝,
默认情况下C++编译器会为我们定义的类⽣成拷贝构造函数和赋值操作符的实现,但是对于我们的智能指针⽽⾔,使⽤系统默认为我们⽣成的赋值操作符的实现,是会有问题的。
拷贝构造函数和赋值构造函数的区别:
1)会调⽤拷贝构造函数的例⼦:
CExample A;
CExample B=A; //把B初始化为A的副本
2)会调⽤赋值构造函数的例⼦:
CExample A;
CExample C;
C = A; //现在需要⼀个对象赋值操作,被赋值对象的原内容被清除,并⽤右边对象的内容填充。
1)中“ CExample B=A; ”语句中的 "=" 在对象声明语句中,表⽰初始化。更多时候,这种初始化也可⽤括号表⽰。 例CExample B(A);
2)中,"=" 表⽰赋值操作。将对象 A 的内容复制到对象C;,这其中涉及到对象C 原有内容的丢弃,新内容的复制。
智能指针类中,有我们⾃⼰申请的内存,所以需要⾃⼰定义拷贝构造函数和赋值构造函数(完成深拷贝)
2.2 定义构造函数和赋值操作符的函数原型
实现智能指针版本2
/*
* file name : smartpointer.h
* desp : 智能指针版本v2
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__
template <typename T> // 将智能指针类定义成模板类
class SmartPointer {
public:
// 默认构造函数
SmartPointer():mPointer(NULL) {std::cout <<"Create null smart pointer."<< std::endl;}
// 接收不同指针类型的构造函数
SmartPointer(T *p):mPointer(p) {std::cout <<"Create smart pointer at "<<static_cast<const void*>(p)<<std::endl;}
// 析构函数
~SmartPointer(){
std::cout << "Release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl;
// 实现内存资源⾃动销毁机制
if (mPointer) delete mPointer;
}
// 拷贝构造函数
SmartPointer(const SmartPointer &other):mPointer(other.mPointer) {
std::cout <<"Copy smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
}
// 赋值操作符
SmartPointer &operator = (const SmartPointer &other) {
// 处理⾃我赋值的情况
if (this == &other) return *this;
// 处理底层指针的释放问题
if (mPointer) delete mPointer;
mPointer = other.mPointer;
std::cout <<"Assign smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
return *this;
}
private:
T *mPointer; // 指向智能指针实际对应的内存资源,根据参数⾃动推导规则,定义内部资源指针类型
};
#endif // __SMARTPOINTER_H__
还是存在⼀些问题:就是当有多个智能指针执⾏同⼀块底层资源,在释放时,每个指针都会去释放⼀次底层资源,这就造成了最后的double free 错误
此时智能指针SmartPointer2将会变成野指针!
所以要引⼊计数:
引⼊引⽤计数的智能指针,其实现主要需要更新以下⼏点:
1接收不同对象类型的构造函数
这个构造函数实现,⽐较简单,直接将引⽤计数加1 即可。
2析构函数
析构函数的实现,不能再直接做delete操作,⽽是需要先对引⽤计数减1,当引⽤计数为0时,才做delete操作。
3拷贝构造函数
拷贝构造函数的实现,底层指针共享,然后将引⽤计数加1 即可。
4赋值操作符
赋值操作的实现,稍微复杂⼀些,涉及到将新指向对象的引⽤计数加1,将原指向对象的引⽤计数减1,如果有需要还要销毁原指向对象。这⾥有⼀点值得注意的地⽅,我们新的赋值操作的实现,不再需
要if (this == &other) return *this;语句处理⾃我赋值的情况,读者可⾃⾏分析⼀下我们新的赋值操作的实现为何不需要通过if语句去处理⾃我赋值的情况。
为了能够使⽤引⽤计数技术,我们的智能指针不能再像原⽣指针那样能⽤可以指向任意资源对象,我们的智能指针只能指向实现了存在⽅法incRefCount和⽅法decRefCount的资源类了。
引⽤计数技术的智能指针v3版本:
/*
* file name : smartpointer.h
* desp : 智能指针版本v3
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__
template <typename T> // 将智能指针类定义成模板类
class SmartPointer {
public:
// 默认构造函数
SmartPointer():mPointer(NULL) {std::cout <<"Create null smart pointer."<< std::endl;}
// 接收不同对象类型的构造函数
SmartPointer(T *p):mPointer(p) {
std::cout <<"Create smart pointer at "<<static_cast<const void*>(p)<<std::endl;
std::cout <<"Create smart pointer at "<<static_cast<const void*>(p)<<std::endl;
/*智能指针指向类T,引⽤计数加1*/
if (mPointer) mPointer->incRefCount();
}
/
/ 析构函数
~SmartPointer(){
std::cout << "Release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl;
// 实现内存资源⾃动销毁机制
if (mPointer && mPointer->decRefCount()==0) delete mPointer;
}
// 拷贝构造函数
SmartPointer(const SmartPointer &other):mPointer(other.mPointer) {
std::cout <<"Copy smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
// 引⽤计数加1
if(mPointer) mPointer->incRefCount();
}
// 赋值操作符
SmartPointer &operator = (const SmartPointer &other) {
T *temp(other.mPointer);
// 新指向对象,引⽤计数值加1
if (temp) temp->incRefCount();
// 原指向对象,引⽤计数值减1,如果减1后引⽤计数为0 销毁原资源对象
if (mPointer && mPointer->decRefCount()==0) delete mPointer;
// 智能指针指向新资源对象
mPointer = temp;
return *this;
}
private:
T *mPointer; // 指向智能指针实际对应的内存资源,根据参数⾃动推导规则,定义内部资源指针类型};
/*引⽤计数基类*/
class RefBase
{
public:
RefBase() : mCount(0){ }
void incRefCount(){
指针函数的作用mCount++;
}
int decRefCount(){
return --mCount;
}
// 调试接⼝,返回对象当前引⽤计数
int getRefCount(){
return mCount;
}
virtual ~RefBase(){ }
private:
int mCount;
};
#endif // __SMARTPOINTER_H__
测试代码(sptestcase3.cpp):
/*
* file name : sptestcase3.cpp
* desp : 智能指针测试代码 case3 测试智能指针的引⽤计数功能
*/
#include <iostream>
#include "smartpointer.h"
/*继承于引⽤计数基类的SomeClass类*/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论