muduo库的Stock例子

        今天在学习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 可在上一篇文件中找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值