C++11智能指针:unique_ptr,shared_ptr,weak_ptr
1.unique_ptr
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Perso
{
public:
string m_name;
Perso() { cout << "调用无参构造函数" << endl; }
Perso(const string& name) : m_name(name) { cout << "调用有参构造函数" << endl; }
//Perso(const string & name){ m_name = name; cout << "调用有参构造函数" << endl; }
~Perso() { cout << "调用析构函数" << endl; }
};
unique_ptr<Perso> func()
{
unique_ptr<Perso>ptr(new Perso("测试"));
return ptr;
}
/******func1()需要一个指针,但不对这个指针负责******/
void func1(const Perso* a)
{
cout << a->m_name.c_str() << endl;
}
/******func2()需要一个指针,对这个指针负责******/
void func2(const Perso* a)
{
cout << a->m_name.c_str() << endl;
delete a;
}
/******func3()需要一个智能指针,但不对这个智能指针负责******/
void func3(const unique_ptr<Perso>& a)
{
cout << a->m_name.c_str() << endl;
}
/******func4()需要一个智能指针,对这个智能指针负责******/
void func4(const unique_ptr<Perso> a)
{
cout << a->m_name.c_str() << endl;
}
int main()
{
//第一种智能指针定义方法(不推荐)
Perso* p = new Perso("李白");
unique_ptr<Perso> ptr1(p);
//unique_ptr<Perso> ptr1 = p; 错误,不能把普通指针直接赋值给智能指针
//unique_ptr<Perso> ptr = ptr1; 错误,不能用于其他unique_ptr拷贝构造
//unique_ptr<Perso> ptr; ptr = ptr1; 错误,不能用=对unique_ptr进行赋值
//p,ptr1, ptr1.get()指向的是同一块地址
//get()方法返回裸指针
cout << "裸指针的值:" << p << endl;
cout << "ptr1输出的结果:" << ptr1 << endl;
cout << "ptr1.get()输出的结果:" << ptr1.get() << endl;
cout << "ptr1的地址" << &ptr1 << endl;
cout << "m_name= " << ptr1->m_name << endl;
//第二种智能指针定义方法
unique_ptr<Perso> ptr2(new Perso("白居易"));
//第三种智能指针定义方法(C++14标准)
unique_ptr<Perso> ptr3 = make_unique<Perso>("杜甫");
//传参
//①传引用(不能传值,unique_ptr 没有拷贝构造函数)
//②不能用同一个裸指针初始化多个 unique_ptr
//智能指针重载了 * 和 -> 操作符,可以像使用指针一样使用 unique_ptr
//智能指针不支持指针的运算(+,-,++,--)
unique_ptr<Perso[]> array1(new Perso[2]); //unique_ptr数组
unique_ptr<Perso[]> array2(new Perso[2]{ string("李商隐"),string("孟浩然") });
unique_ptr<Perso> ptr1(new Perso("智能"));
unique_ptr<Perso> ptr2;
ptr2= unique_ptr<Perso>(new Perso("指针"));
ptr2 = func();//先析构指针对象,再构造测试
unique_ptr<Perso> pu(new Perso("王勃"));
cout << "开始调用" << endl;
func1(pu.get()); //func1()需要一个指针,但不对这个指针负责
func2(pu.release()); //func2()需要一个指针,对这个指针负责
func3(pu); //func3()需要一个智能指针,但不对这个智能指针负责
func4(move(pu)); //func4()需要一个智能指针,对这个智能指针负责
cout << "调用完成" << endl;
}
/*
更多技巧:
1.将一个unqiue_ptr赋给另一个时,如果源unqiue_ptr是一个临时右值,编译器允许这样做,如果源unqiue_ptr
将存在一段时间,编译器将禁止这样做,一般用于函数返回值
2.用nullptr给unqiue_ptr赋值将释放对象,空的nullptr==unqiue_ptr
3.release()释放对原始指针的控制权,将unqiue_ptr置为空,返回裸指针(可用于把unqiue_ptr传递给子函数,
子函数将负责释放对象)
4.std::move()可以转移对原始指针的控制权
5.不要用 unqiue_ptr 管理不是 new 分配的内存
6.unqiue_ptr 也可像普通指针一样,当指向一个类继承体系的基类对象时,也具有多态性质,
如同使用裸指针管理基类对象和派生类对象那样
7.如果程序中调用exit()退出,全局的 unique_ptr 可以自动释放,但局部的 unique_ptr 无法自动释放
*/
2.shared_ptr
/*
shared_ptr 共享它指向的对象,多个 shared_ptr 可以指向(关联)相同的对象,在内部采用计数机制来实现
当新的 shared_ptr 与对象关联时,引用计数加1
当 shared_ptr 超出作用域时,引用计数减1,当引用计数变为0时,没有任何的 shared_ptr 与对象关联,则释放该对象
*/
#include <iostream>
#include <memory>
using namespace std;
class Perso
{
public:
string m_name;
Perso() { cout << "调用无参构造函数" << endl; }
Perso(const string& name) : m_name(name) { cout << "调用有参构造函数" << endl; }
//Perso(const string & name){ m_name = name; cout << "调用有参构造函数" << endl; }
~Perso() { cout << "调用析构函数" << endl; }
};
int main()
{
Perso* p = new Perso("李白");
shared_ptr<Perso> ptr1(p); //用已存在的地址进行初始化
shared_ptr<Perso> ptr2(new Perso("杜甫")); //分配内存并进行初始化
shared_ptr<Perso> ptr3 = make_shared<Perso>("白居易"); //C++11标准
shared_ptr<Perso> ptr4 = ptr1;
cout << "ptr1.use_cout()= " << ptr1.use_count() << endl;
cout << ptr1->m_name << endl;
cout << "ptr4.use_cout()= " << ptr4.use_count() << endl;
cout << ptr4->m_name << endl;
shared_ptr<Perso[]> array1(new Perso[2]);
shared_ptr<Perso[]> array2(new Perso[2]{ string("李清照"),string("李贺")});
return 0;
}
/*
1.use_cout()方法返回引用计数器的值
2.unique()方法,如果 use_cout()为1,返回true, 否则返回false
3.share_ptr 支持赋值,左值的 share_ptr 的计数器将减1,右值 share_ptr的计数器将加1
4.get()方法返回值裸指针
5.不要用同一个裸指针初始化多个 shared_ptr
6.不要用 shared_ptr 管理不是 new 分配的内存
7.用 nullptr 给 shared_ptr 赋值将把计数减1,如果计数为0,将释放对象,空的 shared_ptr==nullptr
8.std::move()可以转移对原始指针的控制权,还可以将 unique_ptr 转移成 shared_ptr
9.reset()改变与资源的关联关系
p.reset(); //解除与资源的关系,资源的引用计数减1
p.reset(new Perso("赋值")); //解除与资源的关系,资源的引用计数减1,关联新资源
10.shared_ptr 也可像普通指针一样,当指向一个类继承体系的基类对象时,也具有多态性质,
如同使用裸指针管理基类对象和派生类对象那样
11.如果程序中调用exit()退出,全局的 shared_ptr 可以自动释放,但局部的 shared_ptr 无法自动释放
12.shared_ptr的引用计数本身是线程安全(引用计数是原子操作)
多个线程同时读写一个 shared_ptr 对象是线程安全的
如果是多个线程对同一个 shared_ptr 对象进行读和写,则需要加锁
多线程读写 shared_ptr 所指向的同一个对象,不管是相同的 shared_ptr 对象,还是不同的 shared_ptr 对象,也需要加锁保护
13.如果 unique_ptr 能解决问题,就不要用 shared_ptr,unique_ptr 的效率更高,占用的资源更少
*/
3.weak_ptr
/*
weak_ptr 是为了配合 shared_ptr 而引入的,它指向一个由 shared_ptr 管理的资源但不影响资源的生命周期
也就是说,将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数
不论是否有 weak_ptr 指向,如果最后一个指向资源的 shared_ptr 被销毁,资源就会释放
weak_ptr 更像是 shared_ptr 的助手而不是智能指针
*/
#include <iostream>
#include <memory>
using namespace std;
class Animal;
class Perso
{
public:
string m_name;
Perso() { cout << "调用无参构造函数Perso" << endl; }
Perso(const string& name) : m_name(name) { cout << "调用有参构造函数Perso" << m_name .c_str() << endl; }
//Perso(const string & name){ m_name = name; cout << "调用有参构造函数Perso" << endl; }
~Perso() { cout << "调用析构函数Perso" << m_name.c_str() << endl; }
weak_ptr<Animal>m_p;
};
class Animal
{
public:
string m_name;
Animal() { cout << "调用无参构造函数Animal" << endl; }
Animal(const string& name) : m_name(name) { cout << "调用有参构造函数Animal" << m_name.c_str() << endl; }
//Animal(const string & name){ m_name = name; cout << "调用有参构造函数" << endl; }
~Animal() { cout << "调用析构函数Animal" << m_name.c_str() << endl; }
weak_ptr<Perso>m_p;
};
int main()
{
shared_ptr<Perso> ptr1 = make_shared<Perso>("李白");
{
shared_ptr<Animal> ptr2 = make_shared<Animal>("老虎");
cout << "ptr1 的引用计数:" << ptr1.use_count() << endl;
cout << "ptr2 的引用计数:" << ptr2.use_count() << endl;
ptr1->m_p = ptr2;
ptr2->m_p = ptr1;
if (ptr1->m_p.expired() == true)
cout << "语句块内部:ptr1>m_p已过期" << endl;
else
cout << "语句块内部:ptr1->m_p.lock()->m_nam " << ptr1->m_p.lock()->m_name << endl;
}
if (ptr1->m_p.expired() == true)
cout << "语句块内部:ptr1>m_p已过期" << endl;
else
cout << "语句块内部:ptr1->m_p.lock()->m_nam " << ptr1->m_p.lock()->m_name << endl;
return 0;
}
/*
1.weak_ptr 乜有重载 * 和 -> 操作符,不能直接访问资源
2.operator=() 把 weak_ptr 或者 shared_ptr 类型指针赋值给 weak_ptr。
3.expired() 判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)。
4.lock() 返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针,如果 weak_ptr 已经过期,则返回空的 shared_ptr 指针;
5.reset() 将当前 weak_ptr 指针置为空指针。
6.swap() 交换
weak_ptr 不控制对象的生命周期,但是它知道对象是否还活着
用 lock() 函数可以把它提升为 shared_ptr ,如果对象还活着,返回有效的 shared_ptr,如果对象死了,返回空的 shared_ptr
lock() 行为是线程安全的
*/
4.指针删除器
/*
在默认情况下,智能指针过期的时候,用 delete 原始指针,释放它管理的资源
可以自定义删除器,改变智能指针释放资源的行为
删除器可以是全局函数、仿函数和 lambada 表达式,形参为原始指针
*/
#include <iostream>
#include <memory>
using namespace std;
class Perso
{
public:
string m_name;
Perso() { cout << "调用无参构造函数" << endl; }
Perso(const string& name) : m_name(name) { cout << "调用有参构造函数" << endl; }
//Perso(const string & name){ m_name = name; cout << "调用有参构造函数" << endl; }
~Perso() { cout << "调用析构函数" << endl; }
};
/********删除器,普通函数*********/
void deletefunc(Perso *tmp)
{
cout << "自定义删除器(全局函数)。" << endl;
delete tmp;
}
/********删除器,仿函数*********/
struct deleteclass
{
void operator()(Perso* tmp)
{
cout << "自定义删除器(仿函数)。" << endl;
delete tmp;
}
};
/********删除器,lambada表达式*********/
auto deletelamb = [](Perso* tmp)
{
cout << "自定义删除器(lambada表达式)。" << endl;
delete tmp;
};
int main()
{
shared_ptr<Perso> ptr1(new Perso("李白")); //用缺省的删除器
unique_ptr<Perso, void(*)(Perso*)>ptr(new Perso("李白"), deletefunc);
unique_ptr<Perso, decltype(deletefunc)*> ptr2(new Perso("李白"), deletefunc);
unique_ptr<Perso, deleteclass> ptr3(new Perso("李白"), deleteclass());
unique_ptr<Perso, decltype(deletelamb)> ptr4(new Perso("李白"), deletelamb);
shared_ptr<Perso> ptr5(new Perso("李白"),deletefunc);
shared_ptr<Perso> ptr6(new Perso("李白"), deleteclass());
shared_ptr<Perso> ptr7(new Perso("李白"), deletelamb);
return 0;
}