C++智能指针

C++没有像Java.NET一样的垃圾回收机制,内存泄露的预防和解决一向是程序编写中的要点。

如果在一个类中进行了动态分配,一般实现“智能指针”行为或值型行为。下面是一个“智能指针”行为类的例子:

#include <iostream>

class Hello

{

public:

       Hello(){std::cout<<"Hello()"<<std::endl;}

       ~Hello(){std::cout<<"~Hello()"<<std::endl;}

};

class World

{

public:

       World(Hello*);

       World(const World&);

       World&operator=(const World&);

       ~World();

private:

       struct SmartP

       {

              friend World;

              SmartP(Hello*);

              ~SmartP();

              unsigned use;

              Hello *ptr;

       };

       SmartP *ptr;

};

inline World::World(Hello*p):ptr(new SmartP(p)){}

inline World::World(const World&w):ptr(w.ptr){++(ptr->use);}

inline World&World::operator=(const World&w)

{

       ++(w.ptr->use);

       this->~World();

       ptr=w.ptr;

       return *this;

}

inline World::~World(){if(0==--(ptr->use))delete ptr;}

inline World::SmartP::SmartP(Hello*p):ptr(p),use(1){}

inline World::SmartP::~SmartP(){delete ptr;}

对这段代码不是很明白的同学可以参照《C++ Primer 中文版 第四版》13.5.1 定义智能指针类。

如果在程序中大量使用这种技巧,是非常劳民伤财的。于是,我们追求一种通用的解决技巧——智能指针。

template<typename T>class SmartP

{

public:

       SmartP(T* =0)throw();

       SmartP(const SmartP&)throw();

       SmartP&operator=(const SmartP&)throw();

       ~SmartP()throw();

       T&operator*()const throw();

       T*operator->()const throw();

private:

       T*p_;

       unsigned*use_;

};

template<typename T>inline SmartP<T>::SmartP(T*p):p_(p),use_(new unsigned(1)){}

template<typename T>

inline SmartP<T>::SmartP(const SmartP&s):p_(s.p_),use_(s.use_){++*use_;}

template<typename T>inline SmartP<T>&SmartP<T>::operator=(const SmartP&s)

{

       ++*s.use_;

       this->~SmartP();

       p_=s.p_;

       use_=s.use_;

       return *this;

}

template<typename T>inline SmartP<T>::~SmartP()

{if(0==--*use_){delete p_;delete use_;}}

template<typename T>inline T&SmartP<T>::operator*()const{return *p_;}

template<typename T>inline T*SmartP<T>::operator->()const{return p_;}

在这里我们使用了使用计数分离技术。World类在使用智能指针后变成了:

class World

{

public:

       World(Hello*);

private:

       SmartP<Hello> ptr;

};

inline World::World(Hello*p):ptr(p){}

真的非常的简练。

在这之后的使用过程中,我发现了一个BUG

class Hello

{

public:

       Hello(){std::cout<<"Hello()"<<std::endl;}

       ~Hello(){std::cout<<"~Hello()"<<std::endl;}

       void Func(){SmartP<Hello> p=this;}

};

void Test()

{

       SmartP<Hello> p=new Hello();

       p->Func();

}

这会导致重复释放,一个危险的问题。原计划在智能指针中加一个全局Map变量来解决这个问题。但是,由于全局依赖问题和效率问题,改为将使用计数与对象捆绑在一起。那样的话,找到动态分配的对象的同时找到了使用计数变量。

我们知道,多态使用的前提是:使用指向派生类对象的基类指针(或引用派生类对象的引用)调用基类的virtual函数。但是,我们设计的智能指针却没有这个功能。这个问题有两种解决方案。第一种解决方案:重载默认转换操作符。第二种解决方案:重载构造函数和赋值操作符。为了使软件可维护,我们采用第一种解决方案。

这两个问题的同时解决使得下面的代码有一定的难度。

template<typename T>class SmartP

{

public:

       SmartP(T*)throw();

       SmartP(T*,unsigned)throw();

       SmartP(const SmartP&)throw();

       SmartP&operator=(const SmartP&)throw();

       ~SmartP()throw();

       T&operator*()const throw();

       T*operator->()const throw();

       template<typename T>operator SmartP<T>()const throw();

private:

       unsigned&Count()const throw();

       T*p_;

};

template<typename T>inline SmartP<T>::SmartP(T*p):p_(p){++Count();}

template<typename T>inline SmartP<T>::SmartP(T*p,unsigned):p_(p){}

template<typename T>inline SmartP<T>::SmartP(const SmartP&s):p_(s.p_){++Count();}

template<typename T>inline SmartP<T>&SmartP<T>::operator=(const SmartP&s)

{

       ++s.Count();

       this->~SmartP();

       p_=s.p_;

       return *this;

}

template<typename T>inline SmartP<T>::~SmartP()

{if(0==--Count()){p_->~T();delete &Count();}}

template<typename T>inline T&SmartP<T>::operator*()const{return *p_;}

template<typename T>inline T*SmartP<T>::operator->()const{return p_;}

template<typename T>

inline unsigned&SmartP<T>::Count()const{return *((unsigned*)(p_)-1);}

template<typename T0>template<typename T1>

SmartP<T0>::operator SmartP<T1>()const{return SmartP<T1>(p_);}

#define NEW(T,P) /

SmartP<T>(new(new(new unsigned char[sizeof(unsigned)+sizeof(T)])/

unsigned(1)+1)T P,0)

很多时候,我们在定义一个智能指针的时候,没有必要立即用它指向一个对象。这是“委托模式”的一个事例。仔细的读者已经发现我们的智能指针现在不具有这种功能了。

很多时候,我们在智能指针的生命期结束之前,需要将使用计数减一。其中一部分情况是为了效率,另一部分是为了实现类似流对象的close函数的功能。

template<typename T>class SmartP

{

public:

       SmartP()throw();

       SmartP(T*)throw();

       SmartP(T*,unsigned)throw();

       SmartP(const SmartP&)throw();

       SmartP&operator=(const SmartP&)throw();

       ~SmartP()throw();

       T&operator*()const throw();

       T*operator->()const throw();

       template<typename T>operator SmartP<T>()const throw();

private:

       unsigned&Count()const throw();

       T*p_;

};

template<typename T>inline SmartP<T>::SmartP():p_(0){}

template<typename T>inline SmartP<T>::SmartP(T*p):p_(p){++Count();}

template<typename T>inline SmartP<T>::SmartP(T*p,unsigned):p_(p){}

template<typename T>

inline SmartP<T>::SmartP(const SmartP&s):p_(s.p_){if(p_)++Count();}

template<typename T>inline SmartP<T>&SmartP<T>::operator=(const SmartP&s)

{

       if(s.p_)++s.Count();

       this->~SmartP();

       p_=s.p_;

       return *this;

}

template<typename T>inline SmartP<T>::~SmartP()

{if(p_&&0==--Count()){p_->~T();delete &Count();}p_=0;}

template<typename T>inline T&SmartP<T>::operator*()const{return *p_;}

template<typename T>inline T*SmartP<T>::operator->()const{return p_;}

template<typename T>

inline unsigned&SmartP<T>::Count()const{return *((unsigned*)(p_)-1);}

template<typename T0>template<typename T1>

SmartP<T0>::operator SmartP<T1>()const{return SmartP<T1>(p_);}

#define NEW(T,P) /

SmartP<T>(new(new(new unsigned char[sizeof(unsigned)+sizeof(T)])/

unsigned(1)+1)T P,0)

下面的代码可以看做是SmartP的简单测试。

class Hello

{

public:

       Hello(){SmartP<Hello>p=this;std::cout<<"Hello()"<<std::endl;}

       virtual ~Hello(){std::cout<<"~Hello()"<<std::endl;}

       void Func(){SmartP<Hello>p=this;std::cout<<"Func"<<std::endl;}

};

class World:public Hello

{

public:

       World(){std::cout<<"World()"<<std::endl;}

       ~World(){std::cout<<"~World()"<<std::endl;}

};

void Test()

{

       SmartP<Hello> p;

       SmartP<Hello> p0=NEW(World,);

       SmartP<World> p1=NEW(World,);

       p0=p1;

       SmartP<Hello> p2(p1);

       SmartP<Hello> p3=p1;

       p3.~SmartP();

       p0->Func();

       p=p0;

       p=p3;

}

还有一类问题——循环引用。这个问题使用引用计数无法解决。但是,我们的智能指针允许手动释放循环引用。下面是一个小例:

class World;

class Hello

{

public:

       Hello(){std::cout<<"Hello()"<<std::endl;}

       virtual ~Hello(){std::cout<<"~Hello()"<<std::endl;}

       SmartP<World> p_;

};

class World

{

public:

       World(){std::cout<<"World()"<<std::endl;}

       ~World(){std::cout<<"~World()"<<std::endl;}

       SmartP<Hello> p_;

};

void Test()

{

       SmartP<Hello> p=NEW(Hello,);

       p->p_=NEW(World,);

       p->p_->p_=p;

       p->p_.~SmartP();

}

到目前为止,我们的智能指针解决了我发现的所有问题。

如果,读者发现了新的问题,请发送邮件到    zhanglibin_1222@163.com    ,或者到我的博客       http://blog.csdn.net/zhanglibin_1222     进行讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值