- 智能指针是什么
智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。
智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放
- 智能指针产生背景
case 1
1 void test1(string &str)
2 {
3 string *ps = new string(str);
4 ...
5 return;
6 }
如上边例子,我们很可能忘记delete,这将导致内存泄漏
case 2
1 void test1(string &str)
2 {
3 string *ps = new string(str);
4 delete ps;
5 return;
6 }
如上述例子,即便我们总是记得delete 指针,上述指针虽然被释放,但是已然变成一个野指针
case 3
1 void test1(string &str)
2 {
3 string *ps = new string(str);
4 if(weird_thing())
5 {
6 throw_exception();
7 }
8 delete ps;
9 ps = nullptr
10 return;
11 }
出现异常的时候,delete将不被执行,因此导致内存泄漏
因此我们使用智能指针最少可以帮我们解决上述问题
1:智能指针能够帮助我们处理资源泄露问题
2:它也能够帮我们处理空悬指针的问题
3:它还能够帮我们处理比较隐晦的由异常造成的资源泄露
- 智能指针
shared_ptr:共享型智能指针
unique_ptr、auto_ptr(已经被抛弃):独占型智能指针
weak_ptr:观察型智能指针
- 使用
shared_ptr
shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁.
9 int main()
8 {
7 int a = 10;
6 shared_ptr<int> pstr = make_shared<int>(a);
5 shared_ptr<int> pstr2(pstr);
4 shared_ptr<int> pstr3;
3 pstr3 = pstr;
2 cout<<"pstr's count:"<<pstr.use_count()<<endl;
1 int *p = pstr.get();
14 cout<<"value is:"<<*p<<endl;
1 }
注意避免循环引用,循环引用会导致堆内存无法正确释放,导致内存泄漏。借用weak_ptr解决循环引用问题
auto_ptr
auto_ptr:独占型智能指针,同一时刻只能有一个指向给定对象,不安全
12 int main()
11 {
10 auto_ptr<int> nums[3]= {
auto_ptr<int> (new int(1)),
auto_ptr<int> (new int(2)),
auto_ptr<int> (new int(3))
};
9
8 auto_ptr<int> ptmp;
7 ptmp = nums[1];
6
5 for(int i = 0;i < 3;++i)
4 {
3 cout<<"nums:"<<*nums[i]<<" ";
2 }
1 cout<<endl;
29 }
程序崩溃,因为ptmp = nums[1];将所有权从nums[1]转让给ptmp,这导致nums[1]不再引用该指针,在auto_ptr放弃所有权后,便可能使用他来访问该对象,当程序打印的时候,发现这是一个空指针,故崩溃
如果使用shared_ptr代替auto_ptr则ptmp和nums[1]指向同一个对象,引用计数由变为2,程序正常运行
shared_ptr<int> ptmp;
ptmp = nums[1];
如果使用unique_ptr呢?编译都无法通过。
unique_ptr
unique_ptr独占型智能指针,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。
unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作。
unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权
int main()
{
unique_ptr<int> nums[3]=
{
unique_ptr<int> (new int(1)),
unique_ptr<int> (new int(2)),
unique_ptr<int> (new int(3))
};
unique_ptr<int> ptmp;
ptmp = nums[1];
for(int i = 0;i < 3;++i)
{
cout<<"nums:"<<*nums[i]<<" ";
}
cout<<endl;
}
编译都无法通过,如果非要使用,可以使用库函数move,将原有的拥有权放弃,同样,原有的拥有权放弃之后,就变成一个空指针,打印时,已然程序奔溃
程序试图将一个unique_ptr赋给另一个时,如果愿unique_ptr是个临时的右值,编译器允许这样做,因为临时对象转让拥有权之后就会被销毁,如果,原unique_ptr将存在一段时间,编译器将禁止这样做
unique_ptr<string> p1;
p1=unique_ptr<string>(new string("aa")); (成功)
unique_ptr<string> p2;
p2=p1 (失败)
auto_ptr/shared_ptr使用new/delete 而非new[]/delete[] ,但unique_ptr有使用new[]/delete[]的版本
unique_ptr<int[]> pa(new int(10))
weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。
int main()
13 {
12 int a = 10;
11 shared_ptr<int> psh = make_shared<int>(a);
10 cout << psh.use_count() << endl;
9
8 weak_ptr<int> pwp(psh);
7 cout << pwp.use_count() << endl;
6
5 if(!pwp.expired())
4 {
3 shared_ptr<int> psh2 = pwp.lock();
2 *psh = 100;
1 cout << pwp.use_count() << endl;
19 }
1 }
- 实现一个智能指针
template<typename T>
class smartPtr
{
private:
T *ptr;
int refcnt;
public:
smartPtr(T *p)
{
cout<<"constructor"<<endl;
if(p!=nullptr)
{
ptr = p;
refcnt+=1;
}
}
~smartPtr()
{
cout<<"destructor"<<endl;
--refcnt;
if(refcnt == 0)
{
delete ptr;
ptr = nullptr;
}
}
smartPtr(const smartPtr<T> &rhs)
{
cout<<"copy constructor"<<endl;
if(ptr == rhs.ptr)
{
refcnt = rhs.refcnt;
++refcnt;
}
}
smartPtr& operator=(const smartPtr<T> &rhs)
{
cout<<"operator = constructor"<<endl;
if(ptr == rhs.ptr)
{
return *this;
}
--refcnt;
if(refcnt == 0)
{
delete ptr;
ptr= nullptr;
}
ptr = rhs.ptr;
refcnt = rhs.refcnt;
++refcnt;
return *this;
}
int getcnt()
{
return refcnt;
}
T &operator*()
{
return *ptr;
}
T &operator->()
{
return ptr;
}
};