std::share_ptr 使用引用计数来让多个对象之间共享一块内存,只有当最后一个变量析构的时候,也就是引用计数为0的时候 才会释放内存,所以内存的释放,就是通过引用计数来判断的,share_ptr在包含了两部分,一部分是裸指针raw_ptr 用来指向堆上申请的地址,一个指向内部隐藏的,管理的对象share_count_object
//第一种方式 直接调用对象的构造函数
std::shared_ptr<int> p1(new int(1));
//第二种方式,先声明对象然后调用reset初始化
std::shared_ptr<int>p2;
p2.reset(new int(1));
//第三种方式 也是推荐的方式以及最高效的方式 使用make_shared
auto ptr = make_shared<int>(100); //auto 也是C++ 11 的新特性
//这种初始化方式等价与 std::shareptr(new int(100));
ps : 1.这里分析下为什么使用make_shared的创建方式会更加的高效了,在我们是使用 方式1的去初始化一个shared_ptr的时候,我们需要先在堆上申请分配一块内存,用来存储数字1,然后用这个变量调用share_ptr的构造函数,再次分配一次内存,而使用make_shared就只需要分配一次内存就可以了
ps 2. 关于智能指针调用reset的初始化方式,这个函数在智能指针没有值的时候调用是用来初始化的,当这个智能指针有值的时候,调用reset函数就会引起智能指针引用计数-1
A: reset 没有值的时候用来初始化指针,有值的时候会让引用计数-1
std::share_ptr<int> p1;
p1.reset(new int(10)); //p1未指向任何地址,reset初始化
std::share_ptr<int> p2;
p2 = p1; //p2 与p1 都指向同一块内存地址 引用计数+1
cout << p2.use_count <<endl; // 2 当前有两个share_ptr 强引用这块地址
p1.reset(); //引用计数-1
cout << p2.use_count <<endl;// 1
B: use_count 获取当前的引用计数的数量 代码见上面
C: get 获取原始指针 不要用
auto ptr = make_shared<int>(100);
int *p = ptr.get(); //获取share_ptr的原始指针 也就是上面讲到的第一部分用来存储真正数据的地方
D: 指定删除器 如果shared_ptr管理的不是new的对象,或者类没有析构函数的时候 应该指定删除器
#include
#include
using namespace std;
void DeleteSharePtr(int *p)
{
cout << "Delete Share Ptr" << endl;
delete p;
}
int main()
{
//生成的时候传入删除器
std::shared_ptr<int>p1(new int(100),DeleteSharePtr);
//也可以是使用lambda表达式来弄
std::shared_ptr<int>p2(new int(120), [](int*p){
cout << "Lambda delete ptr" <<endl;
delete p;
})
std::shared_ptr<int>p3(new int[10],[](int *p){
delete []p;
})
}
PS :当我们用shared_ptr管理数组的时候,一定要指定删除器
A: shared_ptr 不能通过直接赋值来初始化,只能通过它的构造函数或者辅助函数来初始化
std::shared_ptr<int> p1 = new int(10); //方法错误 不能通过这种方式进行初始化
B 不要用一个原始指针初始化多个shared_ptr
int * ptr = new int(10);
std::shared_ptr<int> p1(ptr); //用ptr初始化share_ptr p1 指向这块内存
std::shared_ptr<int> p2(ptr);//用ptr初始化share_ptr p2指向这块内存
// 最后的结果就是ptr所指向的内存 被析构了两次
C: * 不要在函数*实参中创建share_ptrfunction(shared_ptr
因为在不同的C++编译器的计算顺序是不一样的,一般都是从右到左,但也有从左到右的过程,如果我们先new int ,然后在调用g()的时候异常了,那么我们申请的内存就会没有释放,而此时shared_ptr还没有创建,则存在了内存泄漏的风险,这也是推荐使用make_shared得原因
D:通过指定的接口返回this 指针,因为将this指针作为shared_ptr对象返回,会导致重复析构,与B的情况类似,而我们返回的时候需要让目标通过继承std::enable_shared_from_this然后通过基类的接口shared_from_this()来返回
#icnlude<iostream>
#include
using namespace std;
class A
{
public:
shared_ptr<A>GetSelf()
{
return shared_ptr<A>(this);
}
~A()
{
cout << "Deconstrution A" <<endl;
}
}
class B:public std::enable_shared_from_this<A>
{
public :
std::shared_ptr<B> GetSelf()
{
return shared_from_this();
}
~B
{
cout << "Deconstrution B" <<endl;
}
}
int main()
{
std::shared_ptr<A> p1(new A);
std::shared_ptr<A>p2 = p1->GetSelf(); // 错误 将会导致两次析构
auto p3 = make_shared<B>();
auto p4 = p3.GetSelf(); //正确
}
E:* 避免循环引用导致内存泄漏,当我们在两个不同的类里都互相存着对方的成员子对象share_ptr的时候,我们就会发现,这两个类创造出来的对象都存在循环引用的场景,导致内存泄漏 *
#include
#include
using namespace std;
class A;
class B;
class A
{
public:
std::shared_ptr<B> pb;
~A(){cout << "A Destroy" <<endl;}
};
class B
{
public :
std::shared_ptr<A>pa;
~B(){cout <<"B Destroy"<<endl;}
};
int main()
{
{
auto pa = make_shared<A>();
auto pb = make_shared<B>();
pa->pb = pb;
pb->pa = pa;
cout<< pa.use_count ()<<endl; //2
cout<<pb.use_count ()<<endl; //2
}
//在作用域结束的时候我们会发现,根本就没有调用一次析构函数,因为直到作用域结束 两个指针互相引用导致引用计数不能为0,而不调用析构函数
cout << "Main End Return" <<endl;
return 0;
}
Unique_ptr 独占的智能指针,这个指针与shared_ptr 不同,就是在某个时候一定只有一个unique_ptr指向一个特定的对象,,当unique_prt被销毁的时候它所指向的对象也会被销毁
与shared_ptr不同的是,在C++11中没有make_shared这样的函数用来初始化,在C++14中又了make_unique()函数用来初始化一个unique_ptr,而在C11中只能通过直接初始化的形式,由于一个unique_ptr 只存在一个拥有指向的对象,所以它不支持普通的拷贝和操作赋值
#include
unique_ptr<string> p1(new string("12300")); //直接初始化形式
// unique_ptrp2(p1) //unique_ptr(const unique_ptr&) = delete;
//unique_ptrp3;
//p3 = p1 //unique_ptr& operator=(const unique_ptr&) = delete;
unique_ptr<T,func>p2(new string("A"), func) //指定删除器的初始化的形式
A:*unique_ptr虽然不能拷贝和赋值,但是可以通过函数返回给其他的unique_ptr 还可以通过std::move 来转移到其他的unique_ptr 这样它本身就不再拥有原来指针的所有权了 在这里不用感到奇怪就是为什么函数的返回值能够赋值给另外的unique_ptr 而直接调用的却不行了,这是因为直接赋值的unique_ptr& operator=(const unique_ptr&) = delete 这个函数是不能使用的,而函数的返回值 是一个右值,而unique_ptr是实现了右值版本的赋值函数的
#include
unique_ptr<int> my_ptr(new int(1));
unique_ptr<int>p2 = std::move(my_ptr);
B:创建数组形变量的时候与shared_ptr 的不同,unique_ptr是可以指定数组大小的,而shared_ptr是不可以指定的 因为shared_ptr创建数组类型变量时候需要指定删除器
#include
unique_ptr<int []>p1(new int[10]);
shared_ptr<int []>p2(new int[10]); //错误
C:指定删除器与shared_ptr的不同,unique_ptr指定删除器的时候,需要确定删除器的类型
std::shared_ptr<int>(new int(1),[](int*p){
delete p;
})
//unique_ptr 需要指定删除器类型
std::unique_ptr<int,void(*)(int*)>p2(new int(1), [](int*p)
{
delete p;
})
A:release 所有者放弃对指针的控制权返回指针,并将自身置为空
B:reset 如果直接调用reset 就是释放u的对象,如果reset§就是让u指向这个对象
所谓的弱引用是区别与shared_prt的强引用的,shared_ptr的每个引用者都有权限去操作那个共同的指向的对象 而弱引用是不控制对象的生命周期的智能指针,它的出现是为了解决shared_prt相互引用导致的内存泄漏的问题,它可以从一个weak_ptr或者一个shared_ptr的对象构造,它的构造和析构不会引起shared_ptr引用计数的增加和减少,weak_ptr只是提供了一种方位shared_ptr的接口
A: 初始化为空的weak_ptr weak_ptr
B 用shared_ptr来初始化
std::shared_ptr<int> sp(new int(1));
std::weak_ptr<int>wp(sp);
std::weak_ptr<int>wp2;
wp2 = sp;
A: 使用use_count 获取共享对象shared_ptr的数量
std::shared_ptr<int>p1(new int(1));
std::weak_ptr<int>wp1(p1);
wp1.use_count() ;//1
B:* 使用expired() 来判断对象是否已经被释放了*
std::shared_ptr<int>p1(new int(1));
std::weak_ptr<int>wp1(p1);
if(wp1.expired()) //use_count 为0就返回true
cout << "释放了"
else
cout <<"还没了"
C: lock 获取监视的shared_ptr
std::weak_ptr<int>gw;
void f()
{
if(gw.expired()){
cout << "gw无效,资源已释放";
}else {
auto spt = gw.lock();
cout << "gw有效, *spt = " << *spt << endl;
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f(); //可用
}
f();//不可用
return ;
}
C:前面shared_ptr返回this指针就是通过弱引用实现的,shared_from_this();通过lock
D:weak_ptr解决循环引用问题
#include
#include
using namespace std;
class A;
class B;
class A
{
public:
std::weak_ptr<B> pb;
~A(){cout << "A Destroy" <<endl;}
};
class B
{
public :
std::weak_ptr<A>pa;
~B(){cout <<"B Destroy"<<endl;}
};
int main()
{
{
auto pa = make_shared<A>();
auto pb = make_shared<B>();
pa->pb = pb;
pb->pa = pa;
cout<< pa.use_count ()<<endl; //1
cout<<pb.use_count ()<<endl; //1
}
cout << "Main End Return" <<endl;
return 0;
}
A * 多线程创建和开启的时候 通过lambda表达式或者函数参数引用传入智能指针都是不安全的 这样的话就存在多个线程使用一个智能指针*
B *所管理数据的线程安全性问题。显而易见,所管理的对象必然不是线程安全的,必然 sp1、sp2、sp3智能指针实际都是指向对象A, 三个线程同时操作对象A,那对象的数据安全必然是需要对象
A自己去保证。
*