Hallo,
ich habe eine allgemeine Frage an euch.
Wenn ich eine STL Liste verwende, benötige ich ja einen Iterator um mit
der Liste was machen zu können.
Bei vector Listen, benötige ich aber keinen Iterator. Die kann ich ohne
Iterator verwenden.
Stimmt das so?
helpme91 schrieb:> Bei vector Listen, benötige ich aber keinen Iterator. Die kann ich ohne> Iterator verwenden.> Stimmt das so?
Du meinst wegen dem operator[]? Ja, bei std::vector kann man Elemente
direkt mit dem Index ansprechen (Bei std::deque übrigens auch.)
Aber auf was willst du hinaus?
Ich Frage, da ich einen Beitrag gelesen habe wo gesagt wurde, das man
bei vector Listen grundsätzlich ohne Iterator arbeitet. Bei STL Listen
verwendet man aber einen Iterator.
Wollte nur mal nachfragen, ob das so Stimmt.
> Ich Frage, da ich einen Beitrag gelesen habe wo gesagt wurde, das man> bei vector Listen grundsätzlich ohne Iterator arbeitet.> Bei STL Listen verwendet man aber einen Iterator. Wollte nur mal> nachfragen, ob das so Stimmt.
Das ist natürlich nicht richtig. Und std::vector ist keine Liste.
Richtig ist erstens: std::vector ist ein Vektor (keine Liste), std::list
ist eine Liste (kein Vektor).
Zweitens kann man über die Elemente einer Liste nur mit iterator
iterieren, bei einem vector kann man zwischen iterator und einem Zugriff
über den Index wählen (at() oder []).
Will man bei einem vector alle Elemente anfassen und die interne
Reihenfolge ist ok, wird meist der iterator richtig sein. Sonst halt
nicht.
Wenn man den direkten Zugriff gar nicht braucht, ist meistens
std::vector die falsche Wahl gewesen, und std::set oder std::list wäre
besser.
(Abgesehen davon gibt es auch const-Iteratoren, wenn man die Elemente
nicht ändern will.)
helpme91 schrieb:> Was ist dann richtig?
Alle Standard Container haben Iteratoren, auch std::vector. Der wird
typischerweise bei Standard-Algorithmen wie std::sort verwendet, z.B:
1
std::vector<int>v;
2
std::sort(v.begin(),v.end());
Ohne Iterator kann man std::sort nicht benutzen. Selbst C-Arrays haben
Iteratoren, und deren Typ ist einfach ein Zeiger auf das Element, diesen
kann man ebenso an std::sort & Co übergeben. Die Range for loop benutzt
die Iteratoren ebenfalls (implizit).
Bei vector kann man die Elemente zusätzlich auch ohne Iterator
zugreifen, über den Index. Aber den Iterator braucht es eben trotzdem
für die Algorithmen.
Und ein vector ist keine Liste, sondern ein kontinuierlicher
Speicherbereich.
Klaus W. schrieb:> Wenn man den direkten Zugriff gar nicht braucht, ist meistens> std::vector die falsche Wahl gewesen, und std::set oder std::list wäre> besser.
Das kann man so pauschal nicht sagen. std::list hat einen signifikanten
Overhead aufgrund der verteilten Speichernutzung. Daher ist std::vector
in sehr vielen Fällen doch überlegen, obwohl man den Index-basierten
Zugriff gar nicht braucht.
helpme91 schrieb:> Bei vector Listen, benötige ich aber keinen Iterator. Die kann ich ohne> Iterator verwenden.
Alle C++-Standardcontainer bieten Iteratoren, und alle, bei denen das
sinnvoll ist, bieten einen operator[] und eine Methode at() zum Zugriff
per Index. Nutzen kannst du immer alles, was vorhanden ist.
Oliver
helpme91 schrieb:> Was ist dann richtig?
Viele Wege führen nach Rom 😉
Wenn Du Deinen Algorithmus auf Iteratoren aufbaust, dann kannst Du den
Container wechseln, ohne den Algorithmus anpassen zu müssen. So wird es
in der STL gemacht.
Algorithmus bezeichnet hier den Teil Deiner Software, der die Daten in n
der Liste bzw. im Vector verwendet. Container ist die Liste, Vektor oder
sonstige Datenstrukturen.
Verschiedene Container haben unterschiedliche Eigenschaften: So kann
beim Vektor in konstanter Zeit (also unabhängig der gespeicherten
Datenmenge) auf zufällige Elemente zugegriffen werden (über den []
Operator). Das kann die Liste nicht. Dafür kann das Hinzufügen von
Elementen beim Vektor länger dauern; hier arbeitet die Liste in
konstanter Zeit.
In der Praxis: Wenn Du nur über alle Elemente eines Vektors drüber gehen
willst, dann ist es völlig egal ob Du über Iteratoren oder [] gehst.
Ersteres ist vielleicht etwas mehr C++ Stil; letzteres mehr C-artig.
Random .. schrieb:> Geht auch "ohne Iterator":
Eben nicht, wenn das Stl-Objekt keinen Iterator hätte ginge das nicht.
Hier wird eine versteckte Variable für den Iterator angelegt.
helpme91 schrieb:> Eien Frage noch, wie ruft man eine Methode einer Klasse mit einem> Iterator einer Liste auf?
1
Iterator->Funktion();
In C++ heißt es Member-Funktion, "Methode" sagt man bei Java.
helpme91 schrieb:> Also einen polymorphen Zugriff.
Polymorphie hat nichts mit Iteratoren zu tun.
helpme91 schrieb:> Eien Frage noch, wie ruft man eine Methode einer Klasse mit einem> Iterator einer Liste auf?
meiniterator->methode()
*meiniterator ist das Element, und (*meiniterator).methode() wäre der
Aufruf der Methode für das Element.
p->sub... ist dann wie bei Zeigern die Abkürzung für (*p).sub...
helpme91 schrieb:> Also einen polymorphen Zugriff.
Polymorph und STL-Container ist so eine Sache...
Falls du in der Liste bzw. Vektor verschiedene voneinander abgeleitete
Typen haben willst, wird es komplizierter.
Die passen nämlich nicht in lauter gleichartige Elemente des Containers
(ein abgeleitetes Irgendwas kann ja größer sein als ein Element der
Basisklasse).
Dann müsstest du sowas wie shared_ptr<meineElementKlasse> im Container
speichern statt der Elemente direkt (std::vector< shared_ptr<
meineElementKlasse > >).
Beim Zugriff auf ein Element dann entsprechend eine Indirektion mehr
einbauen als im Beispiel oben, weil *meinIterator ja jetzt nicht die
Daten liefert, sondern den shared_ptr darauf.
Klaus W. schrieb:> Sind die 95% belastbar, oder darf ich die anzweifeln? :-)
Klar, ich habe da so eine Statistik... Spaß beiseite, shared_ptr braucht
es nur in speziellen Situationen und erfordert einiges Nachdenken zur
korrekten Anwendung. Bei unique_ptr ist einiges einfacher und klarer,
und es ist eben sehr oft ausreichend. Dazu kommt der nicht geringe
Overhead von shared_ptr für die Referenzzählung, die ja auch
threadsicher sein muss.
Niklas G. schrieb:> Random .. schrieb:>> Geht auch "ohne Iterator":>> Eben nicht, wenn das Stl-Objekt keinen Iterator hätte ginge das nicht.> Hier wird eine versteckte Variable für den Iterator angelegt.
Deshalb die Anführungszeichen :-)
Klaus W. schrieb:> Polymorph und STL-Container ist so eine Sache...>> Falls du in der Liste bzw. Vektor verschiedene voneinander abgeleitete> Typen haben willst, wird es komplizierter.> Die passen nämlich nicht in lauter gleichartige Elemente des Containers> (ein abgeleitetes Irgendwas kann ja größer sein als ein Element der> Basisklasse).
Funktioniert wunderbar, solange der Container Elemente der Basisklasse
aufnimmt. Mit virtuellen Funktionen (oder Prototypen) lässt sich auf
diese Weise problemlos der Call in spezielle Handler realisieren, z.B.
das Zeichnen verschiedener Objekte.
Also:
1
classBASE{
2
virtualDraw()=0;
3
}
4
5
classA:publicBASE{
6
virtualDraw();
7
}
8
classB:publicBASE{
9
virtualDraw();
10
}
11
12
list<BASE>items;
Braucht man ein einzelnes Objekt, muss man entweder wissen, was drin
ist, oder das Ganze per dynamic_cast<A>(obj) testen.
Kommen allerdings Template-Objekte der erbenden Klassen ins Spiel, wird
das ganze etwas komplizierter. Dafür muss man ggf. über eine generische
Zwischenklasse gehen und das template-Objekt in einem Member speichern,
welches dann über virtuelle Funktionen der Basisklasse wiederum
generisch behandelt werden kann.
Das funktioniert nicht. So kann man nur Objekte der Klasse "BASE" in
die Liste einfügen, aber nicht die abgeleiteten, denn dafür ist ggf.
nicht genug Speicher. Es braucht einen Pointer, idealerweise als
unique_ptr oder shared_ptr. In Java geht es direkt, aber da ist ja fast
alles ein "Pointer".
>> Das funktioniert nicht. So kann man nur Objekte der Klasse "BASE" in> die Liste einfügen, aber nicht die abgeleiteten, denn dafür ist ggf.> nicht genug Speicher. Es braucht einen Pointer, idealerweise als> unique_ptr oder shared_ptr. In Java geht es direkt, aber da ist ja fast> alles ein "Pointer".
Stimmt, zugegeben, dieses Detail hatte ich vergessen. Hat sich doch ein
Fehler eingeschlichen :-)
Leider kann man hier nicht editieren.
Also: Meine Beispiele gelten für POINTER auf Klassen verschiedenen Typs
gemeinsamer Basisklasse!
Niklas G. schrieb:> Das funktioniert nicht. So kann man nur Objekte der Klasse "BASE" in> die Liste einfügen, aber nicht die abgeleiteten, denn dafür ist ggf.> nicht genug Speicher.
Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann
abstrakt ist. Ansonsten geht es schon, allerdings mit Object-Slicing.
Dann kann erwünscht sein, meistens ist es das nicht.
abc schrieb:> Was du vllt meinst ist ein normales Array? Das hat natürlich keine> iteratoren.
Jeder Container mit zusammenhängender Speicherung hat Iteratoren, in
diesem Fall Zeiger: jeder Zeiger ist ein Iterator, aber nicht jeder
Iterator ist ein Zeiger. S.a. Beispiel mit der Liste.
Insofern haben auch rohe C-Array Iteratoren, nämlich Zeiger.
std::begin()/std:end() sind dafür entsprechend spezialisiert.
Wilhelm M. schrieb:> Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann> abstrakt ist.
Schnittstellenklassen gibt es in C++ nicht. Wenn "BASE" eine abstrakte
Klasse ist, also eine rein virtuelle Funktion hat wie hier "Draw", geht
es eben auch nicht, weil man keine Instanzen davon anlegen kann, und
"std::list<BASE>" aber eben nur Instanzen von BASE enthalten kann und
nichts anderes. Hier zu sehen:
https://godbolt.org/z/3snhqf5KoWilhelm M. schrieb:> Ansonsten geht es schon, allerdings mit Object-Slicing.
Aber nicht mit abstrakten Klassen.
std::array oder std::vector haben spezielle Iteratoren, in diesem Fall
RandomAccessIterator, die werden für das Sortieren benötigt. Die
einfachsten Iteratoren sind InputIteratoren oder auf Forward-Iteratoren.
Niklas G. schrieb:> Schnittstellenklassen gibt es in C++ nicht.
Selbstverständlich gibt es die! Das sind abstrakte Klassen nur
rein-virtuellen Elementfunktionen (und natürlich virtuellem DTor).
Niklas G. schrieb:> Wilhelm M. schrieb:>> Ansonsten geht es schon, allerdings mit Object-Slicing.>> Aber nicht mit abstrakten Klassen.
Das habe ich für selbstverständlich gehalten ...
Wilhelm M. schrieb:> Selbstverständlich gibt es die! Das sind abstrakte Klassen nur> rein-virtuellen Elementfunktionen (und natürlich virtuellem DTor).
Spannend, wo ist das im C++-Standard definiert?
Wilhelm M. schrieb:> Das habe ich für selbstverständlich gehalten ...
Klang nicht so, wenn du vorher behauptest dass es mit abstrakten Klassen
welche auch "Schnittstellenklassen" sind doch geht.
Wilhelm M. schrieb:> Dort nicht. Warum sollte es dort auch drin stehen?
Alles was es in C++ gibt, ist im C++-Standard definiert. Was nicht im
C++-Standard steht, ist nicht C++, sondern eine Erweiterung oder eigene
Definition. Somit gibt es in C++ selbst keine Schnittstellenklassen, man
kann sich das höchstens selbst so definieren. Wenn sich aber jeder
selbst solche Definitionen bastelt kann es schnell zu
Abstimmungsproblemen kommen.
Niklas G. schrieb:> Wilhelm M. schrieb:>> Dort nicht. Warum sollte es dort auch drin stehen?>> Alles was es in C++ gibt, ist im C++-Standard definiert. Was nicht im> C++-Standard steht, ist nicht C++, sondern eine Erweiterung oder eigene> Definition. Somit gibt es in C++ selbst keine Schnittstellenklassen, man> kann sich das höchstens selbst so definieren. Wenn sich aber jeder> selbst solche Definitionen bastelt kann es schnell zu> Abstimmungsproblemen kommen.
Nur weil es das in anderen, weniger allgemeinen Sprachen wie Java
explizit gibt, muss es das in C++ nicht genauso im Sprachstandard geben.
Schnittstellenvererbung und Liskov sind eine Säule der OOP. Nun ist C++
im Gegensatz etwa zu Java eine Multiparadigmensprache. So unterstützt
sie eben auch z.B. Implementierungsvererbung. Und etwa statische
Polymorphie, und diesen Begriff findest Du so auch nicht im C++
Standard.
Also: Schnittstelle ist ein Konzept, dazu braucht es in der Sprache
keine 1:1 Umsetzung zu geben, lediglich muss die Sprache unterstützen.
Und das tut C++.
Wilhelm M. schrieb:> Also: Schnittstelle ist ein Konzept, dazu braucht es in der Sprache> keine 1:1 Umsetzung zu geben, lediglich muss die Sprache unterstützen.> Und das tut C++.
Ich finde es gefährlich, in einer Antwort auf eine Anfänger-Frage
Konzepte zu erwähnen, die nicht Teil der Sprache sind, und mit fest
definierten Sprachelementen (abstrakte Klasse) zu mischen - und zu
behaupten, sie seien Teil von C++, obwohl eben nicht im Sprachstandard
definiert. Das sollte man wenn schon genau unterscheiden. Zumal es an
der Stelle und bei C++ allgemein sowieso keinen Unterschied macht ob
alle oder einige Funktionen rein-virtuell sind.
Niklas G. schrieb:> Ich finde es gefährlich, in einer Antwort auf eine Anfänger-Frage> Konzepte zu erwähnen, die nicht Teil der Sprache sind, und mit fest> definierten Sprachelementen (abstrakte Klasse) zu mischen.
Dem Anfänger habe ich gar nicht geantwortet, ich habe Dir geantwortet.
Und ich wiederum finde es gefährlich, zu behaupten, eine
OOP-unterstützende Sprache besäße keine Schnittstellenklassen. Das ist
schlicht falsch.
Niklas G. schrieb:> Zumal es an> der Stelle und bei C++ allgemein sowieso keinen Unterschied macht ob> alle oder einige Funktionen rein-virtuell sind.
Bezogen, ob man das nun als Schnittstelle bezeichnet, schon. Vgl. es
einfach mal mit dem interface in Java.
Jeder, der sich mit OOP beschäftigt, sollte das Schnittstellenkonzept
verstanden haben, egal ob es dafür nun ein Schlüsselwort gibt oder
nicht. Das ist vollkommen irrelevant.
Es gibt abstrakte Schnittstellen natürlich schon in C++, die heißen dann
halt nur nicht so (sondern gelegentlich ABC = abstract base class).
Man muß sie auch nicht explizit erwähnen in irgendeinem Standard, weil
sie wie jede andere Klasse auch sind, nur daß man alles weglässt außer
pure virtual Funktionen.
Dementsprechend kann man dann keine Objekte keine davon instantiieren,
und folglich auch nicht in einem Container verwalten.
Insofern verstehe ich nicht, warum hier darum gestritten wird - hat mit
dem Thema doch überhaupt nichts zu tun.
(Daß es Schnittstellen, die keine Klassen sind, bspw. in Java überhaupt
gibt, liegt nur daran daß Java keine Mehrfachvererbung kennt.
Ersatzweise kann man nur von einer einzigen echten Klasse ableiten und
muß den Rest über leere Schnittstellen machen, wo C++ stattdessen von
mehreren Basisklassen erben kann - in denen etwas stehen kann, aber
nicht muß.
So kann man einen Mangel in Java als Feature deklarieren: C++ hat ja
nicht mal Schnittstellen...)
Klaus W. schrieb:> Insofern verstehe ich nicht, warum hier darum gestritten wird - hat mit> dem Thema doch überhaupt nichts zu tun.>> (Daß es Schnittstellen, die keine Klassen sind, bspw. in Java überhaupt> gibt, liegt nur daran daß Java keine Mehrfachvererbung kennt.> Ersatzweise kann man nur von einer einzigen echten Klasse ableiten und> muß den Rest über leere Schnittstellen machen, wo C++ stattdessen von> mehreren Basisklassen erben kann - in denen etwas stehen kann, aber> nicht muß.> So kann man einen Mangel in Java als Feature deklarieren: C++ hat ja> nicht mal Schnittstellen...)
Genau das sage ich doch. Ich weiß auch nicht, warum man das in Zweifel
zieht. Das sind doch wirklich Basics.
Wilhelm M. schrieb:> Dem Anfänger habe ich gar nicht geantwortet, ich habe Dir geantwortet.
Stimmt, aber falsch war es trotzdem - wenn "BASE" abstrakt ist, kann man
es nicht sinnvoll in "std::list<BASE>" nutzen, egal ob es jetzt eine
"Schnittstellenklasse" ist oder nicht. Der Satz
> Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann abstrakt
ist.
ist also falsch und sinnlos, weil es sowieso nicht davon abhängig ist,
ob alle oder einige Funktionen rein-virtuell sind.
Wilhelm M. schrieb:> Und ich wiederum finde es gefährlich, zu behaupten, eine> OOP-unterstützende Sprache besäße keine Schnittstellenklassen. Das ist> schlicht falsch.
Man kann sich so etwas ähnliches wie eine Schnittstellenklasse bauen.
Aber das ist nicht im Standard definiert und hat kein spezielles
Verhalten oder Unterstützung seitens der Sprache.
Wilhelm M. schrieb:> Vgl. es> einfach mal mit dem interface in Java.
Ganz genau - Schnittstellenklassen in Java werden gesondert behandelt
mit speziellen Eigenschaften, in C++ nicht - man kann also durchaus
sagen, dass Java Schnittstellenklassen hat, und C++ nicht.
Niklas G. schrieb:> Man kann sich so etwas ähnliches wie eine Schnittstellenklasse bauen.> Aber das ist nicht im Standard definiert und hat kein spezielles> Verhalten oder Unterstützung seitens der Sprache.
Das ist doch jetzt wirklich falsch.
Im Standard findest Du auch nicht den Begriff Meta-Funktion: und es gibt
sich doch. Genauso wie reine Funktionen oder allg. Funktionen. Nur weil
es Schlüsselwort nicht vorkommt, heißt es nicht, das es das Konzept
nicht gibt.
Mit Deiner Wortklauberei verwirrst Du doch jetzt hier jeden Anfänger.
Niklas G. schrieb:>> Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann abstrakt> ist.
Ja, das war in der Tat falsch von mir: ich hatte den * einfach
hinzugedacht ;-)
Wilhelm M. schrieb:> Mit Deiner Wortklauberei verwirrst Du doch jetzt hier jeden Anfänger.
Also ich muss sagen, Niklas ist deutlich klarer in seinen
Formulierungen.
Dein Beitrag
Wilhelm M. schrieb:> Niklas G. schrieb:>> Das funktioniert nicht. So kann man nur Objekte der Klasse "BASE" in>> die Liste einfügen, aber nicht die abgeleiteten, denn dafür ist ggf.>> nicht genug Speicher.>> Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann> abstrakt ist. Ansonsten geht es schon, allerdings mit Object-Slicing.> Dann kann erwünscht sein, meistens ist es das nicht.
Hat mich erstmal ziemlich verwirrt. Ich habe mich ne ganze Weile
gefragt, was diese Schnittstellenklasse sein soll, die abstrakt und in
einem std::vector gespeichert werden kann. Hättest du nicht unnötig das
Wort Schnittstellenkklasse benutzt, Wäre sofort offensichtlich gewesen,
dass du die Verneinung in dem Satz vergessen hast.
mh schrieb:> Also ich muss sagen, Niklas ist deutlich klarer in seinen> Formulierungen.
Ich habe Schnittstellenklasse gesagt, damit war ganz kurz alles klar.
Dann fing er an zu behaupten, so etwas gäbe es in C++ nicht. Also ich
denke, es mit einem Wort wie Schnittstellenklasse oder ein Schnittstelle
zu sagen, ist ziemlich klar und kurz.
Random .. schrieb:> Stimmt, zugegeben, dieses Detail hatte ich vergessen. Hat sich doch ein> Fehler eingeschlichen :-)
Auch das geht: std::variant<> als Elementtyp ist der Schlüssel zum
Erfolg.
Oh: ich hätte auch diskriminierender Summentyp sagen können, aber sowas
gibt es in C++ nicht, oder doch ;-)
Wilhelm M. schrieb:> Auch das geht: std::variant<> als Elementtyp ist der Schlüssel zum> Erfolg.
Nachdem die Diskussion zum ursprünglichen Thema inzwischen eh vergeigt
ist, schlage ich void* vor ... :-)
Wilhelm M. schrieb:> mh schrieb:>> Also ich muss sagen, Niklas ist deutlich klarer in seinen>> Formulierungen.>> Ich habe Schnittstellenklasse gesagt, damit war ganz kurz alles klar.> Dann fing er an zu behaupten, so etwas gäbe es in C++ nicht. Also ich> denke, es mit einem Wort wie Schnittstellenklasse oder ein Schnittstelle> zu sagen, ist ziemlich klar und kurz.
Das kannst du gerne behaupten. Meine Erklärung hast du ja
praktischerweise ignoriert.
Klaus W. schrieb:> Nachdem die Diskussion zum ursprünglichen Thema inzwischen eh vergeigt> ist, schlage ich void* vor ... :-)
Damit es dann auch richtig schief geht: union
Wilhelm M. schrieb:> Niklas G. schrieb:>> Das funktioniert nicht. So kann man nur Objekte der Klasse "BASE" in>> die Liste einfügen, aber nicht die abgeleiteten, denn dafür ist ggf.>> nicht genug Speicher.>> Falls BASE eine Schnittstellenklasse ist geht das eh, da ja BASE dann> abstrakt ist. Ansonsten geht es schon, allerdings mit Object-Slicing.> Dann kann erwünscht sein, meistens ist es das nicht.
Mir würde nichts einfallen, wo man Slicing bewusst haben will. Und die
gewünschte Polymorphie ist damit auch weg.
Wilhelm M. schrieb:> Also: Schnittstelle ist ein Konzept, dazu braucht es in der Sprache> keine 1:1 Umsetzung zu geben, lediglich muss die Sprache unterstützen.> Und das tut C++.
Es gibt ziemlich viele Konzepte, die man in C++ umsetzen kann. Deshalb
würde nicht nicht sagen, dass die alle Teil von C++ sind.
helpme91 schrieb:> Ich Frage, da ich einen Beitrag gelesen habe wo gesagt wurde, das man> bei vector Listen grundsätzlich ohne Iterator arbeitet.
Verallgemeinerungen sind grundsätzlich falsch.
Klaus W. schrieb:> Experte schrieb:>> Verallgemeinerungen sind grundsätzlich falsch.>> So wie diese.
Gratulation, du hast die Ironie dieses Satzes erkannt.