引用计数

引用计数

本文的核心思想来源于:http://blog.youkuaiyun.com/armman/article/details/1714911 

一 基本概念

在程序中为了防止对象的拷贝,经常使用的一个计数就是“引用计数”。例如:

String a, b, c, d, e;
a = b = c = d = e = "Hello";

如果所有的string对象只是共享一个“Hello”, 那么每个对象都包含该数据会造成内存的浪费,同时在对象拷贝时也会有不小的开销。那么我们可以用基本的引用计数来解决。

C++ standard library中有个例子,我们贴一下完整的代码;

 #ifndef COUNTED_PTR_HPP
   #define COUNTED_PTR_HPP
   /*class for counted reference semantics
    *-deletes the object to which it refers when the last CountedPtr
    * that refers to it is destroyed
    */
   template <class T>
   class CountedPtr {
     private:
       T* ptr;        // pointer to the value
       long* count;   // shared number of owners
     public:
       //initialize pointer with existing pointer
       //-requires that the pointer p is a return value of new
       explicit CountedPtr (T* p=0)
        : ptr(p), count(new long(1)) {
       }
       //copy pointer (one more owner)
       CountedPtr (const CountedPtr<T>& p) throw()
        : ptr(p.ptr), count(p.count) {
           ++*count;
       }
       //destructor (delete value if this was the last owner)
       ~CountedPtr () throw() {
           dispose();
       }
       //assignment (unshare old and share new value)
       CountedPtr<T>& operator= (const CountedPtr<T>& p) throw() {
           if (this != &p) {
               dispose();
               ptr = p.ptr;
               count = p.count;
               ++*count;
           }
           return *this;
       }
     private:
       void dispose() {
           if (--*count == 0) {
                delete count;
                delete ptr;
           }
       }
   };
   #endif /*COUNTED_PTR_HPP*/


分析代码可知,引用计数主要使用一下两个技巧:

1) 由包含对象到包含指针。

template <class T>
   class CountedPtr {
     private:
       T* ptr;        // pointer to the value
       long* count;   // shared number of owners
}

这里, 对象包含的不再是T, 而是T的指针。将一个对象的最占用内存的地方放置到外面,然后自己包含指向它的指针。

(2) 包含一个指向引用计数对象(往往是一个int整数)的指针。

在上面的例子中, count即为引用对象, 包含一个引用对象的指针, 使得对象T的引用计数值对所有CountedPtr对象可见和一致。

一方面,可以把CountedPtr的接口设计成一个指针, 也可以设计成一个带引用计数的T对象。但是不管什么变种, 其核心都是以上两点。

实现图如下:


二: 写时拷贝

引用计数中有一个写时拷贝的技术,类似于os中的Copy-On-Write。例如:

String a = "abcdef";

a[1] = 'g';

我们可以在非const[ ]操作时重新生成一份对象的拷贝,从而避免写操作的影响。但是该技术会碰到一个问题:

String s1 = "Hello";

char *p = &s1[1];

现在看增加一条语句:

String s2 = s1;

下面这样的语句将有不受欢迎的结果:

*p = 'x';                     // modifies both s1 and s2!

那么如何解决上面的问题呢? 其实很简单,可以增加一个标志以指出它是否为可共享的。在最初(对象可共享时)将标志打开,在非constoperator[]被调用时将它关闭。一旦标志被设为false,它将永远保持在这个状态。 


三 加入引用计数的代码架构

考虑设计一个支持引用计数的string类。为了让代码更具有可扩展性,我们可以增加一个引用计数的基类。类似如下:

RCObject的定义如下:

class RCObject {
public:
  RCObject();
  RCObject(const RCObject& rhs);
  RCObject& operator=(const RCObject& rhs);
  virtual ~RCObject() = 0;
  void addReference();
  void removeReference();
  void markUnshareable();
  bool isShareable() const;
  bool isShared() const;
private:
  int refCount;
  bool shareable;
};

注意, 继承基类的子类在使用时,一定要用new 来创建,这样才符合上面提到的第二点。

那么我们可以如下设计string类。


通过继承, 引用计数变量存放在了单一的对象中。但是,该设计存在一些问题, 即每个String类都必须自己修改拷贝构造函数, 赋值函数等,有没有什么办法能自动化完成这些呢? 我们可以使用智能指针来帮我们完成这一切!新的设计如下:



上述设计使得String类自动的支持了引用计数,而不用添加额外的代码,非常的精彩。这里的StringValue类似于CountedPtr,而smart pointer使得String类的拷贝和赋值等操作不用关心引用计数。从而使代码更加简洁和组件化。

引用计数技术的实现类似于STL, 我们也可以使用三大组件。即:

智能指针, StringValue 和 RCObject




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值