C++Primer笔记一-动态内存


内存管理:
静态内存:保存局部static对象、类static数据成员以及定义在任何函数之外的变量。
栈内存:保存定义在函数内的非static对象。
堆内存(自由空间):存储动态分配的对象。
区别:静态内存和栈内存中的对象由编译器自动创建和销毁。堆中的动态对象的生存周期由程序来控制(在代码中显式地声明和销毁)。

12.1 动态内存与智能指针

运算符:new/delete
**C++11:**智能指针

#include <memory>

shared_ptr: 允许多个指针指向一个对象;
unique_ptr: 只有一个指针可以指向一个对象;
weak_ptr: 弱引用,指向shared_ptr指向的对象。

12.1 shared_ptr类

EXAMPLES

shared_ptr<string> p1;     //类似vector,指向string
shared_ptr<list<int>> p2;  //指向int的list,默认初始化保存空指针。

shared_ptr, unique_ptr都支持的操作

shared_ptr<T> sp;
unique_ptr<T> up;//空智能指针
p     //作为条件判断,如果p指向一个对象则为true
*p    //解引用p,获得它指向的对象
p->mem  // <=> (*p).mem
p.get()    //返回p中保存的指针。
swap(p,q) //<=> p.swap(q) , 交换p和q中的指针。

仅适用于shared_ptr

make_shared<T>(args) // 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args参数初始化。
shared_ptr<T> p(q);  //p是shared_ptr q的拷贝。此操作会递增q中的计数器,q中的指针必须能转成T*。
p=q;  //p和q都是shared_ptr,所保存的指针必须能互相转化。
// p中的引用计数递减,q中的引用计数递增,如果p的引用计数变为0,则将其管理的原内存释放。
p.unique()   //若p.use_count()为1,则返回true;否则false
p.use_count() //返回与p共享对象的智能指针数量。

shared_ptr的销毁以及管理对象内存的释放

auto p=make_shared<int>(42);
auto q(p); //拷贝,p管理的对象的引用计数+1;
auto r=make_shared<int>(42);
r=q; //赋值 q管理的对象引用计数+1,r对应的-1。
//由于r指向对象引用计数为0,则自动销毁该对象。

管理某个对象的shared_ptr在离开程序作用域后会自动销毁,如果该对象的引用计数-1,如果为0则该对象的内存也会被释放。

12.1.2 直接管理内存

运算符new分配内存,delete释放new分配的内存。
使用new动态分配和初始化对象
new运算符返回一个指向分配的对象的指针。在类型名之后跟一对空括号对该对象进行值初始化。

int *p=new int;
int *p=new int(1);

用new分配const对象是合法的。一个动态分配的const对象必须进行初始化,返回的指针是一个指向const的指针。

const int* p=new const int(1);

内存耗尽的情况:

//内存不足
int *p1 =new int; //抛出std::bad_alloc
int *p2 = new (nothrow) int; //nothrow 阻止异常抛出,new将返回空指针

释放动态内存
delete表达式释放内存,delete表达式接受一个指针,该指针必须指向一个动态分配的对象或是一个空指针。(new分配的const对象也可以使用delete释放)

delete p;

delete不可释放一个并非new分配的内存,也不可以同时多次释放相同的指针值。动态对象的生存期直到被释放时为止。

12.1.3 shared_ptr和new结合使用

我们可以使用new返回的指针初始化shared_ptr智能指针。接受指针参数的智能指针构造函数是explicit的,因此必须使用直接初始化形式来初始化一个智能指针。

shared_ptr<double> p1;
shared_ptr<int> p2(new int(42)); //正确,p2指向一个值为42的int
shared_ptr<int> p3=new int(0); //错误,不可以将一个内置指针隐式转换为一个智能指针。

定义和改变shared_ptr的其他方法

shared_ptr<T> p(q);  //p管理内置指针q指向的对象;
shared_ptr<T> p(u); //u为unique_ptr。p接管u管理的对象,将u置为空。
shared_ptr<T> p(q,d); //p接管内置指针q管理的对象,q必须指向new分配的对象,且能转换为T*类型。
//p将使用可调用对象d来代替delete。
shared_ptr<T> p(p2,d); //p是shared_ptr p2的拷贝
p.reset();//如果p为唯一指向其对象的shared_ptr, reset会释放该对象
p.reset(q);  //q是指向new分配对象的普通指针,且能转换为T*类型。
p.reset(q,d); //与上类似。

注意: 不要混用普通指针和智能指针。也不要使用get初始化另一个智能指针或为智能指针赋值。

12.1.4 智能指针和异常

1.内置指针在new之后,delete之前出现异常,程序停止,那么该动态内存就永远不会被释放;而使用智能指针可以保证在内存不再需要时将其释放,即使程序块过早结束。
2.智能指针可以在类对象没有定义析构函数函数,而用户没有显式地释放其使用的资源时,保证其资源被正确的释放。
3.可以自定义自己的释放操作。即在智能指针值初始化或者reset时通过参数显式定义我们想要的释放操作。
注意:
不使用相同的内置指针初始化(reset)多个智能指针;
不delete get()的指针;
不使用get来初始化或reset另一个智能指针;
如果使用get()返回的指针,当最后一个对应的智能指针销毁后,你的指针就变为无效了。
如果智能指针管理的资源不是new分配的内存,需要传递给它一个删除器。

12.1.5 unique_ptr

一个unique_ptr与其管理的对象一一对应。unique_ptr不支持普通的拷贝、赋值。
unique_ptr的操作

unique_ptr<T> u1;
unique_ptr<T, D> u1;
unique_ptr<T, D> u1(d);
u=nullptr;
u.release()
u.reset(q)
u.reset(nullptr)

12.1.6 weak_ptr

weak_ptr不控制所致对象生存期的智能指针,指向shared_ptr管理的对象,weak_ptr指向shared_ptr不会改变其引用计数。在shared_ptr引用计数为0时,即使有weak_ptr指向对象,对象还是会被释放。
weak_ptr操作

weak_ptr<T> w;
weak_ptr<T> w(sp);
w=p;
w.reset()
w.use_count()
w.expired() //return true if use_count==0;
w.lock()  // return null shared_ptr if expired==true;

12.2 动态数组

12.2.1 new和数组

分配动态数组

int *ip=new int[n]; //默认初始化为0
string *sp=new string[n]; //默认初始化为空字符串

n必须为整型,但并非必须为常量(直接声明数组必须为常量)。
new分配一个数据,返回一个指向数组首地址的数组元素类型的指针。
由于分配的内存不是一个数组类型(容器类型)

释放动态数组

delete[] ip;

使用unique_ptr管理动态数组
标准库提供:

unique_ptr<int[]> up(new int[10]); //up指向一个包含10个默认初始化int的数组
up.release(); //自动使用delete[]销毁其指针

指向数组的unique_ptr不支持成员访问运算符(. / ->)其他操作不变:

unique_ptr<T[]> u;
unique_ptr<T[]> u(p);
u[i]; //u必须指向一个数组

shared_ptr不直接支持管理动态数组
必须提供自定义的删除器

shared_ptr<int> sp(new int[10],[](int* p){delete[] p;});
sp.reset();
//shared_ptr未定义下标运算符
for(int i=0;i!=10;i++){
	*(sp.get()+i)=i;//使用get获取指向数组首地址的内置指针
}

12.2.2 allocator类

#include <memory>

Allocator支持的操作

allocator<T> a;
a.allocate(n);
a.deallocate(p,n);
a.construct(p,args);
a.destroy(p);

使用allocator需要注意:

  • 使用const指针保存内存起始地址。 auto const p=a.allocate(n);
  • 分配完动态地址中的每个对象是未构造的,需要使用指针访问逐个对象的地址并调用construct函数;
  • 在使用deallocate函数之前必须对动态内存中已经构造的每个对象使用析构函数destroy。

allocator算法

uninitialized_copy(b,e,b2);
uninitialized_copy_n(b,n,b2);
uninitialized_fill(b,e,t);
uninitialized_fill_n(b,n,t);

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值