C++内存管理介绍

目录
  • 1 smart_ptr概述
    • 1.1 RAII进制
    • 1.2 智能指针
    • 1.3 scoped_ptr
    • 1.4 scoped_array
    • 1.6 shared_array
    • 1.7 weak_ptr弱指针
  • 2 总结

前言;

C++继承了C语言的指针,一直以来指针的一些问题困扰着开发人员,常见的指针问题主要有:内存泄露、野指针、访问越界等。值得庆幸的是C++标准委员会给我们提供了auto_ptr智能指针,后面又引入了share_ptr以及weak_ptr帮助我们正确和安全的使用指针,本文主要是介绍boost库提供的解决方案,期望通过本文能够给你提供一个新的天地。

1 smart_ptr概述

在实际开发时,我们会根据不同的编程场景申请不同的资源,对于这些资源的管理需要一个完善的方案,我们希望在资源释放后,C++能够像java,c#一样不用去手动的释放资源而是由系统自动对资源进行回收。

1.1 RAII进制

C++编程时通常使用这种方法管理资源,申请的资源超过生命周期后,生命的对象自动调用析构函数对资源进行正确回收。如此看来,似乎是完美的解决我们的问题,至少在使用时不用手动释放资源。但是这种资源释放的方法同样存在着缺陷,如果对象是在栈上创建得到,那么会自动调用析构函数,结果也是没有问题的,但是如果对象是通过new在堆上创建的呢?结果是析构函数不能自动被调用,同样需要我们使用delete进行显示析构。如果程序在执行时没有调用析构delete进行释放资源,那么同样也会存在内存泄露的风险。

new/delete在编程时一定要遵循配对原则,且要服从谁创建谁释放的规则,这一点不管是新手程序员还是有着丰富编程经验的老手都要认真对待。

1.2 智能指针

从C98开始,C++标准委员会就给我们提供了智能指针:auto_ptr。它部分解决了资源的自动释放问题。

使用方法如下:

std::auto_ptr<int> p (new int);

auto_ptr的构造函数支持new操作符或者由对象工厂创建的对象指针作为参数。对象一经创建就托管了原始指针,因此它可以使用get方法返回指针对象,如:

*p.get() = 100;

auto_ptr受到了大家的欢迎,越来越多的人使用这种技术解决了实际编程中大部分得到资源管理的问题,但是,auto_ptr并不是一种非常完善的技术,也没有覆盖到智能指针的所有领域,尤其是引用计数型的智能指针。同理,在使用auto_ptr的时候也要注意以下几点,避免auto_ptr的滥用。

  • auto_ptr不能共享所有权,即不要让两个auto_ptr指向同一个对象。
  • auto_ptr不能指向数组,因为auto_ptr在析构的时候只是调用delete
  • auto_ptr只是一种简单的智能指针,如有特殊需求,需要使用其他智能指针,比如share_ptr
  • auto_ptr不能作为容器对象。

为了解决auto_ptr的不足,boost库提供了多种类之中从而完善了auto_ptr的不足。这些指针都在boost库的头文件中,

如下所示:

#include <boost/smart_ptr.hpp>
using namespace boost;

1.3 scoped_ptr

该类型指针和auto_ptr类似,但是限制也更加严格,scoped_ptr对象一旦获取对象的管理权就将一直占用,不能在进行管理权转移。scoped_ptr像它的名字一样,只能在起作用域内进行使用。使用后会使得代码相对简单且不会增加多余操作。

scoped_ptr使用方法:

scoped_ptr使用简单,只需要在原来使用new的地方用scoped_ptr进行替换即可,

使用方法如下:

scoped_ptr<string> pStr(new string("hello word"));

操作scoped_ptr指针也很简单,使用方式和普通指针一致,如:

//打印指针指向字符串内容
cout<<pAtr<<endl;
//打印字符长度
cout<<pAtr->size()<<endl;

需要注意的是:1)此指针对象会进行自动释放,无需使用delete进行释放,如果在实际编程时使用了delete,编译器将会报错,大家不妨可以思考下原因是什么欢迎留言。2)scoped_ptr是不能进行赋值、拷贝操作得到,生命周期只限于声明的作用域内。

和auto_ptr指针相比,scoped_ptr的不同点如下:

两者都不能作为容器元素,但是原因却不同。auto_ptr是因为它自身的转义语义,但是scoped_ptr是因为不支持拷贝和复制。

指针所有权不同,auto_ptr是可以进行转义得到,但是scoped_ptr不能进行转义,因为其构造和拷贝函数都是私有的。

1.4 scoped_array

scoped_arrayscoped_ptr基本相同,根本区别是scoped_array是指向数组的,封装了new[],弥补了指向数组的智能指针。

scoped_array使用方法:

scoped_array和scoped_ptr设计思想相同,使用方法也基本一致,如下:

scoped_array<int> pIntArray(new int[10]);

使用时需要注意的是:scoped_array不会对数组范围进行检查,且不能通过数组名+偏移的方式进行获取。

但可以按照以下方式使用:

pIntArray[0]=100;
pIntArray[1]=200;
1.5 shared_ptr

shared_ptr已经被引入C++标准库中,功能强大,可以像普通指针那样使用,它是引用计数型指针,可以被任意的复制和拷贝。同时shared_ptr可以被用作容器元素。

可以说shared_ptr是C++历史上实现智能指针算法中最杰出的代表作。

 shared_ptr使用方法:

shared_ptr指针是最接近原始指针的。他比auto_ptrscoped_ptr应用范围更广,几乎可以百分之百避免程序中的内存泄露,但是使用却又像auto_ptr和scoped_ptr一样简单。

shared_ptr是线程安全的,可以由多个线程进行读取。

使用方式如下:

int main()
{
    //定义一个指针对象
   shared_ptr<int> pInt(new int);
   //对指针进行赋值
   *pInt = 100;
   cout<<*pInt<<endl;
   //调用shared_ptr重载方法
   shared_ptr<int> pInt_cp = pInt;
   cout<<*pInt_cp<<endl;
   pInt.reset();
   assert(!pInt);
   return 0;
}

如上代码所示shared_ptr构造函数中依旧使用new的方法创建一个对象,但这个远远不够,shared_ptr提供make_shared方法来创建一个共享指针对象,

方式如下:

int main()
{
   shared_ptr<string> pStr= make_shared<string>("hello world");
   cout<<*pStr<<endl;
   shared_ptr<vector<int> > pVec = make_shared<vector<int> >(10,2);
   cout<<pVec->size()<<endl;
   return 0;
}

代码输出结果为:

hello world
10

shared_ptr高级用法;

shared_ptr 可以保存任意类型指针,可以理解成是改指针是泛型的。

shared_ptr<void *>退出作用域后调用回调函数,如下:

void fun(void *p){}
shared_ptr<void *> p((void *0),fun);

除此之外,shared_ptr还有其它很多高级用法,如:延时释放、包装成员函数等,感兴趣的话可以自行研究。

1.6 shared_array

shared_array功能和shared_ptr类似,根本区别是包装了new[]操作符,可以指向数组,知道引用计数为0时才会进行释放。

 shared_array使用方法:

shared_arrayshared_ptrscoped_array的结合体。

使用方法如下所示:

int main()
{
   int *p = new int[100];
   shared_array<int> sp(p);
   shared_array<int> sp1=sp;
   sp[0]=10;
   return 0;
}

1.7 weak_ptr弱指针

weak_ptr不具备普通指针的行为,需要和shared_ptr配合使用。主要用于协助shared_ptr工作,观测资源的使用情况。

 weak_ptr使用方法:

weak_ptrshared_ptr共用可以通过shared_ptr或者weak_ptr对象构造,使用时不会产生引用计数增加,析构时也不会导致引用计数的减少。可以使用use_count查看引用计数,也可以使用其等效的方法expired(),如果引用计数为0则表示观测的对已经不存在了。代码示例如下:

int main()
{
   shared_ptr<int> p(new int(100));
   cout<<p.use_count()<<endl;
   weak_ptr<int> wp(p);
   cout<<wp.use_count()<<endl;
   p.reset();
   if(!wp.expired())
   {
       shared_ptr<int> p1=wp.lock();
       *p1 = 1000;
       cout<<wp.use_count()<<endl;
   }
   return 0;
}

2 总结

如上,介绍了boost库提供的几种智能指针的方法、定义和使用,实际编码时可以根据使用场景使用不同的指针,这里尤其要推的是shared_ptr,使用它几乎可以解决我们遇到的所有使用场景。当然boost还提供了一种侵入式智能指针,但是因为其使用要求严格,官方已经不再推荐使用,所以,本文也没有进行介绍。

到此这篇关于C++内存管理介绍的文章就介绍到这了,更多相关C++内存管理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈C++内存管理基础知识

    目录 概述 c++可用内存 c语言的可用内存 c++新增内存区域 new和malloc 智能指针引入 智能指针的实现 java延伸 java语言整体框架 java的垃圾回收机制 总结 概述 内存管理的原理庞大而复杂,然而这些都被操作系统进行了封装,并对外预留了API,这些api被c++调用,同时也被c++再次进行了封装,再面向程序员预留出了语法特性的接口,作为使用c++的程序员,我们只需要掌握c++预留的内存管理特性即可,就像我们开车不需要管变数箱.发动机是怎么变速.点火的,我们只需要掌握汽车给

  • C++ 内存管理原理分析

    1.C/C++中程序内存分布 C/C++中程序内存区域大致划分为:内核空间(这部分用户不能读写).栈.内存映射段.堆.数据段(存储全局数据.静态数据).代码段(存储可执行代码.只读常量,又称常量区). 1.1 内存分布图 1.2 小试牛刀 接下来看下如下代码,思考下每一个变量分别在哪个内存区域? int globalVar = 1; static int staticGlobalVar = 1; void test() { static int staticVar = 1; int localV

  • C++的内存管理详细解释

    目录 一.C/C++内存分布 二.C语言中动态内存管理方式: 1.malloc/calloc/realloc区别: 三.C++中动态内存管理:new/delete 四.实现原理 五.面试常问问题 1.malloc/free和new/delete的区别 2.内存泄漏 内存泄漏分类(了解) 总结 一.C/C++内存分布 栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库.用户可使用系统接口创建共享共享内存,做进程间通信.

  • C++内存管理详细解析

    目录 一.C++内存管理 1. new/delete表达式 2.new/delete重载 3.类内自定义allocator(per-class allocator) 二.多线程内存分配器 1.malloc/free 2.brk和mmap 三.补充知识 1.内存泄漏 2.malloc/free和new/delete的比较 3.RAII规则 一.C++内存管理 C++中有四种内存分配.释放方式: 最高级的是std::allocator,对应的释放方式是std::deallocate,可以自由设计来搭

  • C++嵌入式内存管理详情

    目录 一.Linux内核系统结构 二.查看Linux内存 1.cache 2.buffer 三.内存补齐 前言: 上一篇介绍了软件层面上的内存,并没有涉及很多底层的原理:但在实际工程中,部署一个项目往往需要考虑内存的占用,这里的内存也就是嵌入式板子上的内存:本篇文章就简单介绍一下嵌入式端的一个内存管理: 一.Linux内核系统结构 主要分为五大模块: 本次主要讲解内存管理模块,其他模块不做介绍: 二.查看Linux内存 在Linux环境下,可通过free -m查看内存使用情况: 下图是一台rk3

  • C++内存管理之简易内存池的实现

    目录 什么是内存池? 它的实现过程为: 初步实现 使用嵌入指针改进 更简化:static allocator macor for static allocator 什么是内存池? 频繁的调用 malloc 会影响运行效率以及产生额外的 cookie, 而内存池的思想是预先申请一大块内存,当有内存申请需求时,从内存池中取出一块内存分配给目标对象. 它的实现过程为: 预先申请 chunk 大小的内存池, 将内存池划按照对象大小划分成多个内存块.以链表的形式,即通过指针将内存块相连,头指针指向第一个空

  • C++内存管理介绍

    目录 1 smart_ptr概述 1.1 RAII进制 1.2 智能指针 1.3 scoped_ptr 1.4 scoped_array 1.6 shared_array 1.7 weak_ptr弱指针 2 总结 前言; C++继承了C语言的指针,一直以来指针的一些问题困扰着开发人员,常见的指针问题主要有:内存泄露.野指针.访问越界等.值得庆幸的是C++标准委员会给我们提供了auto_ptr智能指针,后面又引入了share_ptr以及weak_ptr帮助我们正确和安全的使用指针,本文主要是介绍b

  • C语言动态内存管理介绍

    目录 前言: C 语言为内存的分配和管理提供了几个函数: 1.malloc() 用法 2.calloc() 用法 3.realloc() 与 free() 用法 前言: 简单记录一下,内存管理函数 为什么使用动态内存呢? 简单理解就是可以最大限度调用内存 用多少生成多少,不用时就释放而静止内存不能释放 动态可避免运行大程序导致内存溢出 C 语言为内存的分配和管理提供了几个函数: 头文件:<stdlib.h> 注意:void * 类型表示未确定类型的指针  1.malloc() 用法  分配一块

  • 关于C语言动态内存管理介绍

    目录 1.为什么需要动态内存分配 2.有关动态内存函数介绍 2.1 malloc和free 2.2 calloc函数 2.3 realloc函数 3. 常见的动态内存错误 3.1 对NULL指针进行解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 3.6 动态开辟内存忘记释放(内存泄漏) 总结 1.为什么需要动态内存分配 关于这个问题,我们先看看我们之前是如何开辟内存的. i

  • JavaScript内存管理介绍

    简介 低级语言,比如C,有低级的内存管理基元,想malloc(),free().另一方面,JavaScript的内存基元在变量(对象,字符串等等)创建时分配,然后在他们不再被使用时"自动"释放.后者被称为垃圾回收.这个"自动"是混淆并给JavaScript(和其他高级语言)开发者一个错觉:他们可以不用考虑内存管理. 内存生命周期 不管什么程序语言,内存生命周期基本一致: 1.分配你所需要的内存 2.使用它(读.写) 3.当它不被使用时释放   ps:和"把

  • 深入理解C++中的new/delete和malloc/free动态内存管理及区别介绍

    malloc/free和new/delete的区别 malloc/free是C/C++标准库的函数:new/delete是C++操作符. malloc/free只是动态分配内存空间/释放空间:new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理资源. malloc/free需要手动计算类型大小且返回值类型为void*:new/delete可自动计算类型的大小,返回对应类型的指针. malloc/free管理内存失败会返回0:new/delete等的方式管理内存失败会抛出异常

  • Linux内存管理和寻址详细介绍

    目录 1.概念 内存管理模式 地址类型划分 说明: 2.页式管理 x86架构32位cpu x86架构 64位cpu 3.地址划分 4. 调试 结语 1.概念 内存管理模式 段式:内存分为了多段,每段都是连续的内存,不同的段对应不用的用途.每个段的大小都不是统一的,会导致内存碎片和内存交换效率低的问题. 页式:内存划分为多个内存页进行管理,如在 Linux 系统中,每一页的大小为 4KB.由于分了页后,就不会产生细小的内存碎片.但是仍然也存在内存碎片问题. 段页式:段式和页式结合. 地址类型划分

  • 详解关于iOS内存管理的规则思考

    关于iOS内存管理的规则思考 自己生成的生成的对象,自己持有. 非自己生成的对象,自己也能持有. 不在需要自己持有的对象时释放. 非自己持有的对象无法释放. 注:这里的自己是对象使用的环境,理解为编程人员本身也没有错 对象操作和Objective-C方法对应 对象操作 Objectivew-C方法 生成并持有对象 alloc/copy/mutableCopy/new或以此开头的方法 持有对象 retain 释放对象 release 废弃对象 dealloc 自己生成的对象,自己持有 //自己生成

  • 跟我学习javascript的垃圾回收机制与内存管理

    一.垃圾回收机制-GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存. 原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存. JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行. 不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生

  • javascript的内存管理详解

    介绍 低层次的语言,如C,具有低级别的内存管理命令,如:malloc()和free(),需要开发者手工释放内存.然而像javascript这样的高级语言情况则不同,对象(objects, strings 等)创建的时候分配内存,当他们不在使用的时候内存会被自动回收,这个自动回收的过程被称为垃圾回收.因为垃圾回收的存在,让javascript等高级语言开发者产生了一个错误的认识,以为可以不用关心内存管理. 内存生命周期 不管什么样的编程语言,内存的生命周期基本上是一致的. 1.分配你需要的内存 2

  • Cocos2d-x的内存管理总结

    Cocos2d-x引擎的核心是用C++编写的,那对于所有使用该引擎的游戏开发人员来说,内存管理是一道绕不过去的坎. 关于Cocos2d-x内存管理,网上已经有了许多参考资料,有些资料写的颇为详实,因为在内存管理这块我不想多费笔墨,只是更多的将思路描述清楚. 一.对象内存引用计数 Cocos2d-x内存管理的基本原理就是对象内存引用计数,Cocos2d-x将内存引用计数的实现放在了顶层父类CCObject中,这里将涉及引用计数的CCObject的成员和方法摘录出来: 复制代码 代码如下: clas

随机推荐