c++11新特性-11-动态内存(智能指针与new)

一、引言

1、动态分配的对象的生存期与它们在哪里创建是无关的,只有当显示的被释放时,这些对象才会销毁。

2、静态内存,堆和栈内存。
2.1、静态内存是用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。
2.2、栈内存用来保存定义在函数内的非static对象。
2.3、程序用堆来存储动态内存。
2.4、分配在静态或堆内存中的对象由编译器自动创建和销毁。

3、C++中动态内存的管理是通过一对运算符来完成的。

new在动态内存中为对象分配空间并返回一个指向该对象的指针
delete接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

注意:动态内存的使用容易出现问题。有时我们会忘记释放内存。,这种情况下就会产生内存泄漏。

4、为了更容易使用动态内存,新的标准库提供了两种智能指针来管理动态对象。

类型
shared_ptr允许多个指针指向同一个对象
unique_ptr独占所指向的对象
weak_ptr一种弱引用,指向shared_ptr所管理的对象

这三种类型都定义在memory头文件中。

二、动态内存管理

1、使用动态内存的原因

1.1、程序不知道自己需要使用多少对象。
1.2、程序不知道所需对象的准确类型。
1.3、程序需要在多个对象之间共享数据。

2、智能指针

2.1、shared_ptr

2.1.1、shared_ptr定义与初始化
shared_ptr<string> p1 //shared_ptr,可以指向string
shared_ptr<list<int>> p2  //shared_ptr可以指向int的list
shared_ptr<int>p3(new int (42)); //p2指向一个值为42的int
//我们不能进行内置指针到智能指针间的隐式转换。因为智能指针默认使用delete释放它所关联的对象。

默认初始化的智能指针中保存着一个空指针。

2.1.2、shared_ptr操作
shared_ptr<~> sp空智能指针,可以指向任意类型的对象
p将p用作对象,若p指向一个对象,则为true
*p解引用p,获得他指向的对象
p->mem等价于(*p).mem
p.get()返回p中保存的指针
swap(p,q)交换p,q的指针
p.swap(q)等价于swap(p,q)
make_shared<~>(args)返回一个shared_ptr
shared_ptr<~>p(q)p是shared_ptr q的拷贝
p=qp和q都是shared_ptr,用保存的指针必须可以相互转换。此操作会递减p的引用计数,递增q的引用计数。
p.use_count()返回与p共享对象的智能指针数量
p.unique()若p.use_count()为1,返回true否则false
2.1.3、make_shared操作

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。

shared_ptr<int>p3=make_shared<int>(42);  //指向一个值为42的int的shared_ptr
shared_ptr<int>p5=make_shared<int>(); //初始化值为0
shared_ptr<string> p4=make_shared<string>(10,'9'); //p4指向一个值为“9999999999”的string
auto p6=make_shared<vector<string>>();  //正确。
2.1.4、shared_ptr的拷贝和赋值

我们可以认为每个shared_ptr都有一个关联的计数器,通常称为引用计数。

递增情况:

无论何时我们拷贝一个shared_ptr,计数器都会递增。例如:用一个shared_ptr去初始化另一个shared_ptr或将它作为参数传递给一个函数或作为函数的返回值,它所关联的计数器都会递增。

递减情况:

当我们给shared_ptr赋予一个新值或shared_ptr被销毁时(例如,一个局部的shared_ptr离开其作用域)时,会递减。

一旦计数器变为0,他就会自动释放自己所管理的对象。

2.1.5、定义和改变shared_ptr的其他方式
shared_ptr<~>p(q)p管理内置指针q所指向的对象,q必须是new分配的内存
shared_ptr<~>p(u)p从unique_ptr u那里接管了对象的所有权
shared_ptr<~>p(q,d)p接管了内置指针q所指向对象的所有权,p将使用可调用对象d来代替delete
p.reset()若p是唯一指向的对象,那么就释放p。
p.reset(q)若传递了可选的参数内置指针q,会令p指向q,否则将p置空
p.reset(q,d)调用d释放

2.2、unique_ptr

2.2.1、unique_ptr定义与初始化
unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));

由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。但可以传递unique_ptr参数和返回unique_ptr

unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));
unique_ptr<int>p3(p2);  //错误
unique_ptr<double>p4;
p4=p1;  //错误
2.2.2、unique_ptr操作
unique_ptr<~> sp空智能指针,可以指向任意类型的对象
p将p用作对象,若p指向一个对象,则为true
*p解引用p,获得他指向的对象
p->mem等价于(*p).mem
p.get()返回p中保存的指针
swap(p,q)交换p,q的指针
p.swap(q)等价于swap(p,q)
unique_ptr<T,D> u2u2使用D来释放它的指针
unique_ptr<T,D>u(d)用类型为D的对象d代替delete
u=nullptr释放u指向的对象,将u置空
u.release()u放弃对指针的控制,返回指针并将u置空
u.reset()释放u指向的对象
u.reset(q)如果提供了内置指针q,令u指向这个对象,否则将u置空
u.reset(nullptr)

2.3、智能指针和异常

如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不需要时将其释放。

2.4、智能指针陷阱

2.4.1、不能使用相同的内置指针初始化多个智能指针。
2.4.2、不delete get()返回的指针。
2.4.3、不使用get()初始化或者reset另一个智能指针。
2.4.4、如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就无效了。
2.4.5、如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

3、new,delete

3.1、 new

3.1.1、new分配和初始化

在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。

//采用默认初始化方式
int *p=new int;    //p指向一个动态分配的,未初始化的无名对象。
string *p1=new string;    //初始化为空string

//使用直接初始化的方式初始化动态分配的对象
int *pi=new int(1024);  //pi指向的对象的值为1024
string *p2=new string(10,'9');   //*p2=“9999999999”
vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9};

//值初始化方式
string *ps=new string();  //空字符串
int *p=new int();   // 0

//使用auto
auto p1=new auto(obj);  //正确,类型与obj类型相同
auto p2=new auto(a,b,c);  //错误,括号中只能有单个初始化器
3.1.2、new分配const对象
const int *p=new const int(1024);
const string *p1=new const string;

3.1.2.1、一个动态分配的const对象必须进行初始化。
3.1.2.2、对于一个定义了默认构造函数的类类型,其中const动态对象可以隐式初始化,而其他类型的对象必须显示初始化。
3.1.2.3、由于分配的对象是const的,new返回的指针是一个指向const的指针。

3.2、 delete

3.1.1、释放动态内存

为了防止内存耗尽,在动态内存使用完毕后,必须将其归还给系统。
delete表达式接受一个指针,指向我们要释放的对象。

int i,*pi=&i,*p2=nullptr;
double *pd=new double(33),*pd2=pd;
delete i;  //错误,i不是一个指针
delete pi;   //未定义,pi指向一个局部变量
delete pd;   //正确
delete pd2;   //未定义,pd2指向的内存已经被释放了
delete p2;  //正确

我们传递给.delete的指针必须指向动态分配的内存,或者是一个空指针。
释放一块并非new分配的内存或者将相同的指针值释放多次,其行为是未定义的。

3.1.2、释放const对象
const int *pc=new const int();
delete pc;  //正确

虽然一个const对象的值不能被改变,但他本身是可以被销毁的。

3.1.3、生存期

动态对象的生存期,直到被释放才结束,与普通对象不同。

void use_factory(T arg)
{
Foo *p=new factory(arg);
}  //p离开了他的作用域,但他所指向的内存没有被释放
3.1.4、空悬指针

当我们delete一个指针后,指针值就变为无效了,虽然指针值无效了,但是很多机器上指针仍然保存着动态内存的地址。在delete之后,指针就变成了人们常说的空悬指针。

3.3、 new,delete常见问题

3.3.1、忘记delete内存。
3.3.2、使用已经释放掉的对象。
3.3.3、同一块内存释放多次。

4、动态数组

4.1、 new和delete配合

int *pia=new int[n];  //默认初始化
int *pia1=new int[n](); //值初始化
int *pia2=new int[3]{0,1,2};  //花括号列表初始化

delete []pia;  //释放数组

4.1、 unique_ptr

为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号。

unique_ptr<int[]>up(new int[10]);
unique_ptr<T[ ]>uu指向一个动态分配的数组
unique_ptr<T [ ]>u§u指向内置指针p指向的动态分配的数组
u[ i ]返回u中拥有位置i元素值
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值