c++ Event klasy pochodnej

0

krotko mowiac chcialbym utworzyc eventy na zasadzie biblioteki qt. W qt jest mozliwosc podmiany funkcji poprzez dziedziczenie klasy, gdzie w klasie pochodnej po prostu wykonuje sie kod zaraz po wywolaniu funkcji bazowej (rodzica).
moze na przykladzie podam

class Base
{
	public:
		void Event_Something() { cout << "Method from class 'Base' running..." << endl; }
};

class Child : public Base
{
	public:
		void Event_Something() { cout << "Hello I'm your child :)" << endl; }
};

jezeli wywolana by zostala funkcja pochodna klasy 'Child' to wypisaloby mi tylko "Hello I'm your child :)". chcialbym aby gdy funkcja bazowa zostala wywolana, wywolala tez funkcje pochodną. tak jak w bibliotece qt, chce aby calosc odbywala sie niejawnie.

0

To co piszesz nie jest mechanizmem Qt tylko częścią samego języka jakim jest C++. Mowa o polimorfizmie i dziedziczeniu. Żeby to działało trzeba użyć słowa kluczowego virtual w klasie bazowej. Jeśli pod wskaźnikiem typu Base będzie obiekt typu Child to wtedy będzie uruchomiona metoda zdefiniowana w klasie Child.

0

Nie wiem czy Cię dobrze zrozumiałem, ale sprawdz kod:

#include <iostream>
using namespace std;

class base
{
public:
	void Event() { cout << "To jest baza... "; }
};

class child : public base
{
public:
	void Event() { base::Event(); cout << "\nTo jest dziecko..."; }
};

class grandChild : public child
{
public:
	void Event() { child::Event(); cout << "\nTo jest wnuk..."; }
};

int main()
{
	grandChild c;

	c.Event();

	getchar();
	return 0;
}

Wydruk:

To jest baza...
To jest dziecko...
To jest wnuk...

0

pudło MJay:

#include <iostream>
using namespace std;
 
class base
{
public:
        virtual void Event() { cout << "To jest baza... "; }
        virtual ~base() {}
};
 
class child : public base
{
public:
        virtual void Event() { base::Event(); cout << "\nTo jest dziecko..."; }
};
 
class grandChild : public child
{
public:
        virtual void Event() { child::Event(); cout << "\nTo jest wnuk..."; }
};
 
int main()
{
        base* c = new grandChild();
        c->Event();
        delete c;
        c = new child();
        c->Event();
        delete c;
 
        getchar();
        return 0;
}
0

virtual daje Ci tylko możliwość polimorfizmu. A to jest niepotrzebne, bo dla klasy base ma wykonać funkcję base, dla klasy child ma wykonać event z klasy base i event z klasy child, analogicznie dla wnuka. Słówko virtual powoduje tylko niepotrzebne zwiększenie obiektu jak i czas wywołania funkcji. Moim zdaniem zastosowanie polimorfizmu w tym przypadku jest nieuzasadnione.

0
MJay napisał(a)

virtual daje Ci tylko możliwość polimorfizmu. A to jest niepotrzebne, bo dla klasy base ma wykonać funkcję base, dla klasy child ma wykonać event z klasy base i event z klasy child, analogicznie dla wnuka. Słówko virtual powoduje tylko niepotrzebne zwiększenie obiektu jak i czas wywołania funkcji. Moim zdaniem zastosowanie polimorfizmu w tym przypadku jest nieuzasadnione.

virtual jest tutaj jak najbardziej uzasadnione. Wprawdzie post wątkotwórcy nie jest super precyzyjny i widać że wątkotwórca jeszcze nie wszytko łapie to jednak stwierdzenie że można zastąpić akcję inną, która dziedziczy pozwala na odpalenie innej metody. W takim przypadku trzeba skorzystać z polimorfizmu i virtual. W Twoim przypadku zawsze będzie wywoływana metoda z bazowej a celem jest nadpisanie tej metody by odpalała się inna.

0

poza tym przy aplikacji zdarzeniowej zdarzenie wywoływane jest tak rzadko, że czas na wykonanie jest pomijalny. Co innego, gdybyś tą metodę wykonywał milion razy na sekunde.

0

gdzie w klasie pochodnej po prostu wykonuje sie kod zaraz po wywolaniu funkcji bazowej (rodzica).

Z tego wywnioskowalem, ze maja sie odpalic obie funkcje.

jezeli wywolana by zostala funkcja pochodna klasy 'Child' to wypisaloby mi tylko "Hello I'm your child ". chcialbym aby gdy funkcja bazowa zostala wywolana, wywolala tez funkcje pochodną. tak jak w bibliotece qt, chce aby calosc odbywala sie niejawnie.

Ale tu jest napisane zupelnie co innego. W tym miejscu rzeczywiscie chodzi o polimorfizm

0
MJay napisał(a)

gdzie w klasie pochodnej po prostu wykonuje sie kod zaraz po wywolaniu funkcji bazowej (rodzica).

Z tego wywnioskowalem, ze maja sie odpalic obie funkcje.

jezeli wywolana by zostala funkcja pochodna klasy 'Child' to wypisaloby mi tylko "Hello I'm your child ". chcialbym aby gdy funkcja bazowa zostala wywolana, wywolala tez funkcje pochodną. tak jak w bibliotece qt, chce aby calosc odbywala sie niejawnie.

Ale tu jest napisane zupelnie co innego. W tym miejscu rzeczywiscie chodzi o polimorfizm

krwq i Ty napisaliście kod identyczny - z tą różnicą że jeden jest z virtual i drugi nie. Tak więc tutaj virtual jest niezbędny by zastosować to w ramach operacji podmieniania wskaźnika.

0
krwq napisał(a)

poza tym przy aplikacji zdarzeniowej zdarzenie wywoływane jest tak rzadko, że czas na wykonanie jest pomijalny. Co innego, gdybyś tą metodę wykonywał milion razy na sekunde.

Narzut spowodowany polimorfizmem jest narzutem obecnie pomijalnym jeśli chodzi o komputery osobiste. Problem może być tak jak napisałeś gdy jest intensywne korzystanie z metod virtualnych(jakiś algorytm na przykład), lub gdy programujemy na urządzenie z małą ilością pamięci. Co do pierwszej okoliczności(algorytm) ja bym i tak oparł algorytm z wykorzystaniem polimorfizmu a następnie przetestował wydajność - gdyby okazała się że wywołania metody są zbyt kosztowne to dopiero wtedy próbowałbym innego rozwiązania - według zasady: ma działać prawidłowo a dopiero później można optymalizować.

0
class Child : public Base
{
	public:
		void Event_Something() { Base::Event_Something(); cout << "Hello I'm your child :)" << endl; }
};

Ale to jest jawne przecież.

Base::Event_Something();

. W qt jak tworzymy klasę pochodna do widgetu to nie musimy wywolywac w funkcji pochodnej funkcję bazową. I to mnie wlasnie zastanawia jak to jest zrobione.

0

Napisałeś coś takiego

Crunch napisał(a)

jezeli wywolana by zostala funkcja pochodna klasy 'Child' to wypisaloby mi tylko "Hello I'm your child ". chcialbym aby gdy funkcja bazowa zostala wywolana, wywolala tez funkcje pochodną. tak jak w bibliotece qt, chce aby calosc odbywala sie niejawnie.

Według tego co tu napisałeś, usuń z Event_Something klasy Child base::Event_Something() i będzie to robiło to co chcesz, a przynajmniej tak wnioskuję, że to chcesz.

0

tak jak krwq mowi, nie bedzie.. to moze po prostu pokazcie jak dzialaja eventy w qt (z wewnatrz oczywiscie), bo juz prosciej powiedziec nie potrafie.

0

Handlery eventów w Qt to zwykłe metody wirtualne. Nie jest tak jak mówisz, że każde przeładowanie jest niejawnie wołane.

Qt 4.7 Reference: The Event System napisał(a)

The normal way for an event to be delivered is by calling a virtual function. For example, QPaintEvent is delivered by calling QWidget::paintEvent(). This virtual function is responsible for reacting appropriately, normally by repainting the widget. If you do not perform all the necessary work in your implementation of the virtual function, you may need to call the base class's implementation.

Może po prostu chcesz, aby klasa najbardziej bazowa zareagowała na event jako pierwsza, a później ewentualnie ta najbardziej pochodna, która przeładowała dany event? Coś takiego:

class Base
{
private:
	void DoFoo() // handler bazowy, zawsze wołany
	{
		cout << "Method from class 'Base' running..." << endl;
		this->OnFoo();
	}

protected:
        virtual void OnFoo() { } // handler opcjonalny, wołane zawsze "najgłebsze" przeładowanie

public:
	void RaiseFoo() { this->DoFoo(); } // wyzwalacz eventa
};
 
class Child : public Base
{
public:
	virtual void OnFoo() { cout << "Hello I'm your child :)" << endl; }
};

class Grandchild : public Child
{
public:
	virtual void OnFoo() { cout << "Hello I'm your grandchild :)" << endl; }
};

cout << "BASE:" << endl;
Base *base = new Base();
base->RaiseFoo();

cout << "CHILD:" << endl;
Base *child = new Child();
child->RaiseFoo();

cout << "GRANDCHILD:" << endl;
Base *grandchild = new Grandchild();
grandchild->RaiseFoo();

Wynik:

BASE:
Method from class 'Base' running...
CHILD:
Method from class 'Base' running...
Hello I'm your child :)
GRANDCHILD:
Method from class 'Base' running...
Hello I'm your grandchild :)
0
Crunch napisał(a)

tak jak krwq mowi, nie bedzie.. to moze po prostu pokazcie jak dzialaja eventy w qt (z wewnatrz oczywiscie), bo juz prosciej powiedziec nie potrafie.

Programuję w Qt i nie mam teraz pojęcia o co chodzi. Daj jakiś kod przykładowy bo coś mi tu nie gra.

0

Ja chcę zrobić coś w ten deseń (ten na dole kod jest oczywiście nie prawidłowy):

class Event
{
	public:
	void Event_Clicked_run() 
	{
		cout << "Instrukcje klasy bazowej" << endl;
		this->Event_Clicked();
	}
	virtual void Event_Clicked() { }
};

class First_Child : public Event
{
	public:
		void Event_Clicked() 
		{	
			cout << "Hello, here is First_Child" << endl;
		}
};

class Second_Child : public Event
{
	public:
		void Event_Clicked() 
		{	
			cout << "Hello, here is Second_Child" << endl;
		}
};

I w momencie wykonania takiej operacji:

Event *ev = new Event();
ev->Event_Clicked_run();

..chcialbym otrzymac taki wynik w konsoli:

 
Instrukcje klasy bazowej
Hello, here is First_Child
Hello, here is Second_Child

Czyli: Jeżeli wywołałbym "główną" funkcję klasy bazowej Event to wykonałyby się funkcje o tej samej nazwie wszystkich klas pochodnych. Taki wlasnie efekt chciałbym uzyskać.

0

hmm dzieki, ale zaczerpnalem tez mini info z innego zrodla pod haslem "c++ zdarzenia" z pierwszego linku. Zostaly podane tam tylko deklaracje, autor nie rozwinal sie i nie podal wiekszego przykladu. co nie co zmodyfikowalem i chcialbym was zapytac jak to doprowadzic do porzadku, abym mogl wywolac zdarzenia z klasy IControl, gdy zaś ta uruchomila by jej odpowiednik funkcji w klasie MyEvent.

class IMouse
{
	public:
	
};

class IEvent
{
    public:
		virtual void OnClicked_Left(IMouse *pSender) = 0;
		virtual void OnClicked_Right(IMouse *pSender) = 0;
		virtual void OnClicked_Double(IMouse *pSender) = 0;
		virtual void OnClicked_Scroll(IMouse *pSender) = 0;
};

class MyEvent : public IEvent
{
	public:
		void OnClicked_Left(IMouse *pSender) { cout << "Kliknieto lewy przycisk" << endl; }
		void OnClicked_Right(IMouse *pSender) { cout << "Kliknieto prawy przycisk" << endl; }
		void OnClicked_Double(IMouse *pSender) { cout << "Kliknieto podwojnie lewy przycisk" << endl; }
		void OnClicked_Scroll(IMouse *pSender) { cout << "Kliknieto scroll" << endl; }
};
0

Zwróć uwagę na to, że również masz do dyspozycji "Qt Signals and Slots" oraz "Boost.Signals"

0

A bez tego ani rusz? Jezeli nie to bardzo prosilbym o pomoc z powyzszym przykladem.

0
Crunch napisał(a)

Czyli: Jeżeli wywołałbym "główną" funkcję klasy bazowej Event to wykonałyby się funkcje o tej samej nazwie wszystkich klas pochodnych. Taki wlasnie efekt chciałbym uzyskać.

Na mój gust kombinujesz nie znając do końca gruntu. Zapomniałeś że w Qt robi się to przez sygnały i sloty. Trzeba je wcześniej zadeklarować. Klasa bazowa nie wywołuje potomków - potomki są jawnie przypisane do zdarzenia i to ono wywołuje listę zdarzeń podpiętych. Tak więc błąd był już w pytaniu, ponieważ klasa bazowa nie ma nic do rzeczy tutaj.

0

Kombinujecie z niejasnymi wymaganiami. Tak jak ktoś tu napisał nie ma czegoś takiego jak dziedziczenie zdarzeń.
Prawdopodobnie autorowi wątku chodzi o to, żeby w obsłudze zdarzenia wywołać coś zgodnie z hierarchią klas OOP, tak więc prawdopodobnie chodzi o wywołanie w zdarzeniu kodu jak u krwq.

Poza tym pytanie dodatkowe:

  • to ma być rozwiązanie "tak jak w Qt" czy "dla Qt"?
0

Poza tym pytanie dodatkowe:

  • to ma być rozwiązanie "tak jak w Qt" czy "dla Qt"?

Te pierwsze

Prawdopodobnie autorowi wątku chodzi o to, żeby w obsłudze zdarzenia wywołać coś zgodnie z hierarchią klas OOP, tak więc prawdopodobnie chodzi o wywołanie w zdarzeniu kodu jak u krwq.

Zauważ, że kod krwq wygląda tak, jakby to callback wywoływał całe zdarzenie tzn. tam gdzie ma wywolac sie kod "końcowy" - czyli funkcja grandchild, to on wywoluje funkcję klasę przodka, zaś ta z drugiego pokolenia (czyli tam gdzie jest podstawowa 'procedura'). Wedlug mnie to paradoks jesli chodzi o ten system zdarzeń (pokazujecie odwrotnie do zamiaru).

0
Crunch napisał(a)

Prawdopodobnie autorowi wątku chodzi o to, żeby w obsłudze zdarzenia wywołać coś zgodnie z hierarchią klas OOP, tak więc prawdopodobnie chodzi o wywołanie w zdarzeniu kodu jak u krwq.

Zauważ, że kod krwq wygląda tak, jakby to callback wywoływał całe zdarzenie tzn. tam gdzie ma wywolac sie kod "końcowy" - czyli funkcja grandchild, to on wywoluje funkcję klasę przodka, zaś ta z drugiego pokolenia (czyli tam gdzie jest podstawowa 'procedura'). Wedlug mnie to paradoks jesli chodzi o ten system zdarzeń (pokazujecie odwrotnie do zamiaru).

W takim razie to wg mnie nie ma nic wspólnego z dziedziczeniem. Prawie nic.

Możliwe rozwiązanie wygląda tak (pseudo-kod):

typedef (?) HandlerVector; // pseudo-kod
typedef (?) EventHandler; // pseudo-kod

class Baza {
public:
Baza() {
  // pseudo-kod
  registerHandler(Baza::handleEvent);
}
  void executeEvent() {
     runHandlerList(...);
  }  
protected:
  void registerHandler(...) { 
  // dodanie argumentu do listy "handlers"
  }
  void runHandlerList(...) {
    for(HandlerVector::iterator it = handlers.begin(); it != handlers.end(); ++it)
      runHandler(*it,...);
  }
  void runHandler(EventHandler &hnd, ...) {
  // wywolanie
  }
  void handleEvent() {
     cout << "To jest baza... ";
  }
private:
  HandlerVector handlers;
};

class Child {
public:
Child() {
  // pseudo-kod
  registerHandler(Child::handleEvent);
}
protected:
  void handleEvent() {
      cout << "To jest dziecko... ";
  }
};


class GrandChild {
public:
GrandChild() {
  // pseudo-kod
  registerHandler(GrandChild::handleEvent);
}
protected:
  void handleEvent() {
      cout << "To jest wnuk... ";
  }
};

Po wywołaniu:

Baza *ptr = new GrandChild();
ptr->executeEvent();

powinieneś dostać:
To jest baza...
To jest dziecko...
To jest wnuk...

Fragmenty do implementacji oznaczone są jako "pseudo-kod".

Najwygodniej chyba to będzie zrobić przy pomocy Boost:
http://www.boost.org/doc/libs/1_48_0/doc/html/signals/tutorial.html#id3069234

1 użytkowników online, w tym zalogowanych: 0, gości: 1