C++没有内存回收机制,每次程序员new出来的对象需要手动delete,流程复杂时可能会漏掉delete,导致内存泄漏。于是C++引入智能指针,可用于动态资源管理,资源即对象的管理策略。
使用 raw pointer 管理动态内存时,经常会遇到这样的问题:
- 忘记
delete
内存,造成内存泄露。 - 出现异常时,不会执行
delete
,造成内存泄露。
智能指针主要有三种:shared_ptr,unique_ptr和weak_ptr。
shared_ptr
shared_ptr是最常用的智能指针(项目中我只用过shared_ptr)。shared_ptr采用了引用计数器,多个shared_ptr中的T *ptr指向同一个内存区域(同一个对象),并共同维护同一个引用计数器。shared_ptr定义如下,记录同一个实例被引用的次数,当引用次数大于0时可用,等于0时释放内存。
注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用在weak_ptr中介绍。引用传递不加1;
shared_ptr对象每次离开作用域时会自动调用析构函数,而析构函数并不像其他类的析构函数一样,而是在释放内存是先判断引用计数器是否为0。等于0才做delete操作,否则只对引用计数器左减一操作。
接下来看一下构造函数,默认构造函数的引用计数器为0,ptr指向NULL:
SharedPtr() : _ptr((T *)0), _refCount(0)
{
}
~SharedPtr()
{
if (_ptr && --*_refCount == 0) {
delete _ptr;
delete _refCount;
}
}
用普通指针初始化智能指针时,引用计数器初始化为1:
SharedPtr(T *obj) : _ptr(obj), _refCount(new int(1))
{
}
赋值运算符的重载
当用一个shared_ptr<T> other去给另一个 shared_ptr<T> sp赋值时,发生了两件事情:
一、sp指针指向发生变化,不再指向之前的内存区域,所以赋值前原来的_refCount要自减
二、sp指针指向other.ptr,所以other的引用计数器_refCount要做++操作。
template <class T> class shared_ptr;
Member functions
(constructor)
Construct shared_ptr (public member function )
(destructor)
Destroy shared_ptr (public member function )
operator=
shared_ptr assignment (public member function )
swap
Swap content (public member function )
reset
Reset pointer (public member function )
get
Get pointer (public member function )
operator*
Dereference object (public member function )
operator->
Dereference object member (public member function )
use_count
Use count (public member function )
unique
Check if unique (public member function )
operator bool
Check if not null (public member function )
owner_before
Owner-based ordering (public member function template )
Non-member functions
Overloads:
swap
Exchange content of shared_ptr objects (function template )
relational operators
Relational operators ==, !=, <, <=, >, >= (function template )
ostream operator<<
Insert into output stream (function template )
Specific functions:
make_shared
Make shared_ptr (function template )
allocate_shared
Allocate shared_ptr (function template )
static_pointer_cast
Static cast of shared_ptr (function template )
dynamic_pointer_cast
Dynamic cast of shared_ptr (function template )
const_pointer_cast
Const cast of shared_ptr (function template )
get_deleter
Get deleter from shared_ptr (function template )
See also
weak_ptr
Weak shared pointer (class template )
owner_less
Owner-based less-than operation (class template )
实例:
#include "shared_ptr.h"
//class Foo {
//public:
// Foo() { std::cout << "Foo...\n"; }
// ~Foo() { std::cout << "~Foo...\n"; }
//};
//
//struct D {
// void operator()(Foo* p) const {
// std::cout << "Call delete from function object...\n";
// delete p;
// }
//};
void func_shared_ptr(const shared_ptr<int> foo_)
{
cout << "foo_.use_count = " << foo_.use_count() << endl;
}
int stl_shared_ptr()
{
std::shared_ptr<int> p1;
{
std::cout << "use_count:\n";
std::cout << "p1: " << p1.use_count() << '\n';
p1.reset(new int(0));
std::cout << "p1: " << p1.use_count() << '\n';
std::shared_ptr<int> p2(nullptr);
std::cout << "p2: " << p2.use_count() << '\n';
std::shared_ptr<int> p3(new int);
std::cout << "p3: " << p3.use_count() << '\n';
std::shared_ptr<int> p4(new int, std::default_delete<int>());//给出析构函数
std::cout << "p4: " << p4.use_count() << '\n';
std::shared_ptr<int> p5(new int, [](int* p){delete p; }, std::allocator<int>());//给出构造函数和析构函数
std::cout << "p5: " << p5.use_count() << '\n';
{
std::shared_ptr<int> p6(p5);//调用拷贝构造函数 p6原本的减一,p5加一
std::cout << "p5in: " << p5.use_count() << '\n';
std::cout << "p6in: " << p6.use_count() << '\n';
}
std::cout << "p5: " << p5.use_count() << '\n';
std::shared_ptr<int> p6(p5);
std::shared_ptr<int> p7(std::move(p6));//左值变右值,p6失去对指针的权力
std::cout << "p5: " << p5.use_count() << '\n';
std::cout << "p6: " << p6.use_count() << '\n';
std::cout << "p7: " << p7.use_count() << '\n';
std::shared_ptr<int> p8(std::unique_ptr<int>(new int));
std::cout << "p8: " << p8.use_count() << '\n';
std::shared_ptr<C> obj(new C);
std::shared_ptr<int> p9(obj, obj->data);//构造
std::cout << "p9: " << p9.use_count() << '\n';
}
std::cout << "p3: " << p1.use_count() << '\n';
//考察一下引用传递对共享次数 指针的传递就会增加引用计数。
{
auto foo_s_ptr = make_shared<int>(12);// 注意maked的使用
shared_ptr<int> foo_s_ptr2(foo_s_ptr);
cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl;
cout << "foo_s_ptr2.use_count = " << foo_s_ptr2.use_count() << endl;
shared_ptr<int> foo_s_ptr3(foo_s_ptr);
cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl;
func_shared_ptr(foo_s_ptr2);
cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl;
}
//类型
{
std::cout << "constructor with no managed object\n";
std::shared_ptr<Foo> sh1;
}
//调用构造函数
{
std::cout << "constructor with object\n";
std::shared_ptr<Foo> sh2(new Foo);
std::shared_ptr<Foo> sh3(sh2);
std::cout << sh2.use_count() << '\n';
std::cout << sh3.use_count() << '\n';
}
//注册deleter
{
std::cout << "constructor with object and deleter\n";
std::shared_ptr<Foo> sh4(new Foo, D());
std::shared_ptr<Foo> sh5(new Foo, [](Foo *p) {
std::cout << "Call delete from lambda...\n";
delete p;
});
}
return 1;
}
Class unique_ptr
unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr。这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯 C++ 对象时,可使用 unique_ptr,而当构造 unique_ptr 时,可使用make_unique Helper 函数。
拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。
因此std::unique_ptr是一个仅能移动(move_only)的类型。
1.创建
unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。
int main()
{
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int(5));
cout << *pInt;
}
2、无法进行复制构造和赋值操作
unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。
int main()
{
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2(pInt); // 报错
unique_ptr<int> pInt3 = pInt; // 报错
}
3、可以进行移动构造和移动赋值操作
unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。
int main()
{
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2 = std::move(pInt); // 转移所有权
//cout << *pInt << endl; // 出错,pInt为空
cout << *pInt2 << endl;
unique_ptr<int> pInt3(std::move(pInt2));
}
4、可以返回unique_ptr
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。
unique_ptr<int> clone(int p)
{
unique_ptr<int> pInt(new int(p));
return pInt; // 返回unique_ptr
}
int main() {
int p = 5;
unique_ptr<int> ret = clone(p);
cout << *ret << endl;
}
5.unique_ptr使用场景
为动态申请的资源提供异常安全保证
返回函数内动态申请资源的所有权
在容器中保存指针
管理动态数组
作为auto_ptr的替代品
实例
#include "unique_ptr.h"
#include <iostream>
#include <functional>
using namespace std;
void f(const unique_Foo &)
{
std::cout << "f(const unique_Foo&)\n";
}
void TestAutoDestroy()
{
//1. 普通的new对象.
std::cout << "TestDestroy...................." << std::endl;
{
std::unique_ptr<unique_Foo> p1(new unique_Foo);
}
//2. 普通的new[]对象.
{
std::unique_ptr<unique_Foo[]> p2(new unique_Foo[4]);//unique_ptr数组模式
}
//3. 自定义的deleter.和shared_ptr类似可以自己写一个析构函数,有多种方式
{
std::unique_ptr<unique_Foo, unique_D> p3(new unique_Foo);
std::unique_ptr<unique_Foo, std::function<void(unique_Foo*)>> p4(new unique_Foo, [](unique_Foo *p){ delete p; });//类似的lambda 表达式
}
}
void TestOwner()
{
std::cout << "TestOwner...................." << std::endl;
//1. new object.
std::unique_ptr<unique_Foo> p1(new unique_Foo); // p1 owns unique_Foo
if (p1) p1->bar();
{
std::unique_ptr<unique_Foo> p2(std::move(p1)); // now p2 owns unique_Foo
f(*p2);
p1 = std::move(p2); // ownership returns to p1
p2->bar();
std::cout << "destroying p2...\n";
}
p1->bar();
}
void TestArrayOwner()
{
std::cout << "TestArrayOwner...................." << std::endl;
//1. new[] object.
std::unique_ptr<unique_Foo[]> p1(new unique_Foo[4]); // p1 owns unique_Foo
if (p1) p1[0].bar();
{
std::unique_ptr<unique_Foo[]> p2(std::move(p1)); // now p2 owns unique_Foo
f(p2[0]);
p1 = std::move(p2); // ownership returns to p1
p2[0].bar();
std::cout << "destroying p2...\n";
}
p1[0].bar();
}
int stl_unique_ptr()
{
TestAutoDestroy();
TestOwner();
TestArrayOwner();
return 0;
}
weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段.
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少.
std::weak_ptr
是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。
std::weak_ptr
用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr
来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
此外,std::weak_ptr
还可以用来避免 std::shared_ptr 的循环引用。
weak_ptr 在功能上类似于普通指针, 然而一个比较大的区别是, 弱引用能检测到所管理的对象是否已经被释放, 从而避免访问非法内存。
构造新的weak_ptr (公开成员函数) | |
销毁 weak_ptr (公开成员函数) | |
为weak_ptr 赋值 (公开成员函数) |
修改器 | |
释放被管理对象的所有权 (公开成员函数) | |
交换被管理对象 (公开成员函数) | |
观察器 | |
返回管理该对象的 shared_ptr 对象数量 (公开成员函数) | |
检查被引用的对象是否已删除 (公开成员函数) | |
创建管理被引用的对象的shared_ptr (公开成员函数) | |
提供弱指针的基于拥有者顺序 (公开成员函数) |
非成员函数
(C++11) | 特化 std::swap 算法 (函数模板) |
辅助类
(C++20) | 原子弱指针 (类模板特化) |
weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象. 注意, weak_ptr 在使用前需要检查合法性.
expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同.
use_count 返回与 shared_ptr 共享的对象的引用计数.
reset 将 weak_ptr 置空.
weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.
创建及使用
#include "weak_ptr.h"
using namespace std;
class CB;
class CA;
class CA
{
public:
CA(){}
~CA(){ }
void Register(const std::shared_ptr<CB>& sp)
{
m_spb = sp;
cout << "Register_spib.use_count = " << sp.use_count() << endl;
}
private:
std::weak_ptr<CB> m_spb;//这样不会增加m_spb的引用计数 ,那么CA就能释放
};
class CB
{
public:
CB(){};
~CB(){ };
void Register(const std::shared_ptr<CA>& sp)
{
m_spa = sp;
cout << "Register_spia.use_count = " << sp.use_count() << endl;
}
private:
std::shared_ptr<CA> m_spa;//与上条注释的差别所在,无论什么情况下,B的智能指针的释放都得等m_spa的引用计数为1.
};
std::weak_ptr<int> gw;
void f()
{
if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr,且会增加原来的shared_ptr的计数
std::cout << *spt << "\n";
cout << "spt.use_count = " << spt.use_count() << endl;
}
else {
std::cout << "gw is expired\n";
}
}
void weak_ptr_lock()
{
{
auto sp = std::make_shared<int>(42);
cout << "sp.use_count = " << sp.use_count() << endl;
gw = sp;
f();
cout << "sp.use_count = " << sp.use_count() << endl;//使用计数恢复到1
}
f();
}
void stl_weak_circle()
{
std::shared_ptr<CA> spa(new CA);
std::shared_ptr<CB> spb(new CB);
std::weak_ptr<CB> weak_cb = spb;//并不改变计数
std::weak_ptr<CB> weak_cb1(spb);//并不改变计数
cout << "spb.use_count = " << spb.use_count() << endl;
cout << "spa.use_count = " << spa.use_count() << endl;
spb->Register(spa);
spa->Register(spb);
cout << "spb.use_count = " << spb.use_count() << endl;
cout << "spa.use_count = " << spa.use_count() << endl;
}
void stl_weak_ptr()
{
weak_ptr_lock();
stl_weak_circle();
}