Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

12.12.2014, 14:51

C++ Funktionszeiger vererben

Ich arbeite gerade daran, die Windowsfensterkomponenten in eigene Klassen zu packen, um diese einfacher handhaben zu können (Bsp./Vorbild Borlands VCL). Für die Ereignisverarbeitung möchte ich Funktionszeiger verwenden:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//allgemeine Fensterklasse
class CForm
{
public:
    ...
    //Ereignisfunktionszeiger
    void (CForm::*pOnKeyPress) (WPARAM);
    ...
}

//Beispielfenster
class CMyForm : public CForm
{
private:
    ...
    //Funktion, auf die verweist werden soll
    void myFormOnKeyPress(WPARAM wParam);
    //definieren der aller Ereigniszeiger
   void setFunctionPointers () {pOnKeyPress= &CMyForm::myFormOnKeyPress;};
    ...
}



Der Compiler möchte allerdings nicht, dass ich den Ereigniszeiger aus der vererbten Klasse in der Klasse CMyForm festlege und gibt aus: "error: cannot convert 'void (CMyForm::*)(WPARAM) {aka void (CMyForm::*)(unsigned int)}' to 'void (CForm::*)(WPARAM) {aka void (CForm::*)(unsigned int)}' in assignment"

Warum funktioniert das nicht und wie kann ich das Problem beheben?

Mit freundlichen Grüßen
Hendrik

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

12.12.2014, 15:00

Wenn du eh mit Ableitungen arbeiten willst, wieso dann nicht einfach so:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CForm
{
protected:
    ...
    //Ereignisfunktionszeiger
    virtual void onKeyPress(WPARAM) {}
    ...
}

class CMyForm : public CForm
{
private:
    void onKeyPress(WPARAM) { ... }
}


;)

3

12.12.2014, 15:05

Überschriebene Funktionen finde ich an dieser Stelle wenig sinnig, da man mit Funktionszeigern für ein komplettes Fenster nur eine Klasse überschreiben müsste, bei überschriebenen Funktionen müsste man sämtliche Klassen überschreiben, also jeden Button etc.. Das Konzept mit den Funktionszeigern habe ich bei der o.g. VCL kennengelernt und habe es dort als einfach und vorteilhaft kennengelernt.

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

4

12.12.2014, 15:20

Nun, in C++ gibt es http://de.cppreference.com/w/cpp/utility/functional/function

Aber eigentlich stimme ich Dot zu, die herangehensweise mit virtual ist eigentlich das was du zu wollen scheinst. Ich kann deinen Einwand nicht nachvollziehen. Wie man müsste alle Klassen überschreiben?!
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

5

12.12.2014, 15:49

In deinem Code-Beispiel verwendest du genau das, was virtual mit Vererbung anrichtet, bloß auf ungeschickten Umwegen. Deine Basis-Klasse CForm legt die Schnittstelle fest, nämlich, dass sie eine Member-Funktion pOnKeyPress ausführen kann. Deine Sub-Klasse CMyForm übernimmt diese Schnittstelle und legt eine eigene Implementierung drüber. Genau das tut der Code, den dot dargeboten hat, bloß ohne seltsame Umwege über Casts und Dergleichen und ohne auf den Aufruf von setFunctionPointers warten zu müssen. Die Frage ist nun lediglich, ob du eine Standard-Implementierung willst, oder nicht.

C-/C++-Quelltext

1
2
3
4
5
6
7
class CForm {
public:
  // Standard-Implementierung.
  virtual void onKeyPress(WPARAM)  {do_stuff();}
  // Keine Implementierung für CForm.
  virtual void onKeyPress(WPARAM) = 0;
};

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

6

12.12.2014, 20:19

Das Überschreiben der Funktion möchte ich eben nicht, da ich den Zeiger auch auf Funktionen setzen können möchte, die außerhalb der Klasse definiert sind. Im Sinne der Objektorientierung macht es in meinen Augen am meisten Sinn, wenn ein Event, das beim Klicken eines Buttons ausgelöst wird, auch durch den Button zu seiner Ereignisfunktion findet. Es macht wiederum wenig Sinn, die Ereignismethode direkt in der jeweiligen Buttonklasse zum definieren, da man dann, wie gesagt, jede Komponentenklasse überschreiben müsste und im Button auch sämtliche benötigten Informationen und Zugriffe beinhaltet sein müssten, was nicht der Objektorientierung entspricht.

Ich wollte mich halt an der VCL orientieren, bei der alle Methoden in der Formular-/Fensterklasse deklariert und definiert werden und die Komponenten, die die jeweiligen Ereignisse auslösen, Zeiger auf diese Methoden besitzen.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

12.12.2014, 20:28

Mit Memberfunctionpointern wirst du dann aber vermutlich auch nicht glücklich, denn um die aufzurufen brauchst du ein passendes Objekt. Deine Stichworte: Signals & Slots, Events, Delegates. ;)

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

8

12.12.2014, 20:32

Für das was du willst gibt es in C++ keine gute Lösung. Borlands C++ Compiler hat dafür eine gute Erweiterung, die ist aber kein Standard.
Wenn dir Performance egal ist kannst du aber folgendes tun:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <functional> 
using namespace std::placeholders;

//allgemeine Fensterklasse
class CForm
{
public:
    //Ereignisfunktionszeiger
    std::function<void(WPARAM)> pOnKeyPress;
};

//Beispielfenster
class CMyForm : public CForm
{
private:
    //Funktion, auf die verweist werden soll
    void myFormOnKeyPress(WPARAM wParam);
    //definieren der aller Ereigniszeiger
    void setFunctionPointers () {pOnKeyPress = std::bind(&CMyForm::myFormOnKeyPress, this, _1);};
};
Sei stets geduldig gegenüber Leuten, die nicht mit dir übereinstimmen. Sie haben ein Recht auf ihren Standpunkt - trotz ihrer lächerlichen Meinung. (F. Hollaender)

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

12.12.2014, 20:35

Für das was du willst gibt es in C++ keine gute Lösung.

Events und Listener Delegates kann man sich insbesondere in modernem C++ eigentlich ganz nett basteln. Das Problem ist weniger, dass es in C++ keine gute Lösung dafür gibt, sondern viel mehr, dass diese Herangehenweise an sich rein prinzipiell keine gute Lösung für das Problem ist...

Beispiel wie ich es vor langer, langer Zeit gemacht hab:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#ifndef EVENT_INCLUDED
#define EVENT_INCLUDED

#pragma once

#include <list>

namespace GUI
{

  template<class A, class B>
  class EventListener
  {
  private:
    EventListener(const EventListener&);
    EventListener& operator =(const EventListener&);
  public:
    EventListener() {}
    virtual ~EventListener() {}

    virtual void notify(A arg1, B arg2) = 0;
  };

  template<class A>
  class EventListener<A, void>
  {
  private:
    EventListener(const EventListener&);
    EventListener& operator =(const EventListener&);
  public:
    EventListener() {}
    virtual ~EventListener() {}

    virtual void notify(A arg) = 0;
  };

  template<>
  class EventListener<void, void>
  {
  private:
    EventListener(const EventListener&);
    EventListener& operator =(const EventListener&);
  public:
    EventListener() {}
    virtual ~EventListener() {}

    virtual void notify() = 0;
  };

  template<class T, class A, class B = void>
  class EventListenDelegate
    : public EventListener<A, B>
  {
  private:
    EventListenDelegate(const EventListenDelegate&);
    EventListenDelegate& operator =(const EventListenDelegate&);

    typedef void (T::*method_ptr)(A, B);

    method_ptr _method;
    T& _obj;
  public:
    EventListenDelegate(T& object, method_ptr method)
      : _obj(object), _method(method)
    {
    }

    void notify(A arg1, B arg2)
    {
      (_obj.*_method)(arg1, arg2);
    }
  };

  template<class T, class A>
  class EventListenDelegate<T, A, void>
    : public EventListener<A, void>
  {
  private:
    EventListenDelegate(const EventListenDelegate&);
    EventListenDelegate& operator =(const EventListenDelegate&);

    typedef void (T::*method_ptr)(A);

    method_ptr _method;
    T& _obj;
  public:
    EventListenDelegate(T& object, method_ptr method)
      : _obj(object), _method(method)
    {
    }

    void notify(A arg)
    {
      (_obj.*_method)(arg);
    }
  };

  template<class T>
  class EventListenDelegate<T, void, void>
    : public EventListener<void, void>
  {
  private:
    EventListenDelegate(const EventListenDelegate&);
    EventListenDelegate& operator =(const EventListenDelegate&);

    typedef void (T::*method_ptr)();

    method_ptr _method;
    T& _obj;
  public:
    EventListenDelegate(T& object, method_ptr method)
      : _obj(object), _method(method)
    {
    }

    void notify()
    {
      (_obj.*_method)();
    }
  };

  template<class A, class B = void>
  class Event
  {
  public:
    typedef EventListener<A, B> listener_type;
  private:
    Event(const Event&);
    Event& operator =(const Event&);
    typedef std::list<listener_type*> listener_list;
    listener_list listeners;
  public:
    Event() {}
    ~Event() {}

    void addListener(listener_type* listener) { listeners.push_back(listener); }
    void removeListener(listener_type* listener) { listeners.remove(listener); }

    Event& operator +=(listener_type* listener) { addListener(listener); return *this; }
    Event& operator -=(listener_type* listener) { removeListener(listener); return *this; }

    void raise(A arg1, B arg2)
    {
      for (listener_list::iterator it = listeners.begin(); it != listeners.end(); ++it)
        (*it)->notify(arg1, arg2);
    }
  };

  template<class A>
  class Event<A, void>
  {
  public:
    typedef EventListener<A, void> listener_type;
  private:
    Event(const Event&);
    Event& operator =(const Event&);
    typedef std::list<listener_type*> listener_list;
    listener_list listeners;
  public:
    Event() {}
    ~Event() {}

    void addListener(listener_type* listener) { listeners.push_back(listener); }
    void removeListener(listener_type* listener) { listeners.remove(listener); }

    Event& operator +=(listener_type* listener) { addListener(listener); return *this; }
    Event& operator -=(listener_type* listener) { removeListener(listener); return *this; }

    void raise(A arg)
    {
      for (listener_list::iterator it = listeners.begin(); it != listeners.end(); ++it)
        (*it)->notify(arg);
    }
  };
  
  template<>
  class Event<void, void>
  {
  public:
    typedef EventListener<void, void> listener_type;
  private:
    Event(const Event&);
    Event& operator =(const Event&);
    typedef std::list<listener_type*> listener_list;
    listener_list listeners;
  public:
    Event() {}
    ~Event() {}

    void addListener(listener_type* listener) { listeners.push_back(listener); }
    void removeListener(listener_type* listener) { listeners.remove(listener); }

    Event& operator +=(listener_type* listener) { addListener(listener); return *this; }
    Event& operator -=(listener_type* listener) { removeListener(listener); return *this; }

    void raise()
    {
      for (listener_list::iterator it = listeners.begin(); it != listeners.end(); ++it)
        (*it)->notify();
    }
  };
}

#endif  // EVENT_INCLUDED

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (12.12.2014, 20:42)


10

12.12.2014, 20:51

@dot: Ich programmiere erst seit knapp einem Jahr in C++, und das nur nebenbei, und kann deshalb noch nicht wirklich in C++ gute von schlechten Lösungen unterscheiden. Ich habe bisher aber festgestellt, dass Funktionszeiger in Pascal deutlich einfacher zu handhaben sind, da man den Zeiger, wie gesagt, auf jede beliebige Funktion mit entsprechenden Parametern setzen kann.

@Helmut: Leider kennt mein Compiler "std::placeholders" nicht, ich benutze Code::Blocks mit dem GNU GCC Compiler, beides in der Neuesten Version (Fehlermeldung: "error: 'placeholders' is not a namespace-name")?!

edit
@dot: Ich werde mir deinen Quelltext morgen nochmal genauer anschauen, scheint aber doch komplizierter zu werden, als ich dachte :)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Waldmumie« (12.12.2014, 20:57)


Werbeanzeige