引用计数的原理和实例

简介: 引用计数是对共享的动态内存的一种管理方法,STL库的string就是用到了引用计数的方法。本文简单描述引用计数的原理,重点以一个实例来说明怎么在程序中实现引用计数。   1. 概念 引用计数用来记录当前有多少指针指向同一块动态分配的内存。

引用计数是对共享的动态内存的一种管理方法,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\n", s_1.c_str());

    string s_2("bbbb");

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

    string s_3 = s_1;

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

    string s_4 = s_3;

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

    s_1 = s_2;

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

    s_1[2] = 'a';

    printf("the address of s_1 is: %x\n", 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---\n");

    m_count = new int(0);

    *m_count = 1;

    if(str == NULL)

    {

        m_data = new char[1];

        *m_data = '\0';

    }

    int length = strlen(str);

    m_data = new char[length + 1];

    strcpy(m_data, str);

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

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

}

 

String::String(const String& other)

{

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

    m_count = other.m_count;

    (*m_count)++;

    m_data = other.m_data;

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

}

 

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

{

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

    if(this == &other)

    {

        return *this;

    }

  

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

    {

        printf("Delete memeory of %s at %x\n", 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\n", m_data, *m_count);

    return *this;

}

 

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

{

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

       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\n", m_data);

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

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

              return *(m_data+index);

       }

}

 

String::~String()

{

    printf("---destructor---\n");

    (*m_count) --;

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

    if((*m_count) == 0)

    {

        printf("Delete memeory of %s at %x\n", 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

目录
相关文章
|
1月前
|
缓存 Java API
JVM 四种引用和使用场景
在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,Java 4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。
19 0
|
2月前
|
安全 编译器 C++
C++类与对象【对象的初始化和清理】
C++类与对象【对象的初始化和清理】
C++类与对象【对象的初始化和清理】
|
9月前
|
存储 数据库
如何解决循环引用的问题
解决循环引用的问题
111 0
|
9月前
|
算法 Java 关系型数据库
引用计数 vs 根可达算法:深入比较对象存活判定
引用计数 vs 根可达算法:深入比较对象存活判定
138 0
|
10月前
|
存储 算法 安全
深入学习 JVM 算法 - 引用计数法
深入学习 JVM 算法 - 引用计数法
117 0
深入学习 JVM 算法 - 引用计数法
|
缓存 算法 Java
内存管理:判断对象是否存活
在堆里面存放着 Java 世界中几乎所有的对象实例,垃圾收集器在对 Java 堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)。 有两种判断对象是否存活的算法:引用计数算法、可达性分析算法。
107 0
内存管理:判断对象是否存活
为什么说对象是类的一个实例?底层原理是什么?
为什么说对象是类的一个实例?底层原理是什么?
199 0
|
存储 缓存 算法
<JVM上篇:内存与垃圾回收篇>08-对象实例化及直接内存
<JVM上篇:内存与垃圾回收篇>08-对象实例化及直接内存
<JVM上篇:内存与垃圾回收篇>08-对象实例化及直接内存
|
缓存 算法 Java
如何判断对象是否该被回收(引用计数法、可达性分析算法)
概述 垃圾收集器需要完那些内存需要回收? 什么时候回收? 如何回收?
75 0
如何判断对象是否该被回收(引用计数法、可达性分析算法)
|
缓存 Java 关系型数据库
强引用、软引用、弱引用、幻象引用有什么区别和使用场景
强引用、软引用、弱引用、幻象引用有什么区别和使用场景
157 1