Do wywołań zwrotnych, powiadamiania itd. powstał mechanizm sygnałów i slotów. Użycie jest bardzo proste. W klasie "informującej" o zdarzeniu, w nagłówku, definiujemy sygnały, za pomocą których przekazywane będą powiadomienia. Podobnie jak zakres dostępu do składowych - tyle, że zamiast public:, protected:, czy private: używamy signals: - i tak otrzymujemy (przykładowo).
signals:
void connected();
void disconnected();
void connectionError();
void authorizationRequired();Natomiast w klasie "informowanej" (to może być ta sama klasa, o łączeniu sygnałów ze slotami dalej) definiujemy sloty. Sposób jest podobny, tyle że konieczne jest też zawarcie specyfikatora dostępu, skąd mamy np.:
public slots:
void onConnected();
void onDisconnected();
protected slots:
void onAuthorized();
private slots:
void onLoopEnded();Ważna uwaga: każdy slot traktowany jest jak funkcja wirtualna - nawet jeśli go nie używamy, musi zostać zaimplementowany, inaczej pojawi się błąd linkera.
Jak połączyć sygnały i sloty? Sposób jest prostszy, niż by się wydawało - używamy metody statycznej QObject::connect. W podstawowej składni takowe połączenie ma postać: QObject::connect(sender, SIGNAL(connected()), receiver, SLOT(onConnected())); Dużo nawiasów, makra - można to zignorować, grunt, że będzie działać. Dla oszczędzenia pisania istnieje niestatyczna metoda connect, która de facto wywołuje QObject::connect(sender, signal, this, slot);
Miało być o przepełnieniu stosu, a rozpisałem się o podstawach użycia sygnałów i slotów. Otóż, sprawa jest ściśle powiązana. Poprzez instrukcję emit connected(); wywoływane są wszystkie metody połączone z tym sygnałem. Co ważne - wywołanie jest w miejscu użycia słowa emit - czyli w efekcie nakładają się na stos. Wyobraźmy sobie proste połączenie - QTcpSocket::connected() połączone do onConnected() oraz connected() również połączone do onConnected(). W connected() wywołujemy (emitujemy) onConnected() - typowy błąd, nieskończona rekurencja, przepełnienie stosu.
Inna sytuacja, gdy używamy sygnałów i slotów jako wyjątków, przekazujących informację coraz wyżej. Wszystkie parametry przekazywane są przez kopiowanie(!), więc stos buduje się coraz wyższy w trakcie "powrotu" w górę. W efekcie przy kilku-kilkunastu warstwach osiągamy znane i nielubiane stack overflow. Może na PC wyda się to abstrakcją (x86 zakłada domyślnie 1MB stosu), ale na Symbianie, gdzie stos jest liczony w dziesiątkach kB, uważanie na każdy emit jest jak najbardziej właściwe.
Oczywiście nie chcę zniechęcać do używania tego mechanizmu Qt - moim zdaniem to najlepszy mechanizm delegatów, jaki powstał w dowolnym natywnym API (niektórymi cechami przebija nawet eventy/delegaty z .NET), jeśli będziecie pamiętać o stosie i nie przesadzać z "łańcuszkami emitów" czy sygnalizowaniem rekurencyjnym, wszystko powinno działać dobrze.
Brak komentarzy:
Prześlij komentarz