Usando sinais/slots para evitar dependências circulares?
-
21-12-2019 - |
Pergunta
Eu tenho um pedaço deselegante de código C++ Qt onde main cria um item gráfico (o pai) que cria vários itens gráficos filhos.O filho e o pai devem chamar os métodos um do outro, ou seja,os pais precisam dizer aos filhos para fazerem algumas coisas (mover-se, mudar de cor, etc.), e os filhos sinalizarão aos pais para fazerem coisas com os outros filhos.Fazer com que ambos chamem os métodos um do outro está levando a um código circular feio e está se tornando frustrante evitar erros C2027.Faria algum sentido usar um sistema de sinal/slot personalizado como método de comunicação entre a criança e os pais?Ou devo continuar com meu design atual e apenas tentar resolver esses tipos de bugs?
Por exemplo, este código (meu design atual) gera C2027:
Janela principal:
#include "mainwindow.h"
#include "ui_mainwindow.h"
vector<Foo*> FooArray;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
int viewwidth = ui->graphicsView->width();
...
FooArray.push_back(new Foo(5, viewwidth, scene));
FooArray[0]->loadBars();
}
Foo:
#include "Foo.h" //includes bar.h
vector<int> ActiveList;
vector<Bar*> BarArray;
Foo::Foo(int id, int w, QGraphicsScene* scene)
{
this->id = id;
this->width = w;
this->scene = scene;
}
...
void Foo::loadBars()
{
...
BarArray.resize(nitems);
BarArray[k] = new Bar(id, k, this);
BarArray[k]->setPos(...);
scene->addItem(BarArray[k]);
...
}
void Foo::doSomething()
{
...
}
Bar:
#include "Bar.h" //forward declares Foo; isn't executed until Foo exists.
Bar::Bar(int setid, int num, Foo* foo)
{
this->s = setid;
this->n = num;
this->owner = foo;
}
...
void Bar::someFunction()
{
...
owner->doSomething();
...
}
Solução
Sim, é exatamente para isso que servem os sinais e slots.
Agora você tem Bar::someFunction()
que chama Foo::doSomething()
O que você pode fazer é:
- Declarar
doSomething()
como um slot - Faça com que Bar emita um sinal
somethingHappened()
- Ao criar os filhos, conecte seus
somethingHappened()
sinal para os paisdoSomething()
slot
Este é um exemplo de injeção de dependência e permite trocar a classe pai por outra coisa no futuro sem precisar alterar Bar
de forma alguma.