模拟实现C++标准库中的auto_ptr
目录
一、 引言
C++标准库中有一个auto_ptr可供我们参考。这是一个用来包装原生指针(native pointer)的对象,声名狼藉的内存漏洞(memory leak)问题可籍此获得解决。根源在于对象实例化后变量离开作用域会自动被编译器销毁。
--------参考文献 侯捷.STL源码剖析[M].武汉:华中科技大学出版社,2002.6:80-81.
警告:你必须小心在意,不要访问已经被free函数释放了的内存。这个警告看上去很显然,但是这里仍然存在一个很微妙的问题。假定你对一个指向动态分配的内存的指针进行了复制,而且这个指针的几份拷贝散布于程序各处。你无法保证当你使用其中一个指针时它所指向的内存是不是已被另一个指针释放。另一方面,你必须确保程序中所有使用这块内存的地方在这块内存被释放之前停止对它的使用。
--------参考文献 Kenneth A. Reek.C和指针[M].北京:人民邮电出版社,2008.4:225-226.
二、 潜在问题的讨论
如图1所示,指针A指向堆内存;如果此时将指针A的值赋值给指针B,就意味着两个指针指向同一块堆内存。带来的问题是重复free从而程序崩溃。比如说,释放指针A所指向的内存,指针B并无法知晓这件事,故而当指针B去释放该块内存时,相当于释放已经释放的内存,程序就会崩溃!!!
举这个例子的用意是告诉我们,auto_ptr是不允许赋值的。
三、 代码实现
3.1 auto_ptr简单实现
命名说明,为了和C++标准库中的auto_ptr区分,在这里命名为mauto_ptr(m的意思是my)。个人觉得“->”运算符重载时所确定的返回值类型难以理解,现单独拿出来予以讨论:
部分代码段:
从代码可以看出,返回值就是类内成员_ptr自身。如何理解这个问题?不是说过了吗,auto_ptr是对原生指针进行的一次包装,重载成员运算符肯定就是返回原生指针自己了。
template<typename T>
T* mauto_ptr<T>::operator->() //成员运算符重载
{
return _ptr;
}
完整代码段:
#include<iostream>
#include<map>
using namespace std;
template<typename T>
class mauto_ptr //使得指向堆上空间的指针离开作用域自行释放
{
public:
mauto_ptr(T *ptr); //构造方法
~mauto_ptr(); //析构方法
mauto_ptr(mauto_ptr& src); //拷贝构造
T& operator*(); //解引用运算符进行重载
T* operator->(); //成员运算符进行重载
private:
T *_ptr;
};
template<typename T>
mauto_ptr<T>::mauto_ptr(T *ptr) //构造方法
{
_ptr = ptr; //传入的指针类型是指向堆上的指针
}
template<typename T> //析构方法
mauto_ptr<T>::~mauto_ptr()
{
if (NULL != _ptr)
{
delete _ptr;
_ptr = NULL;
}
}
template<typename T>
mauto_ptr<T>::mauto_ptr(mauto_ptr& src) //拷贝构造方法
{
_ptr = src._ptr;
src._ptr = NULL;
}
template<typename T>
T& mauto_ptr<T>::operator*() //解引用运算符进行重载
{
return *_ptr;
}
template<typename T>
T* mauto_ptr<T>::operator->() //成员运算符进行重载
{
return _ptr;
}
3.2 用于举例的一个类
令类mauto_ptr实例化的对象指向该类,调用成员运算符会发生什么?
struct test_mauto_ptr
{
test_mauto_ptr(int val = int()):_value(val){}
int value() { return _value; } //返回元素值
int _value;
};
3.3 测试用例
有一处比较难以理解,特拿出来进行讨论:
mauto_p3->value() //mauto_p3.operator->()->value()
编译器可以进行优化,所以重载函数写好以后, 使用该类实例化的对象就与使用普通指针一样方便!
int main()
{
mauto_ptr<int> auto_p1(new int(10));
int *p = new int(20);
mauto_ptr<int> auto_p2(p);
cout << "构造方法:" << *auto_p1 << endl;
cout << "拷贝构造:" << *auto_p2 << endl;
mauto_ptr<test_mauto_ptr>auto_p3(new test_mauto_ptr(10));
cout << "验证成员访问运算符的重载:" << mauto_p3->value() << endl; //mauto_p3.operator->()->value()
system("pause");
return 0;
}
四、 总结
自动释放原生指针指向的堆内存;
进行拷贝构造之后,用于进行拷贝的指针不可以再进行解引用;
不提供“=”运算符重载;
禁止智能指针之间拷贝构造以及“=”;