从前面的内容可以看出auto_ptr具有比较多的缺陷,使用时容易出错。在C++ 11标准中出现了新的智能指针unique_ptr、 shared_ptr与weak_ptr等
一、unique_ptr
特点:所有权唯一,禁止拷贝和权限转移
缺点:unique_ptr是简单粗暴的防止拷贝,这种比较简单,效率高,但是功能不全面,不支持拷贝和赋值操作。无法共享数据,可自主实现数据共享,但在释放时会导致堆内存重复释放导致系统崩溃。
下面是unique_ptr的实现:
#include<iostream>
#include<memory>
template<typename T>
class Unique_Ptr
{
public:
Unique_Ptr(T* ptr)
:mptr(ptr)
{}
Unique_Ptr()
{
delete mptr;
mptr = NULL;
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
private:
Unique_Ptr(Unique_Ptr<T>&);//将拷贝构造和赋值写在私有下
Unique_Ptr<T>& operator=(Unique_Ptr<T>&);
T* mptr;
};
int main()
{
int* p = new int;
Unique_Ptr<int> up1(p);
Unique_Ptr<int> up2(p);
Unique_Ptr<int> up3(p);
return 0;
}
1.禁止赋值和复制
unique_ptr禁止赋值和复制,“唯一”地拥有其所指对象,同一时刻只能有一个unique_ptr实例指向给定对象。也就是说模板类unique_ptr的copy构造函数以及等号(“=”)操作符是无法使用的。
通过禁止复制和赋值可以较好的改善auto_ptr的所有权转移问题。下面是其禁止复制和赋值的例子:
#include <iostream>
#include <memory>
using namespace std;
void Fun1( unique_ptr<int> up )
{
}
int main()
{
unique_ptr<int> up1 = unique_ptr<int>(new int(10));
//不允许复制,所以以下三个均错误
unique_ptr<int> up2 = up1; // error
unique_ptr<int> up3(up1); // error
Fun1(up1); // error
//不允许赋值('='),所以下面错误
unique_ptr<int> up4;
up4 = up1; // error
return 0;
}
2.针对auto_ptr缺陷的改善
1) 管理数组指针
因为unique_ptr有unique_ptr< X[ ] >重载版本,销毁动态对象时调用delete[],所以可以用unique_ptr来管理数组指针。
unique_ptr< Test[ ] > uptr1(new Test[3]);
//注意 unique_ptr<Test> uptr3(new Test[3]);是不对的
unique_ptr<int[]> uptr2(new int[5]);
2)做容器(vector, list, map)元素
vector<unique_ptr<int> > vec;
unique_ptr<int> ptr1(new int(3));
vec.push_back(std::move(ptr1));
//vec.push_back(ptr1); //由于禁止复制这样不行
二、shared_ptr
特点:shared_ptr的实现原理是通过引用计数来实现,拷贝或赋值时将引用计数加1,析构时只有当引用计数减到0才释放空间,否则只需将引用计数减1即可.shared_ptr共享所有权。
缺点:相互引用 内存泄漏
如图理解引用计数和shared_ptr的关系:
#include<memory>
class Ref_Man //引用计数
{
public:
static Ref_Man* getInstance()
{
if (prm == NULL)
{
prm = new Ref_Man();
}
return prm;
}
int getRef(void* ptr)
{
if (ptr == NULL)
{
return -1;
}
int index = find(ptr);
if (index != -1)
{
return ref_count[index].count;
}
return -1;
}
void addRef(void* ptr)
{
if (ptr == NULL)
{
return;
}
int index = find(ptr);
if (index != -1)
{
ref_count[index].count++;
}
else
{
ref_count[cursize].addr = ptr;
ref_count[cursize++].count = 1;
}
}
void delRef(void* ptr)
{
if (ptr == NULL)
{
return;
}
int index = find(ptr);
if (index != -1)
{
if (getRef(ptr) != 0)
{
--ref_count[index].count;
}
}
}
private:
Ref_Man()
{
cursize = 0;
}
Ref_Man(const Ref_Man&);
int find(void* ptr)
{
int index = -1;
for (int i = 0; i < cursize; i++)
{
if (ref_count[i].addr == ptr)
{
index = i;
break;
}
}
return index;
}
typedef struct Ref
{
void* addr; //堆内存地址
int count; //堆内存所对应的指针数目
}Ref;
Ref ref_count[10];
int cursize;
static Ref_Man* prm;
};
Ref_Man* Ref_Man::prm = NULL;
template<typename T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = NULL) //构造
:mptr(ptr)
{
prm->addRef(mptr);
}
Shared_Ptr(Shared_Ptr<T>& rhs) //拷贝构造
{
mptr = rhs.mptr;
prm->addRef(mptr);
}
Shared_Ptr<T>& operator=(Shared_Ptr<T>&rhs) //赋值
{
if (this != &rhs)
{
prm->delRef(mptr);
if (prm->getRef(mptr))
{
delete mptr;
}
mptr = rhs.mptr;
prm->addRef(mptr);
}
return *this;
}
~Shared_Ptr()
{
prm->delRef(mptr); //--引用计数
if (prm->getRef(mptr) == 0) //获取mptr引用计数,如果为0释放
{
delete mptr;
}
mptr = NULL;
}
T& operator*() //重载*
{
return *mptr;
}
T* operator->() //重载->
{
return mptr;
}
private:
T* mptr;
static Ref_Man* prm;
};
template<typename T>
Ref_Man* Shared_Ptr<T>::prm = Ref_Man::getInstance();
class B;
class A
{
public:
A()
{
std::cout << "A::A()" << std::endl;
}
~A()
{
std::cout << "A::~A()" << std::endl;
}
public:
std::weak_ptr<B> spa;
};
class B
{
public:
B()
{
std::cout << "B::B()" << std::endl;
}
~B()
{
std::cout << "B::~B()" << std::endl;
}
public:
std::weak_ptr<A> spb;
};
int main()
{
std::shared_ptr<A> pa(new A());
std::shared_ptr<B> pb(new B());
pa->spa = pb; //相互引用
pb->spb = pa;
return 0;
}
int main()
{
int* p = new int; //使用weak_ptr后解决了相互引用导致内存泄漏的问题
Shared_Ptr<int> sp1(p);
Shared_Ptr<double> spd = new double;
Shared_Ptr<int> sp2(p);
Shared_Ptr<int> sp3(p);
return 0;
}
shared_ptr相互引用问题
- pa和pb两个智能指针对象分别指向spa和spb,引用计数变成1.
- pa的spa指向pb,pb的spb指向pa,引用计数变成2。
- pa和pb析构,引用计数减到1,但是spa还指向spb。但是spb还指向spa。
- 也就是说spa析构了,pb就释放了。
- 也就是说spb析构了,pa就释放了。
- 但是spa属于pa的成员,pa释放了,spa才会析构,而pa由spb管理,spb属于pb成员,所以这就叫循环引用,谁也不会释放。
weak_ptr的出现,很好的解决了这个问题。
三、weak_ptr
特点:weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它没有重载operator*和->,故而不具有普通指针的行为。它的最大作用在于协助shared_ptr工作。
weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
作用:解决强智能指针shared_ptr相互引用的问题。
代码如下:
template<typename T>
Ref_Man* Shared_Ptr<T>::prm = Ref_Man::getInstance();
class B;
class A
{
public:
A()
{
std::cout << "A::A()" << std::endl;
}
~A()
{
std::cout << "A::~A()" << std::endl;
}
public:
std::weak_ptr<B> spa;
};
class B
{
public:
B()
{
std::cout << "B::B()" << std::endl;
}
~B()
{
std::cout << "B::~B()" << std::endl;
}
public:
std::weak_ptr<A> spb;
};
int main()
{
std::shared_ptr<A> pa(new A());
std::shared_ptr<B> pb(new B());
pa->spa = pb;
pb->spb = pa;
return 0;
}
int main()
{
int* p = new int;
Shared_Ptr<int> sp1(p);
Shared_Ptr<double> spd = new double;
Shared_Ptr<int> sp2(p);
Shared_Ptr<int> sp3(p);
return 0;
}
因为在执行pa->spa = pb;pb->spb = pa;
时weak_ptr的spa和spb不会增加pa和pb的引用计数,这样就保证了正常的释放。
友情链接:
C++:RAII思想和智能指针(auto_ptr,unique_ptr,shared_ptr)详解了引用计数部分,并且提供了线程安全的shared_ptr,很值得学习哦~