智能指针
全局对象在程序启动时分配,在程序结束时销毁;对于局部自动对象,当我们进入其定义所在的程序块时被创建,在离开块时销毁.局部static对象在第一次使用前分配,在程序结束时销毁.
1.普通指针
初始化方法:
//方法一:
string str,*strp;
str="hello";
strp=&str;
//方法二:
string str="hello";
string *strp=&str; //注意这里直接是初始化了一个string对象&;
2.动态内存划分
//1. 分配一个变量空间:
//1.1 指针变量名=new 类型名(初始化值);
int *p=new int(100); //分配一个整型变量空间,并且初始化100;
//1.2 指针变量名=new 类型名;
int *p=new int;//也是分配一个整型变量空间,但是没有进行初始化;
cout<<*p<<endl; //100
//2. 分配连续的空间(数组)
//指针变量名=new 类型名[元素个数];分配连续空间时,不能被初始化;
int *p=new int[10];
//p[1]=200;
//3.采用动态划分内存,需要显式释放
//3.1 释放一个空间 -> delete 指针名;
delete p;
//3.2 释放连续空间 -> delete []指针名;
delete []p;
3.智能指针:->#include
shared_ptr
:允许多个指针指向同一个对象;unique_ptr
:独占指针,"独占"所指向的对象;weak_ptr
:一种若引用,指向share_ptr所管理的对象;
3.1.share_ptr类
智能指针默认初始化时,指针中保存着一个空指针;
智能指针的管理和普通指针类似:
1. 解引用一个智能指针返回它指向的对象;
2. 如果在一个条件判断中使用智能指针,效果就是检测它是否为空;
表1 | shared_ptr和unique_ptr都支持的操作 |
---|---|
shared_ptr sp; | 空智能指针,可以指向类型为T的对象 |
unique_ptr sp; | 空智能指针,可以指向类型为T的对象 |
p | 将p用作一个条件判断,若p指向一个对象,则返回true |
*p | 解引用p,获得它指向的对象 |
p->mem | 等价于(*p).mem |
p.get() | 返回p中保存的指针,要小心使用,若智能指针释放了其对象,返回的指针所指的对象也就会消失 |
swap(p,q) | 交换p和q中的指针 |
表2 | shared_ptr支持的操作 |
---|---|
make_shared(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象.使用args初始化此对象 |
shared_ptr p(q) | p是shared_ptr q的拷贝;此操作会递增q中的计数器.q中的指针必须能够转换成T* |
p=q | p和q都是shared_ptr,所保存的指针必须能够相互转换.此操作,会递减p的引用次数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放 |
p.use_count() | 返回与p共享对象的智能指针数量;可能很慢,主要用于调试; |
p.unique() | 若p.use_count()=1(表明只有一个指针引用),返回true,否则返回false |
3.2.make_shared函数
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数.此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr.
//指向一个值为100的int的shared_ptr;
shared_ptr<int> p1 = make_shared<int>(100);
//指向一个值为"99999"的string
shared_ptr<string> p2 = make_shared<string>(5, '9');
//指向一个值初始化的int,即,值为0
shared_ptr<int> p3 = make_shared<int>();
//指向一个动态分配的vector<string>容器;
vector<string> v_str;
v_str.push_back("zhao");
v_str.push_back("qian");
shared_ptr<vector<string>> p4=make_shared<vector<string>>(v_str);
cout<<(*p4)[0]<<endl;
//最简单的方法是使用auto 创建make_ptr;
auto p5=make_share<string>();
3.3.share_ptr的拷贝与赋值:
当进行拷贝或者赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象;
auto p=make_share<int>(42); //p指向的对象只有p一个引用者;
auto q(p); //p和q指向相同对象,此对象有两个引用者;
我们可以认为,每一个shared_ptr都有一个关联的计数器,通常称为引用计数;,无论何时拷贝一个shared_ptr,计数器都会递增.当我们给share_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部shared_ptr离开其作用域时)计数器就会递减;
一旦一个shared_ptr的计数器为0,他就会自动释放自己所管理的对象;
auto r=make_shared<int>(42);
r=q;//给r赋值,令他指向一个新的地址;
//递增q指向的对象的引用计数;
//递减r原来指向对象的引用计数;
//r原来指向的对象已经没有了引用者,会自动释放;
- 所以shared_ptr 指针会自动销毁锁管理的对象;
即当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象; - 当动态对象不再被使用时,shared_ptr类会自动释放动态对象.对于一块内存,shared_ptr类保证,只要有任何一个shared_ptr对象引用它,它就不会被释放掉;
3.4.share_ptr与new结合使用;(不推荐)==>(推荐make_shared)
对于智能指针,如果不初始化,他会自动初始化为一个空指针;
shared_ptr<double> p1;//shared_ptr 可以指向一个double
//定义时没有初始化,所以p1=null;
shared_ptr<int >p2(new int(45)); // p2指向一个值为45的int;
因为接受指针参数的智能指针是explicit
,所以,我们不能将一个内置指针隐式转换为一个智能指针;必须使用直接初始化形式;
shared_ptr<int> p1=new int(45);//!错误:必须使用直接初始化形式;
shared_ptr<int> p2(new int(45));//正确;
shared_ptr<int> clone(int p)
{
return new int(p);//!错误,隐式转换为:shared_ptr<int>
}
shared_ptr<int> clone(int p)
{
return shared_ptr<int>(new int(p));//正确;
}
一般情况下,一个用来初始化的智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete
释放他所关联的对象.我们可以将智能指针绑定一个指向其它类型的资源的指针上,但是,必须提供自己的操作来替代delete
.
字段 | 作用 |
---|---|
shared_ptr<T> p(q) | p管理内置指针q所指向的对象,q必须指向new 分配的内存,且能够转换为T* 类型 |
shared_ptr<T> p(u) | p从unique_ptr u哪里接管了对象的所有权;将u置空 |
shared_ptr<T> p(q,d) | p接管了内置指针q所指向对象的所有权.q必须指向new 分配的内存,且能够转换为T* 类型.p将使用可调用对象d来代替delete |
p.reset() | 若p是唯一指向其对象的shared_ptr,reset会释放此对象,若传递了可选参数内置指针q,会令p指向q,否则将p置为NULL,若还传递了参数d,对象d来代替delete来释放p |
p.reset(q) | 同上 |
p.reset(q,d) | 同上 |
3.5 share_ptr对象get()函数;==>谨慎使用,可能它指向的内存空间已经释放;
智能指针类型定义了一个名为get()
,他返回一个内置指针,指向指针管理的对象.
此函数是为了这样一个情况设计的,我们需要向不能使用智能指针的代码传递一个内置指针,使用get返回的指针的代码不能delete
此指针;
shared_ptr<int> p(new int(45)); //引用计数为1;
int *q=p.get();
3.6 share_ptr对象reset()函数;
用reset将一个新的指针赋予一个shared_ptr;
p=new int (1024);//错误!需要直接初始化
p.reset(new int(1024)); //p指向一个新对象
4.0 unique_ptr指针
一个unique_ptr拥有它指向的对象,与shared_ptr不同,某一个时刻只能有一个unique_ptr指向一个给定对象;当unique_ptr被销毁时,所指向的对象被销毁;
与shared_ptr不同是,unique_ptr没有类似的make_shared的标准库函数,返回一个unique_ptr.
当我们定义一个unique_ptr时,需要将其绑定到一个new返回的指针,且只能通过直接初始化形式;
unique_ptr<double> p1;//可以指向一个double的unique_ptr
unique_ptr<int> p2(new int(45)); //p2指向一个值为42的int;
由于一个unique_ptr拥有它指向对象的,因此unique_ptr不支持普通的拷贝或赋值操作;
unique_ptr<string> p1(new string("test"));
unique_ptr<string> p2(p1);//错误.unique_ptr不支持拷贝
unique_ptr<string> p3;
p3=p2; //错误.unique_ptr不支持赋值
参数 | 含义 |
---|---|
unique_ptr<T> u1 | 空unique_ptr,可以指向类型为T的对象,u1会使用delete来释放它的指针; |
unique_ptr<T,D> u2 | 空unique_ptr,可以指向类型为T的对象,u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr<T,D> u(d) | 空unique_ptr,可以指向类型为T的对象,用类型为D的对象d代替delete |
u=nullptr | 释放u指向的对象,将u置空 |
u.realse() | u放弃对指针的控制权,返回指针,并将u置空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 释放u指向的对象,如果提供了内置指针q,令u指向这个对象,否则将u置空 |
u.reset(nullptr) | 释放u指向的对象 |
虽然我们不可以拷贝和赋值unique_ptr,但是可以调用release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique_ptr;
//将所有权从p1转移给p2;
unique_ptr<string> p2(p1.release());//realse 成员函数,返回unique_ptr当前保存的指针,并将其置为空;
//因此,p2被初始化为p1原来保存的指针,p1被置为空;
unique_ptr<string> p3(new string("Test"));
//将所有权从p3转移给p2;
p2.reset(p3.release());//reset成员接受一个指针参数,令unique_ptr重新指向给定的指针.如果unique_ptr不为空,它原来指向的对象被释放.
- 调用release会切断unique_ptr和它原来的管理的对象间的联系.release返回的指针通常用来初始化另一个智能指针或给另一个智能指针赋值;
5.0 weak_ptr 弱指针
weak_ptr是一种不控制所指向对象生存周期的智能指针,它指向由一个shared_ptr
管理的对象.将一个weak_ptr
绑定到一个shared_ptr
,不会改变shared_ptr
的引用计数,一旦最后一个指向对象的shared_ptr
被销毁,对象就会被释放.即使,weak_ptr
指向对象,对象也是会被释放.
函数 | 作用 |
---|---|
weak_ptr<T> w | 空weak_ptr,可以指向类型为T的对象 |
weak_ptr<T> w(sp) | 与shared_ptr指向相同的对象的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
auto p=make_shared<int>(45);
weak_ptr<int> wp(p);//wp弱共享p,p的引用计数未改变;
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock,此函数检查weak_ptr指向的对象是否仍存在,如果存在,lock返回一个指向共享对象的shared_ptr.与任何其他shared_ptr类型,只要此shared_ptr存在,它所指向的底层对象也就会一直存在.
#include <iostream>
#include <memory>
using namespace std;
int main()
{
auto p1 = make_shared<int>(45);
shared_ptr<int> p2(p1);
cout << *p1 << endl;
cout << p1.use_count() << endl;
unique_ptr<string> p3(new string("this is a test"));
cout << *p3 << endl;
unique_ptr<string> p4(p3.release());
cout << *p4 << endl;
shared_ptr<string> p5 = make_shared<string>("this is string test");
weak_ptr<string> p6(p5);
cout << p6.use_count() << endl;
return 0;
}