auto_ptr
用法一:
std::auto_ptr<ClassName>m_example(new ClassName ());
用法二:
std::auto_ptr<ClassName >m_example;
m_example.reset(new ClassName());
用法三(指针的赋值操作):
std::auto_ptr<ClassName>m_example1(new ClassName());
std::auto_ptr<ClassName>m_example2(new ClassName());
m_example2=m_example1;
auto_ptr会自动释放内存,则C++会把m_example所指向的内存回收,使m_example1 的值为NULL,所以在C++中,应绝对避免把auto_ptr放到容器中:vector<auto_ptr<MyClass>>m_example;
当用算法对容器操作的时候,很难避免STL内部对容器中的元素实现赋值传递,这样便会使容器中多个元素被置位NULL。正确运用auto_ptr让你的代码更加安全,而对auto_ptr危险但常见的误用会引发间断性发作、难以诊断的bug,而auto_ptr只是众多可能的智能指针之一。许多商业库提供了更复杂的智能指针,用途广泛而令人惊异,从管理引用的数量到提供先进的代理服务。可以把标准C++ auto_ptr看作是一个简易、通用的智能指针,它不包含所有的小技巧,不像专用的或高性能的智能指针。
模拟实现auto_ptr中赋值运算符重载:
#include<iostream>
using namespace std;
template <class T>
class AutoPtr
{
public:
AutoPtr(T* ptr = NULL)
: _ptr(ptr)
{
cout << "AutoPtr()" << endl;
}
AutoPtr(AutoPtr &ap)
:_ptr(ap._ptr)//移交管理权
{
if (_ptr)
{
ap._ptr = NULL;
}
}
AutoPtr<T>& operator =(AutoPtr &ap)
{
if (this != &ap)
{
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;
return *this;
}
}
~AutoPtr()
{
if (_ptr)
{
cout << "~AutoPtr()" << endl;
delete _ptr;
_ptr = NULL;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
void TestAutoPtr()
{
AutoPtr<int> ap1(new int);
*ap1 = 10;
AutoPtr<int> ap2 = ap1 ;
AutoPtr<int> ap3(new int(20));
ap2 = ap3;
}
int main()
{
TestAutoPtr();
system("pause");
return 0;
}
打开内存窗口就会发现将ap1赋值给ap2之后ap1的空间也会释放
auto_ptr特点:
1. 利用特点“栈上对象在离开作用范围时会自动析构”。
2. 对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。
3. auto_ptr通过在栈上构建一个对象a,对象a中
包含了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的。
实例如下:
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
A()
{
cout<<"A()"<<endl;
}
~A()
{
cout<<"~A()"<<endl;
}
};
void fun(bool isThrow)
{
//A *pa= new A; // 方法1
//try
//{
// if(isThrow)
// throw " throw a date ";
//}
//catch(const char* e)
//{
//
// throw;
//}
auto_ptr<A> A(new A); // 方法2
try
{
if(isThrow)
throw "throw a date";
}
catch(const char* e)
{
throw;
}
}
int main()
{
try
{
fun(true);
}
catch(...)
{
cout<<"caught"<<endl;
}
system("pause");
}
如果采用方案1,那么必须考虑到函数在因throw异常的时候释放所分配的内存,这样造成的结果是在每个分支处都要很小心的手动 delete A;。
如果采用方案2,那就无需操心何时释放内存,不管fun()因何原因退出, 栈上对象A的析构函数都将调用,因此托管在之中的指针所指的内存必然安全释放。
至此,智能指针的优点已经很明了了。
但是要注意使用中的一个陷阱,那就是指针的托管权是会转移的。使用赋值运算符重载操作后源变量指针就会变空,因此再用源指针调用就会出现问题。要避免这个问题,可以考虑使用采用了引用计数的智能指针,例如boost::shared_ptr等。auto_ptr不会降低程序的效率,但auto_ptr不适用于数组,auto_ptr根本不可以大规模使用。 shared_ptr也要配合weaked_ptr,否则会很容易触发循环引用而永远无法回收内存。
scopedptr
因为智能指针容易出现拷贝时释放两次的情况,所以ScopedPtr主要是进行防止拷贝,防止拷贝的两条必须要满足的条件是:
(1)设置保护限定符,
(2)对拷贝构造函数和赋值运算符重载进行之声明不定义。
如若只有(2),没有设置保护限定符,若在类外进行定义后,则会出现问题,所以说这两个条件是必不可少的。这样就能够避免上面所出现的问题,但是这样就造成了它在功能上的缺陷。
//ScopedPtr 实现简单的智能指针进行防拷贝
template <class T>
class ScopedPtr
{
public:
ScopedPtr(T * ptr)
:_ptr(ptr)
{ }
~ScopedPtr()
{
cout << "delete" << endl;
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}
T & operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* GetPtr()
{
return _ptr;
}
protected: //防止拷贝
ScopedPtr(ScopedPtr<T> & ap);
ScopedPtr<T> & operator=(ScopedPtr<T> & ap);
private:
T * _ptr;
};
当调用拷贝构造和赋值运算符重载时就会出现下面错误:
1>f:\程序\智能指针\智能指针\scopedptr.cpp(46) : error C2248: “ScopedPtr<T>::ScopedPtr”: 无法访问protected 成员(在“ScopedPtr<T>”类中声明)