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
对象,这形成了循环依赖;p1
和p2
管理的对象的引用计数都是 2。
依输出可见,p1
和p2
管理的对象并没有被析构,因为并没有析构函数中的打印,这有内存泄漏。
这是因为在程序运行结束前,即使离开了p1
的作用域,使得p1
被销毁,但是此时原p1
管理的对象的引用计数仅减去 1,并没有变为 0,另外 1 个在p2
中由p2
的friendSPtr
管理,因此原p1
管理的对象并没有被销毁;p2
管理的对象同理。
循环依赖导致p1
和p2
管理的对象的引用计数锁死,不能降为 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
**/
可见程序运行结束前,p1
和p2
管理的对象成功析构。
还有一种情况是两个或多个对象相互依赖,不要让某个双方都是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>
,程序运行结束前,owner
和pet
管理的对象都不会被析构。
返回管理this
的shared_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()
,可以返回一个管理this
的shared_ptr
。
在调用enable_shared_from_this
类的shared_from_this()
方法之前,必须要先初始化管理某个类的shared_ptr
,即管理某个类的shared_ptr
不能为nullptr
,否则无法返回一个有效的管理this
的shared_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
// -> 程序无异常,完整打印
**/