注意 编译的时候要指定 --std=c++11
例如
g++ myshareptr.cpp --std=c++11
1 智能指针 shared_ptr
1 关注事项
1 如何调用托管对象的成员 函数
2 reset函数 :释放托管对象的所有权(如果有),调用后,该智能指针不再管理任何对象。
代码示例
#include<iostream>
#include<string>
#include<memory>
using namespace std;
class Report
{
private:
string str;
public:
Report(const std::string s) :str(s)
{
cout<<"Object created\n";
}
~Report()
{
cout<<"Object deleted @@@@@@@@@@@@\n";
}
void comment() const
{
cout<<str<<"**************\n";
}
};
int main()
{
//shared_ptr<Report> ps(new Report("using share_ptr"));
Report *pa = new Report("using share_ptr");
shared_ptr<Report> ps(pa);
shared_ptr<Report> ps1 = ps; // 再引用一次 (注意引用的主体是 shared_ptr)
shared_ptr<Report> ps2 = ps;
cout<<"count(): \n"<<ps.use_count()<<endl;
cout<<"count(): \n"<<ps1.use_count()<<endl;
cout<<"count(): \n"<<ps2.use_count()<<endl;
ps->comment(); //注意: 通过 -> 调用管理指针的函数
cout<<"++count(): \n"<<ps.use_count()<<endl;
ps.reset();// 释放托管对象的所有权(如果有),调用后,该智能指针不再管理任何对象。
cout<<"__count(): \n"<<ps.use_count()<<endl;
cout<<"__count(): \n"<<ps1.use_count()<<endl;
cout<<"__count(): \n"<<ps1.use_count()<<endl;
ps1.reset();
ps2.reset();
return 0;
}
2 weak_ptr弱引用
一 shared_ptr 致命的问题就是 循环引用
1 在shared_ptr的使用过程中,当强引用计数为0时,就会释放所指向的堆内存。
2 但是如果和死锁一样,当两个shared_ptr互相引用,那么它们就永远无法被释放了。
例如:
#include <iostream>
#include <memory>
class CB;
class CA {
public:
CA() {
std::cout << "CA()" << std::endl;
}
~CA() {
std::cout << "~CA()" << std::endl;
}
void set_ptr(std::shared_ptr<CB>& ptr) {
m_ptr_b = ptr;
}
private:
std::shared_ptr<CB> m_ptr_b;
};
class CB {
public:
CB() {
std::cout << "CB()" << std::endl;
}
~CB() {
std::cout << "~CB()" << std::endl;
}
void set_ptr(std::shared_ptr<CA>& ptr) {
m_ptr_a = ptr;
}
private:
std::shared_ptr<CA> m_ptr_a;
};
int main()
{
std::shared_ptr<CA> ptr_a(new CA());
std::shared_ptr<CB> ptr_b(new CB());
ptr_a->set_ptr(ptr_b);
ptr_b->set_ptr(ptr_a);
std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;
return 0;
}
编译并运行结果,打印为:
CA()
CB()
2 2
没有调用析构函数:
起初定义完ptr_a和ptr_b时,只有①、③两条引用,即ptr_a指向CA对象,ptr_b指向CB对象。然后调用函数set_ptr后又增加了②、④两条引用,即CB对象中的m_ptr_a成员变量指向CA对象,CA对象中的m_ptr_b成员变量指向CB对象。
这个时候,指向CA对象的有两个,指向CB对象的也有两个。当main函数运行结束时,对象ptr_a和ptr_b被销毁,也就是①、③两条引用会被断开,但是②、④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。
二
weak_ptr的出现就是为了解决shared_ptr的循环引用的问题的。以上文的例子来说,解决办法就是将两个类中的一个成员变量改为weak_ptr对象,比如将CB中的成员变量改为weak_ptr对象,即CB类的代码如下:
class CB {
public:
CB() {
std::cout << "CB()" << std::endl;
}
~CB() {
std::cout << "~CB()" << std::endl;
}
void set_ptr(std::shared_ptr<CA>& ptr) {
m_ptr_a = ptr;
}
private:
std::weak_ptr<CA> m_ptr_a;
};
编译并运行结果,打印为:
CA()
CB()
1 2
~CA()
~CB()
通过这次结果可以看到,CA和CB的对象都被正常的析构了。
流程与上一例子大体相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数。也就是说,CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当main函数返回时,对象ptr_a和ptr_b被销毁,也就是①、③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。
如果仔细看代码的话,会觉得很神奇!定义m_ptr_a修改成std::weak_ptr类型,但是set_ptr函数定义的参数还是std::shared_ptr类型。这个时候为什么没有报错?weak_ptr和shared_ptr的联系是什么呢?
weak_ptr的原理
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。
初始化方式
1 通过shared_ptr直接初始化,也可以通过隐式转换来构造;
2 允许移动构造,也允许拷贝构造。
#include <iostream>
#include <memory>
class Frame {};
int main()
{
std::shared_ptr<Frame> f(new Frame());
std::weak_ptr<Frame> f1(f); // shared_ptr直接构造
std::weak_ptr<Frame> f2 = f; // 隐式转换
std::weak_ptr<Frame> f3(f1); // 拷贝构造函数
std::weak_ptr<Frame> f4 = f1; // 拷贝构造函数
std::weak_ptr<Frame> f5;
f5 = f; // 拷贝赋值函数
f5 = f2; // 拷贝赋值函数
std::cout << f.use_count() << std::endl; // 1
return 0;
}
需要注意,weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
常用操作
w.user_count():返回weak_ptr的强引用计数;
w.reset(…):重置weak_ptr。
如何判断weak_ptr指向对象是否存在?
既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,就不能使用weak_ptr直接访问对象。那么如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。
由于weak_ptr并没有重载operator ->和operator *操作符,因此不可直接通过weak_ptr使用对象,同时也没有提供get函数直接获取裸指针。典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。