C++11 智能指针 weak_ptr

C++11 智能指针 weak_ptr

Written on 2023-01-17

个人学习智能指针记录合集:

std::weak_ptr称为弱引用智能指针,它不具有对对象管理的所有权,因此没有权限对对象进行释放也不能使用操作符*->进行解引用

weak_ptr可以看做是shared_ptr的助手,但不管理shared_ptr内部的对象,称为引用shared_ptr管理的对象。
因此,构造weak_ptr不会引起引用计数的增加,析构weak_ptr也不会引起引用计数的减少。

weak_ptr的主要是用于作为一个观察者,在我们不想额外构造一个shared_ptr,但又想知道管理的对象是否被析构时,用来观测shared_ptr管理的对象是否被析构了。

循环依赖导致的内存泄漏

循环依赖,会导致shared_ptr不能自动的释放对象,造成内存泄漏。

#include <iostream>
#include <memory>
using namespace std;

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    
    shared_ptr<Person> friendSPtr; 
private:
    int m_age = 0; 
}; 

int main(){
    shared_ptr<Person> p1 = make_shared<Person>(18);
    shared_ptr<Person> p2 = make_shared<Person>(20);
    p1->friendSPtr = p2;
    p2->friendSPtr = p1;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
Constructor: person's age = 20
p1 use count = 2
p2 use count = 2

Before main return

**/

这里有p1管理的对象age = 18的”朋友“是age = 20对象,同时p2管理的对象age = 20的”朋友“是age = 18对象,这形成了循环依赖;p1p2管理的对象的引用计数都是 2。

依输出可见,p1p2管理的对象并没有被析构,因为并没有析构函数中的打印,这有内存泄漏。

这是因为在程序运行结束前,即使离开了p1的作用域,使得p1被销毁,但是此时原p1管理的对象的引用计数仅减去 1,并没有变为 0,另外 1 个在p2中由p2friendSPtr管理,因此原p1管理的对象并没有被销毁;p2管理的对象同理。

循环依赖导致p1p2管理的对象的引用计数锁死,不能降为 0,也就不能自动释放。
因此需要一个不拥有管理权的智能指针来代替,即不会导致引用计数增加的智能指针来代替。

weak_ptr就是一个不会导致引用计数增加的智能指针,使用weak_ptr可以解决循环依赖。

初始化

weak_ptr初始化为nullptr,或需要shared_ptr或另一个weak_ptr进行初始化。

#include <iostream>
#include <memory>
using namespace std;

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    
    //shared_ptr<Person> friendSPtr; 
private:
    int m_age = 0; 
}; 

int main(){
    shared_ptr<Person> sPtr {make_shared<Person>(18)};
    cout << "sPtr use count = " << sPtr.use_count() << endl;
    
    weak_ptr<Person> wPtr1; // 创建空weak_ptr
    cout << "wPtr1 use count = " << wPtr1.use_count() << endl;
    weak_ptr<Person> wPtr2(wPtr1); // 根据wPtr1创建wPtr2
    cout << "wPtr2 use count = " << wPtr2.use_count() << endl;

    weak_ptr<Person> wPtr3(sPtr); // 根据sPtr创建wPtr3
    cout << "sPtr use count = " << sPtr.use_count() << endl;
    weak_ptr<Person> wPtr4 = sPtr; // 根据sPtr创建wPtr4
    cout << "sPtr use count = " << sPtr.use_count() << endl;
    weak_ptr<Person> wPtr5(wPtr3); // 根据wPtr3创建wPtr5
    cout << "sPtr use count = " << sPtr.use_count() << endl;
    weak_ptr<Person> wPtr6 = wPtr5; // 通过复制构造函数创建wPtr6
    cout << "sPtr use count = " << sPtr.use_count() << endl;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** cpp
Constructor: person's age = 18
sPtr use count = 1
wPtr1 use count = 0
wPtr2 use count = 0
sPtr use count = 1
sPtr use count = 1
sPtr use count = 1
sPtr use count = 1

Before main return
Destructor: person's age = 18

**/

依输出可见,weak_ptr是没有对sPtr所管理的对象的管理所有权,这个例子中,sPtr所管理的对象的引用计数始终只有 1。

成员函数 .use_count(),获取引用的对象被shared_ptr管理的数量

返回被引用的对象,被shared_ptr管理的数量。
weak_ptr不会引起引用计数的增加或减少。

成员函数 .expired(),检查被引用的对象是否已释放

bool expired() const noexcept;

若被引用对象已被释放或者weak_ptr是否为nullptr,返回值则为true,否则为false
等价于.use_count() == 0

int main(){
    shared_ptr<Person> sPtr {make_shared<Person>(18)};
    weak_ptr<Person> wPtr(sPtr);
    
    cout << (wPtr.expired() ? "Destruct" : "Still alive") << endl;
    cout << (wPtr.use_count() == 0 ? "Destruct" : "Still alive") << endl;
    
    sPtr.reset();
    cout << (wPtr.expired() ? "Destruct" : "Still alive") << endl;
    cout << (wPtr.use_count() == 0 ? "Destruct" : "Still alive") << endl;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
Still alive
Still alive
Destructor: person's age = 18
Destruct
Destruct

Before main return

**/

成员函数 .lock(),转为shared_ptr

当你想要使用weak_ptr来访问管理的对象时,必须将其临时的转为shared_ptr,使用完记得shared_ptr.reset()

std::shared_ptr<T> lock() const noexcept;

如果其管理的对象已经被释放了,.lock()会返回nullptr,也可以用以判断其管理的对象是否被释放。

int main(){
    shared_ptr<Person> sPtr1 {make_shared<Person>(18)};
    weak_ptr<Person> wPtr(sPtr1);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    shared_ptr<Person> sPtr2 = wPtr.lock();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    sPtr2.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    sPtr1.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    shared_ptr<Person> sPtr3 = wPtr.lock();
    if(sPtr3 == nullptr){
        cout << "Destruct" << endl;
    }else{
        cout << "Still alive" << endl;
    }
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
sPtr1 use count = 1
sPtr1 use count = 2
sPtr1 use count = 1
Destructor: person's age = 18
sPtr1 use count = 0
Destruct

Before main return

**/

第一次wPtr.lock(),使得sPtr1管理的对象的引用计数增加 1,变为 2;
.reset()sPtr1管理的对象的引用计数减少 1,变为 1。

之后,sPtr1管理的对象的引用计数变为 0,析构管理的对象,第二次wPtr.lock()的返回值也就是nullptr了。

成员函数 .reset(),解除对被引用的对象的引用

weak_ptr.reset()方法,是解除对被引用的对象的引用。

int main(){
    shared_ptr<Person> sPtr1 {make_shared<Person>(18)};
    weak_ptr<Person> wPtr(sPtr1);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    wPtr.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << (wPtr.expired() ? "wPtr == nullptr" : "wPtr != nullptr") << endl;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
sPtr1 use count = 1
sPtr1 use count = 1
wPtr == nullptr

Before main return
Destructor: person's age = 18

**/

解决循环依赖

就前面一个例子而言,只需将shared_ptr类型改为weak_ptr即可解决。

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    
    //shared_ptr<Person> friendSPtr; 
    weak_ptr<Person> friendSPtr;
private:
    int m_age = 0;
}; 

int main(){
    shared_ptr<Person> p1 = make_shared<Person>(18);
    shared_ptr<Person> p2 = make_shared<Person>(20);
    p1->friendSPtr = p2;
    p2->friendSPtr = p1;
    
    cout << "p1 use count = " << p1.use_count() << endl;
    cout << "p2 use count = " << p2.use_count() << endl;
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
Constructor: person's age = 20
p1 use count = 1
p2 use count = 1

Before main return
Destructor: person's age = 20
Destructor: person's age = 18

**/

可见程序运行结束前,p1p2管理的对象成功析构。

还有一种情况是两个或多个对象相互依赖,不要让某个双方都是shared_ptr类型的,只需将其中一个改为weak_ptr类型的即可,或者都为weak_ptr类型的。

class Animal;

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    
    // shared_ptr<Animal> pet; // 宠物 
    weak_ptr<Animal> pet; // 宠物 
private:
    int m_age = 0; 
}; 

class Animal{
public: 
    Animal(){ cout << "Constructor: animal's age = " << m_age << endl; }
    Animal(int age) : m_age(age){ cout << "Constructor: animal's age = " << m_age << endl; }
    ~Animal(){ cout << "Destructor: animal's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    
    shared_ptr<Person> owner; // 主人 
private:
    int m_age = 0; 
}; 

int main(){
    shared_ptr<Person> owner = make_shared<Person>(18);
    shared_ptr<Animal> pet = make_shared<Animal>(1);
    owner->pet = pet;
    pet->owner = owner;
    
    cout << "owner use count = " << owner.use_count() << endl;
    cout << "pet use count = " << pet.use_count() << endl;
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
Constructor: animal's age = 1
owner use count = 2
pet use count = 1

Before main return
Destructor: animal's age = 1
Destructor: person's age = 18

**/

程序运行结束前,共享智能指针pet被销毁,其管理的对象的引用计数减少 1,变为 0,析构其管理的对象;
pet管理的对象中的成员变量owner随之销毁,使得共享智能指针owner管理的对象的引用计数减少 1,变为 1;
之后,开始共享智能指针owner被销毁,其管理的对象的引用计数减少 1,变为 0,其管理的对象随之被析构。

Person类中,成员变量pet类型为shared_ptr<Animal>,程序运行结束前,ownerpet管理的对象都不会被析构。

返回管理thisshared_ptr

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    shared_ptr<Person> getThisSharedPtr(){ shared_ptr<Person>(this); } 
private:
    int m_age = 0; 
}; 


int main(){
    shared_ptr<Person> p1 = make_shared<Person>(18);
    cout << "p1 use count = " << p1.use_count() << endl;
    
    shared_ptr<Person> p2 = p1->getThisSharedPtr();
    cout << "p1 use count = " << p1.use_count() << endl;
    cout << "p2 use count = " << p2.use_count() << endl;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
p1 use count = 1
Destructor: person's age = 18
    // -> 程序异常,也并没有完整打印
**/

个人理解,这个是因为使用一个原始指针初始化多个shared_ptr造成了异常;
p1实际上是用age = 18这个对象的原始指针初始化的,执行p1->getThisSharedPtr(),内部使用this初始化了一个shared_ptr并返回,this也可视为同一个原始指针,因此是使用了同一个原始指针初始化多个shared_ptr造成了异常。

C++11 中的一个模板类std::enable_shared_from_this<T>的一个方法shared_from_this(),可以返回一个管理thisshared_ptr

在调用enable_shared_from_this类的shared_from_this()方法之前,必须要先初始化管理某个类的shared_ptr,即管理某个类的shared_ptr不能为nullptr,否则无法返回一个有效的管理thisshared_ptr

class Person : public enable_shared_from_this<Person>{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
    shared_ptr<Person> getThisSharedPtr(){ return shared_from_this(); }
private:
    int m_age = 0; 
}; 


int main(){
    shared_ptr<Person> p1 = make_shared<Person>(18);
    cout << "p1 use count = " << p1.use_count() << endl;
    
    shared_ptr<Person> p2 = p1->getThisSharedPtr();
    cout << "p1 use count = " << p1.use_count() << endl;
    cout << "p2 use count = " << p2.use_count() << endl;
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 输出:
Constructor: person's age = 18
p1 use count = 1
p1 use count = 2
p2 use count = 2

Before main return
Destructor: person's age = 18
    // -> 程序无异常,完整打印
**/
posted @ 2023-01-17 23:18  Champrin  阅读(92)  评论(0编辑  收藏  举报