我们在使用智能指针之前,一直是使用的是裸指针
如果我们不delete,会造成内存泄漏
或者由于中间程序的执行顺序和我们预想的不一样,导致我们资源释放的代码写了,但是没有执行到,导致内存资源泄漏
堆内存是我们用户手动开辟,手动释放
用不好裸指针也是造成我们内存资源非法访问,资源泄漏的一种因素
智能指针
可以保证做到资源的自动释放
智能指针就是对普通裸指针的封装
利用栈上构造的对象出作用域自动析构的特点
智能指针能不能定义在堆上?
这样写在语法上没有问题,但是智能指针保证资源自动释放是在智能指针析构的时候。但是现在这个智能指针被放在堆上,这个指针p虽然是智能指针类型,但是它前面是星号,就不是一个对象,它就是一个普通的裸指针,所以,要让堆上的这个智能指针析构,我们首先要delete p;
使用,我们不能直接把智能指针定义在堆上,这样就没意义了。
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* p = nullptr):ptr(p){}
~CSmartPtr()
{
delete ptr;
}
T& operator*()
{
return *ptr;
}
T* operator->()
{
return ptr;
}
private:
T* ptr;
};
class Test
{
public:
void show()
{
cout << "linzeyu" << endl;
}
};
int main()
{
CSmartPtr<int>p (new int);
*p = 20;
cout << *p << endl;
CSmartPtr<Test>q(new Test());
q->show();
(q.operator->())->show();
}
不带引用计数的智能指针
我们先看下面这个例子
我们运行这段代码。
我们发现程序运行崩溃了。
它默认做拷贝构造的话,是浅拷贝,也就是说,在智能指针析构的时候,把同一个资源释放了2次,第2次释放就成了野指针了。
如果我们防止浅拷贝呢?
这样运行代码就不会崩溃了。
但是现在,p1和p2管理的是2个不同的资源,用户如果不了解的话,用户会认为他们想管理的是同一个资源。
现在,我们这个智能指针面临:怎么解决浅拷贝问题?
智能指针使用的使用的时候我们只需要包含头文件:
auto_ptr解析
程序崩溃了。
int main()
{
auto_ptr<int>p(new int(50));
auto_ptr<int>q(p);
cout << *q << endl;
}
int main()
{
auto_ptr<int>p(new int(50));
auto_ptr<int>q(p);
cout << *p<< endl;
}
我们转到auto_ptr的定义去看看
首先会调用release方法,然后把返回结果给_Myptr进行初始化
release做什么事情呢?
我们来图解release的做法如下
auto_ptr的成员变量只有1个裸指针,不带引用计数,它所处理的浅拷贝问题是永远只让最后一个智能指针管理资源,前面的智能指针全部置为nullptr
不推荐使用auto_ptr哦
容器在使用过程中,难免会做容器的拷贝构造,或者容器之间的赋值,就会引起容器每个元素的拷贝构造和赋值,如果使用智能指针auto_ptr,用vec1拷贝构造vec2,那么vec1的所有的裸指针全部被置为nullptr了。
scoped_ptr解析
比auto_ptr的浅拷贝做法还暴力。
把拷贝构造函数和赋值函数直接删除了。
对于scoped_ptr来说,只能使用单个对象。使用拷贝构造函数或者赋值函数,编译器就会报错。
推荐使用unique_ptr
这个智能指针也是只让1个智能指针管理资源,不会让多个智能指针管理同一个资源。
首先,unique_ptr智能指针也是先做了拷贝构造函数和赋值函数的删除。
但是
可以使用这样的操作
std::move:C++11的右值引用,std::move得到当前变量的右值类型,就是对变量进行右值引用的类型强转。
之所以可以这么实现,是因为unique_ptr提供了这样的实现:
临时对象拷贝构造ptr1
unique_ptr的好处是用户自己知道用move把资源管理权移动了。