智能指针
智能指针是为了方便管理动态内存,防止产生内存泄漏,而对指针进行的一层封装,可以通过析构函数,构造函数,赋值运算符重载的处理,而实现的可以自动管理指针所指向的动态内存,防止产生内存泄漏。
资源分配即初始化(RAII):定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
之前的版本中有 auto_ptr,在C++11版本之后提供了shared_ptr、unique_ptr、weak_ptr,包含在头文件< memory>中。
1、auto_ptr (浅拷贝)
1.1 模拟实现
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Auto_ptr
{
public:
//构造函数
Auto_ptr(T* p)
:_ptr(p)
{}
//拷贝构造函数--管理权的转移--只改变指针的指向(浅拷贝)
Auto_ptr(Auto_ptr<T>& ap){
_ptr = ap._ptr;
ap._ptr = NULL;
}
//赋值运算符重载
Auto_ptr<T>& operator=(const Auto_ptr<T>& ap){
//防止自己给自己赋值
if (*this != *ap){
//释放旧空间
if (_ptr){
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
//析构函数
~Auto_ptr<T>(){
if (_ptr)
delete _ptr;
}
private:
T* _ptr;
};
1.2 auto_ptr缺陷
同一空间不能由两个及以上auto_ptr管理,会导致以下问题:
- 调用多次析构函数来释放同一内存空间,引起程序崩溃。
- 如果第二个变量的生命周期先到头,调用析构函数释放内存空间,那么第一个变量就会变成野指针。
auto_ptr的拷贝构造,将源指针的管理权交给目标指针,会使得源指针悬空,对原指针解引用导致很多问题:
auto_ptr不能用来管理数组,析构函数中用的是delete
2、scoped_ptr
scoped_ptr其实是对auto_ptr的改进,禁止拷贝,即一个空间只能被一个对象管理:
- 拷贝构造函数和赋值运算符重载函数只声明不实现
- 用private对其进行访问限定,防止在类外定义
2.1 模拟实现
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Scoped_ptr{
public:
//构造函数
Scoped_ptr(T* p){
_ptr = p;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
//析构函数
~Scoped_ptr<T>(){
if (_ptr)
delete _ptr;
}
private:
Scoped_ptr(const Scoped_ptr<int>&sp);//拷贝构造
Scoped_ptr& operator=(const Scoped_ptr<int>&sp);//赋值运算符的重载
T* _ptr;
};
2.2 缺陷
一个空间只能被一个对象管理,使用不方便
【注】:有兴趣可以定制一个删除器(使用函数指针或者仿函数的形式实现),来智能管理delete,free,fclose等。
3、scoped_array
scoped_array 和 scoped_ptr的功能是一样的,只是scoped_array管理的对象是数组,需要重载[]的形式。
3.1 模拟实现
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Scoped_array{
public:
//构造函数
Scoped_array(T* p){
_ptr = p;
}
T& operator[](size_t i){
return _ptr[i];
}
//析构函数
~Scoped_array<T>(){
if (_ptr)
delete[] _ptr;
}
private:
//拷贝构造
Scoped_array(const Scoped_array<int>& s);
//赋值运算符的重载
Scoped_array& operator=(const Scoped_array<int>& s);
T* _ptr;
};
4、share_ptr
加入引用计数,防止出现多次析构同一空间的情况。在这里的引用计数要在堆上创建,否则出了作用域该引用计数就会被销毁了。
每创建一个share_ptr对象,给该对象模型的引用计数加一,用来标记此时这个空间有多少个指针指向。
在每次构造函数里,对前四个字节里的值,加一;
在析构函数里,先最前四个字节里的值减一,再判断前四个字节里的值是否为0,若为0,则释放内存空间。
在拷贝构造函数里,则需要考虑以下几种情况:
【情况一】
【情况二】
4.1 模拟实现
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Share_ptr
{
public:
//构造函数
Share_ptr(const T* p)
:_ptr(p)
, _pCount(new int(1))
{}
Share_ptr(Share_ptr<T>* sp, int* spCount)
:_ptr(sp)
, _pCount(spCount)
{}
//拷贝构造函数
Share_ptr(const Share_ptr<T>& sp){
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
//赋值运算符重载
Share_ptr& operator=(Share_ptr<T>& sp){
if (this != &sp){
if (--(*_pCount) == 0){
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
return *this;
}
T&operator*(){
return *_ptr;
}
T*operator->(){
return _ptr;
}
~Share_ptr(){
if (--(*_pCount) == 0){
delete _ptr;
delete _pCount;
}
}
private:
T* _ptr;
int* _pCount;
};
4.2 缺陷
- 线程安全问题,改进措施:加上删除器,实现安全代码;
- 循环引用的问题,可能会导致内存泄漏;
循环引用:
5、weak_ptr(弱指针–>解决循环引用问题)??
弱指针:弱指针是指当指针指向原来空间时,引用计数不在进行加1
【注】:不是管理和释放当前的指针,而是避免了一次引用计数
模拟实现
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Weak_ptr;
template<class T>
class Share_ptr
{
friend class Weak_ptr<T>;
public:
//构造函数
Share_ptr(const T* p)
:_ptr(p)
, _pCount(new int(1))
{}
Share_ptr(Share_ptr<T>* sp, int* spCount)
:_ptr(sp)
, _pCount(spCount)
{}
//拷贝构造函数
Share_ptr(const Share_ptr<T>& sp){
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
//赋值运算符重载
Share_ptr& operator=(Share_ptr<T>& sp){
if (this != &sp){
if (--(*_pCount) == 0){
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
return *this;
}
T&operator*(){
return *_ptr;
}
T*operator->(){
return _ptr;
}
~Share_ptr(){
if (--(*_pCount) == 0){
delete _ptr;
delete _pCount;
}
}
private:
T* _ptr;
int* _pCount;
};
template<class T>
class Weak_ptr
{
public:
//可以将sharedptr的对像传给他,匿名对象
Weak_ptr(Share_ptr<T>& sp){
//这里将sharedptr的对象给weakptr(弱指针)进行初始化,此时弱指针指向的是sharedptr的对象
_ptr = sp._ptr;
}
Weak_ptr()
:_ptr(NULL)
{}
Weak_ptr& operator=(Share_ptr<T>& sp){
_ptr = sp._ptr;
return *this;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
private:
T* _ptr;
};
/*****************************使用实例*******************************/
struct ListNode
{
public:
int _data;
Weak_ptr<ListNode> _next;
Weak_ptr<ListNode> _prev;
~ListNode(){
cout << "delete ListNode" << endl;
}
};
void TestSharePtrCycle()
{
Share_ptr<ListNode> cur = new ListNode;//使用弱指针
Share_ptr<ListNode> next = new ListNode;
cur->_next = next;//weakptr接收的是sharedptr的对象
next->_prev = cur;
}