TarsCpp 组件 之 智能指针详解

在这里插入图片描述

作者 Eaton

导语 在 C++ 中,内存管理是十分重要的问题,一不小心就会造成程序内存泄露,那么怎么避免呢?通过智能指针可以优雅地管理内存,让开发者只需要关注内存的申请,内存的释放则会被自动管理。在文章 开源微服务框架 TARS 之 基础组件 中已经简要介绍过,TARS 框架组件中没有直接使用 STL 库中的智能指针,而是实现了自己的智能指针。本文将会分别对 STL 库中的智能指针和 TarsCpp 组件中的智能指针进行对比分析,并详细介绍 TARS 智能指针的实现原理。

目录

智能指针

简介

在计算机程序中,泄露是常见的问题,包括内存泄露和资源泄露。其中资源泄露指的是系统的 socket、文件描述符等资源在使用后,程序不再需要它们时没有得到释放;内存泄露指的是动态内存在使用后,程序不再需要它时没有得到释放。

内存泄露会使得程序占用的内存越来越多,而很大一部分往往是程序不再需要使用的。在 C++ 程序中,内存泄露常见于我们使用了 new 或者 malloc 申请动态存储区的内存,却忘了使用 delete 或者 free 去释放内存,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

随着计算机应用需求的日益增加,应用的设计与开发日趋复杂,开发人员在开发过程中处理的变量也越来越多。如何有效进行内存分配和释放、防止内存泄漏逐渐成为开发者面临的重要难题。为了解决忘记手动释放内存造成的内存泄露问题,智能指针诞生了。

常见的智能指针的使用场景,包括类中的成员变量(指针型)和普通的变量(指针型)。智能指针可以实现指针指向对象的共享,而无需关注动态内存的释放。通用实现技术是引用计数(Reference count),下一部分会介绍,简单讲就是将一个计数器与类指向的对象相关联,跟踪有多少个指针指向同一对象,新增一个指针指向该对象则计数器 +1,减少一个则执行 -1

引用计数原理

在这里插入图片描述

引用计数是智能指针的一种通用实现技术,上图为大致流程,基本原理如下:

  1. 在每次创建类的新对象时,初始化指针并将引用计数置 1
  2. 当对象作为另一对象的副本而创建时(复制构造函数),复制对应的指针并将引用计数 +1
  3. 当对一个对象进行赋值时,赋值操作符 = 将左操作数所指对象的引用计数 -1,将右操作数所指对象的引用计数 +1
  4. 调用析构函数数,引用计数 -1
  5. 上述操作中,引用计数减至 0 时,删除基础对象;

STL 库中的智能指针 shared_ptr 和 TARS 智能指针都使用了该引用计数原理,后面会进行介绍。

STL 库的智能指针

C++ 标准模板库 STL 中提供了四种指针 auto_ptr, unique_ptr, shared_ptr, weak_ptr

auto_ptr 在 C++98 中提出,但其不能共享对象、不能管理数组指针,也不能放在容器中。因此在 C++11 中被摒弃,并提出 unique_ptr 来替代,支持管理数组指针,但不能共享对象。

shared_ptrweak_ptr 则是 C++11 从标准库 Boost 中引入的两种智能指针。shared_ptr 用于解决多个指针共享一个对象的问题,但存在循环引用的问题,引入 weak_ptr 主要用于解决循环引用的问题。

接下来将详细介绍 shared_ptr,关于其它智能指针的更多信息和用法请读者自行查阅。

shared_ptr

shared_ptr 解决了在多个指针间共享对象所有权的问题,最初实现于 Boost 库中,后来收录于 C++11 中,成为了标准的一部分。shared_ptr 的用法如下

#include <memory>
#include <iostream>
using namespace std;

class A 
{
   
public:
    A() {
   };
    ~A()
    {
   
        cout << "A is destroyed" << endl;
    }
};

int main()
{
   
    shared_ptr<A> sptrA(new A);
    cout << sptrA.use_count() << endl;
    {
   
        shared_ptr<A> cp_sptrA = sptrA;
        cout << sptrA.use_count() << endl;
    }
    cout << sptrA.use_count() << endl;
    return 0;
}

上述代码的意思是 cp_sptrA 声明并赋值后,引用计数增加 1cp_sptrA 销毁后引用计数 -1,但是没有触发 A 的析构函数,在 sprtA 销毁后,引用计数变为 0,才触发析构函数,实现内存的回收。执行结果如下

1
2
1
A is destroyed

shared_ptr 主要的缺陷是遇到循环引用时,将造成资源无法释放,下面给出一个示例:

#include <memory>
#include <iostream>
using namespace std;

class B;
class A
{
   
public:
    A() : m_sptrB(nullptr) {
   };
    ~A()
    {
   
        cout << " A is destroyed" << endl;
    }
    shared_ptr<B> m_sptrB;
};

class B
{
   
public:
    B() : m_sptrA(nullptr) {
   };
    ~B()
    
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值