虫 虫
您好,感谢您接受我们的采访。能简单地介绍一下您的名字吗? |
我的英文名叫smart pointer,中文名我自己都不清楚,有人叫我“聪明指针”,有人又叫我“灵巧指针”,还有“智能指针”,都不太爽。不过就连smart pointer也不是名副其实,因为我并不是一个指针(pointer)。
您不是一个指针?那您是? |
其实我是某种形式的类(Class),不过我的特长就是模仿C/C++中的指针,当然大家也就稀里糊涂地叫我 pointer了。所以希望大家一定要记住两点:我是一个类而非指针,但我的特长是模仿指针。
那您怎么做到像指针的呢? |
易容术知道吗?C++的模板技术和运算符重载给了我很大的发挥空间。为了装得像,首先我必须是高度类型化的(strongly typed),模板给了我这个功能;其次我需要模仿指针主要的两个运算符->和*,那就需要进行运算符重载。
这样说似乎很抽象,您能详细说明一下吗? |
其实很容易勾画出中国人的基本特点:黄皮肤、黑头发(大部分老人除外)、能吃苦、能存钱、爱打麻将。当然不排除例外,我只是觉得我接触的大部分中国人都这样。同样,对于smart pointer,也可以给出一个大概的轮廓。
template<class T> class SmartPtr {
public:
SmartPtr(T* p = 0);
SmartPtr(const SmartPtr& p);
~SmartPtr();
SmartPtr& operator =(SmartPtr& p);
T& operator*() const {return *the_p;}
T* operator->() const {return the_p;}
private:
T *the_p;
}
正如我所说的,这只是一个大概的印象,很多东西是可以更改的。比如可以去掉或加上一些const,这都需要根据具体的应用环境而定。注意重载运算符*和->,正是它们使我看起来跟普通的指针很相像。而由于我是一个类,在构造函数、析构函数中您都可以通过恰当的编程达到一些不错的效果。
那您能给大家举一个例子吗? |
当然可以。比如C++标准库里的std::auto_ptr就是应用很广的一个例子。它的实现在不同版本的STL中虽有不同,但原理都是一样,大概是下面这个样子:
template<class X> class auto_ptr
{
public:
typedef X element_type;
explicit auto_ptr(X* p = 0) throw(): the_p(p) {}
auto_ptr(auto_ptr<X>& a) throw(): the_p(a.release()){}
auto_ptr<X>& operator =(auto_ptr<X>& rhs) throw()
{
reset(rhs.release());
return *this;
}
~auto_ptr() throw() {delete the_p;}
X& operator* () const throw() {return *the_p;}
X* operator-> () const throw() {return the_p;}
X* get () const throw() {return the_p;}
X* release() throw()
{
X* tmp = the_p;
the_p = 0;
return tmp;
}
void reset(X* p = 0) throw()
{
if (the_p!=p)
{
delete the_p;
the_p = p;
}
}
private:
X* the_p;
};
关于auto_ptr的使用我不想多说,这不是我们今天的主要话题。它的主要优点是不用delete,可以自动回收已经被分配的空间,由此可以避免资源泄露的问题。很多Java的拥护者经常不分黑白的污蔑C++没有垃圾回收机制,其实不过是贻笑大方而已。抛开在网上许许多多的商业化和非商业化的C++垃圾回收库不提,auto_ptr就足以有效地解决这一问题。并且即使在产生异常的情况下,auto_ptr也能正确地回收资源。这对于写出异常安全(exception-safe)的代码具有重要的意义。
那在使用smart pointer的过程中,是否有什么值得注意的问题呢? |
这个问题就太泛泛了,针对不同的smart pointer,有不同的注意事项。比如auto_ptr,您就不能把它用在标准容器里,因为它只在内存中保留一份实例。不过我相信把握我前面说的两个原则:smart pointer是类而不是指针,是模仿指针,那么一切问题都好办。比如,smart pointer作为一个类,那么以下的做法就可能有问题。
SmartPtr<int> p;
if(p==0)
if(!p)
if(p)
很显然,p不是一个真正的指针,这么做可能出错。而SmartPtr的设计也是很重要的因素。您可以加上一个bool SmartPtr::null() const来进行判断。如果坚持非要用上面的形式,那也未尝不可。
我们就加上operator void* ()试试:
template<class T> class SmartPtr {
public:
...
operator void*() const {return the_p;}
...
private:
T* the_p;
};
这招在basic_ios中就使用过了。这里也可以更灵活地处理,比如类本身需要operator void*()这样地操作,那么上面这招就不灵了。那我们还有重载operator !()等等方法。不怕做不到,只怕想不到。
您能总结一下smart pointer的实质吗? |
smart pointer的实质就是一个外壳,一层包装。正是多了这层包装,我们可以做出许多普通指针无法完成的事,比如前面资源自动回收,或者自动进行引用记数,比如ATL中CComPtr和CComQIPtr这两个COM接口指针类。然而事事都是一把双刃剑,正由于多了这些功能,又会使smart pointer丧失一些功能。一定切记,画虎画皮难画骨,smart pointer毕竟和真正的指针是大大不同的。
非常您接受我们的采访。祝您在中国玩得愉快! |
谢谢