关于C++为什么不加入垃圾回收机制解析

2020-01-06 16:40:57王振洲

进入函数foo 退出函数
| A
V |
auto_ptr<string>::auto<string>() auto_ptr<string>::~auto_ptr<string>()
| A
V |
string::string() string::~string()
| A
V |
_data=new char[] delete [] _data
| A
V |
使用资源 -----------------------------------> 释放资源

现在我们拥有了最简单的垃圾回收机制(我隐瞒了一点,在string中,你仍然需要自己编码控制对象的动态创建和销毁,但是这种情况下的准则极其简单,就是在构造函数中分配资源,在析构函数中释放资源,就好像飞机驾驶员必须在起飞后和降落前检查起落架一样。),即使在foo函数中发生了异常,str的生存期也会结束,C++保证自然退出时发生的一切在异常发生时一样会有效。

auto_ptr<>只是智能指针的一种,它的复制行为提供了所有权转移的语义,即智能指针在复制时将对内部维护的实际指针的所有权进行了转移,例如

auto_ptr < string > str1( new string( < str1 > ) );
cout << str1->c_str();
auto_ptr < string > str2(str1); // str1内部指针不再指向原来的对象
cout << str2->c_str();
cout << str1->c_str(); // 未定义,str1内部指针不再有效

某些时候,需要共享同一个对象,此时auto_ptr就不敷使用,由于某些历史的原因,C++的标准库中并没有提供其他形式的智能指针,走投无路了吗?

另一种智能指针

但是我们可以自己制作另一种形式的智能指针,也就是具有值复制语义的,并且共享值的智能指针。

需要同一个类的多个对象同时拥有一个对象的拷贝时,我们可以使用引用计数(Reference Counting/Using Counting)来实现,曾经这是一个C++中为了提高效率与COW(copy on write,改写时复制)技术一起被广泛使用的技术,后来证明在多线程应用中,COW为了保证行为的正确反而导致了效率降低(Herb Shutter的在C++ Report杂志中的Guru专栏以及整理后出版的《More Exceptional C++》中专门讨论了这个问题)。

然而对于我们目前的问题,引用计数本身并不会有太大的问题,因为没有牵涉到复制问题,为了保证多线程环境下的正确,并不需要过多的效率牺牲,但是为了简化问题,这里忽略了对于多线程安全的考虑。

首先我们仿造auto_ptr设计了一个类模板(出自Herb Shutter的《More Execptional C++》),


template < typename T >
class shared_ptr
{
private:
 class implement // 实现类,引用计数
 {
 public:
  implement(T* pp):p(pp),refs(1){}
  
  ~implement(){delete p;}
  
  T* p; // 实际指针
  size_t refs; // 引用计数
 };
 implement* _impl;


public:
 explicit shared_ptr(T* p)
  : _impl(new implement(p)){}


 ~shared_ptr()
 {
  decrease(); // 计数递减
 }


 shared_ptr(const shared_ptr& rhs)
  : _impl(rhs._impl)
 {
  increase(); // 计数递增
 }
 
 shared_ptr& operator=(const shared_ptr& rhs)
 {
  if (_impl != rhs._impl) // 避免自赋值
  {
   decrease(); // 计数递减,不再共享原对象
   _impl=rhs._impl; // 共享新的对象
   increase(); // 计数递增,维护正确的引用计数值
  }
  return *this;
 }


 T* operator->() const
 {
  return _impl->p;
 }
  
 T& operator*() const
 {
  return *(_impl->p);
 }
 
private:
 void decrease()
 {
  if (--(_impl->refs)==0)
  { // 不再被共享,销毁对象
   delete _impl;
  }
 }
 
 void increase()
 {
  ++(_impl->refs);
 }
};