智能指针之atuo_ptr源码剖析

由于c++没有垃圾回收机制,像堆只能手动开辟内存,手动释放,像栈只能系统开辟,系统释放,于是智能指针出现了,它实现了内存的手动开辟,系统释放,防止了内存泄漏问题;

我们知道, 栈对象在离开其作用域的时候, 会自动调用析构函数, 所以, 可以考虑把某一栈对象与某一堆内存绑定,且在其析构函数中释放堆内存,  那么, 在该栈对象离开作用域时, 堆内存自动释放, 这就是智能指针(本质是栈对象)的原理。  这个栈对象装得像指针一样, 所以我们称之为智能指针, 其实, 它不过就是个普通的栈对象而已。 

在c++ 98 中只有auto-ptr,这个指针是不完善的,现在几乎都要被摒弃了,但我们还是要去了解一下他的思想,明白为何要被摒弃的呢?以及在c++11上又是如何对他如何改进的呢?

//自动指针即auto_ptr,不同于scoped_ptr指针的是自动指针会转移使用权
//在进行赋值或者拷贝构造之后,原来的auto_ptr会失去对所管指针的拥有权,并且将自己的指针赋为NULL
//同时,在赋值和拷贝构造之后,原来的auto_ptr的指针会指向NULL,也是它最大的弊端之一;

#include <iostream>
using namespace std;


// 简单类
class A
{
public:
void fun()
{

}
};


template<class T>

// 类模板
class auto_ptr
{
public:

// explicit构造函数, 禁止类型转化
explicit auto_ptr(T *p = 0) throw()
: m_bIsOwner(p != 0), m_ptr(p)
{
cout << "debug1" << endl;
}

// owner转移
auto_ptr(const auto_ptr<T>& y) throw()
: m_bIsOwner(y.m_bIsOwner), m_ptr(y.release())
{
cout << "debug2" << endl;
}

// owner转移
auto_ptr<T>& operator=(const auto_ptr<T>& y) throw()
{
cout << "debug3" << endl;

if (this != &y) // 当前对象不是y对象
{
cout << "debug4" << endl;

if (m_ptr != y.get()) // 当前对象绑定的地址不是y对象绑定的地址
{
cout << "debug5" << endl;

if (m_bIsOwner) // 如果当前对象已经绑定堆, 则要先释放
{
cout << "debug6" << endl;
delete m_ptr;
}

cout << "debug7" << endl;

m_bIsOwner = y.m_bIsOwner; // 转移owner
}
else if (y.m_bIsOwner) // 当前对象与y绑定到同一块堆上, 且y是owner, 则把y的owner转移给当前对象
{
cout << "debug8" << endl;

m_bIsOwner = true;
}

cout << "debug9" << endl;

m_ptr = y.release(); // 让y不再是owner
}

cout << "debug10" << endl;

return *this; // 返回当前对象的引用
}

// 析构函数
~auto_ptr()
{
cout << "debug11" << endl;

if (m_bIsOwner) // 只有拥有owner属性才释放堆, 这样避免重复释放
{
cout << "debug12" << endl;

delete m_ptr; // 即使m_ptr是空指针也木有关系
}
}

// 重载对象的*运算符, 使得对象"看起来"像指针, 可以执行*p操作
T& operator*() const throw()
{
cout << "debug13" << endl;

return *get();
}

// 重载对象的->运算符
T *operator->() const throw()
{
cout << "debug14" << endl;

return get();
}

// 获得对象绑定的地址
T *get() const throw()
{
cout << "debug15" << endl;

return m_ptr;
}

// 去掉对象的owner属性
T *release() const throw()
{
cout << "debug16" << endl;

((auto_ptr<T> *)this)->m_bIsOwner = false;
return m_ptr;
}

private:
bool m_bIsOwner; // 对象是否拥有为owner的标志
T *m_ptr; // 对象绑定的指针
};


int main()
{
{
cout << "------------------------------" << endl;

// 用法错误, 因为构造函数中有explicit, 不允许类型转化
//auto_ptr<int> p = new int(10);
}


{
cout << "------------------------------" << endl;

// ok
auto_ptr<int> p(new int(10));
}


{
cout << "------------------------------" << endl;

// 下面代码有严重的运行期错误, 实际上是尝试delete栈上的内容
int a = 10;
//auto_ptr<int> p(&a);
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p(new int(10));

// 错误, p虽然"看似像"指针, 其本质是对象, delete p;是未定义行为
//delete p;
}


{
cout << "------------------------------" << endl;

int *q = new int(10);
auto_ptr<int> p(q);

// 错误, q释放一次, p释放一次, 重复释放啊
//delete q;
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0;

// 有debug3的打印, 但没有debug4, 知道原因了吧
p0 = p0;
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));

// 注意, 这是初始化, 不是复制, 所以不会有debug3的打印
auto_ptr<int> p1 = p0;
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1;

// 注意, 这才是赋值, 所有有debug3, debug4, debug5, debug7, debug9, debug10的打印
// 为什么没有debug6呢? 因为当前对象p1还不是owner
p1 = p0;
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1(new int(20));

// 有debug6的打印, 因为当先释放p1绑定的对象, 否则内存又泄露了啊
p1 = p0;
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));

// 把owner转给p1
auto_ptr<int> p1(p0);

// 终于见到你了, debug8
p0 = p1;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p(new int(10));

// 见到你了, debug13
cout << *p << endl;
}


{
cout << "------------------------------" << endl;

auto_ptr<A> p(new A());

// 终于见到你了, debug15
p->fun();
}


{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1(p0);
auto_ptr<int> p2(p1);

// 实际上, p3才是最后的winner, 才是最后的owner, 所以释放堆的重任在p3身上
auto_ptr<int> p3(p2);
}


{
cout << "------------------------------" << endl;

// oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
int *q = new int[3];
auto_ptr<int> p(q);
}


{
cout << "------------------------------" << endl;

// oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
int *q = new int[3];
auto_ptr<int> p(q);

// 已经说过, 下面语句会造成内存重复释放
//delete q;
}


// 最后说明一下, auto_ptr不适合做容器的元素, 这一点我们以后会再次讨论到


return 0;

}

原文链接:https://blog.csdn.net/stpeace/article/details/45155487

测试用例及其好的一篇博客,思路很清晰

原文地址:https://www.cnblogs.com/xcb-1024day/p/11331117.html