第12章 动态内存
12.1 动态内存和智能指针
在C++中,动态内存的管理是通过运算符new和delete完成的。
智能指针类似常规指针,重要的区别是它负责自动释放所指向的对象。
智能指针类型 | |
---|---|
shared_ptr | 允许指针指向同一个对象 |
unique_ptr | “独占”所指向的对象 |
weak_ptr | 弱引用,指向shared_ptr所管理的对象 |
12.1.1 shared_ptr类
12.1.2 直接管理内存
new和delete运算符
12.1.3 shared_ptr和new综合使用
share_ptr<double>p1;
share_ptr<int> p2(new int(42));//p2指向一个值为42的int
接受指针参数的智能指针构造函数是explicit的,因此,不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针:
shared_ptr<int> p1=new int(1024);//错误:必须使用直接初始化形式 int*创建shared_ptr(错误)
shared_ptr<int> p2(new int(1024));//正确:使用了直接初始化形式
12.1.4 智能指针和异常
在程序块正常处理或发生了异常,局部对象都会被销毁。若程序块中使用了智能指针,智能指针类可以确保内存被释放;若程序块中使用内置指针管理内存,并且在new之后delete之前发生了异常,则内存不会被正常释放。
12.1.5 unique_ptr
unique_ptr某个时刻只能指向一个对象,当它被销毁时,它所指向的对象也被销毁。不支持拷贝和赋值操作。
unique_ptr<int>p2(new int(42));
unique_ptr<string> p1(new string("Strgosaurus"));
unique_ptr<stirng>p2(p1);//错误:不支持拷贝
unique_ptr<string>p3;
p3=p2;//错误:unique_ptr不支持赋值
//可以调用release或reset将指针的所有权由一个(非const)unique_ptr转移给另一个unique
//将所有权从p1转移给p2
unique_ptr<string> p2(p1.release());//realease将p1置空
unique_ptr<string> p3(new string("Trex"));
//将所有权从p3转移到p2
p2.reset(p3.release());//reset释放了p2原来指向的内存
12.1.6 weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,它不会改变shared_ptr的引用计数。
创建一个weak_ptr时,需要使用shared_ptr来初始化它:
auto p=make_shared<int>(42);
weak_ptr<int> wp(p);//wp弱共享p
检查指针类
定义StrBlob类的一个伴随指针类StrBlobPtr,该类保存一个weak_ptr,指向StrBlob的data成员。通过使用weak_ptr,不会影响一个给定的StrBlob所指向的vector的生存期。但是,可以阻止用户访问一个不再存在的vector的企图。
//对于访问一个不存在元素的尝试,StrBlobPtr抛出一个异常 .h
class StrBlobPtr{
public:
StrBlobPtr():curr(0){}
StrBlobPtr(StrBlob &a,size_t sz=0):wptr(a.data),curr(sz){}
string& deref() const;
StrBlobPtr& incr();//前缀递增
private:
//若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t,const string&) const;
weak_ptr<vector<string>> wptr;
size_t curr;//在数组中的位置
};
//.cpp
shared_ptr<vector<string>> StrBlobPtr::check(size_t i,const string &msg) const{
auto ret=wptr.lock();//vector还存在吗
if(!ret){
throw runtime_error("unbound StrBlobPtr");
}
if(i>=ret->size()){
throw out_of_range(msg);
}
return ret;//否则,返回指向vector的shared_ptr
}
12.2 动态数组
标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。
分配动态数组的类必须定义自己版本的操作,在拷贝、复制以及销毁对象时管理所关联的内存。
12.2.1 new和数组
T *p=new T[];
返回T类型的指针,指向动态数组的首位。不用范围for语句来处理动态数组中的元素。
释放动态数组
delete [] p;//逆序销毁
智能指针和动态数组
unique_ptr可以管理new分配的数组
unique_ptr<int[]> up(new int[10]);
shared_ptr不直接支持的管理动态数组.若希望使用它管理动态数组,必须提供自己定义的删除器。
shared_ptr<int>sp(new int[10],[](int *p){delete[] p});
12.2.2 allocator类
new在灵活性上存在局限性,它将内存分配和对象构造组合在一起。当分配一大块内存时,我们通常在内存上按计划分配,只有需要对象时才真正执行对象创建操作。
allocator类
allocator类定义在memory头文件中,帮助我们将内存分配和对象构造分离开来。
allocator<string> alloc;//可以分配string的allocator对象
auto const p=alloc.allocate(n);//分配n个未初始化的string