最近在忙自己的研究生科研工作和尽量在不看源码的情况下写一个玩具版的muduo
(我已经看过陈硕的《Linux多线程服务端编程:使用muduo C++网络库》,相当于按自己的理解再写一遍),没太有时间写C++对象模型的后面部分,等组会开完后再继续写。
今天就写一下几天前看到的一个小技巧,也即标题:std::weak_ptr<void>
绑定到所有类型的std::shared_ptr
。
std::weak_ptr
我们知道weak_ptr目的是为了防止只使用std::shared_ptr导致的循环引用,从而导致内存泄漏。一个经典的例子如下:
#include <iostream>
#include <vector>
#include <memory>
#include <string>
class Child;
class Parent {
public:
Parent(const std::string& name)
: m_name(name),
m_children()
{}
~Parent();
void addChild(std::shared_ptr<Child>& child) {
m_children.push_back(child);
}
const std::string&
getName() const {
return m_name;
}
std::vector<std::shared_ptr<Child>>&
getChildren() {
return m_children;
}
private:
std::string m_name;
std::vector<std::shared_ptr<Child>> m_children; // Parent对象使用shared_ptr来持有Child对象
};
class Child {
public:
Child(const std::string& name, std::shared_ptr<Parent>& parent)
: m_name(name),
m_parent(parent)
{}
~Child() {
std::cout << m_name << "'s destruction" << std::endl;
}
void showParentName() const {
std::shared_ptr<Parent> parent = m_parent.lock();
if (parent) {
std::cout << m_name << "'s parent: " << parent->getName() << std::endl;
} else {
std::cout << m_name << "'s parent has destructed" << std::endl;
}
}
private:
std::string m_name;
std::weak_ptr<Parent> m_parent; // Child对象使用weak_ptr来引用Parent对象
};
Parent::~Parent() {
std::cout << m_name << "'s destruction" << std::endl;
}
void func() {
std::shared_ptr<Parent> parent = std::make_shared<Parent>("Parent01");
std::shared_ptr<Child> child = std::make_shared<Child>("Child01", parent);
parent->addChild(child);
child->showParentName();
}
int main() {
func();
}
// Output:
// Child01's parent: Parent01
// Parent01's destruction
// Child01's destruction
我们可以看到Parent
和Child
对象均正常析构了。
std::weak_ptr与其绑定的std::shared_ptr
在上面的代码中,如果有其他地方持有std::shared_ptr<Child>
,那么在Parent
析构时,被该std::share_ptr<Child>
持有的Child
对象不会析构,而且Child::showParentName
会正常识别出其Parent
对象已经被析构。这就是std::weak_ptr
能判断其绑定的std::shared_ptr
管理的对象是否已经析构。
但有一个问题,如果我只是用std::weak_ptr
来判断其绑定的std::shared_ptr
管理的对象是否已经析构,但其绑定的std::shared_ptr
管理的对象类型不一定怎么办?正如标题所言,std::weak_ptr<void>
可以绑定到所有类型的std::shared_ptr
,所以只要使用一个std::weak_ptr
即可。
我知道这个用法的来源是陈硕的muduo
网络库,在muduo
中,Channel
用于管理一个socket描述符的读、写、出错事件,并调用相应的回调,但有一个问题是Channel
类并不持有该socket描述符(只存有该socket描述符,但其生命期并不归Channel
管理),那如何判断Channel
对应的管理socket描述符的类是否已经析构呢(因为Channel
的读写出错回调往往是通过std::bind或者lambda包裹的socket描述符的持有者的private方法,如果持有者已经析构,再调用回调会导致段错误从而core dump)?muduo
就是再Channel
中使用std::weak_ptr<void>
。其有一个方法Channel::tie
,接受const std::shared_ptr<void>&
类型的参数,此参数要求传入持有socket描述符管理者对象的std::shared_ptr
。muduo
将此参数赋值给给std::weak_ptr<void>
对象,使其可以监控socket描述符管理者对象是否已经析构。部分代码如下:
// muduo/net/Channel.cc
void Channel::tie(const std::shared_ptr<void>& obj)
{
tie_ = obj; // std::weak_ptr<void> tie_
tied_ = true; // bool tied_
}
void Channel::handleEvent(T