读GoF的《Design Pattern》的收获还是很大的,虽然我还不能完全理解里面的精髓。其中有一个例子就加深了我对auto_ptr的理解。
在这本书中关于代理模式的讲解中提到了关于指针的代理(MyMath类是我自己实现用来演示的,它主要就是实现了一个Show的函数,可以用来打印提示符):
Code
//
// File :
// MyMath.h
// Description:
// About one implementation of auto ptr
// From GoF's Proxy Pattern
// Copyright : 2009 - 2010
// Author : Diwore
// Contact : dinglifedream@163.com
// Compiler : Visual Studio 2008
#include "MyMath.h"
#include <stdlib.h>
#pragma once
// ImagePtr
class ImagePtr
{
public:
ImagePtr(const char* a_szFileName);
virtual ~ImagePtr();
virtual MyImage* operator -> ();
virtual MyImage& operator* ();
private:
MyImage* LoadImage(const char* a_szFileName);
private:
MyImage* m_pImage;
char* m_pFileName;
};
// ImagePtr Implementation
ImagePtr::ImagePtr(const char* a_szFileName)
{
int len = sizeof(a_szFileName);
m_pFileName = new char[len + 1];
strncpy(m_pFileName, a_szFileName, len);
*(m_pFileName + len) = '\0';
m_pImage = NULL;
}
ImagePtr::~ImagePtr()
{
if(NULL != m_pImage)
delete m_pImage;
if(NULL != m_pFileName)
delete m_pFileName;
m_pImage = NULL;
m_pFileName = NULL;
}
MyImage* ImagePtr::operator -> ()
{
return LoadImage(m_pFileName);
}
MyImage& ImagePtr::operator * ()
{
return *LoadImage(m_pFileName);
}
MyImage* ImagePtr::LoadImage(const char* a_szFileName)
{
if(NULL == m_pImage)
m_pImage = new MyImage(a_szFileName);
return m_pImage;
}
/*
* Client Code :
* ImagePtr l_Image = ImagePtr("Image1.jpg");
* //(l_Image.operator ->())->Show();
* l_Image->Show();
*/
这里的ImagePtr类重载了->, *两个符号,这样我们在使用的时候就可以声明一个栈的变量,但是像指针一样使用它。只要我们在编写ImagePtr类的时候不要忘掉释放内存就很少会在出现内存泄露的问题了。即使在抛异常的情况下,因为它是栈上的变量,所以在它退出栈的时候就会自动调用它的析构函数了。
由这个的实现,然我想起了auto_ptr的使用,是否它的实现用的就是这样的思想呢?如下面我模仿它的实现:
Code
//
// File :
// my_auto_ptr2.h
// Description:
// Imitate the implementation of auto_ptr<TemplateName>
// Copyright : 2009 - 2010
// Author : Diwore
// Contact : dinglifedream@163.com
// Compiler : Visual Studio 2008
#pragma once
#define NULL 0
template <class TElement>
class my_auto_ptr2
{
public:
my_auto_ptr2();
my_auto_ptr2(TElement* a_pElement);
virtual ~my_auto_ptr2();
virtual TElement* operator -> ();
virtual TElement& operator * ();
virtual TElement* operator = (void* a_ptr);
private:
TElement* m_pElement;
};
// my_auto_ptr2 Implementation
template<class TElement>
my_auto_ptr2<TElement>::my_auto_ptr2()
{
m_pElement = new TElement();
}
template<class TElement>
my_auto_ptr2<TElement>::my_auto_ptr2(TElement* a_pElement)
{
m_pElement = a_pElement;
}
template<class TElement>
my_auto_ptr2<TElement>::~my_auto_ptr2()
{
if(NULL != m_pElement)
delete m_pElement;
m_pElement = NULL;
}
template<class TElement>
TElement* my_auto_ptr2<TElement>::operator -> ()
{
return m_pElement;
}
template<class TElement>
TElement& my_auto_ptr2<TElement>::operator * ()
{
return *m_pElement;
}
template<class TElement>
TElement* my_auto_ptr2<TElement>::operator = (void* a_ptr)
{
if(NULL != m_pElement)
{
delete m_pElement;
m_pElement = NULL;
}
m_pElement = (TElement*)a_ptr;
return m_pElement;
}
我刚开始还想到了另一种类似的方法,它可以实现推迟类的创建。
Code
//
// File :
// my_auto_ptr.h
// Description:
// Imitate the implementation of auto_ptr<TemplateName>
// Copyright : 2009 - 2010
// Author : Diwore
// Contact : dinglifedream@163.com
// Compiler : Visual Studio 2008
#pragma once
#define NULL 0
/*
* ICreator is used for a class to derive
* so that it can be used to create a class using
* a specifical parameters.
* Every class that derived from ICreator should
* implement Create() method.
*/
class ICreator
{
public:
virtual void* Create() = 0;
};
// my_auto_ptr class
// Used as auto_ptr
// Note: TElement should have a default constuctor
template <class TElement>
class my_auto_ptr
{
public:
my_auto_ptr();
my_auto_ptr(ICreator* a_Creator);
virtual ~my_auto_ptr();
virtual TElement* operator -> ();
virtual TElement& operator * ();
virtual TElement* operator = (void* a_ptr);
private:
TElement* LoadElement();
private:
TElement* m_pElement;
ICreator* m_pCreator;
};
// my_auto_ptr Implementation
template<class TElement>
my_auto_ptr<TElement>::my_auto_ptr()
{
m_pElement = NULL;
m_pCreator = NULL;
}
// ICreator is used to Create an element
// with user define constructor
template<class TElement>
my_auto_ptr<TElement>::my_auto_ptr(ICreator *a_Creator)
{
m_pElement = NULL;
m_pCreator = a_Creator;
}
template<class TElement>
my_auto_ptr<TElement>::~my_auto_ptr()
{
if(NULL != m_pElement)
delete m_pElement;
if(NULL != m_pCreator)
delete m_pCreator;
m_pElement = NULL;
m_pCreator = NULL;
}
template<class TElement>
TElement* my_auto_ptr<TElement>::operator -> ()
{
return LoadElement();
}
template<class TElement>
TElement& my_auto_ptr<TElement>::operator * ()
{
return *LoadElement();
}
template<class TElement>
TElement* my_auto_ptr<TElement>::operator = (void* a_ptr)
{
if(NULL != m_pElement)
{
delete m_pElement;
m_pElement = NULL;
}
m_pElement = (TElement*)a_ptr;
return m_pElement;
}
template<class TElement>
TElement* my_auto_ptr<TElement>::LoadElement()
{
if(NULL == m_pElement)
{
if(NULL != m_pCreator)
m_pElement = (TElement*)m_pCreator->Create();
else
m_pElement = new TElement();
}
return m_pElement;
}
只是这个在实现中需要注意两点:1. 对泛型的类型,一定要提供一个默认构造函数,否则会编译出错。2. 如果需要在构造的时候使用自己的构造函数,则需要编写一个派生于ICreator接口的类,然后声明一个指针实例传入my_auto_ptr类中。
缺点:
不管是我的模仿实现还是auto_ptr的本身实现,都有两个缺点:
1. 不能在STL中使用他们。个人理解是它们的实例是栈上的实例,而STL内部的成员要求基本上是堆上的实例,这样当它们的对象退出栈的时候,这个对象也就销毁了,这样存在STL中的指针就没什么意义了,所以C++标准之处这样做会产生不可预料的结果。
2. 不可以在构造函数上使用数组(my_auto_ptr2和auto_ptr中)。如一下的语句是不合法的:
my_auto_ptr2<char> str = my_auto_ptr2(new char[10]);
因为数组是要用delete []语句来销毁的,但是默认中的销毁是用delete来直接销毁指针的内容的,所以这样做会产生不可预料的结果。