Guten Abend, ich bin noch am Anfang meiner C++-Gehversuche, aber beim Sichten aller Array-ähnlichen Konstrukte, die ich in meinem Buch und auf https://en.cppreference.com bislang gesichtet habe, scheint es nur "arrays" gleicher Klassen zu geben. Angenommen, ich hätte eine Gruppe von abgeleiteten Klassen einer Basisklasse, die alle die gleiche Methode (z.B. disp()) besitzen - gibt es eine Möglichkeit, diese in eine Art array zu packen und mit einer for-Schleife darüberzuiterieren? Kann mir jemand das passende Stichwort geben?
:
Pointer (bzw. Iteratoren) und virtuelle Funktionen. Das ist DIE Grundstruktur in C++. Oliver
:
Bearbeitet durch User
Du kannst zB Objekte von Typ LKW, Motorrad, Fahrrad und PKW haben, die alle von "Fahrzeug" abgeleitet sind. Die kannst du dann in ein Array von Fahrzeugen speichern und kannst die Methode fahren() aufrufen, falls diese Methode in Fahrzeug definiert ist.
Gähn schrieb: > Die kannst du dann in ein Array von Fahrzeugen Wenn ich die Objekte aus "Motorrad", "Auto" etc. in ein Array mit "Fahrzeug" packe - gehen nicht die zusätzlichen Attribute von "Motorrad" verloren? Wird dann nicht die Methode Auto::fahren() anstelle Motorrad::fahren() aufgerufen (oder geht schief, wenn Auto::fahren() nur eine virtuelle Funktion ist?)
:
Bearbeitet durch User
Walter T. schrieb: > Gähn schrieb: >> Die kannst du dann in ein Array von Fahrzeugen > > Wenn ich die Objekte aus "Motorrad", "Auto" etc. in ein Array mit > "Fahrzeug" packe - gehen nicht die zusätzlichen Attribute von "Motorrad" > verloren? Wird dann nicht die Methode Auto::fahren() anstelle > Motorrad::fahren() aufgerufen (oder geht schief, wenn Auto::fahren() mir > eine virtuelle Funktion ist? Richtig, das ist mal wieder absoluter Blödsinn was hier geschrieben wird.
1 | #include <array> |
2 | #include <cstdio> |
3 | #include <memory> |
4 | |
5 | struct Fahrzeug { |
6 | virtual void foo() const { puts(__PRETTY_FUNCTION__); } |
7 | };
|
8 | |
9 | struct Auto : Fahrzeug { |
10 | void foo() const final { puts(__PRETTY_FUNCTION__); } |
11 | };
|
12 | |
13 | struct LKW : Fahrzeug { |
14 | void foo() const final { puts(__PRETTY_FUNCTION__); } |
15 | };
|
16 | |
17 | int main() { |
18 | std::array<std::unique_ptr<Fahrzeug>, 2u> fahrzeuge{}; |
19 | fahrzeuge[0u] = std::make_unique<Auto>(); |
20 | fahrzeuge[1u] = std::make_unique<LKW>(); |
21 | fahrzeuge[0u]->foo(); // Auto::foo |
22 | fahrzeuge[1u]->foo(); // LKW::foo |
23 | }
|
:
Bearbeitet durch User
Das funktioniert natürlich auch mit der gewünschten for-Schleife:
1 | for(const auto& f: fahrzeuge) |
2 | f->foo(); |
Danke, das heisst, prinzipiell ist es möglich. Dann habe ich mit unique_ptr und "iterator" ja erst einmal wieder den passenden Lesestoff. Um ehrlich zu sein, ist die Aussicht, über eine Liste unterschiedlicher Objekte iterieren zu können, überhaupt erst der Grund, mit C++ angefangen zu haben. Könnt ihr mir noch verraten, ob es prinzipiell möglich ist, diese Liste komplett im Flash eines Mikrocontrollers zu halten? (Auf Anhieb sehe ich da kein ernsthaftes Hindernis, aber ich kenne die Möglichkeiten der Sprache noch nicht.)
:
Bearbeitet durch User
Walter T. schrieb: > Um ehrlich zu sein, ist die Aussicht, über eine Liste unterschiedlicher > Objekte iterieren zu können, überhaupt erst der Grund, mit C++ > angefangen zu haben. 1) das wäre auch in jeder anderen OOP-Sprache möglich. Du kannst immer Objekte iterieren, die eine gemeinsame Basisklasse haben und die in einem Konstrukt stecken, über das man iterieren kann. 2) man kann das auch in Nicht-OOP-Sprachen realisieren. Man muss halt nur das OOP-Feature "Polymorphie" mit den Mitteln dieser Sprache abbilden. Für C würde das beispielweise bedeuten: Iteriert wird z.B. über ein Array von Strukturen und diese Strukturen enthalten Funktionszeiger für all den Scheiß, der bei C++ als virtuelle Methoden auftaucht. Der Witz ist: viel weniger schick als in C++ wird es auch in C nicht. Jedenfalls sobald man für ein spezielles Objekt im Zuge der Iteration dann doch das braucht, was nicht schon in der gemeinsamen Basisklasse als virtuelle Methode existiert. Dann sieht das in C++ schon wieder fast genauso aufwendig aus, wie in C...
Walter T. schrieb: > Danke, das heisst, prinzipiell ist es möglich. Dann habe ich mit > unique_ptr und "iterator" ja erst einmal wieder den passenden Lesestoff. > > Um ehrlich zu sein, ist die Aussicht, über eine Liste unterschiedlicher > Objekte iterieren zu können, überhaupt erst der Grund, mit C++ > angefangen zu haben. > > Könnt ihr mir noch verraten, ob es prinzipiell möglich ist, diese Liste > komplett im Flash eines Mikrocontrollers zu halten? (Auf Anhieb sehe ich > da kein ernsthaftes Hindernis, aber ich kenne die Möglichkeiten der > Sprache noch nicht.) Code landet "immer" in der .text Section eines Mikrocontrollers und damit im Flash. Egal von welcher Programmiersprache der kommt. Ich glaub du würdest gerne andere Fragen stellen, bist aber noch nicht weit genug um diese formulieren zu können. Du musst glaub ich erst einmal verstehen was Polymorphie im Prinzip ist, welche Arten es davon gibt und wie diese in C++ repräsentiert werden können.
Walter T. schrieb: > Könnt ihr mir noch verraten, ob es prinzipiell möglich ist, diese Liste > komplett im Flash eines Mikrocontrollers zu halten? Die Liste der Obejkte ist nur eine Liste von Zeigern (auch wenn die möglicherweise Referenzen heißen: im Kern sind's doch nur dämliche Zeiger). Das Problem ist eher, dass die Objekte selber Speicherplatz im RAM benötigen. Und virtuelle Methoden tragen hier sogar ziemlich dick auf...
c-hater schrieb: > 2) man kann das auch in Nicht-OOP-Sprachen realisieren. Man muss halt > nur das OOP-Feature "Polymorphie" mit den Mitteln dieser Sprache > abbilden. Für C würde das beispielweise bedeuten: Iteriert wird z.B. > über ein Array von Strukturen und diese Strukturen enthalten > Funktionszeiger für all den Scheiß, der bei C++ als virtuelle Methoden > auftaucht. Ja, habe ich momentan. Das ist häßlich, und die Definition ist extrem fehleranfällig. (Entweder Identifikation des Datentyps über enum und Aufruf der ausführenden Funktion über switch/case oder eben über Funktionszeiger direkt im Array oder beides.) Da erhoffe ich mir über templates + virtual functions eine erhebliche Vereinfachung. Vincent H. schrieb: > Code landet "immer" in der .text Section eines Mikrocontrollers und > damit im Flash. Egal von welcher Programmiersprache der kommt. > [...] > Ich glaub du würdest gerne andere Fragen stellen, bist aber noch nicht > weit genug um diese formulieren zu können. Ich meine nicht den Code, sondern das array. Wenn aus dem array ein Element im RAM landet, kann ich damit leben. Wenn das komplette array ins RAM geladen wird, bin ich am Ende. c-hater schrieb: > Das Problem ist eher, dass die Objekte selber Speicherplatz im RAM > benötigen. Und virtuelle Methoden tragen hier sogar ziemlich dick auf... Das heisst, konstante Objekte werden vor dem Lesen von Attributen grundsätzlich ins RAM kopiert? Oder vor dem Ausführen einer Methode? Oder wird nur der vtable ins RAM kopiert? Nicht, dass das schlimm wäre... damit kann ich leben. Nur nicht mit allen gleichzeitig.
:
Bearbeitet durch User
c-hater schrieb: > Das Problem ist eher, dass die Objekte selber Speicherplatz im RAM > benötigen. Und virtuelle Methoden tragen hier sogar ziemlich dick auf... VTables brauchen keinen RAM. (außer wohl beim AVR?) Walter T. schrieb: > Das heisst, konstante Objekte werden vor dem Lesen von Attributen > grundsätzlich ins RAM kopiert? Oder vor dem Ausführen einer virtuellen > Funktion? Es ist eigentlich ganz simpel. C++ tut was C tut. Virtuelle Funktionen haben nichts mit dem RAM Verbrauch zu tun.
Vincent H. schrieb: > Walter T. schrieb: >> Gähn schrieb: >>> Die kannst du dann in ein Array von Fahrzeugen >> >> Wenn ich die Objekte aus "Motorrad", "Auto" etc. in ein Array mit >> "Fahrzeug" packe - gehen nicht die zusätzlichen Attribute von "Motorrad" >> verloren? Wird dann nicht die Methode Auto::fahren() anstelle >> Motorrad::fahren() aufgerufen (oder geht schief, wenn Auto::fahren() mir >> eine virtuelle Funktion ist? > > Richtig, das ist mal wieder absoluter Blödsinn was hier geschrieben > wird. Wieso denn Blödsinn? Selbstverständlich ist es möglich, es so zu machen wie von Gähn beschrieben. Hier der Beweis: Code im Anhang und die Ausgabe siehe unten.
1 | D:\Dateien\C++>g++ -Wall Fahrzeug.cpp -o Fahrzeug.exe |
2 | |
3 | D:\Dateien\C++>Fahrzeug.exe |
4 | Ich bin der LKWuppdich |
5 | Mim Moped durch die Stadt |
6 | I want to ride my bicycle, I want to ride my bike |
7 | Fahrn Fahrn Fahrn auf der Autobahn |
Wer statische Polymorphie möchte, nimmt std::variant und z.B. std::visit.
Vincent H. schrieb: > c-hater schrieb: >> Das Problem ist eher, dass die Objekte selber Speicherplatz im RAM >> benötigen. Und virtuelle Methoden tragen hier sogar ziemlich dick auf... > > VTables brauchen keinen RAM. (außer wohl beim AVR?) > > Walter T. schrieb: >> Das heisst, konstante Objekte werden vor dem Lesen von Attributen >> grundsätzlich ins RAM kopiert? Oder vor dem Ausführen einer virtuellen >> Funktion? > > Es ist eigentlich ganz simpel. C++ tut was C tut. > Virtuelle Funktionen haben nichts mit dem RAM Verbrauch zu tun. Natürlich brauchen vtables RAM. Selbst wenn du sie in C selber bastelst, brauchen sie RAM (function pointer). Die Information welcher Art ein Objekt ist, muss zur Laufzeit/dynamisch vorliegen. So funktioniert dynamic_cast und RTTI und deshalb hat es auch entsprechenden overhead. DU kannst deine Klasse auch gnu::packed machen und dann sizeof(...) vergleichen. https://en.wikipedia.org/wiki/Virtual_method_table#cite_note-4 https://en.cppreference.com/w/cpp/language/virtual
Walter T. schrieb: > Das heisst, konstante Objekte werden vor dem Lesen von Attributen > grundsätzlich ins RAM kopiert? Oder vor dem Ausführen einer Methode? > Oder wird nur der vtable ins RAM kopiert? Das kommt drauf an. Es gibt dafür keine Notwendigkeit, aber je nach Compiler und Architektur wird es ggf. trotzdem gemacht. Vincent H. schrieb: > c-hater schrieb: >> Das Problem ist eher, dass die Objekte selber Speicherplatz im RAM >> benötigen. Und virtuelle Methoden tragen hier sogar ziemlich dick auf... > > VTables brauchen keinen RAM. (außer wohl beim AVR?) Sofern sich da nichts geändert hat, kopiert AVR-GCC beim Start alle vtables in den RAM. Das liegt aber im Wesentlichen daran, dass beim AVR RAM und Flash unterschiedlich angesprochen werden müssen und das im Compiler so nicht vorgesehen ist. Mark B. schrieb: > Wieso denn Blödsinn? Selbstverständlich ist es möglich, es so zu machen > wie von Gähn beschrieben. Nur arbeitet dein Code halt nicht so wie beschrieben. Er speichert keine Fahrzeuge im vector, sondern Zeiger. C++ Kenner schrieb: >> Es ist eigentlich ganz simpel. C++ tut was C tut. >> Virtuelle Funktionen haben nichts mit dem RAM Verbrauch zu tun. > > Natürlich brauchen vtables RAM. Sie brauchen Speicher. Ob das RAM sein muss, hängt nur von den Limitierungen des Compilers ab. > Selbst wenn du sie in C selber bastelst, brauchen sie RAM (function > pointer). Nein. vtables ändern sich zur Laufzeit nicht. Und bei Objekten, die komplett konstant sind, spricht auch nichts dagegen, dass der vtable-Pointer das auch ist. In dem Fall wird exakt 0 RAM dafür benötigt. > Die Information welcher Art ein Objekt ist, muss zur Laufzeit/dynamisch > vorliegen. Nur wenn das Objekt zur Laufzeit dynamisch erzeugt wurde. Und auch dann ist es nur der vtable-Pointer, nicht die ganze vtable, die im RAM stehen muss.
C++ Kenner schrieb: > Wer statische Polymorphie möchte, nimmt std::variant und z.B. > std::visit. std::variant ist, auch wenn es beim dispatch via std::visit so aussieht, eigentlich keine statische Polymorphie.
Danke für Deinen Beitrag! Es ist nicht ganz, was ich suche *), aber trotzdem interessant. Mark B. schrieb: > Hier der Beweis: Code im Anhang Dieser Teil hier:
1 | int main() |
2 | {
|
3 | LKW *lkw1 = new LKW; |
4 | Motorrad *motorrad1 = new Motorrad; |
5 | Fahrrad *fahrrad1 = new Fahrrad; |
6 | PKW *pkw1 = new PKW; |
7 | |
8 | |
9 | Fahrzeug* fahrzeug_array[4] = { lkw1, motorrad1, fahrrad1, pkw1 }; |
10 | |
11 | for (int i=0; i<4; i++) |
12 | {
|
13 | fahrzeug_array[i]->fahren(); |
14 | }
|
15 | |
16 | return 0; |
17 | }
|
wird in cppinsights zu:
1 | int main() |
2 | {
|
3 | LKW * lkw1 = new LKW(); |
4 | Motorrad * motorrad1 = new Motorrad(); |
5 | Fahrrad * fahrrad1 = new Fahrrad(); |
6 | PKW * pkw1 = new PKW(); |
7 | Fahrzeug * fahrzeug_array[4] = {static_cast<Fahrzeug *>(lkw1), static_cast<Fahrzeug *>(motorrad1), static_cast<Fahrzeug *>(fahrrad1), static_cast<Fahrzeug *>(pkw1)}; |
8 | for(int i = 0; i < 4; i++) |
9 | {
|
10 | fahrzeug_array[i]->fahren(); |
11 | }
|
12 | |
13 | return 0; |
14 | }
|
Es läuft also implizit ein static cast auf einen Zeiger auf einen Objekt der Klasse Fahrzeug, wenn das Zeiger-Array erzeugt wird. Wieso wird ein Zeiger, der auf ein Objekt der Klasse Fahrzeug zeigt, als Objekt der Klasse Motorrad dereferenziert? *) letztendlich will ich am Ende eine Konstante mit tabellenartiger Initializer-Liste. Wenn ich tausend konstante Variablen mit Zeigern drauf erzeugen muss, wird es noch unübersichtlicher als sowieso schon. Aber ich gehe mal davon aus, dass das irgendwie --wie auch bei einem cstring-Array-- geht.
Walter T. schrieb: > Wieso wird ein > Zeiger, der auf ein Objekt der Klasse Fahrzeug zeigt, als Objekt der > Klasse Motorrad dereferenziert? Wird es nicht. Lediglich beim Aufruf von virtuellen Funktionen wird beim Aufruf eben die Funktion der abgeleiteten Klasse aufgerufen. Sie muss dazu aber auch in der Basisklasse deklariert sein. Walter T. schrieb: > *) letztendlich will ich am Ende eine Konstante mit tabellenartiger > Initializer-Liste. Führ das doch mal genauer aus. Vielleicht kann man eine bessere Möglichkeit finden.
Walter T. schrieb: > *) letztendlich will ich am Ende eine Konstante mit tabellenartiger > Initializer-Liste. https://youtu.be/zBkNBP00wJE Oliver
Programmierer schrieb: > Führ das doch mal genauer aus. Vielleicht kann man eine bessere > Möglichkeit finden. In Pseudocode will ich irgendwann einmal etwas wie das hier implementiert haben:
1 | // Konstantes Array im Flash
|
2 | const Gfxobj MainScreen[] = |
3 | {
|
4 | Circle({.x=0, .y=10, .r=20, .color=color::red, .bgcolor::red }), |
5 | Rectangle({.x=100, .y=10, .w=20, .h=10, .color=color::blue }), |
6 | Textfield({.x=100, .y=10, .w=20, .h=10, .color=color::black, .s = "Hallo" }), |
7 | ...
|
8 | }
|
9 | |
10 | int main() |
11 | {
|
12 | ...
|
13 | |
14 | for( auto Obj : MainScreen ) |
15 | {
|
16 | Obj->draw(); |
17 | }
|
18 | |
19 | ...
|
20 | }
|
Sprich: große, konstate Arrays mit gemischten Typen und darüber iterieren können. Aber ich suche die Lösung noch gar nicht jetzt. Ich bin bei meinem C++-Buch erst bei Seite 135. Der Zweck des Threads war nur, abschätzen zu können, ob ich hier überhaupt in die richtige Richtungs ziele (und nach Seite 1007 die Lösung finden können werde). Sprich: Ob es für mein aktuelles Problem sinnvoller ist, C++ zu lernen, oder meine Zeit lieber darin zu investieren, einen Codegenerator für C zu schreiben.
Walter T. schrieb: > Könnt ihr mir noch verraten, ob es prinzipiell möglich ist, diese Liste > komplett im Flash eines Mikrocontrollers zu halten? (Auf Anhieb sehe ich > da kein ernsthaftes Hindernis, aber ich kenne die Möglichkeiten der > Sprache noch nicht.) falls es das deine Frage ist - man kann die Objekte auch nur auf dem Stack, oder global anlegen - falls deinen Objekte nicht auf dem Heap liegen müssen
1 | #include <array> |
2 | #include <cstdio> |
3 | #include <memory> |
4 | |
5 | struct Fahrzeug { |
6 | virtual void foo() const { puts(__PRETTY_FUNCTION__); } |
7 | };
|
8 | |
9 | struct Auto : Fahrzeug { |
10 | void foo() const final { puts(__PRETTY_FUNCTION__); } |
11 | };
|
12 | |
13 | struct LKW : Fahrzeug { |
14 | void foo() const final { puts(__PRETTY_FUNCTION__); } |
15 | };
|
16 | |
17 | Auto f1; |
18 | |
19 | int main() { |
20 | std::array<Fahrzeug*, 3u> fahrzeuge{}; |
21 | |
22 | LKW f2; |
23 | Auto f3; |
24 | |
25 | fahrzeuge[0u] = &f1; |
26 | fahrzeuge[1u] = &f2; |
27 | fahrzeuge[2u] = &f3; |
28 | |
29 | fahrzeuge[0u]->foo(); // Auto::foo |
30 | fahrzeuge[2u]->foo(); // LKW::foo |
31 | fahrzeuge[1u]->foo(); // Auto::foo |
32 | }
|
Wenn alles konstant ist, kann man das auch statisch machen:
1 | #include <iostream> |
2 | #include <tuple> |
3 | |
4 | class Auto |
5 | {
|
6 | public:
|
7 | constexpr void foo() const { puts(__PRETTY_FUNCTION__); } |
8 | };
|
9 | |
10 | class Lkw |
11 | {
|
12 | public:
|
13 | constexpr void foo() const { puts(__PRETTY_FUNCTION__); } |
14 | };
|
15 | |
16 | int main() |
17 | {
|
18 | Auto car; |
19 | Lkw lkw; |
20 | const std::tuple<Auto&, Lkw&> myTuple{car, lkw}; |
21 | |
22 | std::get<0>(myTuple).foo(); |
23 | std::get<1>(myTuple).foo(); |
24 | |
25 | }
|
Oliver
Die Frage ist: Kann ich ein globales Array mit anonymen Variablen anlegen, wenn diese einen unterschiedlichen Typ haben?
Walter T. schrieb: > Sprich: große, konstate Arrays mit gemischten Typen und darüber > iterieren können. Das Problem ist, dass die unterschiedlichen Typen unterschiedlich groß sind, und daher nicht in ein Array gepackt werden können, denn wie soll die Adresse von Element X berechnet werden, wenn nicht mit X*sizeof(T) ? Daher der Trick, dass im Array nur der Zeiger steht, denn alle Zeiger sind gleich groß. Das impliziert aber, dass die eigentlichen Objekte noch irgendwo anders abgelegt sind, wo der Zeiger dann hin zeigt. Auf dem Heap anlegen via "new" geht zwar, ist aber für Mikrocontroller ineffizient, und wenn die ganze Struktur sowieso fix ist, auch unnötig. Am Logischsten wäre es, vor "MainScreen" alle Objekte einmal manuell zu definieren, und im Array dann Pointer darauf anzulegen. Das wäre allerdings eine Menge Boilerplate-Code. Daher habe ich mal aus Spaß im Anhang eine Klasse PolymorphicContainer implementiert, welche die Objekt-Instanzen in einem Tupel ablegt, und in einem Array die Pointer darauf. Damit kann man den Code fast genau so hinbekommen wie du ihn haben wolltest:
1 | constexpr PolymorphicContainer MainScreen { |
2 | Circle({.x=0, .y=10, .r=20, .fgcolor=color::red, .bgcolor=color::red }), |
3 | Rectangle({.x=100, .y=10, .w=20, .h=10, .fgcolor=color::blue }), |
4 | Textfield({.x=100, .y=10, .w=20, .h=10, .fgcolor=color::black, .s="Hallo" }), |
5 | };
|
6 | |
7 | int main () { |
8 | for (const Geo* obj : MainScreen.array) { |
9 | obj->draw (); |
10 | }
|
11 | }
|
Die designated Initializer erfordern C++20. So kannst du das Array iterieren und virtuelle Funktionen wie gehabt nutzen. Falls gewünscht kannst du per "std::get<0> (MainScreen.tuple)" o.ä. direkt auf bestimmte Elemente zugreifen und erhältst dann auch den konkreten Typ, allerdings muss der Index fix und dem Compiler bekannt sein. Zum "Vergleich" habe ich noch die Funktion "staticIteration" mitgeliefert, mit der man durch das Tupel iterieren kann, ohne den Umweg über das Array und Zeiger:
1 | staticIteration (MainScreen.tuple, [&] (const auto& elem) { |
2 | elem.draw (); |
3 | });
|
Die Variable "elem" hat dabei immer den konkreten Typ, somit kann man auf die spezifischen Elemente der abgeleiteten Klasse zugreifen. Somit werden hier keine virtuellen Funktionen gebraucht. Allerdings wird hier keine Schleife erzeugt, sondern es wird für jedes Element im Tupel Code für den Aufruf generiert. Dadurch kann der Code sehr schnell sehr groß werden, was dann nicht unbedingt geeignet ist.
Walter T. schrieb: > Die Frage ist: Kann ich ein globales Array mit anonymen Variablen > anlegen, wenn diese einen unterschiedlichen Typ haben? du hast mindestens 4 Antworten dazu
Programmierer schrieb: > Daher habe ich mal aus Spaß im Anhang eine Klasse PolymorphicContainer > implementiert, Die 100 Punkte für maximale Anfängerverwirrung gehen definitiv an dich :) @Walter T. einfach ignorieren :)
Die Frage ist ungenau gestellt. Was sind "anonyme Variablen"? Ein Array kann nur Elemente von selben Typ speichern, das ist in C++ nicht anders als in C. Der Typ kann aber, wie oben in den Beispielen, ein Pointer oder eine Referenz auf eine Basisklasse sein, während die dazugehörigen Instanzen abgeleitete Klassen der Basisklasse sind. Oliver
std::array<Fahrzeug*, 3u> fahrzeuge{}; entspricht Fahrzeug* fahrzeug[3];
Programmierer schrieb: > [...] Danke, dafür werde ich mir wohl noch etwas Zeit nehmen müssen, bis ich das verstehe. Programmierer schrieb: > Das Problem ist, dass die unterschiedlichen Typen unterschiedlich groß > sind, und daher nicht in ein Array gepackt werden können, denn wie soll > die Adresse von Element X berechnet werden, wenn nicht mit X*sizeof(T) ? Oliver S. schrieb: > Die Frage ist ungenau gestellt. Was sind "anonyme Variablen"? Meinem Verständnis nach analoges Beispiel in C:
1 | const char *const MyList[3] = {"Text1", "Text2", "Text3"}; |
List ist ein Pointer Array auf anonyme Variablen gleichen Typs unterschiedlicher Größe, lässt sich aber in einem Rutsch hinschreiben. In C ein Beispiel mit einem Pointer Array auf anonyme Variablen unterschiedlichen Typs, das sich in einem Rutsch hinschreiben lässt, und mit dem man auch noch etwas anfangen kann, bekomme ich gerade nicht hin.
:
Bearbeitet durch User
Walter T. schrieb: > Meinem Verständnis nach analoges Beispiel in C: String-Literale sind ein Sonderfall, weil deren Lebenszeit von C/C++ automatisch global gemanagt wird. Walter T. schrieb: > In C ein Beispiel mit einem Pointer Array auf anonyme Variablen > unterschiedlichen Typs, dass sich in einem Rutsch hinschreiben lässt, > bekomme ich gerade nicht hin. Auch mit gleichem Typ geht es nicht, außer bei String-Literalen... Du musst die Variablen irgendwo hin ablegen. Meine PolymorphicContainer-Klasse vereinfacht das. Mehrfach malloc/new nutzen geht natürlich auch, aber das ist für solche Konstrukte eben Overhead. Walter T. schrieb: > Danke, dafür werde ich mir wohl noch etwas Zeit nehmen müssen, bis ich > das verstehe. Du kannst die Klasse auch so verwenden wie sie ist ohne die Details zu verstehen, ist ja bei Nutzung von std::vector etc auch nicht anders, dessen Innereien sind auch schwer verdaulich. Die Verwendung ist ja am Beispiel ersichtlich.
Walter T. schrieb: > Die Frage ist: Kann ich ein globales Array mit anonymen Variablen > anlegen, wenn diese einen unterschiedlichen Typ haben? Nein, siehe oben. Du kannst aber einen heterogenen Container anlegen.
1 | #include <cstdio> |
2 | #include <tuple> |
3 | |
4 | struct A { |
5 | void foo() const { puts(__PRETTY_FUNCTION__); } |
6 | };
|
7 | |
8 | struct B { |
9 | void foo() const { puts(__PRETTY_FUNCTION__); } |
10 | };
|
11 | |
12 | struct C { |
13 | void foo() const { puts(__PRETTY_FUNCTION__); } |
14 | };
|
15 | |
16 | constexpr std::tuple abc{A{}, B{}, C{}}; |
17 | |
18 | int main() { |
19 | std::get<A>(abc).foo(); |
20 | std::get<B>(abc).foo(); |
21 | std::get<C>(abc).foo(); |
22 | }
|
Sehr ich das richtig, dass ich über ein tuple nicht in einer for-Schleife mit einer Auto-Variable drüberiterieren kann, und bei der Initialisierung an zwei Stellen (in der Deklaration und in der Initializer List) Buch führen muss, um welchen Datentyp es sich handelt? Das würde es für eine Liste mit > 100 Einträgen eigentlich schon direkt disqualifizieren. Irgendwie fürchte ich gerade, dass ich so oder so um einen Codegenerator nicht herumkomme.
:
Bearbeitet durch User
Beitrag #6486949 wurde von einem Moderator gelöscht.
Walter T. schrieb: > Sehr ich das richtig, dass ich über ein tuple nicht in einer > for-Schleife mit einer Auto-Variable drüberiterieren kann Richtig. Walter T. schrieb: > und bei der > Initialisierung an zwei Stellen (in der Deklaration und in der > Initializer List) Buch führen muss, um welchen Datentyp es sich handelt? Auch richtig. Daher macht man schlauerweise beides in einem. Walter T. schrieb: > Das würde es für eine Liste mit > 100 Einträgen eigentlich schon direkt > disqualifizieren. Daher kann man es sich mit Wrappern einfacher machen, wie das gezeigte PolymorphicContainer. Die gezeigte staticIteration Funktion ermöglicht ein Iterieren über die Elemente eines tuple, aber das ist keine echte Schleife, im erzeugten Code wird das komplett ausgerollt. Walter T. schrieb: > Irgendwie fürchte ich gerade, dass ich so oder so um einen Codegenerator > nicht herumkomme. Doch. Wie gezeigt ist das Problem lösbar. Man darf aber nicht erwarten dass das mit einfachsten Mitteln direkt geht. Code-Generatoren sind im Endeffekt meistens mehr Arbeit als sie helfen.
Beitrag #6486952 wurde von einem Moderator gelöscht.
Beitrag #6486955 wurde von einem Moderator gelöscht.
Walter T. schrieb: > Sehr ich das richtig, dass ich über ein tuple nicht in einer > for-Schleife mit einer Auto-Variable drüberiterieren kann, und bei der > Initialisierung an zwei Stellen (in der Deklaration und in der > Initializer List) Buch führen muss, um welchen Datentyp es sich handelt? In einem range-for geht das nicht nein, da die Typen unterschiedlich sind und sich keinen Iterator teilen. In einem generischen Algorithmus geht das schon. Walter T. schrieb: > Das würde es für eine Liste mit > 100 Einträgen eigentlich schon direkt > disqualifizieren. > > Irgendwie fürchte ich gerade, dass ich so oder so um einen Codegenerator > nicht herumkomme. Nachdem du nachwievor kein Wort darüber verlierst was du vor hast wird dir niemand weiterhelfen können. Fakt ist, dass C++ aktuell über keine Reflection verfügt. Die Möglichkeiten der Metaprogrammierung werden zwar ständig ausgebaut, sind aber dadurch trotzdem noch begrenzt.
Beitrag #6486957 wurde von einem Moderator gelöscht.
Beitrag #6486965 wurde von einem Moderator gelöscht.
Vincent H. schrieb: > Nachdem du nachwievor kein Wort darüber verlierst was du vor hast Ich habe es oben angedeutet: Beitrag "Re: C++ : Mit for-Schleife über Objekte verschiedener Klassen iterieren" Man stelle sich die Tabelle/Array/Sonstwas mit > 200 Einträgen vor. Viele Variablen unterschiedlichens Typs, aber alle jeweils mit einer Funktion gleichens Namens. Vielleicht sind die "polymorphic container" genau die Lösung. Ich muss sie mir mal in Ruhe angucken.
Geben wir Moby für die nächsten drei Stunden seine Spielwiese, um sich auszutoben. Ich muss eh noch mein Buch weiterlesen.
Walter T. schrieb: > Man stelle sich die Tabelle/Array/Sonstwas mit > 200 Einträgen vor. > Viele Variablen unterschiedlichens Typs, aber alle jeweils mit einer > Funktion gleichens Namens. Der Übersichtlichkeit halber auf godbolt -> https://godbolt.org/z/Yxn97e
:
Bearbeitet durch User
Beitrag #6486988 wurde von einem Moderator gelöscht.
Vincent H. schrieb: > Der Übersichtlichkeit halber auf godbolt -> > https://godbolt.org/z/Yxn97e Ah, ich sehe meinen Fehler:
1 | constexpr std::tuple abc{A{}, B{}, C{}}; |
Ich habe händisch ergänzt:
1 | constexpr std::tuple<A, B, C> abc{A{}, B{}, C{}}; |
und mit C++17 kompiliert. C++20 erfordert offensichtlich nicht mehr die Vorab-Auflistung des Datentyps im Tupel. Das ist ja regelrecht brauchbar. Ich bin entzückt. Jetzt gönne ich mir erst einmal im Laufe des Tages ein Toolketten-Update.
:
Bearbeitet durch User
Genannt CTAD: https://en.cppreference.com/w/cpp/language/class_template_argument_deduction /edit Sollt mit C++17 eigentlich auch gehn.
:
Bearbeitet durch User
Ist hier gerade ein Beitrag zuviel gelöscht worden? Ich erinnere mich an o Vincent H. schrieb: > Sollt mit C++17 eigentlich auch gehn. Tut es auch. Ich habe es angepasst, bevor ich das Kompilieren probiert habe, weil mein Buch "ab C++20" dazu schreibt.
Danke für alle hilfreichen Tipps! Diese Diskussion hat dafür gesorgt, dass ich Licht am Ende des Tunnels sehe!
Diene Fragen lassen ja eher vermuten, daß du noch auf dem Weg zum Tunneleingang bist. Das Licht am Ende des C++ - Tunnels ist noch weit weg. Oliver
Rolf M. schrieb: > Mark B. schrieb: >> Wieso denn Blödsinn? Selbstverständlich ist es möglich, es so zu machen >> wie von Gähn beschrieben. > > Nur arbeitet dein Code halt nicht so wie beschrieben. Er speichert keine > Fahrzeuge im vector, sondern Zeiger. Es war doch zu Beginn von einem Array die Rede: Walter T. schrieb: > diese in eine Art array zu packen und mit einer for-Schleife > darüberzuiterieren? Wenn es ein Array sein soll, geht es nur so. Wenn es auch eine andere Datenstruktur sein kann, geht es auch anders. Ich denke der Themenersteller ist noch nicht so weit um genau entscheiden zu können, was denn für seinen Anwendungsfall besser geeignet wäre. ;-)
Ich habe "eine Art Array" für unspezifisch genug gehalten. In Matlab heißen die "Cell Array", in Python "list". Was in C++ der passende Datentyp ist, war ja gerade die Frage. Eine geordnete Menge unterschiedlicher Datentypen. In Matlab sind die "Cell Arrays" von der Nutzung wie structs mit Index anstelle Schlüsselwort. Also das absolute Gegenteil von exotisch. (Aber Matlab ist ohnehin etwas außergewöhnlich. Dort kann man auch sehr bequem über die Felder von structs iterieren.) In Python haben "lists" auch keinen Seltenheitswert. In C++ scheint das Anliegen so exotisch zu sein, dass es schwierig ist, die Frage danach zu formulieren.
:
Bearbeitet durch User
C++ erlaubt grundsätzlich recht viele verschiedene Ansätze, ein und dasselbe Problem zu lösen. Die Komplexität dieser Sprache ist schon sehr hoch.
Walter T. schrieb: > In C++ scheint das Anliegen so exotisch zu sein, > dass es schwierig ist, die Frage danach zu formulieren. Die Frage ist nur deshalb schwierig zu formulieren, weil du anscheinend nicht weißt, was du eigentlich fragen willst. Eine Antwort auf deine nicht gestellte Frage ist: C++ ist wie C eine streng typisierte Sprache, Matlab und Python sind das nicht. Oliver
Oliver S. schrieb: > Die Frage ist nur deshalb schwierig zu formulieren, weil du anscheinend > nicht weißt, was du eigentlich fragen willst. Wenn man einen Ausdruck so präzise formulieren kann, dass ihn ein Compiler versteht, benötigt man kein Forum.
Walter T. schrieb: > Irgendwie fürchte ich gerade, dass ich so oder so um einen Codegenerator > nicht herumkomme. WEnn es um hunderte von konstanten Objekten geht, ist das sowieso das Mittel der Wahl. Nur ein Hinweis am Rande: Die statischen, zur Compilezeit ausgewerteten Datentypen wie tuple brauchen genau das, nämlich Compiletime. Die aktuellen Compiler sind inzwischen zwar dafür gerüstet, aber ein tuple mit ein paar hundert Elementen könnte da doch zu unliebsamen Überraschungen führen. Ein gcc 4.1 (oder so) crasht schon bei mehr als einer handvoll Elementen... Oliver
Walter T. schrieb: > Ich habe "eine Art Array" für unspezifisch genug gehalten. In Matlab > heißen die "Cell Array", in Python "list". Was in C++ der passende > Datentyp ist, war ja gerade die Frage. Eine geordnete Menge > unterschiedlicher Datentypen. > > In Matlab sind die "Cell Arrays" von der Nutzung wie structs mit Index > anstelle Schlüsselwort. Also das absolute Gegenteil von exotisch. (Aber > Matlab ist ohnehin etwas außergewöhnlich. Dort kann man auch sehr bequem > über die Felder von structs iterieren.) In Python haben "lists" auch > keinen Seltenheitswert. In C++ scheint das Anliegen so exotisch zu sein, > dass es schwierig ist, die Frage danach zu formulieren. Normalerweise würde ich dafür eine std::list verwenden und Zeiger auf die Basisklasse darin speichern. Je nachdem, welche Anforderungen du noch an den Container hast (Zugriff per Key?, Sortierung?, etc.), kämen noch andere in Betrachtung: std::map, std::set, std::vector. merciless
Die Frage ist halt, was willst du denn im List Body dann mit den Objekten machen? Das Äquivalent zur Python list wäre vmtl sowas wie std::vector<std::any>. Da passen die Objekte leicht rein, und man kann auch leicht darüber iterieren, nur danach irgendwas damit zu tun wird schwer. Etwas sinnvoller wäre ein std::vector<std::variant<...>>. Mit std::visit kann man dann relativ lesbaren Code schreiben, der die einzelnen Typen z.B. per function template specialization behandelt. Oder eben die bereits vorgeschlagene Vererbungshierarchie. Für dein Beispiel mit den Geometrieelementen scheint mir das die geeignetste Lösung. Sind dir die vtables zu groß, kannst du sie dir selber schreiben, z.B. per templates.
Dirk K. schrieb: > std::list Wie selbst der C++-Standard selber schreibt, ist std::vector die beste Wahl für die allermeisten Anwendungen. So wohl auch hier. Alles in allem klingt die Anwendung von Walter aber sehr danach, daß er ein „Array“ von zur Compilezeit bekannten konstanten Elemente sucht. Da ist std::tuple schon nicht verkehrt, dann gibt es gar keinen run time overhead. Oliver
Ich finde, ziemlich viele Anwendungen von std::array lassen sich durch eine initializer list ersetzen, z.B. der oben genannte. auto const items = {1, 2, 3, 4}; for (auto item: items) { ... }
Wenn die Menge der Typen zur Compilezeit bekannt ist, kommt ggf. std::variant noch ins Spiel ;-)
Oliver S. schrieb: > Dirk K. schrieb: >> std::list > > Wie selbst der C++-Standard selber schreibt, ist std::vector die beste > Wahl für die allermeisten Anwendungen. So wohl auch hier. Jein, wenn z.B. viele Inserts() gemacht werden, ist std::vector ungeeignet, weil sonst zu oft umkopiert werden muss (std::vector garantiert, dass die Einträge im Speicher hintereinander liegen. Ist der anfänglich reservierte Speicher voll, wird neu allokiert und der ganze Inhalt umkopiert.). Noch schlimmer wird es beim Löschen von Einträgen. std::list wäre in dem Fall performanter. merciless
Dirk K. schrieb: > Jein, wenn z.B. viele Inserts() gemacht werden, > ist std::vector ungeeignet, Deshalb gibt es std::list, und für andere Anwendungsfälle auch noch die andere Container, was ja völlig ok ist. In einer älteren Version des Standards oder in seinem Umfeld stand m.E sinngemäß mal drin: Nimm immer std::vector. Wenn nicht, musst du es ausführlich begründen. Die Anwendung des TO sieht jetzt nicht danach aus, daß er was anderes braucht. Oliver
Oliver S. schrieb: > Nimm immer std::vector. Wenn nicht, musst du es ausführlich begründen. Wenn die Menge an Objekten sowieso fest ist und sich zur Laufzeit nicht ändert, wozu dann ein Vector? Dann tut es doch auch ein schnödes Array?
Oliver S. schrieb: > https://youtu.be/zBkNBP00wJE Das ist ein sehr schöner Vortrag. Danke für den Tipp!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.