C++ 内存空间初探

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值