Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Member-Funktionen speichern und aufrufen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
30.06.2005, 12:59 Uhr
~DarthDevilous
Gast


Ich hab folgendes problem:
In meiner Klassenhierarchie hat jede Klasse ihre eigene Kombination von beliebigen "Zuständen". Mit jedem Zustand sind zwei Funktionen verbunden, eines für das Eintreten und eines für das Verlassen des Zustandes. Diese Funktionen sind natürlich Member-Funktionen der jeweiligen Klassen. Jetzt muss aber eine Liste der Zustände erstellt werden, damit auf diese überhaupt zugregriffen werden kann. Wie ist es nun möglich, mittels dieser Liste Zeiger zu den verschieden Funktionen zu speichern und mittels dieser Zeiger dann die Funktionen für jeweilige Instanzen dieser Klasse aufzurufen?
Zum verständnis ein bisschen Pseudo-Code:

C++:
struct Status {
    const char *lpszStatusName,
    thiscall void(*lpfnOnEnterStatus)(void), // <-- Was hier, thiscall gibt's ja nicht
    thiscall void(*lpfnOnLeaveStatus)(void)
};
static Status CMyClass::StatusList[] = {
    { "StatusName1", CMyClass::OnEnterStatus1, CMyClass::OnLeaveStatus1 },
    { "StatusName2", CMyClass::OnEnterStatus2, CMyClass::OnLeaveStatus2 },
    { NULL, NULL, NULL }
};

void GotoStatus(CSomeClass *pClass, const char *lpszStatusName)
{
    Status *pStatus = &pClass->StatusList[0];
    for (int i=0; pStatus->lpszStatusName; pStatus=&pClass->StatusList[i++])
    {
        if (strcmp(pStatus->lpszStatusName, lpszStatusName) == 0)
        {
            pClass->pCurrentStatus->lpfnOnLeaveStatus(); // <-- Was hier?
            pCurrentStatus = pStatus;
            pClass->pCurrentStatus->lpfnOnEnterStatus(); // <-- Und hier?
            break;
        }
    }
}




Bearbeitung von Windalf:

tags gefixt...


Dieser Post wurde am 30.06.2005 um 13:07 Uhr von Windalf editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
30.06.2005, 13:57 Uhr
Th



Du mußt Member-Funktionszeiger verwenden, z.B.


C++:
void (CMyClass::*lpfnOnEnterStatus)()



Aufruf dann mit

C++:
(pClass->*lpfnOnLeaveStatus)();


Falls pClass nicht vom Typ CMyClass ist, dann mußt du evtl. noch casten...
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
30.06.2005, 14:11 Uhr
virtual
Sexiest Bit alive
(Operator)


Endlich mal eine nette Frage.

Du willst mehr über die Memberfunktion Adaptoren der STL wissen

Mal folgender Code

C++:
#include <iostream>
#include <functional>

struct base_class {

    virtual void method1() { std::cerr<<"base_class::method1"<<std::endl; }
    void method2() { std::cerr<<"base_class::method2"<<std::endl; }
};

struct derived_class: public base_class {

    virtual void method1() { std::cerr<<"derived_class::method1"<<std::endl; }
    void method2() { std::cerr<<"derived_class::method2"<<std::endl; }
};



int main() {
    // Definition der Funktion adaptoren
    std::mem_fun_t<void, base_class> m1(&base_class::method1);
    std::mem_fun_t<void, base_class> m2(&base_class::method2);

    // Zwo Objecte
    base_class* b = new base_class;
    derived_class* d = new derived_class;

    // Funktionszeiger ausprobieren

    m1(b);
    m2(b);
    m1(d);
    m2(d);

    delete b;
    delete d;
}  


Vor main werden einfach mal ein paar klasse definiert. Interessant sind die Zeilen mit dem std::mem_fun_t: Die definieren sog. MemberFunktion Adaptoren. Das sind klassen, welche sich wie Funktionen verhalten ("Funktoren" genannt). Diese Spezielle Form is ausgelegt für nicht statische Methoden von Klassen, die keinen parameter erwarten (das "void" bezieht sich auf den Rückgabewert). Oben sind also für method1 und method2 die entsprechenden Adaptoren definiert worden.

Die unteren vier Zeilen zeigen, wie man die Objekte benutzt: nämlich einfach analog zu Funktionen, welche einen Zeiger auf die Basisklasse erwarten.

Beachte, daß die Dinger auch mit virtuellen Methoden umgehen können:
method1 ist ja virtuell, so daß m1 tatsächlich zur jeweils spezialisieren Methode verzweigt; method2 ist aber nicht virtuell, so daß m2 stets die Methode aufruft, die du explizit angegeben hast. Das ist die Ausgabe des Programms:

Code:
base_class::method1
base_class::method2
derived_class::method1
base_class::method2


--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)

Dieser Post wurde am 30.06.2005 um 14:13 Uhr von virtual editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
30.06.2005, 14:29 Uhr
DarthDevilous



@Th müsste es beim zweiten Teil nicht
C++:
pClass->(pCurrentState->lpfnOnLeaveStatus)()
oder so heissen?

Dieser Post wurde am 30.06.2005 um 14:29 Uhr von DarthDevilous editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
30.06.2005, 20:48 Uhr
DarthDevilous



Es gibt ein kleines Problem. Bei mir ist es so, das in der "derived_class" neue Funktionen definiert und benutzt werden, die es in der "base_class" nicht gibt, ich habe jedoch nur die Klassendefinition der base_class. Auf mein Beispiel angewandt heisst das, CMyClass stammt von CSomeClass ab. Kann ich trotzdem eine Funktion void CDerivedClass::SomeFunction(void) auf ein pointer void(CBaseClass::*lpFunction)(void) casten, obwohl es diese in der CBaseClass gar nicht gibt? Und wie rufe ich dann diese Funktion auf, wenn ich die Definition (d.h. auch der Name) von CDerivedClass nicht kenne?
Dieser Post wurde am 30.06.2005 um 20:49 Uhr von DarthDevilous editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
30.06.2005, 21:20 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


ohne Name geht gar nix. Ansonsten geht das normalerweise schon, jedoch ist die Klassendefnition bekannt bzw muss bekannt sein, sonst würde er den Namen ja nicht annehmen.
--
class God : public ChuckNorris { };
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
01.07.2005, 08:42 Uhr
virtual
Sexiest Bit alive
(Operator)


Spielst Du auf mein Beispiel an? - Ja, das geht, aber du musst
1. Casten
2. Sicherstellen, daß der Memberfunction Adaptor nie für eine Klasse benutzt wird, die die Methode nicht bereitstellt. Nachfolgend ein Beispiel, basierend auf dem post davor:

C++:
#include <iostream>
#include <functional>

struct base_class {

    virtual void method1() { std::cerr<<"base_class::method1"<<std::endl; }
    void method2() { std::cerr<<"base_class::method2"<<std::endl; }
};

struct derived_class: public base_class {

    virtual void method1() { std::cerr<<"derived_class::method1"<<std::endl; }
    void method2() { std::cerr<<"derived_class::method2"<<std::endl; }
    void method3() { std::cerr<<"derived_class::method3"<<std::endl; } //<--- gibts nur in derived_class
};



int main() {
    // Definition der Funktion adaptoren
    std::mem_fun_t<void, base_class> m1(&base_class::method1);
    std::mem_fun_t<void, base_class> m2(&base_class::method2);
    std::mem_fun_t<void, base_class> m3((void (base_class::*)())&derived_class::method3);                      
    // Zwo Objecte
    base_class* b = new base_class;
    derived_class* d = new derived_class;

    // Funktionszeiger ausprobieren

    m1(b);
    m2(b);
    m1(d);
    m2(d);
    m3(d);
}  


(es wäre korrekter, einen reinterpret_cast zu benutzen, habe es hier aber mal unterlassen)
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)

Dieser Post wurde am 01.07.2005 um 08:44 Uhr von virtual editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
01.07.2005, 18:44 Uhr
DarthDevilous



Da die adaptoren von der derived_class für dessen eigene funktionen fesgelegt werden, ist punkt 2 i.o. Auf mein erstes beispiel angewandt heist das:

C++:
struct Status {
    const char *lpszStatusName,
    std::mem_fun_t<void, CBaseClass> OnEnterStatus,
    std::mem_fun_t<void, CBaseClass> OnLeaveStatus
};

typedef void(CBaseClass::*fnStatus)(void);

static Status CDerivedClass::StatusList[] = {
    { "StatusName1", reinterpret_cast<fnStatus>(OnEnterStatus1), reinterpret_cast<fnStatus>(OnLeaveStatus1) },
    { "StatusName2", reinterpret_cast<fnStatus>(OnEnterStatus2), reinterpret_cast<fnStatus>(OnLeaveStatus2) }
};

void GotoStatus(CBaseClass *pClass, const char *lpszStatusName)
{
    Status *pStatus = pClass->StatusList;
    for ( ; pStatus->lpszStatusName; pStatus++)
    {
        if (strcmp(pStatus->lpszStatusName, lpszStatusName) == 0)
        {
            pClass->pCurrentStatus->OnLeaveStatus(pClass);
            pCurrentStatus = pStatus;
            pClass->pCurrentStatus->OnEnterStatus(pClass);
            break;
        }
    }
}


Dieser Post wurde am 01.07.2005 um 19:27 Uhr von DarthDevilous editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
03.07.2005, 13:42 Uhr
Spacelord
Hoffnungsloser Fall


Hi,
eventuell ist für dich auch das State(Zustand) Pattern von Interesse.Das State Pattern zieht die ganze Geschichte von der anderen Seite auf. Anstatt in deiner Statusstruktur Pointer auf die Memberfunktionen zu speichern würden dann die Klassen deiner Klassenhierarchie nen Pointer auf ein Zustandobjekt halten und sämtliche zustandsabhängigen Methoden würden in dieser Zustandsklasse implementiert.
Bei einem Aufruf einer Methode deiner Klasse wird der Methodenaufruf dann lediglich an das gerade "aktive" Zustandsobjekt delegiert.
Das Pattern bringt dadurch natürlich einige neue Klassen mit ins Rennen auf der anderen Seite hälst du aber deine eigentlichen Fachklassen von dem zustandsabhängigem Verhalten frei und hast langfristig mit den einzelnen Zustandsklassen besser wartbare Komponenten.
Ich denke dass du mit dieser Lösung deutlich flexibler bist.
Das Pattern lässt einem viele Freiheiten und in der Beispielimplementierung hab ich mal einfach nen paar Punkte festgelegt.
In dem Beispiel:
-haben die Zustandsklassen keinen eigenen Zustand,können deshalb von allen Fachklassen genutzt werden und sind deshalb als Singleton ausgelegt.
-aendern nur die Zustandsklassen den Zustand des Fachobjekts.
-ist die Klasse zustand friend von der Fachklasse um auf die Attribute/Methoden zugreifen zu können

base.h (Die eigentliche Fachklasse)

C++:
#ifndef __BASE_H
#define __BASE_H

class zustand;

class base
{
    friend class zustand;
public:
    base();
    void method1();
    void method2();
private:
    zustand* zu_;
    void aendereZustand(zustand* z);
};

#endif



base.cpp


C++:
#include "base.h"
#include "initZustand.h"

base::base():zu_(initZustand::Instance())
{
}

void base::aendereZustand(zustand* z)
{
    zu_ = z;
}

void base::method1()
{
    zu_->method1(this);
}

void base::method2()
{
    zu_->method2(this);
}



zustand.h (Basisklasse der Zustände)


C++:
#ifndef __ZUSTAND_H
#define __ZUSTAND_H

class base;

class zustand
{
public:
    virtual ~zustand(){};
    virtual void method1(base*)=0;
    virtual void method2(base*)=0;
protected:
    virtual void enter()=0;
    virtual void leave()=0;
    void aendereZustand(base* b,zustand* z);
};

#endif



zustand.cpp

C++:
#include "zustand.h"
#include "base.h"

void zustand::aendereZustand(base* b,zustand* z)
{
    leave();
    b->aendereZustand(z);
    z->enter();
}



initZustand.h (erster konkreter Zustand)

C++:
#ifndef __INIT_ZUSTAND_H
#define __INIT_ZUSTAND_H

#include "zustand.h"

class initZustand:public zustand
{
public:
    static zustand* Instance()
    {
        if (_instance==0)
            _instance= new initZustand();
        return _instance;
    }
    virtual void method1(base* b);
    virtual void method2(base* b);
protected:
    virtual void enter();
    virtual void leave();
private:
    static initZustand* _instance;
    initZustand(){};
    initZustand(initZustand& rhs){};
};

#endif




initZustand.cpp

C++:
#include <iostream>
#include "initZustand.h"
#include "folgezustand.h"

initZustand* initZustand::_instance=0;

void initZustand::method1(base* b)
{
    std::cout<<"method1 von initZustand aufgerufen\nMethod1 wechselt den Zustand des Objektes.\n";
    aendereZustand(b,folgezustand::Instance());
}

void initZustand::method2(base* b)
{
    std::cout<<"method2 aus initZustand aufgerufen\n";
}

void initZustand::enter()
{
    std::cout<<"In den Zustand initZustand gewechselt!\n";
}

void initZustand::leave()
{
    std::cout<<"Der Zustand initZustand wird verlassen!\n\n";
}



folgezustand.h (zweiter konkreter Zustand)

C++:
#ifndef __FOLGEZUSTAND_H
#define __FOLGEZUSTAND_H

#include "zustand.h"

class folgezustand:public zustand
{
public:
    static zustand* Instance()
    {
        if (_instance==0)
            _instance= new folgezustand();
        return _instance;
    };
    virtual void method1(base* b);
    virtual void method2(base* b);
    void method3();
protected:
    virtual void enter();
    virtual void leave();
private:
    static folgezustand* _instance;
    folgezustand(){};
    folgezustand(folgezustand& rhs){};
};
#endif



folgezustand.cpp

C++:
#include <iostream>
#include "zustand.h"
#include "folgezustand.h"
#include "initZustand.h"

folgezustand* folgezustand::_instance=0;

void folgezustand::method1(base* b)
{
    std::cout<<"method1 aus folgezustand aufgerufen\n";
}

void folgezustand::method2(base* b)
{
    std::cout<<"method2 aus folgezustand aufgerufen\n";
    std::cout<<"method2 aus folgezustand aendert den Zustand des Objektes.\n";
    method3();
    aendereZustand(b,initZustand::Instance());
}

void folgezustand::method3()
{
    std::cout<<"..und ruft die,in der Basisklasse unbekannte,Methode 3 auch noch auf...\n";
}

void folgezustand::enter()
{
    std::cout<<"In den Zustand folgezustand gewechselt!\n";
}

void folgezustand::leave()
{
    std::cout<<"Der Zustand folgezustand wird verlassen!\n\n";
}



und noch die main.cpp....

C++:
#include <iostream>
#include "base.h"

using namespace std;

int main()
{
    base b;// initialer Zustand ist initZustand
    b.method2();//keine Zustandsänderung
    b.method1();//Zustandsaenderung nach folgezustand
    b.method1();//keine Zustandsaenderung
    b.method2();//Zustandsaenderung nach initZustand

    return 0;
}



Wenn dir das Ganze zu "aufwendig" ist kannst du dir,anstelle von mem_fun_t, mal boost::function anschauen.

MfG Spacelord
--
.....Ich mach jetzt nämlich mein Jodeldiplom.Dann hab ich endlich was Eigenes.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: