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

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

11

26.01.2007, 08:16

Ich hab so mehr oder weniger was, fahre allerdings gleich los zum Praktikum und komme dann erst wieder gegen 12 zu was. Zum Stil: Das kann man nicht wirklich zementieren wie ich finde aber ich geb mal persönliche Anmerkungen:

1) Ich mag dieses ganze "ich muss unbedingt alle typedefs auflösen" Gehabe persönlich garnicht. Dass man statt DWORD unsigned long schreibt mag ja noch angehen aber du könntest einer dieser Kandidaten sein die das ohne wirklichen Sinn bis zum äussersten treiben ...

2) Globale Variablen sind BÄH! Ohne Grundlagen von OOP seh ich aber ein, dass es nicht immer einfach ist die wegzubekommen. Tipp: Schreib dir einen Fenstermanager ;)

3) Warum die run Variable? Schleifen lassen sich auch mit break verlassen ;) Abgesehen davon empfinde ICH es als eher ungelungen bei solchen Sachen einfach Variablen als Abbruchbedingung zu verwenden. Macht bei dir jetzt nicht viel aus weil dus ja immerhin kommentiert hast. Sprechender finde ich aber persönlich ungefähr sowas:

C-/C++-Quelltext

1
while(myapp.isRunning())

4) Über den OOP Ansatz müssen wir glaube ich nicht viel reden, ich schätze du wirst deine Fehler in Kürze selber verstehen ;)

koschka

Community-Fossil

Beiträge: 2 862

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

12

26.01.2007, 10:09

Also:

Software wird heutztage in der Wirtschaft und auch im akademischen Bereich zu miest mit objektorientiert geschrieben.
Das heisst du suchst zuerst alle Objekte in deiner Software raus, überlegst welche Methoden die haben könnten und welche Attribute. Dass spezifizierst du solange, bis jede Klasse einen eigenen Aufgabenbereich hat, sowie Klassen die abhängig von jener sind oder erben. All dies wird "Modelling" genannt.

So würde dies auch recht übersichtlich werden.

Auf jedenfall müsstest du Sachen wie die WndProc verbergen. Davon darf der Benutzer nichts mehr mitbekommen. Genauso wie die while Schleife mit PeekMsg.

Letztendlich dürfte der Benutzer bei einem ordentlichen Framework nur Zugriff auf solche Klassen wie "Window", "Button", ... u.s.w. haben.
Dein Ansatz ist indes nicht schlecht, das du sagst: "Hay, jedes Fenster kann Buttons haben."
Aber die Buttons (bzw. die HWND's, oder Besser Instanzen der Klasse Button) müsstest du auch irgendwie speichern, in Form einer Liste oder eines Maps...
Die Klasse Button hat dann wieder die Aufgabe Buttons zu erstellen, eine OnClick Methode anzugeben u.s.w.


Man sieht sehr schnell das man ohne Planung bei so etwas komplexen wie einem Wrapper spätestens an den Knackpunkten scheitern wird.




Das ist die Theorie, wie man es machen sollte um ein einigermaßen ordentliches Framework zu bekommen. Das Du das jetzt aber nicht gleich 1zu1 umsetzen kannst ist mir auch klar :). Für Dich ist das was da oben steht würd ich sagen doch schonmal ganz gut.

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

13

26.01.2007, 10:31

Mir ist eben noch was eingefallen: In Davids Buch, imho ab der zweiten Auflage, findet sich im Anhangteil ein Beispiel für einen Gamestatemanager. Schau dir den doch mal an.

Ansonsten immer gut als OOP Idee: Programmiere ein Rollenspiellikes Inventarsystem mit mehreren Arten von Objekten. Das ist imho ein recht greifbares Beispiel und sollte dir die meisten Grundlagen mit auf den Weg geben. Zum Ansatz:

Du brauchst eine Art Inventarmanager bzw das Inventar selbst. Dieses verwaltet die Gegenstände bzw. Objekte. Dann erstellst du ein Basisobjekt von welchem du alle weiteren ableitest. Überleg dir also welche Eigenschaften und Methoden JEDER Gegenstand braucht.

Wenn du dann schonmal ein beliebig großes Inventar mit beliebigen Objekten hast, biste schonmal ganz gut. Aber dann hab ich was schwierigeres.

Programmiere einen "Beutel" innerhalb des Inventars der z.B. nur Edelsteine aufnehmen kann. Implementiere dann noch eine Funktion die dir ALLE Gegenstände im Inventar, auch aus den "Subinventars" auslesen kann.

Pfeif dabei btw auf die Grafik, Konsole rult ;)

Wenn du DAS hinbekommen hast bist du auch fit für einen einfacheren Wrapper. Das Prinzip wird sich dort 1 zu 1 wiederholen.

14

26.01.2007, 13:13

ich schreib mir auch gerade ein kleines Framework.

Ich hab vor die WndProc als privates Datenelement zu machen und dass man sich dann ne Fensterklasse macht und die von meiner Fensterklasse ableitet und die WndProc überschreibt (Java like!).
Ich weiß nicht ob das so super ist aber ich finde es sinnvoll!
Das Böse ist des Menschensbeste Kraft - Friedrich Nietzsche

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

15

26.01.2007, 21:04

Jedes grafische Element ist immer auch ein eigenes Fenster, daher hab ich bei mir die Klassenstruktur wie folgt aufgebaut:

Es gibt eine Basisklasse Window (unvollständiges Listing):

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
class Window
{
    typedef std::list< Window* > WindowListType;

    struct WindowCmp
        : public std::binary_function< Window*, HWND, bool >
    {
        bool operator()( Window* ptr, HWND handle ) const
        {
            return ( ptr->GetHandle() == handle );
        }
    };

private:
    HWND hWnd;
    Window* owner;

    // Controllist, ...


    static WindowListType windows;

protected:
    bool CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance );
    virtual LRESULT HandleNotifications( UINT uMsg, WPARAM wParam, LPARAM lParam );
    virtual void DoDestroy();

    static bool RegisterWindow( const char* Name, DWORD Style, HINSTANCE hInstance );
    static LRESULT APIENTRY WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

public:
    Window( Window* owner = NULL );
    virtual ~Window() = 0;

    void Destroy();
    void Show( bool show );
    void SetFont( HFONT font );
    void SetRedraw( bool redraw );
    HWND GetHandle() const;
    HWND GetOwner() const;

    // ...


    virtual void SetText( const std::string& text );
    virtual std::string GetText() const;
    virtual int GetTextLength() const;

    // ...


    virtual void OnClose();
    virtual void OnDestroy();

    // Other Notifications ...

};


Diese ist Abstrakt und kann allein nicht verwendet werden. Dafür kann sie für jegliche Typen von Fenstern als Basisklasse fungieren.
Zum Beispiel WindowFrame (ebenfalls unvollständig):

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
class WindowFrame
    : public Window
{
private:
    static unsigned long count;

public:
    WindowFrame( Window* owner = NULL )
        : Window( owner )
    {
        // xyz

    }

    ~WindowFrame()
    {
        // xyu

    }

    void Create( const std::string& name, int x, int y, int nWidth, int nHeight )
    {
        HINSTANCE hInst = GetModuleHandle( NULL );
        std::string ClassName = boost::str( boost::format( "%1%%2%_%3%" ) % "WindowFrame" % name % count++ );

        if ( !RegisterWindow( ClassName.c_str(), CS_OWNDC, hInst ) )
        {
            throw std::logic_error( "Error while registering the window" );
        }
        
        if ( !CreateWindowEx( 0, ClassName.c_str(), name.c_str(), WS_OVERLAPPEDWINDOW, x, y, nWidth, nHeight, 0, 0, hInst ) )
        {
            throw std::logic_error( "Error while creating the window" );
        }

        Show( true );
    }

    void OnDestroy()
    {
        Window::OnDestroy();
        PostQuitMessage( 0 );
    }
};


Außerdem gibt es eine weitere abstrakte Basisklasse die von Window erbt, nämlich Control (Miniauszug daraus).

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
class Control
    : public Window
{
public:
    Control( Window* owner )
        : Window( owner )
    {}

    virtual ~Control() = 0
    {}
};


Diese Klasse fungiert als Basis für alle Windowcontrolls (Button, Label, Dropdownboxen, ...) (Genauso umvollständig):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Button
    : public Control
{
private:
    // Notification, Events, ...


public:
    Button( Window* owner )
        : Control( owner )
    {}

    void Create( int x, int y, int nWidth, int nHeight, const char* text )
    {
        // Code zum erstellen des Buttons

    }

    // Notification Handling

};


Der owner hält eine Liste aller zugeordneten Elemente, wird eines zerstört wird es aus der Liste genommen. Außerdem ist es möglich Funktoren als Eventreceiver zu definieren (nicht im Code gezeigt).

Das läuft dann so ab:

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
#include "Window.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
    MSG msg;
    WindowFrame wnd;
    Button btn( &wnd );
    Checkbox chktest( &wnd );
    Label lblText( &wnd );

    try
    {
        wnd.Create( "Foobar", 0, 0, 800, 600 );
        btn.Create( 325, 275, 75, 25, "Button1" );
        chktest.Create( 100, 100, 120, 25, "Yeehaw", true );
        lblText.Create( true, false );
    }
    catch ( const std::logic_error& e )
    {
        MessageBox( 0, "Cannot create the window", "Error", MB_OK | MB_ICONERROR );
    }

    while ( true )
    {   
        if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            if ( msg.message == WM_QUIT )
            {
                break;
            }

            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            POINT p = wnd.GetCursorPos();
            std::string str = boost::str( boost::format( "Mousepos: %1%:%2%" ) % p.x % p.y );
            lblText.SetText( str );
        }
    }

    return 0;
}
@D13_Dreinig

koschka

Community-Fossil

Beiträge: 2 862

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

16

26.01.2007, 21:11

Hmmn machen Ansätze sind ganz gut, aber OnClick bzw. OnDestroy würd ich nicht als Template Method benutzen, sondern einen Funktionszeiger übergeben, weche Funktion er ausführen soll. Ausserdem würd ich die while Schleife auf jedenfall kapseln (vorallem weil bei mehreren Fenstern ja noch was rein muss wenn ich mich richtig erinnere)

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

17

26.01.2007, 21:14

Zitat von »"koschka"«

Hmmn machen Ansätze sind ganz gut, aber OnClick bzw. OnDestroy würd ich nicht als Template Method benutzen, sondern einen Funktionszeiger übergeben, weche Funktion er ausführen soll. Ausserdem würd ich die while Schleife auf jedenfall kapseln


Template? Wo? Hab ich was verpasst? ;) Die OnXYZ Methoden dienen nur Klassenintern als Sprungpunkt bei Notifikationen oder Events. Nach außen hin läuft das über Funktoren (nicht Funktionszeiger ;)). Die Schleife Kapseln, vielleicht, aber nicht auf der Stufe!

grüße
@D13_Dreinig

koschka

Community-Fossil

Beiträge: 2 862

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

18

26.01.2007, 21:19

Ne, nix mit Templates :). Template Method ist ein Entwurfsmuster. (wie singleton) und genau das hast du drin bei OnClick und OnDestroy.

Wenn du OnClick und OnDestroy mit Zeigern machst, brauchst du die Klasse nicht erben.

Übrigends sind abstrakte Methoden in C++ pure virtual ( virtual bool bla() = 0; )

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

19

26.01.2007, 21:30

Zitat von »"koschka"«

Ne, nix mit Templates :). Template Method ist ein Entwurfsmuster. (wie singleton) und genau das hast du drin bei OnClick und OnDestroy.


Ah, klar! Hatte die Möglichkeit eben irgendwie nicht erfasst. Jetzt weiß ich was du meinst! ;) Allerdings sind nicht OnClick und OnDestroy die Template Methoden.

Zitat von »"koschka"«


Wenn du OnClick und OnDestroy mit Zeigern machst, brauchst du die Klasse nicht erben.


Aber genau das will ich tun, vererben!

Zitat von »"koschka"«


Übrigends sind abstrakte Methoden in C++ pure virtual ( virtual bool bla() = 0; )


Und nun? Is doch der Fall! :)
@D13_Dreinig

koschka

Community-Fossil

Beiträge: 2 862

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

20

26.01.2007, 21:36

Du solltest immer Methoden, von denen du willst, dass die erbenden Klasse diese unbedingt implementieren müssen (kann auch leer sein) immer abstrakt machen. (Bei OnClick und OnDestroy würd ich das machen)

Bei Java ist das besser gelöst mit abtract bool foo();, in Cpp sieht das so aus: virtual bool foo() = 0; Ist nicht sonderlich schön, funzt aber :)

Das mit dem erben ist im ürbiegen - wenn ich so mal drüber nachdenke ne ganz akzeptable Idee :).

Werbeanzeige