引用计数的原理和实例

引用计数是对共享的动态内存的一种管理方法,STL库的string就是用到了引用计数的方法。本文简单描述引用计数的原理,重点以一个实例来说明怎么在程序中实现引用计数。

1. 概念

引用计数用来记录当前有多少指针指向同一块动态分配的内存。当有指针指向这块内存时,计数器加1;当指向此内存的指针销毁时,计数器减1。当引用计数为0时,表示此块内存没有被任何指针指向,此块被共享的动态内存才能被释放。

2. STL库的string利用引用计数共享内存

如下例:

#include <iostream>

#include <string>

using namespace std;

int main()

{

    string s_1("aaaa");

    printf("the address of s_1 is: %x ", s_1.c_str());

    string s_2("bbbb");

    printf("the address of s_2 is: %x ", s_2.c_str());

    string s_3 = s_1;

    printf("the address of s_3 is: %x ", s_3.c_str());

    string s_4 = s_3;

    printf("the address of s_4 is: %x ", s_4.c_str());

    s_1 = s_2;

    printf("the address of s_1 is: %x ", s_1.c_str());

    s_1[2] = 'a';

    printf("the address of s_1 is: %x ", s_1.c_str());

    return 0;

}

结果如下:

the address of s_1 is: 9042014

the address of s_2 is: 904202c  

the address of s_3 is: 9042014    //s_3与s_1共享同一块内存

the address of s_4 is: 9042014    //s_4与s_1也共享同一块内存

the address of s_1 is: 904202c    //将s_2赋给s_1, s1与s_2也赋给同一块内存, 此时s_3和s_4共享”aaaa”内存,s_1和s_2共享”bbbb”内存

the address of s_1 is: 9042044    //写s_1, 新的内存分配给s1

STL string共享内存的原则(copy on write):

利用初始化或赋值生成多个内容相同的string,当没有对这些string进行写操作时,它们会共享同一块内存。当有写操作出现时,才会有新的内存重新分配。

3. 利用引用计数实现简易String

问题:我们对String类的内存进行计数,但引用计数这个变量是类的什么类型的成员变量?

解答:

1) 普通私有成员变量,每个类都有一个独立的计数器变量,在新的类对象拷贝构造和赋值的时候需要进入原来的类对象中去获取这个计数器,再进行修改,在同一个类对象之间缺乏通用性。

2) static变量,类的所有对象公用一个计数器变量。字符串”aaaa”和”bbbb”使用不同的内存,它们应该分别有对应自己内存的计数器变量。

3) 结论,对每一块内存分配一个变量,可以在动态分配字符串内存是多分配一个字节,将计数器的值放在第一个字节中;也可以在动态分配内存前先动态分配一个int型的内存来存放这个计数器。

String的代码如下:

#include <iostream>

#include <cstring>

#include <cassert>

class String

{

public:

    String(const char *str);

    String(const String& other);

    String& operator = (const String& other);

    char& operator [] (int index);

    ~String();

private:

    int *m_count;

    char *m_data;

};

int main()

{

    String s1("aaaa");

    String s2("bbbb");

    String s3 = s1;

    String s4 = s3;

    s1 = s2;

    s1[2] = 'a';

    return 0;

}

String::String(const char *str = NULL)

{

    printf("---constructor--- ");

    m_count = new int(0);

    *m_count = 1;

    if(str == NULL)

    {

        m_data = new char[1];

        *m_data = '';

    }

    int length = strlen(str);

    m_data = new char[length + 1];

    strcpy(m_data, str);

    printf("Allocate memory of %s at %x ", m_data, m_data);

    printf("The Refcount of %s is %d ", m_data, *m_count);

}

String::String(const String& other)

{

    printf("---copy constructor--- ");

    m_count = other.m_count;

    (*m_count)++;

    m_data = other.m_data;

    printf("The Refcount of %s is %d ", m_data, *m_count);

}

String& String::operator = (const String& other)

{

    printf("---assign value--- ");

    if(this == &other)

    {

        return *this;

    }

  

    if(--(*m_count) == 0)

    {

        printf("Delete memeory of %s at %x ", m_data, m_data);

        delete[] m_data;

        delete m_count;

        //m_data = NULL;

        //m_count = NULL;

    }

   

    m_count = other.m_count;

    m_data = other.m_data;

    (*m_count)++;

    printf("The Refcount of %s is %d ", m_data, *m_count);

    return *this;

}

char& String::operator [] (int index)

{

    printf("---operator []--- ");

       int length = strlen(m_data);

       assert(index >= 0 && index < length);

            //引用计数为1时不用重新分配内存

       if((*m_count) == 1)

       {

           return *(m_data+index);

       }

            //引用计数大于1时需要重新分配内存

       if((*m_count) > 1)

       {

              (*m_count)--;

              int tmp_count = *m_count;

              m_count = new int(0);

              *m_count = 1;

                   char* tmp = new char[length+1];

              strcpy(tmp, m_data);

              m_data = tmp;

              printf("Re-Allocate memory at %x ", m_data);

              printf("The new Refcount (Re-Allocated) is %d ", *m_count);

              printf("The old Refcount is %d ", tmp_count);

              return *(m_data+index);

       }

}

String::~String()

{

    printf("---destructor--- ");

    (*m_count) --;

    printf("The Refcount of %s is %d ", m_data, *m_count);

    if((*m_count) == 0)

    {

        printf("Delete memeory of %s at %x ", m_data, m_data);

        delete[] m_data;

        delete m_count;

        //m_data = NULL;

        //m_count = NULL;

    }

}

运行结果如下:

---constructor---

Allocate memory of aaaa at 9509018

The Refcount of aaaa is 1

---constructor---

Allocate memory of bbbb at 9509038

The Refcount of bbbb is 1

---copy constructor---

The Refcount of aaaa is 2

---copy constructor---

The Refcount of aaaa is 3

---assign value---

The Refcount of bbbb is 2

---operator []---

Re-Allocate memory at 9509058

The new Refcount (Re-Allocated) is 1

The Refcount of original String bbbb is 1

---destructor---                                                //析构顺序:s4, s3, s2, s1

The Refcount of aaaa is 1                               //aaaa的引用计数为2,析构后减1

---destructor---           

The Refcount of aaaa is 0                               //aaaa的引用计数为0,释放内存               

Delete memeory of aaaa at 9509018

---destructor---

The Refcount of bbbb is 0

Delete memeory of bbbb at 9509038

---destructor---

The Refcount of bbab is 0

Delete memeory of bbab at 9509058

原文地址:https://www.cnblogs.com/lidabo/p/3904721.html