文章目录
- shared_ptr
- unique_ptr
- weak_ptr
- 循环引用导致内存泄露
- 使用weak_ptr修复内存泄露
shared_ptr
可共享的指针
shared_ptr中包含两个指针,一个指向具体实例对象,另一个指向控制块(contorl block),控制块中包含一个引用计数和其他一些数据。
- shared_ptr结构
- 两个shared_ptr指向同一个对象
- 初始化:
- 使用new pointer
std::shared_ptr<T> ptr(new T());
std::shared_ptr<T> ptr = std::make_shared<T>();
- 使用existing pointer
shared_ptr<T> ptr(already_existing_pointer);
shared_ptr<T> ptr = make_shared(already_existing_pointer);
example:
std::shared_ptr<int> p1;std::shared_ptr<int> p2 (nullptr);std::shared_ptr<int> p3 (new int);std::shared_ptr<int> p4 (new int, std::default_delete<int>());std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());std::shared_ptr<int> p6 (p5);std::shared_ptr<int> p7 (std::move(p6));std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));std::shared_ptr<int> foo = std::make_shared<int> (10); //same as: std::shared_ptr<int> foo (new int(10));auto baz = std::make_shared<std::pair<int,int>> (30,40);
shared_ptr相关的一些方法
Method | Description |
---|---|
reset() | Resets the std::shared_ptr to empty, releasing ownership of the managed object. |
use_count() | Returns the current reference count, indicating how many std::shared_ptr instances share ownership. |
unique() | Check if there is only one std::shared_ptr owning the object (reference count is 1). |
get() | Returns a raw pointer to the managed object. Be cautious when using this method. |
swap(shr_ptr2) | swaps the contents (ownership) of two std::shared_ptr instances. |
Example:
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:void show(){cout << "A::show()" << endl;}
};
void testSharedPtr()
{shared_ptr<A> p1 = make_shared<A>();cout << "p1 use_count: " << p1.use_count() << endl;cout << "p1 address: " << p1.get() << endl;cout << "p1 isUnique: " << p1.unique() << endl;shared_ptr<A> p2 = p1;cout << "====== execute p2 = p1 ======" << endl;cout << "p1 use_count: " << p1.use_count() << endl;cout << "p2 use_count: " << p2.use_count() << endl;cout << "p2 address: " << p2.get() << endl;cout << "p2 isUnique: " << p2.unique() << endl;p1.reset();cout << "====== execute p1.reset() ======" << endl;cout << "p1 use_count: " << p1.use_count() << endl;cout << "p2 use_count: " << p2.use_count() << endl;cout << "p1 address: " << p1.get() << endl;cout << "p2 address: " << p2.get() << endl;cout << "p1 isUnique: " << p1.unique() << endl;cout << "p2 isUnique: " << p2.unique() << endl;p1.swap(p2);cout << "====== execute p1.swap(p2) ======" << endl;cout << "p1 use_count: " << p1.use_count() << endl;cout << "p2 use_count: " << p2.use_count() << endl;cout << "p1 address: " << p1.get() << endl;cout << "p2 address: " << p2.get() << endl;cout << "p1 isUnique: " << p1.unique() << endl;cout << "p2 isUnique: " << p2.unique() << endl;
}
Result:
p1 use_count: 1
p1 address: 0000016C4EEC4BF0
p1 isUnique: 1
====== execute p2 = p1 ======
p1 use_count: 2
p2 use_count: 2
p2 address: 0000016C4EEC4BF0
p2 isUnique: 0
====== execute p1.reset() ======
p1 use_count: 0
p2 use_count: 1
p1 address: 0000000000000000
p2 address: 0000016C4EEC4BF0
p1 isUnique: 0
p2 isUnique: 1
====== execute p1.swap(p2) ======
p1 use_count: 1
p2 use_count: 0
p1 address: 0000016C4EEC4BF0
p2 address: 0000000000000000
p1 isUnique: 1
p2 isUnique: 0
unique_ptr
独占指针,只能转移所有权,不能拷贝赋值
void testUniquePtr()
{unique_ptr<A> p1(new A());cout << "p1 address: " << p1.get() << endl;unique_ptr<A> p2 = move(p1);cout << "====== execute \"p2 = move(p1)\" ======" << endl;cout << "p1 address: " << p1.get() << endl;cout << "p2 address: " << p2.get() << endl;//unique_ptr<A> p2 = p1; //compile error: can't copy unique_ptr
}
Result:
p1 address: 000001507EE7DCE0
====== execute "p2 = move(p1)" ======
p1 address: 0000000000000000
p2 address: 000001507EE7DCE0
weak_ptr
为了解决循环引用问题而引入的,weak_ptr不拥有其指向的对象,也就是说,让weak_ptr指向shared_ptr所指向对象,对象的引用计数并不会增加。
auto ptr = make_shared<string>("test");
weak_ptr<string> wp1{ ptr };
cout << "use count: " << ptr.use_count() << endl;// use count: 1
weak_ptr无法直接访问对象成员
struct Square {int size = 0;
};
auto sp = make_shared<Square>();
weak_ptr<Square> wp{ sp };
cout << wp->size << endl; // compile-time ERROR
//通过shared_ptr访问对象成员
//weak_ptr不持有对象,也就是说weak_ptr指向的对象可能析构了,但weak_ptr却不知道。所以需要判断weak_ptr指向的对象是否还存在,有两种方式:
//1. weak_ptr的use_count()方法,判断引用计数是否为0。
//2. 调用weak_ptr的expired()方法,若对象已经被析构,则expired()将返回true。
auto sp = make_shared<Square>();
weak_ptr<Square> wp{ sp };if (!wp.expired())
{auto ptr = wp.lock(); // get shared_ptrcout << ptr->size << endl;
}
循环引用导致内存泄露
#include "shared_ptr.h"
#include <iostream>
#include <memory>
using namespace std;
class Son;
class Father
{
public:Father(){cout << "Father construct" << endl;}virtual ~Father(){cout << "Father destruct" << endl;}shared_ptr<Son> son;
};class Son
{
public:Son(){cout << "Son construct" << endl;}virtual ~Son(){cout << "Son destruct" << endl;}shared_ptr<Father> father;
};void testWeakPtr()
{shared_ptr<Father> pFather = make_shared<Father>();cout << "pFather use_count: " << pFather.use_count() << endl;shared_ptr<Son> pSon = make_shared<Son>();cout << "pSon use_count: " << pSon.use_count() << endl;pFather->son = pSon;cout << "====== execute \"pFather->son = pSon\" ======" << endl;cout << "pFather use_count: " << pFather.use_count() << endl;cout << "pSon use_count: " << pSon.use_count() << endl;pSon->father = pFather;cout << "====== execute \"pSon->father = pFather\" ======" << endl;cout << "pFather use_count: " << pFather.use_count() << endl;cout << "pSon use_count: " << pSon.use_count() << endl;cout << "==========" << endl;
}
Result:
Father construct
pFather use_count: 1
Son construct
pSon use_count: 1
====== execute "pFather->son = pSon" ======
pFather use_count: 1
pSon use_count: 2
====== execute "pSon->father = pFather" ======
pFather use_count: 2
pSon use_count: 2
==========
分析:
main函数退出之前,Father和Son对象的引用计数都是2。
son指针销毁,这时Son对象的引用计数是1。
father指针销毁,这时Father对象的引用计数是1。
由于Father对象和Son对象的引用计数都是1,这两个对象都不会被销毁,从而发生内存泄露。
使用weak_ptr修复内存泄露
#include "shared_ptr.h"
#include <iostream>
#include <memory>
using namespace std;
class Son;
class Father
{
public:Father(){cout << "Father construct" << endl;}virtual ~Father(){cout << "Father destruct" << endl;}shared_ptr<Son> son;
};class Son
{
public:Son(){cout << "Son construct" << endl;}virtual ~Son(){cout << "Son destruct" << endl;}weak_ptr<Father> father;
};void testWeakPtr()
{shared_ptr<Father> pFather = make_shared<Father>();cout << "pFather use_count: " << pFather.use_count() << endl;shared_ptr<Son> pSon = make_shared<Son>();cout << "pSon use_count: " << pSon.use_count() << endl;pFather->son = pSon;cout << "====== execute \"pFather->son = pSon\" ======" << endl;cout << "pFather use_count: " << pFather.use_count() << endl;cout << "pSon use_count: " << pSon.use_count() << endl;pSon->father = pFather;cout << "====== execute \"pSon->father = pFather\" ======" << endl;cout << "pFather use_count: " << pFather.use_count() << endl;cout << "pSon use_count: " << pSon.use_count() << endl;cout << "==========" << endl;
}
Result:
Father construct
pFather use_count: 1
Son construct
pSon use_count: 1
====== execute "pFather->son = pSon" ======
pFather use_count: 1
pSon use_count: 2
====== execute "pSon->father = pFather" ======
pFather use_count: 1
pSon use_count: 2
==========
Father destruct
Son destruct
分析:
main函数退出前,Son对象的引用计数是2,而Father的引用计数是1。
son指针销毁,Son对象的引用计数变成1。
father指针销毁,Father对象的引用计数变成0,导致Father对象析构,Father对象的析构会导致它包含的son_指针被销毁,这时Son对象的引用计数变成0,所以Son对象也会被析构。
Reference:
https://www.geeksforgeeks.org/shared_ptr-in-cpp/
https://cplusplus.com/reference/memory/shared_ptr/shared_ptr/
https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-in-cpp/
谈谈 shared_ptr 的那些坑