智能指针
1 变量保存
- 静态内存区:保存局部static对象、类static数据成员以及任何函数之外的变量。
- 栈内存:保存定义在函数内的非static对象。分配在静态或栈内存中
- 堆内存:保存动态分配的对象。
- 分配在静态或栈中的对象由编译器自动创建和销毁。
- 生命周期:对于栈对象,仅在其定义的程序块运行时才存在;static对下那个在使用之前分配,在程序结束时销毁。动态对象的生存有程序控制,在不使用时,程序必须显式地销毁它们。
2 智能指针
智能指针是模板,比必须给出类型。其使用方式和普通指针类似。解引用一个只能指针返回它指向的对象。如果在一个判断语句中使用只能指针,效果是检测它是否为空。
2.1 shared_ptr
(1)shared_ptr的定义和使用:
一种最安全的分配和使用动态内存的方法:使用make_share 标准库函数
make_shared 定义在memory中
#include <memory>
using namespace std;
int main()
{
//默认初始化的智能指针中保存着一个空指针
std::shared_ptr<std::string> p1; //shared_ptr,可以指向string
std::shared_ptr<list<int>> p2;
//如果p1不为空,检查它是否指向一个空string
if (p1 && p1->empty())
*p1 = "hi"; //解引用,并赋值
//指向一个值为42的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);
//指向一个值为"9999999999"的string,可以使用string的构造函数
shared_ptr<string> p4 = make_shared<string>(10, '9');
//不提供任何参数,执行值初始化
shared_ptr<int> p5 = make_shared<int>();
//指向一个动态分配的空的vector<int>
auto p6 = make_shared<vector<int>>();
return 0;
}
以上几种是使用make_shared函数的例子。
(2)shared_ptr的拷贝和赋值
auto p = make_sahred<int>(42); //p指向的对象只有p一个引用
auto q(p); //p和q都指向相同对象,此对象有两个引用者
auto r = make_shared<int>(42); ///r指向的int只有一个引用者
r = q; //给r赋值,令它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//r原来的对象已没用引用者,会自动释放
每一个shared_ptr都有一个关联的计数器,称为引用计数。拷贝一个shared_ptr:用一个shared_ptr初始化另一个shared_ptr,或将其作为参数传递给一个函数,或作为一个函数的返回值,它的引用计数都会递增。
当给shared_ptr赋予一个新值或是shared_ptr被销毁时,引用计数就会递减。
当一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。
即 当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象,同时也会自动动态对象的内存。
(3)shared_ptr和new结合使用
如果我们不初始化的智能指针,它就会被初始化为一个空指针。除了make_shared,还可以使用new返回的指针初始化只能指针
shared_ptr<double> p1;
shared_ptr<int>p2(new int(42));
接收指针参数的智能指针构造函数是explicit的,因此不能将一个内置指针隐转换为一个智能指针,必须使用直接初始化的方式。
同样的,一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针
shared_ptr<int> p1 = new int(1024); //错误,必须使用直接初始化的方式
shared_ptr<int>p2(new int(1024)); //正确。
shared_ptr<int> foo(int p)
{
return new int(p); //错误,隐式转换为shared_ptr
}
shared_ptr<int> foo(int p)
{
return shared_ptr<int>(new int(p)); //正确
}
一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象。
(4)不要混合使用普通指针和智能指针
//在函数被调用是ptr被创建被初始化
vodi process(shared_ptr<int> ptr)
{
} //ptr离开作用域,被销毁
shared_ptr<int> p(new int(42)); //引用计数为1
process(p); //拷贝p会递增引用计数,在process中为2
int i = *p; //正确,引用计数为1
int *x(new int(1024)); //x是一个普通指针
process(x); //错误,不能将int *转换为shared_ptr<int>
process(shared_ptr<int>(x)); //合法,但内存会释放
int j = *x; //未定义的:x 是一个空悬指针
process函数是值传递的,因此调用process时,会拷贝一份实参,在process中的ptr的引用计数会递增,当离开process后,ptr会被销毁,引用计数递减。
对于指针x,调用process时,创建了一个临时的shared_ptr传递个process,这个临时shared_ptr指向的内存与x指向的内存是用一块内存,当离开process作用域后,临时对象就被销毁了,它所指向的内存也会被释放。当x继续指向(已经释放的)内存,从而变成一个空悬指针。
(5) 不要使用get 初始化一个只能指针或为一个指针赋值
智能指针的get函数返回一个内置指针,它指向智能指针管理的对象。
shared_ptr<int> p(new int(42)); //引用计数wei 1
int *q = p.get(); //正确,但是不要让它管理的指针被释放
{
//未定义,两个独立的shared_ptr<int>指向相同的内存
shared_ptr<int>(q);
}//程序块结束,q被销毁,它所指向的内存被释放
int foo = *p; //未定义,p指向的内存已经被释放