目录
1. 几种智能指针
1. auto_ptr: c++11中推荐不使用他(放弃)
2. shared_ptr: 每添加一次引用 就+1,减少一次引用,就-1;做到指针进行共享
3. unique_ptr: 一个指针同时只能有一个使用者使用
4. weaked_ptr: 与shared_ptr搭配使用
1.1 weak_ptr
参考:https://zh.cppreference.com/w/cpp/memory/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 所管理的对象组成的环状引用。(打破shared_ptr的循环引用)
-
若这种环被孤立(例如无指向环中的外部共享指针),则
shared_ptr
引用计数无法抵达零,而内存被泄露。 -
能令环中的指针之一为弱指针以避免此情况。
-
循环引用的问题:该被调用的析构函数没有被调用
#include <iostream>
#include <memory>
using namespace std;
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
class Child
{
public:
ParentPtr father;
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
};
typedef std::shared_ptr<Child> ChildPtr;
class Parent {
public:
ChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
};
void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
}
int main()
{
testParentAndChild();
return 0;
}
问题:c只有调用p的析构的时候,才能被释放。p只有调用c的析构的时候,才能被释放。。形成了循环引用,造成了内存泄露
因此,引入新的智能指针,weak_ptr
1.2 weak_ptr基本用法
一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效
#include <iostream>
#include <cassert>
#include <memory>
using namespace std;
class Object
{
public:
Object(int id) : m_id(id) {
std::cout << "init obj " << m_id << std::endl;
}
~Object() {
std::cout << "bye bye " << m_id << std::endl;
}
int id() const {
return m_id;
}
private:
int m_id;
};
void sharedPtrWithWeakPtr()
{
typedef std::shared_ptr<Object> ObjectPtr;
typedef weak_ptr<Object> WeakObjectPtr;
ObjectPtr obj(new Object(1));
// 一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效
WeakObjectPtr weakObj2; // 裸指针
WeakObjectPtr weakObj(obj); // 指向shared_ptr指针
WeakObjectPtr weakObj3(obj);
cout << "obj use count is " << obj.use_count() << endl; // 1--尽管weak_ptr也指向obj,但他只是监听者,本身并不影响引用计数次数
{
// weak_ptr使用方法
// 外部至少还有一个shared_ptr来管理资源,同时p自己本身的资源 -- p.use_count >= 2
auto p = weakObj.lock(); // auto == ObjectPtr
if (p) {
cout << p.unique() << endl; // 0 -- p.use_count() >= 2
}
else {
}
}
// 不指向某份资源
// obj.reset();
// {
// auto p = weakObj.lock(); // auto == ObjectPtr
// if (p) {
// assert(false);
// }
// else {
// cout << "null" << endl; // 如果调用reset,就会执行这句话
// }
// }
// 此时, Object(1)已经失效
obj.reset(new Object(2)) ;
{
auto p = weakObj.lock(); // 返回值如果有效, 在外部必须有weakObj指向同一个资源
if (p) {
assert(false);
}
else {
cout << "null " << endl; // null
}
}
weakObj = obj; // 又有效了
// 想知道weak_ptr有没有管理一份资源(外部有没有资源), 又不想生成一个shared_ptr
if (weakObj.expired()) {
// 资源过期了 -- true
cout << "no ptr" << endl;
}
else {
cout << "have resource\n";
}
}
int main()
{
sharedPtrWithWeakPtr();
return 0;
}
1.3 解决类之间循环引用
一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效
#include <iostream>
#include <cassert>
#include <memory>
using namespace std;
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;
class Child
{
public:
WeakParentPtr father; // 只要一环换成 weak_ptr, 即可打破环
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
};
typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;
class Parent {
public:
ChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
};
void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
cout << (c->father).use_count() << endl;
cout << (p->son).use_count() << endl;
}
int main()
{
testParentAndChild();
return 0;
}
析构函数成功调用, 注意析构顺序,谁是weak_ptr对象,就先析构谁。
1.4 用 enable_shared_from_this从this转换到shared_ptr
-
类中函数接口需要一个本对象智能指针的const引用 (如何生成本对象的智能指针)
-
用 enable_shared_from_this从this转换到shared_ptr (使用CRTP来使用 本对象的智能指针)
主要代码:
handleChildAndParent(shared_from_this(), ps);
完整代码:
#include <iostream>
#include <cassert>
#include <memory>
using namespace std;
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;
class Child : public std::enable_shared_from_this<Child> // 奇异模板参数 CRTP
{
public:
WeakParentPtr father; // 只有一环换成 weak_ptr, 即可打破环
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
void CheckRelation() {
}
};
typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;
void handleChildAndParent(const ParentPtr& p, const ChildPtr& c);
class Parent : public enable_shared_from_this<Parent> {
public:
WeakChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
void CheckRelation() {
auto ps = son.lock();
if (ps) {
//原理: 类中存在weak_ptr指针,通过一系列方式转换成 shared_ptr,然后传参
handleChildAndParent(shared_from_this(), ps); // 使用CRTP来使用 本对象的指针 ★ ★ ★ ★
}
cout << "after call checkRelation\n";
}
};
void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
cout << (c->father).use_count() << endl;
cout << (p->son).use_count() << endl;
p->CheckRelation();
}
void handleChildAndParentRef(const Parent& p, const Child& c)
{
auto cp = c.father.lock();
auto pc = p.son.lock();
if (cp.get() == &p && pc.get() == &c)
{
cout << "right relation" << endl;
}
else {
cout << "oop !!!\n";
}
}
// const xxx&: 减少拷贝次数
void handleChildAndParent(const ParentPtr& p, const ChildPtr& c)
{
auto cp = c->father.lock();
auto pc = p->son.lock();
if (cp == p && pc == c)
{
cout << "right relation\n";
}
else {
cout << "oop!!!\n";
}
}
int main()
{
testParentAndChild();
return 0;
}