1.前言
在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化;delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极其困难的。有时会忘记释放内存,在这种情况下会产生内存泄露;有时在尚有指针引用内存的情况下就释放了它,在这种情况下就会产生引用非法内存的指针。
为了更容易(同时也更安全)地使用动态内存,C++11标准库提供了两种智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指的对象。C++11标准库提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。C++11标准库还定义了一个名为weak_ptr的辅助类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。智能指针是模板类而不是指针。类似vector,智能指针也是模板,当创建一个智能指针时,必须提供额外的信息即指针可以指向的类型。默认初始化的智能指针中保存着一个空指针。智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。如果在一个条件判断中使用智能指针,效果就是检测它是否为空。
2.使用
2.1 初始化
1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
int a = new int(100);
std::shared_ptr ptr(a); //我们不能写成std::shared_ptr ptr = a;这样写错误
2.通过make_shared函数得到
std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
std::shared_ptr<int> ptr1 = std::make_shared<int>(); //创建一个空指针
3.拷贝初始化
std::shared_ptr<int> ptr2(ptr1);
//std::shared_ptr<int> ptr2 = ptr1;这样赋值是错误的,只要是智能指针,这样直接用=赋值是有问题的必须std::shared_ptr<int> ptr2(ptr1);
2.2 检查 shared_ptr 对象的引用计数
ptr1.use_count();
2.3 reset函数
当智能指针调用了reset函数的时候,就不会再指向这个对象了,所以如果还有其它智能指针指向这个对象,那么另外一个智能指针的use_count()函数结果会减1
2.4 shared_ptr是一个伪指针
shared_ptr充当普通指针,我们可以将*
和->
与 shared_ptr 对象一起使用,也可以像其他 shared_ptr 对象一样进行比较;
3.完整示例
#include <iostream>
#include <memory> // 需要包含这个头文件
int main()
{
// 使用 make_shared 创建空对象
std::shared_ptr<int> p1 = std::make_shared<int>();
*p1 = 78;
std::cout << "p1 = " << *p1 << std::endl; // 输出78
// 打印引用个数:1
std::cout << "p1 Reference count = " << p1.use_count() << std::endl;
// 第2个 shared_ptr 对象指向同一个指针
std::shared_ptr<int> p2(p1);
// 下面两个输出都是:2
std::cout << "p2 Reference count = " << p2.use_count() << std::endl;
std::cout << "p1 Reference count = " << p1.use_count() << std::endl;
// 比较智能指针,p1 等于 p2
if (p1 == p2) {
std::cout << "p1 and p2 are pointing to same pointer\n";
}
std::cout<<"Reset p1 "<<std::endl;
// 无参数调用reset,无关联指针,引用个数为0
p1.reset();
std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;
// 带参数调用reset,引用个数为1
p1.reset(new int(11));
std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;
// 把对象重置为NULL,引用计数为0
p1 = nullptr;
std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;
if (!p1) {
std::cout << "p1 is NULL" << std::endl; // 输出
}
return 0;
}
4. weak_ptr
利用weak_ptr可解决shared_ptr循环引用导致的内存泄露问题,具体参考
https://blog.csdn.net/albertsh/article/details/82286999
总结
shared_ptr
可以共享内存,每次共享计数器加1,在离开函数作用域或对象析构时计数器减1,计数器为0时析构,可复制.
weak_ptr
是辅助shared_ptr用的,可共享,但不增加计数器,使用前需要判断所指向的对象是否还在
unique_ptr
独占内存,不可共享,也就不能复制,但可以交出内存对象所有权。在离开函数作用域或对象析构时可自动析构所指向的对象。
关于三者介绍详见https://zhuanlan.zhihu.com/p/92815756
参考:
- https://blog.csdn.net/shaosunrise/article/details/85228823
- https://blog.csdn.net/fengbingchun/article/details/52202007