动态内存与智能指针

  • shared_ptr<T> sp:空指针,可以指向类型为T的对象
  • unique_ptr<T>up:空指针,可以指向类型为T的对象
  • p:p用作一个条件判断,若p指向一个对象,则true
  • *p:解引用p,获得它指向的对象
  • p->me,:等价于(*p).mem
  • p.get():返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。
  • swap(p,q):交换p于q中的指针
  • p.swap(q):交换p和q中的指针
  • make_shared<T>(args):返回一个shared_ptr,指向一个动态分配的类型为T的对象,使得args初始化此对象
  • shared_ptr<T> p(q):p是shared_ptr的拷贝‘此操作会递增q的计数器。q中的指针必须转换为T*
  • p=q:p和q都是shared_ptr,所保存的指针必须能相互交换。此操作会递减p的引用计数,递增q的引用计数,递增p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放
  • p.unique():若p.use_count()为1,返回true;否则返回false;
  • p.use_count():返回与p共享对象的智能指针数量;可能很慢,主要用于调试
//指向一个值为42的int的shared_ptr
shared_ptr<string> p3=make_share<int>(42)
//p4指向一个值为"999999999"的string
share_ptr<string> p4=make_shared<string>(10,'9');
//p5指向一个值初始化的int,即,值为0
shared_ptr<int> p5=make_shared<int>();

shared_ptr的拷贝和赋值

auto p=make_shared<int>(42);//p指向的对象只有一个引用者
auto q(p)//p和q指向相同对象,此对象有两个引用者
auto r=make_shared<int>(42);
r=q;//给r赋值,令其指向令一个地址
//递增q指向的对象的引用
//递减r原来指向的对象的引用奇数
//r原来指向的对象已经没有引用者,会自动释放
``
## shared_ptr还会自动释放相关联的内存
当动态对象不再被使用是,shared_ptr类会自动释放动态对象,这一特征使得动态内存的使用非常容易。例如,我们可能有一个函数,它返回一个shared_ptr,指向一个FOO类型的动态分配的对象,对象通过一个类型为T的参数惊醒初始化

```cpp
shared_ptr<Foo> Factory(T arg)
{
	return make_shared<Foo>(arg) 
}

由于factory返回一个shared_ptr,所以我们可以确定它分配的对象会在恰当的时候被释放。例如,下面的函数将factory返回的shared_pte保存在局部变量中

void use_factory(T arg)
{
	shared_ptr<Foo> p=factory(arg);
}

由于p是use_factory的局部变量,在use_factory结束时它将被销毁。当p被销毁时,将递减其引用计数并检查它是否为0。在此例中,p时唯一一个引用factory返回的内存对象。由于p将要销毁,p指向的这个对象也会被销毁,所占用的内存也会被释放
但如果有其他shared_ptr也指向这块内存,它就不会被释放掉:

shared_ptr<Foo> use_factory(T args)
{
	shared_ptr<Foo> use_factory(arg);
	return p;//当我们返回p时,引用计数进行了递增操作
}//p离开了作用域,但它指向的内存不会被释放掉

在此版本中,use_factory中return 会返回一个p的拷贝。拷贝一个shared_ptr会增加所管理对象的引用计数值。现在当p被销毁时,它所指向的内存还会有其他使用者。对于一块内存,shared_ptr类保证只要有任何shared_ptr对象应用它,它就不会被释放掉。

直接管理内存

使用new动态分配和初始化对象

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

int *pi=new int//pi指向一个动态分配的,未初始化的对象

此new表达式在自由空间构造一个Int型对象,并返回指向该对象的无名对象
默认情况下,动态分配的对象时默认初始化的,这意味着内置类型或组合类型的对象的值时未定义的,而类对象将用默认构造函数进行初始化:

string *pi=new string;//初始化为空string
int *pi=new int;//pi指向一个未初始化的int

我们可以直接初始化方式来分配一个动态分配的对象,我们可以使用传统的构造方式,在新标准下,也可以使用列表初始化(使用花括号)

int *pi=new int (1024);  //pi指向的对象的值为1024
string *pi=new string(10,'9');
vector<int> *pv=new vector<int>{0,1,2,3,4};

也可以对动态分配的对象进行初始化,只需在类型名之后跟一个括号即可

string *ps1=new string;//默认初始化为空string
string *ps=new string;//值初始化为空string
int *pi1=new int;//默认初始化;*pi1的值未定义
int *pi2=new int();//值初始化0,*pi2为0

动态分配的const对象

用new分配const对象时合法的:

//分配并初始化一个const int
const int *pci=new const int(1024);
//分配并默认初始化一个const的空string
const string *pcs=new const string;

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

内存耗尽

默认情况下,如果new不能分配所要求的内存空间,它会抛出一个类型为bad_alloc的异常。我们可以改变使用new的方式来阻止他抛出异常

//如果分配失败,new返回一个空指针
int *p1 = new int;//如果分配失败,new抛出std::bad_alloc
int *p2 = new (nothrow) int;//如果分配失败,new返回一个空指针

释放动态内存

与new类型相似,delete表达式也执行两个动作:销毁给定的指针指向的对象来释放对应的对应的内存。

shared_ptr和new结合使用

如前所示,如果我们不初始化一个智能指针,它就会被初始化为一个空指针

  • shared_ptr<T>:p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型
  • shared_ptr<T> p(u):p从unique u 那里接管了对象的所有权;将u置位空p接管可内置指针q指向的对象的所有权。q必须能转换为T*类型。
  • shared_ptr<T> p(u):p将使用可调用对象d来代替delete
  • p.rest():reset会释放此对象,将p置位空。

智能指针和异常

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

void f()
{
	shared_ptr<int> sp(new int (42));//分配一个新对象
	//这段代码抛出一个异常,且在f中被被捕获 
}//在函数结束时shared_pte自动释放

函数的退出也有两种可能,正常处理结束或者发生或者发生异常,无论哪种情况,局部对象都会被销毁。
与之相对的,当异常发生时,我们直接管理的内存是不会自动释放的。如果使用内置指针管理内存,且在new之后再对应的delete之前发生了异常,则内存不会被释放

void f()
{
	int *pi =new int(42);//动态分配一个新对象
	//这段代码抛出一个异常,且在f中尚为捕获
	delete ip;//在退出之前释放内存
}

如果在new和delete之前发生异常,且异常未在f中被捕获,则内存就永远不会被释放了。在函数f之外没有指针指向这块内存,因此就无法释放它了。

unique_ptr

一个unique_ptr“拥有”它所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。

  • unique_ptr<T> u1:空unique_ptr,可以指向类型为T的对象。u1会使用delete对象来释放它的指针;
  • unique_ptr<T,D> u1:空unique_ptr,可以指向类型为T的对象。u2会使用一个类型为D的可调用对象来释放它的对象。
  • unique_ptr<T,D> u2:空unique_ptr,指向类型为T的对象,用类型为D的对象ddairi delete
  • u=nullptr:释放u指向的对象,将u置位空
  • u.release():u放弃指针的控制权,返回指针,并将u置位空
  • u.reset(q):释放u指向的对象,令u指向q
  • u.reset(nullptr):释放u指向的指针,将u置为空

与shared_ptr不同,没有类似make_shared的标准函数库返回一个unique_ptr。当我们定义一个unique_ptr时,需要将其绑定一个new返回的指针上,类似shared_ptr初始化unique_ptr,初始化unique_ptr必须采用直接初始化形式

unique_ptr<double> p1;//可以指向一个double的unique_ptr
unique_ptr<int> p2(new int(42))//p2指向一个值为42的int

由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作;

unique_ptr<string> p1(new string ("Stegosaurus"));
unique_ptr<string> p2(p1);//错误:unique_ptr不支持拷贝
unique_ptr<string> p3;
p3=p2;//错误unique_ptr不支持赋值

虽然我们不能拷贝或赋值unique_ptr,但可以通过release或者reset将指针的所有权从一个(非const)unique_ptr转移给一个unique:

unique_ptr<string> p2(p1.release());//release将p1置位空
unique_ptr<string> p3(new string ("Trex"));//将所有权从p3转移给p2
p2.release(p3.release());//reset释放了p2原来指向的内存

release成员返回unique_ptr当前保存的指针并将其置位空。因此,p2被初始化为p1原来保存的指针,而p1被置位空

传递unique_ptr参数和返回unique_ptr

不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr:

unique_ptr<int> clone(int p){
	return unique_ptr<int>(new int(p));
}

还可以返回一个局部对象的拷贝

unique_ptr<int> clone(int p){
		unique_ptr<int> ret(new int (p));
		//...
		return ret;
}

向unique_ptr传递删除器

我们必须在尖括号中unique_ptr指向类型之后提供删除器类型。在创建或reset一个折中unique_ptr类型的对象时,必须提供一个指定类型的可调函数。

//p指向一个类型为objT的对象,并使用一个类型为delTde对象释放objT对象
//它会调用一个名为fcn的delT类型对象
unique_ptr<objT,delT>p(new objT,fcn)

weak_ptr

  • weal_ptr/ w:空weal_ptr可以指向类型为T的对象
  • weak_ptr/w(sp):与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型
  • w=p:p可以时一个shared_ptr或一个weak_ptr。赋值后w与p共享对象
  • w.reset():将w置位空
  • w.use_count():与w共享的shared_ptr数量
  • w.expired():如果w.use_count()为0,返回true,否则返回false
  • w.lock():如果expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr

weak_ptr是一种不可控所指向对象生存周期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使由weak_ptr指向对象,对象也会被释放,因此,weak_ptr的名字抓住了这种智能指针"弱"共享对象的特点。
当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它:

auto p=make_shared<int>42;
weak_ptr<int>wp(p);//wp弱共享p;p的引用计数未改变

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否存在。如果存在,lock返回一个指向共享对象的shared_ptr。与任何其他shared_ptr类似,只要此shared_ptr存在,它指向的底层就会一直存在。例如:

if (shared_ptr<int> np=wp.lock()){//如果np不为空则条件成立
//在if中,np与p共享对象
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值