1.智能指针的使用及其原理
1.1 RAII
- RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源的简单技术
- 在对象构造的时候获取资源,接着控制对资源的访问使之在对象的生命周期期间内始终保持有效,最后在对象析构的时候释放资源。这么做有两个好处
1.不需要显示地释放内存
2.采用这种方式,对象所需的资源在生命周期内始终保持有效。 - 用昨天抛异常导致无法释放空间的地方举例:
template<class T>
class Smartptr
{
public:
Smartptr(T* ptr = nullptr)
:_ptr(ptr)
{
}
~Smartptr()
{
if (_ptr)
{
delete _ptr;
cout << "delete[] "<< endl;
}
}
private:
T* _ptr;
};
double Division(int a, int b)
{
if (b == 0)
throw "Division by zero condition!";
else
return ((double)a / (double)b);
}
void Func()
{
int* array = new int[10];
memset(array, 0, sizeof(int)* 10);
Smartptr<int> s(array);
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
system("pause");
}
- 结果
由图可知在抛出了异常的同时释放了空间。
1.2智能指针的作用
- 上面写的Smartptr还不能被称为智能指针,因为它还没有具备指针的行为。指针可以解引用,也可以通过->访问,空间中的内容。因此Smartptr还需重载*,->重载,才能像指针一样使用。
template<class T>
class Smartptr
{
public:
Smartptr(T* ptr = nullptr)
:_ptr(ptr)
{
}
~Smartptr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
struct Date
{
int year;
int month;
int day;
};
int main()
{
Smartptr<int> p(new int);
*p = 10;
cout << *p << endl;
//需要注意的是这里应该是p1.operator()->()->year = 2019
//本来应该是p1->->year这里语法上为了可读性,省略了一个->
Smartptr<Date> p1( new Date);
p1 -> year = 2019;
p1 -> month = 3;
p1 -> day = 31;
system("pause");
}
总结:
智能指针的原理:
1.RAII特性
2.重载operator*和operator->,具有指针一样的行为。
1.3 std :: auto_ptr(C++98)
- C++98的库中就提供auto_ptr的智能指针。下面是代码实现
//C++的智能指针都存在<memory>这个库里边
#include<memory>
class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
~Date()
{
cout << "~Date" << endl;
}
int _year;
int _month;
int _day;
};
int main()
{
auto_ptr<Date> ap(new Date);
auto_ptr<Date> copy(ap);
ap->_year = 2019;
system("pause");
}
运行结果:
结论:
auto_ptr的问题:当对象拷贝赋值之后,前面的对象就悬空了。由于缺陷明显,所以很多公司都禁止使用auto_ptr智能指针。
1.4 std::unique_ptr(C++11)
- unique_ptr的设计思路非常简单,就是不让拷贝和赋值
#include<memory>
class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
~Date()
{
cout << "~Date" << endl;
}
int _year;
int _month;
int _day;
};
int main()
{
unique_ptr<Date> up(new Date);
unique_ptr<Date> copy(up);
system("pause");
}
运行失败,失败原因:
1.5 std::shared_ptr
- shared_ptr原理:通过引用计数的方法来实现多个shared_ptr对象之间共享资源。例子:宿舍出门的时候总是让最后一个人关门。
1.shared_ptr在其内部,给每个资源维持了一份计数,用来记录该资源被几个对象共享
- 在对象被销毁的时候,就说明这个对象不使用该资源了,对象的引用-1
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
- 如果不是0,说明除了自己还有别人在使用这个资源,不能释放该资源,不然其他对象都会变成野指针。
代码实现:
int main()
{
shared_ptr <Date> up(new Date);
shared_ptr <Date> copy(up);
cout << "count:" << up.use_count() << endl;
cout << "count:" << copy.use_count() << endl;
system("pause");
}
结果:
2.模拟实现auto_ptr, unique_ptr, shared_ptr
2.1 模拟实现auto_ptr
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr = nullptr)
:_ptr(ptr)
{
}
~AutoPtr()
{
if (_ptr)
{
delete _ptr;
}
}
//一旦发生拷贝,将ptr中资源转移到当前对象中,然后让ptr与其管理的资源单开联系
//避免了一个空间被多个对象使用时造成的崩溃问题。
AutoPtr(AutoPtr<T>& ptr)
:_ptr(ptr._ptr)//初始化
{
ptr._ptr = NULL;//将被拷贝赋值为0
}
AutoPtr<T>& operator= (AutoPtr<T>& ptr)
{
//日常检查是不是自己为空
if (this != &ptr)
{
if (_ptr)
delete _ptr;
//将ptr对象转移到当前对象中
_ptr = ptr._ptr;
ptr._ptr == NULL;
}
return *this;//operator运算一定要有返回值
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
int main()
{
AutoPtr<Date> ap(new Date);
AutoPtr<Date> copy(ap);
ap->_year = 2019;
system("pause");
}
2.2 模拟实现unique_ptr
template<class T>
class UniquePtr
{
public:
UniquePtr(T* ptr = nullptr)
:_ptr(ptr)
{
}
~UniquePtr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
//C++98防拷贝:只声明不实现+声明成私有
UniquePtr(UniquePtr<T> cosnt &);
UniquePtr<T> operator= (UniquePtr<T> const &);
//C++11防拷贝:delete
UniquePtr(UniquePtr<T> cosnt &) = delete;
UniquePtr<T> operator= (UniquePtr<T> const &) = delete;
private:
T* _ptr;
};
C++98的防拷贝有一个缺陷,如果友元存在的话会出现错误。
2.3 模拟实现shared_ptr
简单的实现(网络还没学网络…)
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = nullptr)
:_ptr(ptr)
,_Rcount(new int(1))
{
}
~SharedPtr()
{
if (_ptr)
{
delete _ptr;
}
}
SharedPtr(SharedPtr<T>& ptr)
:_ptr(ptr._ptr)
, _Rcount(ptr._Rcount)
{
++(*_Rcount);//计数空间++
}
SharedPtr<T> operator= (SharedPtr<T>& s)
{
if (this != &s)
//if(_ptr != s._ptr)
{
//如果被赋值的数计数为1的话,直接将原计数空间和原地址释放
if (--(*_Rcount) == 0)
{
delete _ptr;
delete _Rcount;
}
//将赋值计数空间和赋值地址给到被赋值计数空间和被赋值地址
_ptr = s._ptr;
_Rcount = s._Rcount;
//计数++
++(*_Rcount);
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;//指向管理资源的指针
int* _Rcount;//引用计数
};