之前对std::bind使用时,往往在类CA中std::bind(&CA::func, this),其中的func是类CA的一个成员函数,后面带入的this往往是无脑的写入其中。
muduo中TcpClient析构函数源码如下:
TcpClient::~TcpClient()
{
LOG_INFO << "TcpClient::~TcpClient[" << name_
<< "] - connector " << get_pointer(connector_);
TcpConnectionPtr conn;
bool unique = false;
{
MutexLockGuard lock(mutex_);
unique = connection_.unique();
conn = connection_;
}
if (conn)
{
assert(loop_ == conn->getLoop());
// FIXME: not 100% safe, if we are in different thread
CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);
loop_->runInLoop(
std::bind(&TcpConnection::setCloseCallback, conn, cb));
if (unique)
{
conn->forceClose();
}
}
else
{
connector_->stop();
// FIXME: HACK
loop_->runAfter(1, std::bind(&detail::removeConnector, connector_));
}
}
今天在看muduo的TcpClient::~TcpClient类时,发现CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);并没有将类自身的指针传入std::bind中,而是将外面一个对象(EventLoop类)的函数及指针传入其中。
这样作者就很好的避免了,对象析构之后,外面某个调用还来操作析构对象原来操作的内存,导致因为操作非法内存系统崩溃。
进一步思考:为什么TcpClient的TcpConnection没有作为成员变量,生命周期随着TcpClient的生命周期而生死。其中析构函数的loop_->runInLoop(std::bind(&TcpConnection::setCloseCallback, conn, cb));这一句可以说明问题。TcpClient析构之后,TcpConnection还有后续的处理。
(1)对于类的成员函数来讲,需要采用std::bind(&类名::函数名, 类对象的指针,函数参数列表)的方式指定具体的函数对象;如,上述loop_->runInLoop(std::bind(&TcpConnection::setCloseCallback, conn, cb));
(2)对于非类的成员函数来讲,只需要采用std::bind(&函数名, 函数参数列表)的方式即可;如:上述CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);。// 此处的detail为namespace
由此想到之前无脑写入this,没有考虑对象析构之后的相关处理。因此在函数对象要考虑一下执行该函数对象时,对象是否还有效。