当超出范围时,std :: bind会延长对象寿命,例如char数组,如果是,它将如何工作?

时间:2018-12-06 12:19:57

标签: c++ bind

我在范围内使用char arry并将其绑定到ThreadPool来执行任务,如下所示:

void test_thread_pool_arr(){

    auto lb_print = [](const char* str){
        info("print msg: %s, and i will sleep 2s", str);
        sleep(2);
    };

    ThreadPool tp(3);

    {
        char arr[10];
        memset(arr, '1', sizeof arr);
        arr[9] = '\0';

        tp.addTask(std::bind(lb_print, arr));
        tp.addTask(std::bind(lb_print, arr));
    } // leave scope so, arr should be invalid

    info("leave scope....");

    tp.exit(); // tell ThreadPool to join and then exit.
    tp.join();
    info("ending....");
}

输出如下:

2018/12/06-20:07:59 leave scope....
2018/12/06-20:07:59 print msg: 111111111, and i will sleep 2s
2018/12/06-20:07:59 print msg: 111111111, and i will sleep 2s
2018/12/06-20:08:01 ending....

我认为当arr离开它的作用域时,它应该被销毁,但是它的输出是正常的(任务函数lb_print只是获得了arr的地址,但是arr被销毁了)。

为什么? std :: bind是否会延长其寿命?如果是这样,它如何运作?

另一个带有类对象的示例:

class BindScope{
    public:
        BindScope(int i=0):n_(i){
            info("BindScope ctor %d", n_);
        }

        ~BindScope(){
            info("BindScope ~dtor %d", n_);
        }

        void print()const{
            info("do print %d", n_);
        }

    int n_;
};

void test_thread_pool_scope(){
    auto lb_print = [](const BindScope& bs){
        bs.print();
    };

    ThreadPool tp(3);

    {
        BindScope bs(4);
        tp.addTask(std::bind(lb_print, std::ref(bs)));
        tp.addTask(std::bind(lb_print, std::ref(bs)));
    }// bs be destoryed, but tp do task normally

    info("out scope");
    tp.exit();
    tp.join();
    info("ending.........");
}

输出如下:

2018/12/06-20:14:03 BindScope ctor 4
2018/12/06-20:14:03 BindScope ~dtor 4
2018/12/06-20:14:03 out scope
2018/12/06-20:14:03 do print 4
2018/12/06-20:14:03 do print 4
2018/12/06-20:14:03 ending.........

我们可以看到,当对象bs超出范围时,它会被破坏,但是ThreadPool会正常执行任务。为什么?

3 个答案:

答案 0 :(得分:2)

char数组的生存期没有延长,只是指向它的指针。

跟随悬空的指针,体验不确定的行为。

如果它是一个std数组,它将被复制。

未定义的行为可以做任何事情,包括“似乎正在工作”。

答案 1 :(得分:2)

实际上,这两种情况都会导致不确定的行为。您引用了悬空指针和悬空引用。任何事情都可能发生。在某些情况下,它可以“正常”运行。

答案 2 :(得分:0)

是的,它将引用悬挂指针。如下示例:

class BindScope{
    public:
        BindScope(int i=0):n_(i){
            arr_ = new char[n_+1];
            memset(arr_, '1', n_);
            arr_[n_] = '\0';

            info("BindScope ctor %d", n_);
        }

        ~BindScope(){
            info("BindScope ~dtor %d", n_);
            delete[] arr_;
        }

        void print()const{
            info("do print %d, %s", n_, arr_);
        }

    int n_;
    char* arr_; // test for outof scope
};

void test_thread_pool_scope(){
    auto lb_print = [](const BindScope& bs){
        bs.print();
    };

    ThreadPool tp(3);

    {
        BindScope bs(4);
        bs.print();  // test for normally output.

        tp.addTask(std::bind(lb_print, std::ref(bs)));
        tp.addTask(std::bind(lb_print, std::ref(bs)));
    }  //  bs got destoryed.

    info("out scope");
    tp.exit();
    tp.join();
    info("ending.........");
}

输出如下:

2018/12/06-20:36:43 BindScope ctor 4
2018/12/06-20:36:43 do print 4, 1111    // output normal
2018/12/06-20:36:43 BindScope ~dtor 4
2018/12/06-20:36:43 out scope
2018/12/06-20:36:43 do print 4,         // output wrong
2018/12/06-20:36:43 do print 4,         // output wrong
2018/12/06-20:36:43 ending.........

我们可以看到,bs的存储被还原后,ThreadPool任务lb_print的工作是错误的(outpur错误)。