1 C++中一般将内存空间分为5个区域:

  • 栈:函数内的局部变量存放点,由编译器自动分配和释放
  • 堆:程序员malloc/new分配,用free/delete释放。系统最后会回收
  • 静态存储区:存放全局变量和静态变量static。程序结束后由系统释放
  • 常量存储区:存储常量
  • 程序代码区:存储程序代码的区域

堆vs栈:

: 空间有限。普通的定义int a = 4; 就是在栈中,分配速度快,但是不能控制内存,无法手工释放
:只要不超过实际物理内存都可以分配,分配速度比较慢,不用的时候可以自行释放

2 new/delete

2.1 new/delete介绍

new/delete:是标识符。C++中使用new/delete取代malloc/free来分配和释放内存。
sizeof(关键字/运算符),new/delete(关键字/运算符),不是个函数
三种用法:

int *a1 = new int;
int *a2 = new int(5);   //初始值为5
int *a3 = new int[10]; //内存单元个数delete a1;
delete a2;
delete[] a3;

注1:new加括号和不加括号的区别,在没有构造函数的时候,new ()会将成员变量清零,不然就是随机值。有构造函数时两者一样
注2:new出来的对象必须手动delete进行释放,系统不会帮你释放

2.2 new的运行步骤

相对于malloc/free而言,new/delete干的事更多。
new做了两件事:

  1. 调用operator new()来分配内存
  2. 调用构造函数初始化内容
A *pa = new A();//调用构造函数
delete pa;//调用析构函数

new/delete具备堆上内存进行初始化/反初始化的能力,而malloc/free则没有

2.3 operator new()和operator delete()

operator new()和operator delete()本质上是函数

void *myorgpoint = operator new(100);//分配了100个字节;

new内部有记录机制,知道某个指针占用多少字节

2.4 new/delete和new[]/delete[]

如果对于一个对象,使用new[]来分配内存,却用单独的delete来释放内存,这个对象需要满足以下一个条件:

  1. 对象的类型要么是一个内置类型
  2. 要么是自定义类型但是没有析构函数

A *pA = new A4;
delete pA;
如果有析构函数,会占用4+4的空间
如果没有析构函数,则只占用4字节的空间
调用一次析构函数,而不是两次析构函数,而且释放的空间会出现问题

:new产生的对象,不能用delete[]来释放!
####2.5 nullptr vs NULL

 cout << typeid(nullptr).name() << endl;cout << typeid(NULL).name() << endl;


两个是不同的类型。如果我们仍然使用NULL去表示指针,那么在函数重载时就无法区分空指针和整数类型了。

new/delete的问题小结

new出来必须delete
delete之后,该块内存无法再使用
同一块内存释放两次也会报异常

3 C++智能指针

解决裸指针可能遇到的问题,裸指针包装成智能指针,能自动释放new出来的内存。有四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr。目前auto_ptr已经被unique_ptr完全取代了,所以auto_ptr已经强烈不建议被再使用。
这三种指针都是类模板。

3.1 shared_ptr介绍

共享所有权,不是被一个shared_ptr拥有,而是被多个shared_ptr之间相互写作。
工作原理:引用技术,每个shared_ptr的拷贝指向相同的内存,所以只有最后一个指向内存的shared_ptr不再指向该对象时,该对象才被析构
类模板:用到尖括号,指针可以指向的类型
格式:shared_ptr<指向的格式> 智能指针名;
使用方法

//法一:智能指针和new搭配使用
shared_ptr<int> pi(new int(100));
shared_ptr<int> pi2 = new int(100); //这时错的!!因为shared_ptr是个类,构造函数explicit,不能隐式转换。
int *p1 = new int();
shared_ptr<int> pi3(p1);  //裸指针可以用来初始化智能指针,但是不推荐
//法二:make_shared函数,标准库里的函数模板
shared_ptr<int> p2 = make_shared<int>(100);
shared_ptr<string> p3 = make_shared<string>(5,'a');

在堆中分配初始化一个对象,返回指向该对象的shared_ptr

shared_ptr计数
两种情况下shared_ptr引用计数会加1:

  1. 有一个指针来初始化另一个指针
  2. 把智能指针当作实参往函数里传
  3. 作为函数的返回值(如果有对象去接的话)

引用计数的减少

  1. 原指针指向新对象
  2. 局部的shared_ptr离开作用域

3.2 shared_ptr常用操作

  1. use_count():返回多少个智能指针指向某个对象,主要用于调试
  2. unique():是否该智能指针独占某个对象
  3. reset():复位
    若pi是唯一指向某个对象的智能指针,释放该对象,将pi置为空指针
    若pi不是唯一,则计数减一,将pi置空
    带参数时,释放指向对象,让pi指向新对象
    空指针也可以用reset来指向新对象
  4. *解引用
  5. p.get():返回裸指针,适用于一些第三方的库,不需要delete
  6. swap():std::swap(p1,p2);
  7. =nullptr
  8. 智能指针能作为if直接判断的条件
  9. 删除器,系统无法帮我们自动析构的对象,比如说自己定义的类。
shared_ptr<A[]> pA(new A[10);
shared_ptr<A> pA1(new A[10], std::default_delete<A[]>());

3.3 weak_ptr

weak_ptr指向一个由shared_ptr管理的对象,但是weak_ptr这种指针不控制计数器,当计数器为0时,对象会释放,不管有没有weak_ptr指向该对象
作用:监视强引用生命周期用的,对于shared_ptr是一种扩充
创建:

auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi);

强引用计数不会改变,弱引用计数会改变
常用操作:

  1. lock():返回一个shared_ptr指针
  2. use_count():shared_ptr的计数值
  3. expired():若use_count()为0,则返回true
  4. reset():弱引用计数减一,强引用不变
尺寸问题

weak_ptr和shared_ptr是裸指针的两倍,内部有两个指针一个指向对象,一个指向控制块

3.4 shared_ptr陷阱

裸指针问题
int *p = new int(100);
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);

会有问题,因为p1和p2用的两个不同的计数,p1释放一次,p2也会释放一次,会异常!后续的初始化只能用智能指针来初始化

get返回指针问题

同理,get返回的指针不能delete,否则智能指针回收不了了,也不能将其他智能指针帮到get指针上

死锁问题
class A{public:shared_ptr<B> psb;
};
class B{public:shared_ptr<A> psa;
};
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());

3.5 unique_ptr

概念介绍

同一个时刻,只有一个unique_ptr指针指向这个对象,当unique指针被销毁时,它所指的对象也被销毁

unique_ptr操作
unique_ptr<string> ps1(new string("I love China");
unique_ptr<string> ps2(ps1);//不行!
unique_ptr<string> ps2 = ps1;

要用std::move
unique_ptr ps2(std::move(ps1));
unique_ptr ps2 = std::move(ps1);

release():放弃对指针的控制权,将智能指针置空并返回裸指针

unique_ptr<string> ps3(ps1.release());
ps1.release();//不行!!直接内存泄漏

reset(): 释放智能指针所指的对象,并将智能指针置空
带参数,释放智能指针所指的对象,并让智能指针指向新对象

ps1.reset(ps2.release);

总结

智能指针的主要目的:帮助我们释放内存,以防止我们忘记释放内存时造成的内存泄漏。
auto_ptr的问题:不能再容器中保存,也不能从函数中返回,而且shared_ptr, unique_ptr在编译的时候就会出错,不会在运行中出错,崩溃的问题
智能指针的选择,如果需要多个指向同一个对象的指针,选择shared_ptr,
除此之外,首选unique_ptr

C++ 内存空间初探相关推荐

  1. python 申请内存空间、用于创建多维数组_python 申请内存空间,用于创建多维数组的实例...

    以三维数组为例 先申请1个一维数组空间: mat = [None]*d1 d1是第一维的长度. 再把mat中每个元素扩展为第二维的长度: for i in range(len(mat)): mat[i ...

  2. Java虚拟机的内存空间有几种

    Java虚拟机的内存空间有几种?(1)问题分析: JVM(虚拟机)的内存划分 不同的数据使用的是哪一块内存空间 (2)核心答案讲解: Java虚拟机有那几块内存空间: 1)栈内存:方法运行时所进入的内 ...

  3. malloc一次性最大能申请多大内存空间

    受用户态内存地址空间的限制.64 位系统下分配几个 T 不成问题. 著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:zz matrix 链接:http://www.zhi ...

  4. 在请求分页虚存管理系统中_请求分页式系统中,以页为单位管理用户的虚空间,以段为单位管理内存空间_学小易找答案...

    [简答题]OS作为接口,通过哪几种方式实现? [简答题]并发与并行有什么区别? [判断题]请求分页式系统中,以页为单位管理用户的虚空间,以段为单位管理内存空间 [简答题]微内核是否是完整的OS? [单 ...

  5. 关于共用体所占的内存空间的问题

    共用体 `共用体(联合)` 共用体类型变量的定义 共用体变量的引用方式 共用体类型数据的特点 [例1]分析程序运行结果 [实训内容3]输入并运行以下程序,从输出的结果体会"共用"的 ...

  6. linux 进程 内存布局 初探

    在进程中,分配了很多的内存空间,这些一个个的内存空间 ,可以称之为 "段" 简单理解进程就是"一个独立的程序 "在C中就是 有一个main入口的程序 (针对UN ...

  7. C#之CLR内存原理初探

    C#之CLR内存原理初探 投稿:shichen2014 字体:[增加 减小] 类型:转载 时间:2014-08-04 我要评论 这篇文章主要介绍了C#之CLR内存原理初探,有助于读者进一步理解C#的运 ...

  8. python3 赋值与内存空间

    先看一个例子: x = [1, 2, 3] y = x x[1] = 100print(f"{x=} {y=}") 输出: x=[1, 100, 3] y=[1, 100, 3] ...

  9. Linux 用户进程内存空间详解

    经常使用top命令了解进程信息,其中包括内存方面的信息.命令top帮助文档是这么解释各个字段的. VIRT , Virtual Image (kb) RES, Resident size (kb) S ...

最新文章

  1. GNS3模拟VPC注意几点
  2. linux多路径策略配置,linux 多路径配置
  3. MySQL执行外部sql脚本
  4. mysql索引查询 with_查找mysql中的低效索引
  5. java类函数默认的保护级别_事件说明
  6. mysql高德地图设计_如何优雅的制作那些好看的地图
  7. 【机器学习】岭回归(L2正则在干嘛!)
  8. 在Python中使用一个元素创建一个元组
  9. SQL Server安装文件挂起错误解决办法【转帖】
  10. 1137.第N个泰波那契数
  11. Firebase可监控网页应用程序效能 更新其Analytics受众系统
  12. python---用python实现冒泡排序
  13. Linux源码编译nginx
  14. android手机访问协议页面,易语言修改IE协议头留言手机版网页
  15. Java基础(员工工资管理系统)
  16. 制作颜色选择器(全)
  17. oreo另一个意思_墓碑上的“故,显,考,妣”是什么意思?
  18. unity制作mmd视频
  19. uboot介绍:介绍uboot的基本概念、用法和实现方式
  20. 讲讲如何写论文和发论文(通信类)

热门文章

  1. Android 动画的分类
  2. 艰难的时候总会过去,只要你能坚持下来~
  3. Twitter的分布式自增ID算法snowflake
  4. weak_ptr概述,weak_ptr常用操作、尺寸
  5. 数据库 linux 编译,部署mariadb数据库到linux(源码编译安装)
  6. 2020年书法落款_快来排排2020鼠年书法落款时间表,收!
  7. 作用域链涉及了什么计算机底层知识,你必须知道的Javascript知识点之深入理解作用域链的介绍...
  8. java 正则判断二进制_用正则表达式判断一个二进制数是否能被3整除
  9. oracle 数组的用法,Oracle数组用法
  10. SetWindowPos()详解