- 如果在一个条件判断中使用智能指针,效果就是检测它是否为空。
shared_ptr 和 unique_ptr都支持的操作
shared_ptr独有的操作
- make_shared(args) 返回一个shared_ptr,指向一个动态内存分配的类型为T的对象。使用args初始化此对象
- shared_ptrp(q) p是shared_ptr q的拷贝;此操作会递增q中的计数器。q中的指针必须能转换为T*
- p=q 两个都是shared_ptr ,所保存的指针必须能相互转换。此操作会减少p的引用计数,增加q的引用计数。如果p的引用计数达到0,则将其管理的内存释放
- p.unique() 若p.use_count()为1,返回true,否则false
- p.use_count() 返回与p共享对象的智能制造的数量;可能很慢,主要用于调试。
delete只能释放一块动态分配的内存,或者空指针。释放一块非new分配的内存或者将相同的指针释放多次,其行为都是未定义的。
虽然一个const对象的值不能被改变,但是它本身是可以被销毁的。
shared_ptr与new结合使用
可以用new返回的指针来初始化智能指针
shared_ptr<int>p2(new int (42));
注意:接受指针参数的智能指针的构造函数是explicit的,因此不能将一个内置指针隐式转换为一个智能制造,必须直接初始化
shared_ptr<int>p1=new int(2024) //错误,必须使用直接初始化形式
类似的
shared_ptr<int> clone(int p){ return new int(p); }
也是错误的,可以修改成
shared_ptr<int> clone(int p){ return shared_ptr<int>(new int(p)); }
不要混合使用普通指针和智能指针,使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时会被释放
也不要使用get初始化另一个智能指针或者为智能指针赋值。get函数是为了这种情况而设计的:需要向不能使用智能指针的代码传递一个内置指针。使用get返回的指针的代码不能delete该指针。
p.reset() 如果p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了参数内置指针q,
p.reset(q) 会令p指向q,否则会将p置为空。若还传递了参数d,将会调用d而不是delete来释放q
p.reset(q,d)
使用我们自己的释放操作
不是所有的类都有析构函数,分配了资源,但是没有析构函数来释放这些资源的类,可能会遇到与使用动态内存相同的错误——忘记释放资源。类似的,如果在资源分配和释放之间发送了异常,程序也会发生资源泄露。
void f(信息)
{
//获得一个连接,使用完后记得释放
connection c=connect(&d);
//使用
//如果f退出前忘记 调用 disconnection,就无法关闭c了
}
同样的,即使记得,如果在关闭前发生异常,也会资源泄露
处理方法,使用我们自己的释放操作
void f(信息)
{
connection c=connect(&d);
shared_ptr<connection> p(&c,disconnection);
}
当p被销毁时,它不会对自己保存的指针执行delete,而是调用disconnection
当f正常退出,或者异常,p都会被销毁
unique_ptr
一个unique_ptr拥有它指向的对象,某一刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr销毁时,它指向的对象也会被销毁。正因如此,unique_ptr不支持普通的拷贝或赋值操作
unique_ptr<string>p1(new string("hello"));
unique_ptr<string>p2(p1) //错误!不能拷贝
unique_ptr<string>p3;
p3=p2;//错误,不支持赋值
unique_ptr的操作
-
unique_ptru1; 空unique_ptr -
unique_ptr<T,D> u2; 空unique_ptr,使用D来释放它的指针 -
unique_ptr<T,D>u(d) -
u=nullptr; 释放u指向的对象,将u置为空 -
u.release() u放弃对指针的控制权,返回指针,并将u置为空 注意,这里只是切断了联系 -
u.reset() 释放u指向的对象 -
u.reset(q) 如果提供了内置指针q,会将u指向这个对象,否则将u置为空 -
u.reset(nullptr)
关于release
release只是切断了联系,release返回的指针通常用来初始化另一个智能指针,或给智能指针赋值,如果我们不用另一个智能指针保存release返回的指针,那么我们就要手动释放
p2.release();//错误,p2不会释放内存,我们丢失了指针
auto p =p2.release();//正确,后面我们需要delete它
weak_ptr
它指向一个由shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象也会被释放掉。
weak_ptrw 空w,可以指向T类型的对象
weak_ptrw(sp) 与shared_ptr sp指向相同对象的weak_ptr,T必须是能转换为sp指向的类型
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
if(shared_ptr<int>np = wp.lock())
{
}
只有wp指向的对象不为空,即lock返回一个shared_ptr时,才会进入条件。安全访问共享对象。
动态数组
不能用begin或end来处理动态数组
释放动态数组,要在其指针前面加上空的方括号对,数组元素将按逆序销毁
如果忽略了,其行为是未定义的。
为了用一个unique_ptr管理动态内存,我们必须在对象类型后面加上一对空括号
unique_ptr<int[]>up(new int [10]);
当unique_ptr指向一个数组时,我们不能用点和箭头成员运算符,比较其指向的是数组。我们可以使用下标运算符来访问。
注意:unique_ptr指向动态数组时,其release会使用delete[] 自动销毁数组,这和前面的release有点不同
unique_ptr<int[]>up(new int [42]);
up.release();//up自动用delete[]销毁其指针
注意:与unique_ptr不同,shared_ptr不直接支持管理动态数组,如果希望,必须提供自己定义的删除器
shared_ptr<int>sp(new int[10],[](int *p){ delete []p;})
在本例中,我们提供了一个lambda,否则默认情况下它的删除是用delete,上面说过对动态数组delete没加方括号,其行为是未定义的。
同时,shared_ptr未定义下标运算符,而且智能指针不支持指针算术运算,因此,为了访问数组中的元素,必须用get获取一个内置指针,如何来访问
for(size_t i =0;i!=10;++i)
*(sp.get()+i)=i;
allocator类
头文件中
它帮助我们将内存分配和对象构造分离开来,使用例子:
allocator<string>alloc; //可以分配string的allocator对象
auto p =alloc.allocate(n); //分配n个未初始化的string
标准库allocator类及其用法:
-
allocatora 定义了一个名为a的allocator对象 -
a.allocate(n) 分配一段原始的,未构造的内存,保存n个类型为T的对象,返回值应该是首位 -
a.deallocate(p,n) 释放从T*指针p中地址开始的内存;p必须是之前由allocate返回的指针。内存需要提前destroy -
a.construct(p,args) p必须是一个类型为T*的指针,指向一块原始内存;args被传递给类型为T的构造函 数,用来在p指向的内存中构造对象 -
a.destroy§ p为T*类型的指针
auto q=p //p是指向首位的
alloc.construct(q++); //*q为空字符串
alloc.construct(q++,10,'c')//*q为10个c
alloc.construct(q++,"hi")//*q为hi
while(q!=p)
alloc.destroy(--q);
alloc.deallocate(p,n);
拷贝和填充未初始化内存的算法
-
uninitialized_copy(b,e,b2) 从迭代器b,e之间,拷贝元素到b2开始的原始内存中,b2的内存必须足够 -
uninitialized_copy_n(b,n,b2) 从迭代器b开始拷贝n个元素 -
uninitialized_fill(b,e,t) 在迭代器b,e之间的原始内存内填充t -
uninitialized_fill_n(b,n,t) 在迭代器b开始的n个内存填充t
|