目录
一.什么是智能指针?
智能指针包括两部分:
1.RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文柄、网络连接、互斥量等等)的简单技。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好:
- 不需要显式地释放资
- 采用这种方式,对象所需的资源在其生命期内始终保持有
2.像指针一样(实现像指针一样的功能)
- 重载operator*
- 重载operator->
二.为什么需要智能指针??
为了更好的解决以下两个问题所以出现了智能指针
1.当我们malloc出来的内存没有去释放时就会出现问题,就会存在内存泄漏问题
2.异常安全问题。当在malloc和delete之间出现出现抛异常时,那么也会出现安全异常问题
三. 思维导图
代码:
template<class T>
class SmartPtr
{
public:
//RAII
SmartPtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
free _ptr;
}
//像指针一样
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
void MergeSort()
{
int* tmp = new int;
// 讲tmp指针委托给了sp对象,用时老师的话说给tmp指针找了一个可怕的女朋友!天天管着你,直到你go
SmartPtr<int> sp(tmp);
// _MergeSort(a, 0, n - 1, tmp);
// 这里假设处理了一些其他逻辑
vector<int> v(1000000000, 10);
//
}
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10;
cout << *sp1 << endl;
SmartPtr<int> sparray(new Date);
// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
sparray->_year = 2018;
sparray->_month = 1;
sparray->_day = 1;
system("pause");
return 0;
}
四.模拟实现三个智能指针
这些智能指针的头文件在#inlude<memory>中
1.auto_ptr
auto_ptr是C++ 98中出现的智能指针,也是最早被创造出来的智能指针。
原理:采用管理权转移的方法,当拷贝构造一个对象时,就会将管理权转交给新的对象。将原来的对象置空。(这一点可在后边的代码部分中体现出来,也可以打开监视窗口进行监控,这里就不做演示了)。
缺点:由于采用了管理权转移的方式,那么缺陷也随之而来,我们不能使用该智能指针去拷贝构造。如果拷贝构造后会将原来的对象置空,那么原来的对象将不能被赋值(虽然该方法解决了在析构的时候会被析构两次的缺点,但确产生了新的缺点,这里很像是浅拷贝)
注意:该智能指针存在很大缺陷所以在公司中不会让用,但是面试中会被经常问到
1.1 模拟实现部分
template<class T>
class Auto_Ptr
{
public:
Auto_Ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
~Auto_Ptr()
{
if (_ptr)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
Auto_Ptr(Auto_Ptr<T>& cp)
:_ptr(cp._ptr)
{
cp._ptr = nullptr;
}
Auto_Ptr<T>& operator=(Auto_Ptr<T> ap)
{
if (this != &ap)
{
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr; //可以很明显看出原来的被置空
}
return *this;
}
private:
T* _ptr;
};
2.unique_ptr
该智能指针是在Boost库中(在C++ 11中引入了)
原理:为了避免auto_ptr出现的问题,该智能指针简单粗暴的直接不让进行拷贝构造和赋值。
缺点:不能进行拷贝构造
注意:在面试中如果要让写一个智能指针没有其他要求,最好写unique_ptr最容易实现
2.1模拟实现
template<class T>
class Unique_Ptr
{
Unique_Ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
~Unique_Ptr()
{
if (_ptr)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
//C++ 98
//Unique_Ptr(Unique_Ptr<T>& cp);
//Unique_Ptr<T>& operator=(Unique_Ptr<T> ap);
//C++ 11
Unique_Ptr(Unique_Ptr<T>& cp) = delete;
Unique_Ptr<T>& operator=(Unique_Ptr<T> ap) = delete;
T* _ptr;
};
3.shared_ptr
该智能指针是在Boost库中(在C++ 11中引入了)
原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。例如:最后一个出宿舍的人必须将门锁了。
- shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象
- 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了
缺点:会有循环引用的问题,存在线程安全问题
3.1 图解原理
3.2 模拟实现
template<class T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = nullptr)
:_ptr(ptr)
, pCount(new int(1))
{
if (_ptr == nullptr)
*pCount = 1;
}
~Shared_Ptr()
{
if (_ptr && ((*pCount)--) == 0)
{
delete _ptr;
delete pCount;
}
}
Shared_Ptr(Shared_Ptr<T>& cp)
{
_ptr = cp._ptr;
pCount = cp.pCount;
if (_ptr)
++(*pCount);
}
Shared_Ptr<T>& operator=(Shared_Ptr<T> ap)
{
if (_ptr != ap._ptr)
{
//释放旧资源
if (_ptr && pCount == 0)
{
delete _ptr;
delete pCount;
}
_ptr = ap._ptr;
pCount = ap.pCount;
if (_ptr)
++(*pCount);
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int UseCount()
{
return *pCount;
}
private:
T* _ptr;
int* pCount;
};