Forum: PC-Programmierung C++ Klasse Zeiger auf Instanz übergeben


von Karl (Gast)


Lesenswert?

Im speziellen stell ich mir die Frage ob ich über den Konstruktor den 
Zeiger auf das gerade angelegte Klassenobjekt übergeben kann.

Anders gefragt wie kann ich am besten der Klasse die Adresse der gerade 
erstellten Instanz bekanntgeben?

Zuerst die Instanz bilden und dann einer Methode den Zeiger auf die 
Instanz  übergeben, kann ich mir schon vorstellen.

von Wilhelm M. (wimalopaan)


Lesenswert?

this

von Rolf M. (rmagnus)


Lesenswert?

Karl schrieb:
> Im speziellen stell ich mir die Frage ob ich über den Konstruktor den
> Zeiger auf das gerade angelegte Klassenobjekt übergeben kann.

Der Konstruktor der Klasse kennt doch die Adresse des Objekts. Warum 
musst du ihm die "übergeben"?

> Anders gefragt wie kann ich am besten der Klasse die Adresse der gerade
> erstellten Instanz bekanntgeben?

Geht es dir darum, eine Liste alle Instanzen zu führen, oder was soll 
die Klasse mit einem Zeiger auf das Objekt machen?

> Zuerst die Instanz bilden und dann einer Methode den Zeiger auf die
> Instanz  übergeben, kann ich mir schon vorstellen.

Erkläre doch mal etwas genauer, was du vor hast.

von Karl (Gast)


Lesenswert?

Ich nutze FreeRtos welches in C geschrieben ist.
Nun übergebe ich der Funktion xTaskCreate die "Thread" Methode der 
Klasse Ca, von der ich vor der xTaskCreate Funktion eine Instanz oCa 
erstellt habe.

Da ich nun in dieser statischen Thread Methode keine  weitere 
KlassenMethode von oCa aufrufen konnte, habe ich im Argument 
"pvParameters" von xTaskCreate eben die Adresse von oCa übergeben.

Ein Typcast auf Ca und nun konnte ich aus der Thread Metode von oCa auf 
alle anderen Methoden von oCa zugreifen.

Soweit so gut. Ob das sauber ist ...... ?

Nun frage ich mich ob ich die Adresse der Instanz oCa über xTaskCreate 
pvParameters     übergeben muss.

Tippe gerade auf dem Handy..... hoffe es war verständlich, ansonsten 
kann ich später code beifügen.

von Mikro 7. (mikro77)


Lesenswert?

Karl schrieb:
> Da ich nun in dieser statischen Thread Methode keine  weitere
> KlassenMethode von oCa aufrufen konnte, habe ich im Argument
> "pvParameters" von xTaskCreate eben die Adresse von oCa übergeben.

Richtig. Und ja, man kann den Thread bereits im Constructor starten.

Ich bevorzuge es, einen Constructor weitgehend Exception-frei zu halten 
und vollständig zu initialisieren bevor drauf zugegriffen wird: Erst 
wird eine Instanz der Klasse erzeugt (mit "leerem" Thread) und dann der 
Thread gestartet. Bspw.
1
   static Foo* Foo::make(...)
2
   {
3
        auto foo = new Foo(...) ;
4
        foo->thread = new Thread(start,(void*)foo) ;
5
        return foo ;
6
   }

Im echten Anwendungsfall würde ich Smart Pointer nutzen.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Mikro,

das ist für mich noch erklärungsbedürftig.

Was ich verstehe:

Eine statische Implementierung der Methode make() der Klasse Foo.

In dieser Methode wird ein Objekt foo der Klasse FOO, in der wir uns 
gerade befinden, auf dem Heap erzeugt.
Was bedeutet hier auto?
Interpretiere ich das auto hier richtig?
Foo foo = new Foo(...) ; äquivalent?


Diese Zeile verstehe ich eigentlich nicht.

foo->thread = new Thread(start,(void*)foo) ;

was passiert hier genau?

Danke!

von Chris F. (chfreund) Benutzerseite


Lesenswert?

Karl schrieb:
> Was bedeutet hier auto?

Das gibt es ab C++11. Der Compiler folgert durch den Ergebnistyp der 
Erstellung den Typ. Ist total unnötig hier, Du kannst auch einfach
1
Foo * pF = new Foo(...);
2
pF->thread = new Thread(start,(void*)foo) ;
3
return pF;

schreiben. Es wird ja sowieso in der nächsten Zeile ein Memberzugriff 
über einen Zeiger verwendet.

Insgesamt ist das alles eh sehr komisch. Die üblichere Variante um Tasks 
anzulegen ist, dass diese run() und activate() Methoden bekommen.

Wenn Du also ein "aktives Objekt" machen willst, dann sollte das nur 
eine Taskklasse sein und bei activate() wird dann
1
BaseType_t xTaskCreate(    TaskFunction_t pvTaskCode, ...)

von freeRTOS aufgerufen, mit run() als Taskfunction.

Dieser Matsch wo man um jeden Preis neue Sprachmittel einsetzt ist für 
mich nicht nachvollziehbar. Das auto ist für Lambdafunktionen und 
Vorgänge wo sich später der Rückgabewert ändert oder wo 
Initialisierlisten einfacher im Code dargestellt werden sollen (C++14).

von Karl (Gast)


Lesenswert?

Ich verstehe folgenden Zusammenhang noch nicht wirklich.
1
Foo * pF = new Foo(...);
2
pF->thread = new Thread(start,(void*)pF) ;
3
return pF;


Hier (unten) wird wohl auf dem Heap durch das new ein Objekt der Klasse 
Foo angelegt.
pF enthält die Adresse auf dieses Objekt und der Pointer *pF zeigt 
darauf, wenn man das so interpretieren kann. Über pF-> werden public 
Methoden und Felder im Objekt pF der Klasse Foo erreicht.
1
Foo * pF = new Foo(...);


Was passiert hier unten?
Foo hat eine Methode thread.
Und es gibt eine Klasse Thread. Deren Konstruktor zwei Parameter 
übergeben bekommt. "start" und die Adresse von pF.

Meine Vermutung:
Wird hier ein Objekt der Klasse Thread auf dem Heap angelegt und
der Zeiger pF->thread enthält durch "=" die Adresse der Instanz von 
Thread?
Thread ist doch hier eine Klasse, oder?
1
pF->thread = new Thread(start,(void*)pF) ;

von Chris F. (chfreund) Benutzerseite


Lesenswert?

Karl schrieb:
> Meine Vermutung:
> Wird hier ein Objekt der Klasse Thread auf dem Heap angelegt und
> der Zeiger pF->thread enthält durch "=" die Adresse der Instanz von
> Thread?
> Thread ist doch hier eine Klasse, oder?pF->thread = new
> Thread(start,(void*)pF) ;

Was die Klasse "Thread" ist hat

Wie schon vorher gesagt: Wenn Du für Deinen Fall ein aktives Objekt 
erstellen willst, dann mach das mit den Methoden á la run() und 
activate() und nicht mit zusätzlichen Klassen.

Vielleicht meint der mikro77 auch sowas in der Art von
1
std::thread
 aus C++11 und das "start" eine globale run()-Funktion ist die das 
Objekt als Argument annimmt.
Dann gibt er dem aufrufenden Code einen Zeiger auf das Objekt zurück und 
es gibt einen Member "thread" der dann den laufenden stl-Thread 
repräsentiert. Warum  run() selbst keine Methode darin sein darf ist mir 
auch nicht klar. Ist aber auch egal, weil das hier Kaffeesatzleserei 
ist.

von Karl (Gast)


Lesenswert?

Chris F.

Erstell doch einen etwas ausführlichen Weg, wie du das sauber machen 
würdest.
Ich habe da noch so meine Anlaufschwierigkeit.

Ich z.B. erstelle ein Objekt auf dem Heap.
Dessen Konstruktor bekommt ein QueueHandel übergeben, wenn ich in der 
Klasse ein Queue habe.

xTaskCreate bekommt dann noch in einem parameter die statische thread 
funktion übergeben und über einen weiteren Parameter übergebe ich die 
Adresse des Objektes, damit ich über einen static cast auf weitere 
methoden des Objektes zugreifen kann. Wegen C und C++.

Nun suche ich eben nach einem Weg, dass möglicherweise optimaler zu 
lösen.

Nur konnte ich dir mangels Kenntnisse nicht 100% folgen.

von Mikro 7. (mikro77)


Lesenswert?

Hallo Karl,

du kannst das einfach so machen, wie du selbst eingangs beschrieben 
hast. Das wäre in etwa:
1
struct Thread
2
{
3
  Thread(void(*f)(void*),void *data) ;
4
} ;
5
struct Foo
6
{
7
    Thread thread ;
8
    Foo() : thread(&Foo::start,(void*)this) {} 
9
    static void start(void *data) ;
10
} ;

Thread ist hier eine Klasse die zum Ausführen asynchroner Funktionen 
dient.  Ob das nun ein RTOS Task (handle) ist, ein Posix Thread (handle) 
oder was auch immer, typischerweise gibt es einen Einsprungspunkt (hier 
start) und mindestens einen Parameter (hier die Instanz der Klasse).

Problematisch wird es wenn die start Funktion bereits auf die Instanz 
zugreift, bevor diese überhaupt vollständig initialisiert ist. Im obigen 
Beispiel kann das nicht passieren. In der Praxis hat man aber häufig 
mehr als einen Parameter, und es ist nicht immer einfach umzusetzen, 
dass der Thread-Aufruf erst am Ende der Initialisierungsliste ausgeführt 
wird.

Komplizierter wird es zudem, wenn die Initialisierung nicht oder nicht 
vollständig über die Initialisierungsliste geschieht, und wenn mit 
Exceptions gerechnet werden muss.

Ein Lösungsansatz wäre das von mir oben beschriebene Verfahren: Erzeuge 
die Instanz mit "leerem" Thread, und starte den Thread danach.
1
struct Foo
2
{
3
    Thread* thread ;
4
    Foo() : thread(nullptr) {} 
5
    static void start(void *data) ;
6
} ;
7
Foo* make()
8
{
9
    auto foo = new Foo() ;
10
    foo->thread = new Thread(start,(void*)foo) ;
11
    return foo ;
12
}

Jetzt wird die Instanz ohne Thread erzeugt, und der Thread im Anschluss 
gestartet. Das hat allerdings den Nachteil, dass der Benutzer immer mit 
zwei Zuständen von Foo rechnen muss (mit oder ohne gestarteten Thread). 
Also versteck man den Constructor und macht nur die make Funktion 
sichtbar.
1
struct Foo
2
{
3
    static Foo* make() ;
4
private:
5
    Thread* thread ;
6
    Foo() : thread(nullptr) {} 
7
    static void start(void *data) ;
8
} ;

Die Klasse "Thread" dient hier lediglich als generischer Platzhalter. 
Diese kannst du durch den RTOS Task ersetzen.

Karl schrieb:
> Was bedeutet hier auto?

http://en.cppreference.com/w/cpp/language/auto

Karl schrieb:
> Diese Zeile verstehe ich eigentlich nicht.
> foo->thread = new Thread(start,(void*)foo) ;

Typischerweise werden Threads mit einer Start-Funktion (start) und einem 
Zeiger auf Daten aufgerufen (hier die Instanz der Klasse Foo). Wie du 
das als RTOS Taks abbildest, musst du wissen. Auf Anhieb sehe ich die 
Parameter pvTaskCode und pvParameters.

Beitrag #5138094 wurde von einem Moderator gelöscht.
Beitrag #5140111 wurde von einem Moderator gelöscht.
Beitrag #5140120 wurde von einem Moderator gelöscht.
Beitrag #5140143 wurde von einem Moderator gelöscht.
Beitrag #5142607 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.