乐趣区

关于c++:More-Effective-C技术篇Reference-counting引用计数

  • Reference counting 这项技术,容许多个等值对象共享同一实值。此技术的倒退有两种动机,第一是为了简化 heap object 周边的簿记工作。它能够打消“记录对象拥有权”的负荷,因为当对象使用了 reference counting 技术,它便领有它本人。一旦不再有任何人应用它,它便会主动销毁本人。reference counting 的第二个倒退动机则只是为了实现一种常识。如果许多对象有雷同的值,将那个值存储屡次是件愚昧的事。最好是让所有等值对象共享一份实值就好。
  • 以下是一个蕴含了 Copy-On-Write 技术的 Reference counting 基类,有趣味倡议去看看原书中的文章,作者一步步疏导写出这个类,写的很具体,这里因为篇幅起因就不赘述。

    // RCObject.h
    // template class, 用来产生 smart pointers-to-T objects;T 必须继承自 RCObject 
    template<class T>
    class RCPtr {
    public:
      RCPtr(T* realPtr = 0);
      RCPtr(const RCPtr& rhs);
      ~RCPtr();
    
      RCPtr& operator=(const RCPtr& rhs);
    
      T* operator->() const;
      T& operator*() const;
    
    private:
      T *pointee;
      void init();};
    
    // base class, 用于 reference-counted objects
    class RCObject {
    public:
      void addReference();
      void removeReference();
    
      void markUnshareable();
      bool isShareable() const;
    
      bool isShared() const;
    
    protected:
      RCObject();
      RCObject(const RCObject& rhs);
      RCObject& operator=(const RCObject& rhs);
      virtual ~RCObject() = 0;
      
    private:
      int refCount;
      bool shareable;
    };
    
    // RCObject.cpp
    RCObject::RCObject() : refCount(0), shareable(true) {
    
    }
    
    RCObject::RCObject(const RCObject& rhs) : refCount(0), shareable(true) {
    
    }
    
    RCObject& RCObject::operator=(const RCObject& rhs) {return *this;}
    
    RCObject::~RCObject () {}
    
    void RCObject::addReference() {++refCount;}
    
    void RCObject::removeReference() {if (--refCount == 0) {delete this;}
    }
    
    void RCObject::markUnshareable() {shareable = false;}
    
    bool RCObject::isShareable() const {return shareable;}
    
    bool RCObject::isShared() const {return refCount > 1;}
    
    template<class T>
    void RCPtr<T>::init () {if (pointee == 0)
          return;
      if (pointee->isShareable() == false) {pointee = new T(*pointee);
      }
      pointee->addReference();}
    
    template<class T>
    RCPtr<T>::RCPtr(T* realPtr) : pointee(realPtr) {init();
    }
    
    template<class T>
    RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) {init();
    }
    
    template<class T>
    RCPtr<T>::~RCPtr() {if (pointee) {pointee->removeReference();
      }
    }
    
    template<class T>
    RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){if (pointee != rhs.pointee) {if (pointee) {pointee->removeReference();
          }
          pointee = rhs.pointee;
          init();}
      return *this;
    }
    
    template<class T>
    T* RCPtr<T>::operator->() const {return pointee;}
    
    template<class T>
    T& RCPtr<T>::operator*() const {return *pointee;}
  • 而后,定义一个简略的 String 类,在 String 中蕴含示意其实值的 struct StringValue,其中 StringValue 继承自下面的 RCObject,使其领有援用计数的能力。String 代码如下所示。

    // String.h
    // 应用性 class,这是应用程序开发人员接触的层面
    #include <string.h>
    #include "RCObject.h"
    class String {
    public:
      String(const char *initValue = "");
    
      const char& operator[] (int index) const;
      char& operator[] (int index);
    
    private:
      // 以下 struct 用以体现字符串实值
      struct StringValue : public RCObject {
          char *data;
    
          StringValue(const char *initValue);
          StringValue(const StringValue& rhs);
          void init(const char *initValue);
          ~StringValue();};
      RCPtr<StringValue> value;
    };
    
    // String.cpp
    void String::StringValue::init(const char *initValue) {data = new char[strlen(initValue) + 1];
      strcpy(data, initValue);
    }
    
    String::StringValue::StringValue(const char *initValue) {init(initValue);
    }
    
    String::StringValue::StringValue(const StringValue& rhs) {init(rhs.data);
    }
    
    String::StringValue::~StringValue() {delete [] data;
    }
    
    String::String(const char *initValue) : value(new StringValue(initValue)) {
    
    }
    
    const char& String::operator[](int index) const {return value->data[index];
    }
    
    char& String::operator[](int index) {if (value->isShared()) {value = new StringValue(value->data);
      }
      value->markUnshareable();
      return value->data[index];
    }
  • 再来写个 main,简略验证下。

    #include <String.h>
    #include <iostream>
    int main()
    {
      String s1 = "hello";
      String s2 = s1;
    
      std::cout << "s1[3] =" << s1[3] << std::endl;
      std::cout << "s2[3] =" << s2[3] << std::endl;
    
      s2[3] = 'x';
    
      std::cout << "s1[3] =" << s1[3] << std::endl;
      std::cout << "s2[3] =" << s2[3] << std::endl;
    }
// 输入
s1[3] = l
s2[3] = l
s1[3] = l
s2[3] = x
  • Reference counting 是个优化技术,其应用前提是:对象经常共享实值。如果这个假如失败,reference counting 反而会赔上更多内存,执行更多代码。从另一个角度看,如果你的对象的确有“独特实值”的偏向,reference counting 应可同时节俭你的工夫和空间。
  • 简略说,以下是应用 reference counting 改善效率的最适当机会:

    • 绝对少数的对象共享绝对大量的实值(必要)。
    • 对象实值的产生或销毁老本很高,或是它们应用许多内存(非必要)。
退出移动版