#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);
При этом выполняется следующая последовательность событий. Здесь происходит довольно много вещей, которые имеют решающее значение для этой тайны:
-
Создается временный
other
объект, и его конструктор выполняет то, что он делает. Обратите внимание, что объект является временным, поэтому его конструктор в конечном итоге привязывает егоfptr
к временному объекту! Вы начинаете видеть проблему, но давайте все равно закроем цикл: -
Объекту присваивается
this->b[i]
. Фактически это копия. -
Исходные временные объекты уничтожаются.
Конечным результатом является то, что 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;
}