C++ primer chapter12:动态内存

12.1动态内存与智能指针

在这里插入图片描述
智能指针类型:

  • shared_ptr:允许多个指针指向同一对象(指向同一对象的多个指针管理不善会出问题)
  • unique_ptr:独占指向的对象
  • weak_ptr:指向shared_ptr管理的对象

12.1.1 shared_ptr类

vector一样,智能指针也是模板,在尖括号中给出类型,如:

 shared_ptr<string>  p1;

默认初始化的智能指针为空指针。智能指针与普通指针使用方式一致。解引用可以返回其指向的对象

// 如果p1不为空, 检查它指向的字符串是否为空
if(p1 && p1->empty()){
	*p1 = "hi";
}

shared_ptr与unique_ptr支持的操作

在这里插入图片描述

make_shared函数

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

// p4指向一个"9999999999"的string
shared_ptr<string> p4 = make_shared<string>(10, '9');

在这里插入图片描述
通常使用auto定义一个对象保存make_shared的结果:

auto p6 = make_shared<vector<string>>();//空的vector<string>

shared_ptr的拷贝与赋值

每个shared_ptr都有一个相关的引用计数,到底是用计数器还是其他数据结构来记录有多少个指针共享对象完全由标准库决定
引用计数递增情况:

  • 用一个shared_ptr初始化另一个shared_ptr
  • shared_ptr作为参数传递给一个函数
  • 作为函数的返回值

引用计数递减情况:

  • shared_ptr赋一个新值
  • 局部shared_ptr离开作用域

如果一个对象的引用计数变为0,则shared_ptr通过对象的析构函数销毁对象。
在这里插入图片描述

12.1.2 直接管理内存(new delete)

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

默认情况下,动态分配的对象是默认初始化的。这意味着:

  • 内置类型与组合类型的对象值是未定义的,就是不知道是啥玩意儿。如int* p = new int;,p所指想的对象值不知道是什么。
  • 类类型的对象调用默认的构造函数进行初始化

如果要对动态分配的对象进行初始化,可以使用括号初始化或者列表初始化或者调用类的构造函数进行初始化
如:string* s1 = new string();使用string的默认构造函数初始化一个空串。
一个动态分配的const对象必须被初始化。

内存耗尽

如果new不能分配所需要的空间,则直接抛出一个bad_alloc异常,该异常在头文件new中。
当然也可以阻止其抛出异常,如:

int* p2 = new (nothrow) int;//如果分配失败,返回一个空指针,不抛出异常

这种形式成为定位new(placement new)

指针值和delete

1.编译器不能分辨指针指向的是静态对象还是动态分配的对象。写程序时要注意。
2.编译器不能分辨一个指针指向的内存是否被释放掉了。多次delete会出错。
3.动态分配的const的对象值虽然不能改变,但是可以使用delete将其销毁。
4.delete指针之后,指针变为空悬指针,指向一块曾保留数据现在变为无效的内存的指针。应该将其置为nullptr
在这里插入图片描述
在这里插入图片描述

12.1.3 shared_ptr和new结合使用

对于一个shared_ptr,如果不对其初始化,则其为一个空指针,不能将内置指针隐式转换为shared_ptr,必须使用直接初始化的形式。
在这里插入图片描述
一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针会delete其指向的对象。
不要混合使用普通指针和智能指针,因为智能指针会delete其指向的对象,在程序中不知道什么时候可能智能指针就将对象delete掉了,再使用普通指针访问对象就会出错!!!

12.1.4 智能指针与异常

发生异常时,智能指针能保证内存被释放,但是内置指针管理的不行。
在这里插入图片描述
在这里插入图片描述

12.1.5 unique_ptr

  • 没有类似make_shared的标准库函数返回一个unique_ptr
  • 初始化unique_ptr必须使用直接初始化形式,如:
unique_ptr<double> p1;
unique_ptr<int> p2(new int(1024));
  • unique_ptr不支持普通的拷贝或赋值操作,当然也有从函数中返回这种特殊情况
unique_ptr<string> p1(new string("dfhgah"));
unique_ptr<string> p2(p1);//错误, 不支持拷贝
unique_ptr<string> p3;
p3 = p1;//错误, 不支持赋值
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;//作为返回值
}
  • 使用reset()release()将指针的所有权从一个unique_ptr转移到另一个。
    在这里插入图片描述

  • release()放弃对指针的控制,并返回一个指针,如果返回的指针没人用,内存将不会被释放。如:p2.release()会丢失指针且其指向的内存不会被释放。

  • reset()会释放指针指向的对象,u.reset(q)会将u指向q,u原来指向的对象会被释放。

在这里插入图片描述

12.1.6 weak_ptr

  • weak_ptr要用shared_ptr或者weak_ptr 来初始化。
  • weak_ptr不增加引用计数。
  • weak_ptr访问对象需借助lock()函数。
  • weak_ptr主要是为了解决shared_ptr循环引用的问题。具体见weak_ptr浅析

12.2 动态数组

12.2.1 new和数组

  • 使用new分配数组时,得到的不是数组类型的对象,而是得到一个数组元素类型的指针。
  • 不管是单个分配还是数组,都会默认初始化,C++11可以使用列表初始化。列表中的元素数量大于分配数量会抛出bad_array_new_length异常。
  • new分配大小为0的数组,不会失败,而是返回一个合法的非空指针,但不能用它进行解引用。
  • 使用delete销毁数据时,总是先销毁最后一个,倒序销毁。

12.2.2 allocator(后续补上!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值