C++定义了两个运算符来分配和释放动态内存。运算符new分配内存,delete释放new分配的内存。
相对于智能指针,使用这两个运算符管理内存很容易出错。而且,自己直接管理内存的类与使用智能指针的类不同,他们不能依赖类对象拷贝、赋值和销毁操作的任何默认定义。
使用new动态分配和初始化对象
【资料图】
在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针
默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值将是未定义的。而类类型对象将用默认构造函数进行初始化
我们可以使用直接初始化方式来初始化一个动态分配的对象。我们可以使用传统的构造方式(圆括号),在C++11标准下,也可以使用列表初始化(花括号)
也可以对动态分配的对象进行值初始化,只需在类型名之后跟一对空括号即可
对于定义了自己的构造函数的类类型(如string)来说,要求值初始化是没有意义的,不管采用什么形式,对象都会通过默认构造函数来初始化。
但是对于内置类型,两种形式的差别就很大了,值初始化的内置类型对象有着良好定义的值,而默认初始化的对象的值则是未定义的。类似的,对于类中那些依赖于编译器合成的默认构造函数的内置类型成员,如果他们未在类内被初始化,那么他们的值也是未定义的。
如果我们提供了一个括号包围的初始化器,就可以使用auto从初始化器来推断我们想要分配的对象的类型。但是由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才能使用auto
动态分配的const对象
用new分配const对象是合法的
类似于普通const对象,一个动态分配的const对象必须进行初始化。
由于分配的对象是const的,new返回的指针是一个指向const的指针
内存耗尽
虽然现代计算机通常都配备大容量内存,但是自由空间被耗尽的情况还是有可能发生的。一旦一个程序用光了他所有可用的内存,new表达式就会失败。默认情况下,如果new不能分配所要求的内存空间,他会抛出一个类型未bad_alloc的异常。我们可以改变使用new的方式来阻止他抛出异常
我们称这种形式为定位new,原因我们后面会解释,此例中,我们给new传递额外的参数nothrow,让他不能抛出异常,一旦new不能分配所需内存,他会返回一个空指针。
bad_alloc和nothrow都定义在头文件new中
释放动态内存
为了防止内存耗尽,在动态内存使用完毕后,必须将其还给系统。我们通过delete表达式来将动态内存归还。
delete表达式接受一个指针,指向我们要释放的对象
delete表达式执行两个动作,销毁给定的指针指向的对象;释放对应的内存。
指针值和delete
释放一块非new分配的内存,或者将相同指针值释放多次,是未定义的行为
对于未定义的行为,编译器一般不能分辨,尽管他们是错的,但是他们还是会通过编译。
const对象的值不能改变,但是可以销毁。
动态对象的生存期知道被释放为止
shared_ptr管理的内存在最后一个shared_ptr销毁时会被自动释放。但对于通过内置指针类型来管理的内存并不是这样。对于一个内置指针管理的动态对象,知道被显式释放之前他都是存在的。
返回指向动态内存的指针(而不是智能指针)的函数给其调用者增加了一个额外负担——调用者必须记得释放内存。
在本例中,p是指向factory的唯一指针。所以我们必须在use_factory中释放内存
如果我们的系统中其他地方需要用到use_factory所分配的对象,我们就应该修改此函数,让他返回一个指针。
注:如果同一块内存释放两次可能会破坏自由空间。
delete之后重置指针值......
当我们delete一个指针后,指针就变得无效了。虽然指针无效了,但在很多机器上指针依然保存着(已经释放的)动态内存的地址。在delete之后,指针就变成了人们所说的空悬指针。
未初始化指针的所有缺点空悬指针都有。
我们可以在指针即将要离开作用域之前释放掉他所关联的内存,这样在指针关联内存被释放掉之后,就没有机会使用指针了。如果我们要保留指针,可以在delete之后将nullptr赋予指针。
......这只是提供了有限的保护
如果有多个指针指向同一个对象,那么delete其中一个,其他都变得无效了。
shared_ptr和new结合使用
如前所述,我们不初始化一个智能指针,它就会被初始化为一个空指针,我们还可以用new返回的指针来初始化智能指针。
我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针
p1的初始化隐式的要求编译器用一个new返回的int*来创建一个shared_ptr,但是我们不能进行内置指针到智能指针的隐式转化。
同样的我们不能在一个返回shared_ptr的函数中返回一个普通指针
我们必须显式的绑定
默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能内存默认使用delete释放他所关联的对象。我们可以将智能指针绑定到一个指向其他类型的资源的指针上,但是为了这样做,必须提供自己的操作来代替delete,这个我们马上介绍。
不要混合使用普通指针和智能指针......
shared_ptr可以协调对象的析构,但这仅限于自身拷贝(也是shared_ptr)之间。这也是为什么我们推荐使用make_shared而不是new的原因。这样我们在分配对象的同时就将shared_ptr与之绑定。从而避免无意中将同一块内存绑定到多个独立创建的shared_ptr上
process的参数传值方式是传递的,因此实参会被拷贝到ptr中。这样引用计数会递增,因此process运行中,引用技术值至少为2。process结束时,ptr的引用计数会递减,但是不会为0,所以ptr被销毁时,ptr指向的内存不会被释放
虽然不能传递给process一个内置指针,但是可以传递一个显式创建的临时的shared_ptr。但是这样很可能导致错误
将一个shared_ptr绑定到一个普通指针,就将内存的管理交给了shared_ptr。所以在离开其定义域后引用计数会递减。所以一旦这么做,我们就不应该使用内置指针来访问shared_ptr所指向的内存了。
......也不要使用get初始化另一个智能指针或为智能指针赋值
智能指针类型定义了一个名为get的函数,他返回一个内置指针指向智能指针管理的对象。此函数是为了我们需要向不能使用智能指针的代码传递一个内置指针。使用get返回的指针的代码不能delete。
虽然编译器不会报错,但是将另一个智能指针绑定到get返回的指针上是错误的。
注:get用来将指针的访问权限传递给代码,你只有在确定代码不会delete指针的情况下,才能使用get。
注:永远不要用get初始化另一个智能指针或为另一个智能指针赋值
其他shared_ptr操作
一般reset和unique一起使用,来控制多个shared_ptr共享的对象。在改变底层对象之前,我们检查自己是否是当前对象仅有的用户,在改变之前制作一份新的拷贝
标签:
C++定义了两个运算符来分配和释放动态内存。运算符new分配内存,delete释放new分配的内存。相对于智能指针
1、您好copy,进不了系统可以按照以下方法刷机:1 在手机关机或者重启状态下,同时长按电源键和音量加键,
阅读是孩子认知这个世界、学习文化文明的重要方式。5月11日,中牟县雁鸣湖镇妇联、太平庄村家长学校、太平
1、冶炼炉的大小最小为3x3,最大为11x11。2、2、冶炼炉高度最低应为2。3、3、冶炼炉的底部应该铺满焦黑方块
涂药膏、打吊瓶、人工植皮……合力拯救800岁“皂角伟丈夫”---在荆门市东宝区仙居乡老街,有一棵800岁的...
优化金融消费环境,提振金融消费信心——民生银行广州分行在行动
【当前独家】地铁8号线北延段车站设置将敲定,利好花都这些板块