引用计数的原理和实例

简介: 引用计数是对共享的动态内存的一种管理方法,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

目录
相关文章
|
消息中间件 运维 监控
微服务架构的优点和缺点分别有哪些?
微服务架构的优点和缺点分别有哪些?
1292 0
微服务架构的优点和缺点分别有哪些?
|
11月前
|
敏捷开发 数据可视化 BI
远程团队看板工具全指南:2025年最强推荐与实践策略
《远程团队看板工具:提升协作效率的利器》摘要 远程看板工具正成为现代团队协作的核心,通过可视化任务流、实时同步和进度追踪,有效解决远程办公中的信息不对称问题。本文系统介绍了看板工具的基本概念、核心功能(包括任务可视化、多人协作、时间管理等),并对比了Trello、Jira、Asana等主流产品的特点。针对选型策略,建议从团队规模、易用性、集成能力三个维度考量。文章还分享了任务拆解、每日站会等实用技巧,并解答了数据安全等常见问题。最后强调,合适的看板工具能显著提升远程团队的工作效率和凝聚力。
303 5
|
7月前
|
机器学习/深度学习 人工智能 缓存
让AI评测AI:构建智能客服的自动化运营Agent体系
大模型推动客服智能化演进,从规则引擎到RAG,再到AI原生智能体。通过构建“评估-诊断-优化”闭环的运营Agent,实现对话效果自动化评测与持续优化,显著提升服务质量和效率。
3330 86
让AI评测AI:构建智能客服的自动化运营Agent体系
|
6月前
|
人工智能 Shell API
Claude Skills|将 Agent 变为领域专家
Claude Skills 是 Anthropic 推出的模块化能力包,基于文件系统在沙盒环境中运行,通过渐进式披露机制向 Agent 注入标准化的流程知识(SOP),实现任务自动化。它与 MCP 协同,分别提供内部知识与外部工具支持,有望成为工业级 Agent 的核心组件。
Claude Skills|将 Agent 变为领域专家
|
9月前
|
存储 人工智能 安全
函数计算进化之路:AI Sandbox 新基座
AI Agent Sandbox 是应对 AI 代理自主性风险的关键技术,提供安全隔离环境以执行代码、交互应用和处理敏感数据。它解决了三大挑战:隔离与安全、状态管理与成本、可扩展性与运维。阿里云函数计算凭借物理隔离架构、Serverless 弹性与成本优势,结合会话亲和、隔离及存储安全等创新能力,成为 AI Agent Sandbox 的理想运行时平台,助力 AI 技术安全落地与商业化发展。
|
4月前
|
人工智能 Kubernetes 机器人
一键部署 OpenClaw:基于 ACS Agent Sandbox 构建企业级 AI Agent 应用
本文将详细介绍如何基于 ACS Agent Sandbox 一键部署 OpenClaw,实现按需休眠与秒级唤醒,并介绍其与钉钉等应用的集成方法。
1657 7
|
6月前
|
Java 关系型数据库 MySQL
Spring Boot事务配置管理
本文介绍了Spring Boot中事务的使用及常见陷阱。通过@Transactional注解可轻松实现事务管理,确保数据操作的原子性。重点剖析了三大易踩坑点:异常类型不匹配导致未回滚、try-catch吞掉异常、事务与锁范围不一致引发并发问题,助你在实际项目中有效规避风险。
|
6月前
|
人工智能 NoSQL 前端开发
springai
SpringAI整合多款主流大模型,支持对话、函数调用与RAG等架构,提供统一API简化开发。涵盖Ollama、OpenAI等平台,实现聊天机器人、智能客服、知识库问答(如ChatPDF)及多模态交互,助力快速构建AI应用。
1370 0
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
3804 37
微服务架构解析:跨越传统架构的技术革命
|
Java 应用服务中间件 Spring
SpringBoot 响应请求是串行还是并行?
Spring Boot 在默认情况下通过 Servlet 容器的线程池实现并行处理 HTTP 请求。通过适当的线程池配置,可以进一步优化并发性能。此外,Spring Boot 提供了异步处理机制(如使用 `@Async` 注解)和反应式编程模型(Spring WebFlux),使得应用能够处理更高的并发负载。在具体项目中,可以根据需求选择合适的处理模型,以充分利用 Spring Boot 的并发处理能力。
573 21