一、RAII思想的引入
#include <iostream>
using namespace std;
#if 0
// C++中动态申请的资源需要用户自己手动释放
// 如果操作不当,容易造成内存泄漏
// 能否做到让资源自动被释放:RAII
// RAII : 将资源交给对象管理,对象被销毁时自动调用析构函数可以在析构函数中将资源释放掉
template<class T>
class smart_ptr
{
public:
smart_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
// 具有指针类似的效果
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~smart_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
T* Get()
{
return _ptr;
}
private:
T* _ptr;
};
void TestSmartPtr1()
{
//int* p = new int(10);
//smart_ptr<int> sp(p);
// ...
//delete p;
smart_ptr<int> sp(new int(10));
*sp = 100;
// ...
}
struct A
{
int a;
int b;
int c;
};
void TestSmartPtr2()
{
smart_ptr<int> sp1(new int(10));
*sp1 = 100;
smart_ptr<A> sp2(new A());
sp2->a = 10;
sp2->b = 20;
sp2->c = 30;
}
// 上述smart_ptr已经很好了,达到了刚开始的需求
// 新的问题:类中涉及到资源的管理时,如果没有显式实现拷贝构造 已经 赋值运算符重载
// 则编译器会以浅拷贝的方式实现,smart_ptr就会有问题
// 解决:能够深拷贝解决吗? 答案:不可以
int main()
{
// TestSmartPtr1();
TestSmartPtr2();
return 0;
}
#endif
二、对于memory库中auto_ptr的使用
// 智能指针:对原生态指针进行了封装,以类的方式管理用户的资源,在析构方法中将资源释放掉
// RAII + 具有指针类似的行为 + 如何解决浅拷贝的问题
// 因此:解决深拷贝方式的不同,实现了不同版本的智能指针
// 第一种智能指针
// C++98 auto_ptr 原理:
// RAII + 具有指针类似的行为 + 资源转移来解决浅拷贝问题的
#include <memory>
#if 0
class A
{
public:
A()
{
cout << "A::A()" << endl;
}
~A()
{
cout << "A::~A()" << endl;
}
int _a;
int _b;
};
void TestAutoPtr()
{
auto_ptr<A> ap1(new A());
ap1->_a = 10;
ap1->_b = 20;
// 当ap1拷贝构造ap2时,ap1会将自己管理的资源
// 转移给ap2,然后ap1不在管理了,即:指向空
auto_ptr<A> ap2(ap1);
ap2->_a = 100;
ap2->_b = 200;
// ap1->_a = 10; // 运行崩溃:因为ap1将资源转移走之后指向NULL
auto_ptr<A> ap3;
// 当ap2给ap3赋值时,ap2会将自己管理的资源
// 转移给ap3,然后ap2不在管理资源了,即:指向空
ap3 = ap2;
A* pa1 = new A();
A* pa2 = nullptr;
pa2 = pa1;
pa1->_a = 10;
pa2->_b = 20;
}
int main()
{
TestAutoPtr();
return 0;
}
#endif
三、实现自己自己的auto_ptr
#if 0
namespace bite
{
template<class T>
class auto_ptr
{
public:
// RAII
auto_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
// 具有指针类似的行为
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// 资源转移
auto_ptr(auto_ptr<T>& ap)
: _ptr(ap._ptr)
{
ap._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;
};
}
#endif
四、auto_ptr版本二:加入资源权限的转移
// C++98中auto_ptr的改造:
// RAII + 具有指针类似的行为 + 解决浅拷贝的方式:资管管理权限的转移
// 资管管理权限: 对资源释放的权限
namespace bite
{
template<class T>
class auto_ptr
{
public:
// RAII
auto_ptr(T* ptr = nullptr)
: _ptr(ptr)
, _owner(false)
{
if (_ptr)
{
_owner = true;
}
}
~auto_ptr()
{
if (_ptr && _owner)
{
delete _ptr;
_ptr = nullptr;
_owner = false;
}
}
// 具有指针类似的行为
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// 资源转移
auto_ptr(const auto_ptr<T>& ap)
: _ptr(ap._ptr)
, _owner(ap._owner)
{
ap._owner = false;
}
auto_ptr<T>& operator=(const auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr && _owner)
{
delete _ptr;
}
_ptr = ap._ptr;
_owner = ap._owner;
ap._owner = false;
}
return *this;
}
private:
T* _ptr;
mutable bool _owner; // 如果为true,表明该对象具有释放资源的权限
};
}
class A
{
public:
A()
{
cout << "A::A()" << endl;
}
~A()
{
cout << "A::~A()" << endl;
}
int _a;
int _b;
};
void TestAutoPtr1()
{
bite::auto_ptr<A> ap1(new A());
ap1->_a = 10;
ap1->_b = 20;
// 当ap1拷贝构造ap2时,ap1会将自己管理的资源
// 转移给ap2,然后ap1不在管理了,即:指向空
bite::auto_ptr<A> ap2(ap1);
ap2->_a = 100;
ap2->_b = 200;
ap1->_a = 10; // 运行成功
bite::auto_ptr<A> ap3;
// 当ap2给ap3赋值时,ap2会将自己管理的资源
// 转移给ap3,然后ap2不在管理资源了,即:指向空
ap3 = ap2;
A* pa1 = new A();
A* pa2 = nullptr;
pa2 = pa1;
pa1->_a = 10;
pa2->_b = 20;
delete pa1;
}
void TestAutoPtr2()
{
bite::auto_ptr<A> ap1(new A());
ap1->_a = 10;
ap1->_b = 20;
if (true)
{
bite::auto_ptr<A> ap2(ap1);
ap1->_a = 100;
ap2->_b = 200;
}
// 当if结束时,ap2,退出了自己的程序范围,调用析构函数,ap2将资源释放了
// 因此ap1内部的指针就变成了野指针
ap1->_a = 1000;
ap1->_b = 2000;
}
// C++标准建议:在什么情况下都不要使用auto_ptr
int main()
{
// TestAutoPtr1();
TestAutoPtr2();
return 0;
}