智能指针是类模版,在栈上创建智能指针。
把普通指针交给智能指针对象
智能指针对象过期时,调用析构函数释放普通指针的内存。
c++11的主流三种智能指针:
1、unique_ptr
unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。
包含头文件#include <memory>
AA* p = new AA("西施");
unique_ptr<AA> pu1(p);
AA是一个类,当使用unique_ptr时,将应用pu1管理p指针
使用unique_ptr有三种方法:
方法一:
unique_ptr<AA> p0(new AA("西施")); //分配内存并初始化
方法二:
unique_ptr<AA> p0 = make_unique<AA> ("西施"); //c++14标准
方法三:
AA* p = new AA("西施");
unique_ptr<AA> pu1(p); //用已存在的地址初始化,这种方法不太好,但易于理解
违法操作:
1、
AA* p = new AA("西施");
unique_ptr<AA> pu1 = p; //错误,不能把普通指针直接赋值给智能指针
unique_ptr<AA> pu3 = new AA("西施"); //错误,不能把普通指针直接给智能指针
unique_ptr<AA> pu2 = pu1; //错误,unique_ptr不能进行拷贝
unique_ptr<AA> pu3;
pu3 = pu1; //这个也是违法的,错误,不能用=对unique_ptr进行赋值
2、不要用同一裸指针初始化多个unique_ptr对象
3、get()方法返回裸指针
4、不要用unique_ptr管理不是new分配的内存
具体unique_ptr指针指向分析:
AA* p = new AA("西施");
unique_ptr<AA> pu1(p);
cout << "裸指针的值是:" << p <<endl; //000002B970F36070
cout << "pu1输出的结果是:" << pu1 <<endl; //000002B970F36070
cout << "pu1.get()输出的结果是:" << pu1.get() <<endl; //000002B970F36070
cout << "pu1的地址是:" << &pu1 <<endl; //00000D22257FAA8
所以p(裸指针)、pu1、pu1.get()指向的是一样的,而&pu1是存放裸指针的地址。
用于函数的参数
传引用(不能传值,因为unique_ptr没有拷贝构造函数)。
裸指针
不支持指针的运算
二、更多技巧
1、将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器禁止这么做。一般用于函数的返回值。
举例:
unique_ptr<AA> func(){
unique_ptr<AA> pp(new AA("西施3"));
retrn pp; //
}
int main()
{
unique_ptr<AA> pu1(new AA("西施1"));
unique_ptr<AA> pu2;
pu2 = pu1; //错误
pu2 = unique_ptr<AA> (new AA("西施1")); //可行,用匿名对象赋值
pu2 = func(); //可行,用函数的返回值赋值
}
2、用空指针nullptr给unique_ptr赋值将释放对象,空的unique_ptr == nullptr;
3、release()释放对原指针的控制权,将unique_ptr置为空,返回裸指针。
4、std::move()可以转移对原始指针的控制权。(可用于把unique_ptr传递给子函数,子函数形参也是unique_ptr)
举例:
void func1 (const AA *a){
只读
}
void func2 ( AA *a){
delete a;
}
void func3 (const unique_ptr<AA> &a)
void func4 (unique_ptr<AA> a)
int main{
unique_ptr<AA> pu(new AA("西施"));
func1(pu.get());
func2(pu.release());
func1(pu);
func1(move(pu));
}
5、reset()释放对象
6、swap()交换两个unique_ptr的控制权
7、unique_ptr也可像普通指针那样,当指向一个类继承体系时,也具有多态性质
8、unique_ptr不是绝对安全的,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放
9、unique_ptr提供了支持数组的具体化版本
智能指针shared_ptr
介绍:shared_ptr共享它指向的对象,多个shared_ptr可以指向(关联)相同的对象,在内部采用计数机制来实现。
当新的shared_ptr与对象关联时,引用计数增加1。
当shared_ptr超出作用域时,引用技术减1。当引用技术变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象。
1、基本用法
shared_ptr的构造函数也是explicit,但是没有删除拷贝构造函数和赋值函数。
1、初始化
方法一:
shared_ptr<AA> p0(new AA("西施")); //分配内存并初始化
方法二:
shared_ptr<AA> p0 = make_shared<AA> ("西施"); //c++11标准,效率更高,推荐这一种
方法三:
AA* p = new AA("西施");
shared_ptr<AA> pu1(p); //用已存在的地址初始化,这种方法不太好,但易于理解
方法四:
shared_ptr<AA> p0(new AA("西施"));
shared_ptr<AA> p1(p0); //用已存在的shared_ptr初始化,技术加1.
shared_ptr<AA> p1 = p0; //用已存在的shared_ptr初始化,技术加1.
2、使用方法
1、智能指针重载了*和->操作符,可以像使用指针一样使用shared_ptr。
2、use_count()方法返回引用计数器的值。
3、unique()方法,如果use_.count()为1,返回true,否则返回false。
4、shared支持普通的拷贝和赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1。
赋值时,左值的左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1,例如:p0 = p1;
5、get()方法返回裸指针
6、不要用同一个裸指针初始化多个shared_ptr。
7、不要用shared_ptr管理不是new分配的内存。
3、用于函数的参数
与unique_ptr的原理相同
4、不支持指针的运算(+、-、++、--)
2、更多细节
1、用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空的shared_ptr == nullptr。
2、std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr。
3、reset()改变与资源的关联关系。
pp.reset(); //接触与资源的关系,资源的引用计数减1.
pp.reset(new AA("bbb")); //解除与资源的关系,资源的引用计数减1。关联新资源。
4、swap()交换两个shared_ptr的控制权。
void swap(shared_ptr<T> &_Right);
5、shared_ptr也可像普通指针那样。当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。
6、sahred_ptr不是绝对安全的,如果程序中调用exit()退出,全局的sahred_ptr可以自动释放,但局部的shared_ptr无法释放
7、shared_ptr提供了支持数组的具体化版本。
数组版本的shared_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。
8、shared_ptr的线程安全性:
(1)shared_ptr的引用计数本身的线程安全(引用计数是原子操作)。
(2)多个线程同时读同一个shared_ptr对象是线程安全的。
(3)如果是多个线程对同一个shared_ptr对象进行读和写在,则需要加锁。
(4)多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。
9、如果unique_ptr能解决问题,就不要用shared_ptr。unique_ptr的效率更高,占用的资源更少。
智能指针weak_ptr
shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一资源。如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。
weak_ptr是什么
weak_ptr是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。
weak_ptr更像是shared_ptr的助手而不是智能指针。
weak_ptr没有重载->和*操作符,不能直接访问资源。
有以下成员函数:
1)operator = (); //把shared_ptr或weak_ptr赋值给weak_ptr。
2)expired(); //判断它指资源是否已过期
3)lock(); //返回shared_ptr,如果资源已过期,返回空的shared_ptr。
4)reset(); //将当前weak_ptr指针置空。
5)swap(); //交换
weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。
用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。
提升的行为(lock())是线程安全的。
所以:因为lock()是原子操作,不会被其他线程占用
shared_ptr<BB> pp = pa->m_p.lock(); //把weak_ptr提升为shared_ptr
if (pp == nullptr)
cout << "语句块内部:pa->m_p已过期。\n";
else
cout << "语句块内部:pp->m_name=" << pa->m_p.lock() -> m_name() << endl;