вектор функций обратного вызова и привязки указателя функции

#c

#c

Вопрос:

Я новичок в std::function и пытаюсь реализовать функцию обратного вызова. В следующем коде «Callback_t» содержит функцию, которая содержит вектор вызываемой функции. Класс «other» — это вложенный класс внутри «SomeClass». Объект «SomeClass» содержит массив вложенных объектов класса «b». Конструктор «другого» класса присваивает указатель на функцию «fptr». Я помещаю эту функцию в вектор класса обратного вызова «Callback_t». Когда я запускаю этот код, я получаю ошибку сегментации при вызове первой функции в векторе. Я не могу понять, что не так с утверждением

 this->loc_ptr->set_of_cb.push_back(this->b[i].fptr);
  

Однако, если я заменю его на

 this->loc_ptr->set_of_cb.push_back(std::bind(amp;other::func, amp;(this->b[i])))
  

код работает отлично. Мне нужна помощь, чтобы понять, что не так с исходным утверждением.

 #include <functional>
#include <iostream>
#include <vector>
typedef std::function<void(void)> func_type;
class Callback_t {
public:
    std::vector<func_type> set_of_cb;
    void myCallback()
    {
        for (int i = 0; i < set_of_cb.size(); i   ){
                set_of_cb[i]();
        }
    }
};
class SomeClass;
class SomeClass {
private:
        Callback_t *loc_ptr;
        int a[10];
        class other{
                public:
                int id;
                SomeClass *loc;
                func_type fptr;
                other(){};
                other(SomeClass *loc, int id){
                        this->id = id;
                        this->loc =loc;
                        fptr = std::bind(amp;other::func,this);
                }
                void func(void){
                        this->loc->a[id] = loc->a[id] * id;
                        return;
                }
        };

public:
    other *b;

    //other b[10];
    SomeClass(Callback_t *a = nullptr){
        this->loc_ptr = a;
         this->b = new other[10];
        for(int i =0; i <10;i  ){
                this->a[i] = i;
                this->b[i] = other(this, i);
                this->loc_ptr->set_of_cb.push_back(this->b[i].fptr);
        }
    }
    void read(void){
        for(int i =0; i <10;i  ){
                std::cout<<a[i]<<std::endl;
        }
    }
};

int main()
{
    Callback_t *tmp;
    tmp = new Callback_t;
    SomeClass tmp1(tmp);
    tmp1.read();
    tmp->myCallback();
    tmp1.read();
    delete tmp;

}
  

Ответ №1:

             other(SomeClass *loc, int id){
                    this->id = id;
                    this->loc =loc;
                    fptr = std::bind(amp;other::func,this);
            }
  

Конструктор привязывается fptr к this , который является сконструированным объектом. Теперь обратите пристальное внимание:

   this->b[i] = other(this, i);
  

При этом выполняется следующая последовательность событий. Здесь происходит довольно много вещей, которые имеют решающее значение для этой тайны:

  1. Создается временный other объект, и его конструктор выполняет то, что он делает. Обратите внимание, что объект является временным, поэтому его конструктор в конечном итоге привязывает его fptr к временному объекту! Вы начинаете видеть проблему, но давайте все равно закроем цикл:

  2. Объекту присваивается this->b[i] . Фактически это копия.

  3. Исходные временные объекты уничтожаются.

Конечным результатом является то, что b[i] связанная функция в конечном итоге привязывается к временному объекту, который теперь уничтожается. Это приводит к неопределенному поведению и сбою.

И с вашей рабочей альтернативой:

 this->loc_ptr->set_of_cb.push_back(std::bind(amp;other::func, amp;(this->b[i])))
  

Вы привязываете std::function к действительному экземпляру объекта, в b[i] .

Вот и все.

Ответ №2:

Другой ответ объясняет, что происходит не так в вашем коде. Что осталось сделать, так это показать пример более канонического примера достижения того, к чему вы стремитесь (с небольшой помощью лямбда-функций). Конечно, std::bind() тоже работает, но это до C 11, и я думаю, что в настоящее время большинство предпочло бы сделать это так, как я делаю в своем коде ниже.

 #include <iostream>
#include <functional>
#include <vector>

class Foo {
public:
    void FooFun() {
        std::cout << "Foo::FooFun() called" << std::endl;
    }
};

class Bar {
public:
    void BarFun() {
        std::cout << "Bar::BarFun() called" << std::endl;
    }
};

using CallbackFun_t = std::function<void()>; 
using Callbacks_t = std::vector<CallbackFun_t>;

int main(int argc, const char * argv[]) {
    Foo foo{};
    Bar bar{};

    Callbacks_t callbacks
        { [amp;foo]{ foo.FooFun();} 
        , [amp;bar]{ bar.BarFun();}
        };

    for( autoamp; cb : callbacks ) {
        cb();
    }

    return 0;
}