今天在学习muduo库,照着文中敲代码,结果编译出错了,借这个机会学习了一下 std::bind() 函数(文中都是 boost::,这里我都用std:: 替代),因为目前工作到现在,在实际编码中还没有用过这个函数。编译错误很简单,就是函数调用没匹配,如:
完全没看懂。于是看了一下参考:std::bind,参数传递方式有两种,一种是直接传参数,一种是占位符。点位符的意思是在函数对象调用的时候,其实参对应哪个形参,根据占位符决定。直接看参考里例子:
// bind example
#include <iostream> // std::cout
#include <functional> // std::bind
// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}
struct MyPair {
double a,b;
double multiply() {return a*b;}
};
int main () {
using namespace std::placeholders; // adds visibility of _1, _2, _3,...
// binding functions:
auto fn_five = std::bind (my_divide,10,2); // 直接传递参数,10和2作为fn_five的实际参数
std::cout << fn_five() << '\n'; // 5
auto fn_half = std::bind (my_divide,_1,2); // my_divide的第一个参数用点位符,则实际调用时,10作为第一个参数
std::cout << fn_half(10) << '\n'; // 5
auto fn_invert = std::bind (my_divide,_2,_1); // my_divide的第二个参数将由实际调用时的第一个参数传入,第一个参数将由实际调用时的第二个参数传入
std::cout << fn_invert(10,2) << '\n'; // 0.2
auto fn_rounding = std::bind<int> (my_divide,_1,_2); // my_divide 两个参数根据实际调用时的正常顺序传入
std::cout << fn_rounding(10,3) << '\n'; // 3
MyPair ten_two {10,2};
// binding members:
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
std::cout << bound_member_fn(ten_two) << '\n'; // 20
auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
std::cout << bound_member_data() << '\n'; // 10
return 0;
}
我编译出错的原因为是把 static void weakDeleteCallback(StockFactory *wkFactory, Stock *stock) 中的 static 去掉了,没有static声明则这个函数就相当于全局函数了。这样在绑定的时候这个函数就是成员函数了,而绑定成员函数时,std::bind()的第二个参数应该是这个成员函数所在的对象,所以如果是成员,则需要这样:
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, this, this, std::placeholders::_1));
因为c++的成员函数指针调用必须要用对象,第一个 this 就是这个对象,第二个this是传给 weakDeleteCallback 的第一个参数。
其实文中对 pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, this, std::placeholders::_1)); 中的 bind() 函数的第二个参数进行了 3 种类型的说明,分别列举如下:
1,shared_from_this ,是线程安全,只是会延长对象的生命期
stockFactory_shared_from_this.h
#include <memory>
#include <string>
#include <map>
#include "guard.h"
class Stock
{
public:
Stock(const std::string &key): mKey(key)
{
}
std::string key()
{
return mKey;
}
private:
std::string mKey;
};
class StockFactory: public std::enable_shared_from_this<StockFactory>
{
public:
StockFactory()
{
}
~StockFactory()
{
printf("StockFactory destruct\n");
}
std::shared_ptr<Stock> get(const std::string &key)
{
std::shared_ptr<Stock> pStock;
CGuard guard(mMtx);
std::weak_ptr<Stock> &wStock = mVector[key];
pStock = wStock.lock();
if(!pStock)
{
// 这里用 shared_from_this() 传给 bind 绑定后返回的函数里, 将延长对象的生命期
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, shared_from_this(), std::placeholders::_1));
wStock = pStock; // wStock 是引用,这里是将新创建的 shared_ptr 插入 map 中
}
return pStock;
}
private:
static void weakDeleteCallback(std::shared_ptr<StockFactory> wkFactory, Stock *stock)
{
printf("Stock object destruct\n");
if(wkFactory)
{
wkFactory->removeStock(stock);
}
else
{
printf("StockFactory is not exist\n");
}
delete stock;
}
void removeStock(Stock *stock)
{
if(stock)
{
CGuard guard(mMtx);
mVector.erase(stock->key());
}
}
private:
CMutex mMtx;
std::map<std::string, std::weak_ptr<Stock>> mVector;
};
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, shared_from_this(), std::placeholders::_1));
2,this,是有问题的,程序会crash
stockFactory_this.h
#include <memory>
#include <string>
#include <map>
#include "guard.h"
class Stock
{
public:
Stock(const std::string &key): mKey(key)
{
}
std::string key()
{
return mKey;
}
private:
std::string mKey;
};
class StockFactory: public std::enable_shared_from_this<StockFactory>
{
public:
StockFactory()
{
}
~StockFactory()
{
printf("StockFactory destruct\n");
}
std::shared_ptr<Stock> get(const std::string &key)
{
std::shared_ptr<Stock> pStock;
CGuard guard(mMtx);
std::weak_ptr<Stock> &wStock = mVector[key];
pStock = wStock.lock();
if(!pStock)
{
// 这里直接将 this 指针绑定到的 bind 返回的函数里对象里,存在危险,因为可能 StockFactory 析构了,在函数对象里还在用 StockFactory 对象
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, this, std::placeholders::_1));
wStock = pStock; // wStock 是引用,这里是将新创建的 shared_ptr 插入 map 中
}
return pStock;
}
private:
static void weakDeleteCallback(StockFactory *wkFactory, Stock *stock)
{
printf("Stock object destruct\n");
if(wkFactory) //这里无法判断对象是还在存在,因为它只是一个地址(只要不为0就能使用,但不一定是 StockFactory 对象)
{
wkFactory->removeStock(stock);
}
else
{
printf("StockFactory is not exist\n");
}
delete stock;
}
void removeStock(Stock *stock)
{
if(stock)
{
CGuard guard(mMtx);
mVector.erase(stock->key());
}
}
private:
CMutex mMtx;
std::map<std::string, std::weak_ptr<Stock>> mVector;
};
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, this, std::placeholders::_1));
3,weak_ptr,这个才是正确的
stockFactory_weak_ptr.h
#include <memory>
#include <string>
#include <map>
#include "guard.h"
class Stock
{
public:
Stock(const std::string &key): mKey(key)
{
}
std::string key()
{
return mKey;
}
private:
std::string mKey;
};
class StockFactory: public std::enable_shared_from_this<StockFactory>
{
public:
StockFactory()
{
}
~StockFactory()
{
printf("StockFactory destruct\n");
}
std::shared_ptr<Stock> get(const std::string &key)
{
std::shared_ptr<Stock> pStock;
CGuard guard(mMtx);
std::weak_ptr<Stock> &wStock = mVector[key];
pStock = wStock.lock();
if(!pStock)
{
// 将 shared_from_this() 转换为 weak_ptr, 这样就不会延长对象的生命期
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, std::weak_ptr<StockFactory>(shared_from_this()), std::placeholders::_1));
wStock = pStock; // wStock 是引用,这里是将新创建的 shared_ptr 插入 map 中
}
return pStock;
}
private:
static void weakDeleteCallback(const std::weak_ptr<StockFactory> &wkFactory, Stock *stock)
{
printf("Stock object destruct\n");
//尝试提升为 shared_ptr
std::shared_ptr<StockFactory> factory(wkFactory.lock());
if(factory) //如果 factory 还在,则删除对应 stock
{
factory->removeStock(stock);
}
else
{
printf("StockFactory is not exist\n");
}
delete stock;
}
void removeStock(Stock *stock)
{
if(stock)
{
CGuard guard(mMtx);
mVector.erase(stock->key());
}
}
private:
CMutex mMtx;
std::map<std::string, std::weak_ptr<Stock>> mVector;
};
pStock.reset(new Stock(key), std::bind(&StockFactory::weakDeleteCallback, std::weak_ptr<StockFactory>(shared_from_this()), std::placeholders::_1));
我们来看 main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stockFactory_weak_ptr.h"
// 这里示例 Stock 对象先析构,StockFactory 后析构
void testLongLifeFactory()
{
std::shared_ptr<StockFactory> factory(new StockFactory);
{
std::shared_ptr<Stock> stock = factory->get("NYSE:IBM");
std::shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
assert(stock == stock2);
// 出了这个作用域 Stock 析构
}
// 出了这个函数 StockFactory 析构
}
// 这里示例 StockFactory 对象先析构,Stock 后析构
void testShortLifeFactory()
{
std::shared_ptr<Stock> stock;
{
std::shared_ptr<StockFactory> factory(new StockFactory);
stock = factory->get("NYSE:IBM");
std::shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
assert(stock == stock2);
// 出了这个作用域 StockFactory 析构
}
// 出了这个函数 stock 析构
}
int main()
{
// testLongLifeFactory();
testShortLifeFactory();
printf("main exit\n");
return 0;
}
对于两个函数 testLongLifeFactory() ,上面 3 个头文件那样使用都是没有问题的,因为 testLongLifeFactory() 函数里 StockFactory 对象于 stock 析构之后才析构,在函数 static void weakDeleteCallback(const std::weak_ptr<StockFactory> &wkFactory, Stock *stock) 里使用是没有问题的。关键是函数 testShortLifeFactory(),因为在函数里 StockFactory 对象会先于 Stock 对象析构,这样函数 static void weakDeleteCallback(StockFactory *wkFactory, Stock *stock) 里可能就存在风险。
所以我们只看testShortLifeFactory() 函数
main.cpp 里 #include "stockFactory_weak_ptr.h" 的执行结果:
可以看到 StockFactory 先析构,所以函数里对 weak_ptr 进行提升后, 先判断再操作:
当 #include "stockFactory_shared_from_this.h" 时执行结果:
可以看到 Stock 对象先析构了,而只看函数应该是 StockFactory 对象先析构才对:
void testShortLifeFactory()
{
std::shared_ptr<Stock> stock;
{
std::shared_ptr<StockFactory> factory(new StockFactory);
stock = factory->get("NYSE:IBM");
std::shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
assert(stock == stock2);
// 出了这个作用域 StockFactory 析构
}
// 出了这个函数 stock 析构
}
这种结果就是因为把 this 的 shared_ptr 指针传递给了 bind() 返回的 function 对象,所以即使出了创建 factory的作用域还是没有释放。这就是文中提到的用 shared_ptr 会延长对象生命期。
当 #include "stockFactory_this.h"
用 this 的话,static void weakDeleteCallback(StockFactory *wkFactory, Stock *stock) 实现稍微有点区别:
static void weakDeleteCallback(StockFactory *wkFactory, Stock *stock)
{
printf("Stock object destruct\n");
if(wkFactory) //这里无法判断对象是还在存在,因为它只是一个地址(只要不为0就能使用,但不一定是 StockFactory 对象)
{
wkFactory->removeStock(stock);
}
else
{
printf("StockFactory is not exist\n");
}
delete stock;
}
第一个参数就是 StockFactory 类型的指针,但在这里是无法判断指针是否有效的。
GDB 跟踪可以看到虽然入参是 wkFactory=0x60a010,但这个地址在这一步应该是被释放了。文中主要是讲 weak_ptr 的使用,这个 weak_ptr 能解决两个对象相互引用的问题。
代码中用到的 mutex.h, guard.h 可在上一篇文件中找到。