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

TrommlBomml

Community-Fossil

  • »TrommlBomml« ist der Autor dieses Themas

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

11

26.02.2014, 08:37

Erstmal Danke für eure zahlreichen Antworten! Ich geb mal meinen Senf dazu:

Zitat von »Schorsch«

Deine zweite Variante wird in den meisten Fällen wohl nicht so gut sein. Es sei denn 0 wäre in dem Fall kein valider Wert in deiner Collection. Am besten man wirft hier einfach eine Exception. Damit ist man immer auf der sicheren Seite. Auch wenn sich der Wertebereich der Collection ändert. Auch wenn das hier sicherlich nur ein Beispiel von dir war.


Das sehe ich genauso. Ich habe diese Variante auch nur der Vollständigkeit halber aufgeführt.

Ich gebe in solchen Fällen gerne eine Iterator Range zurück. Also so etwas in der Art:

template <typename Iterator>
class Range {
public:
Range(Iterator begin, Iterator end);
auto begin() const -> Iterator;
auto end() const -> Iterator;
private:
Iterator m_begin, m_end;
};

Das lässt sich bei Bedarf dann auch prima in range-based for Loops benutzen.

Ich habe aber auch schon mit der Möglichkeit geliebäugelt, foreach-Methoden für das Iterieren über private Container anzubieten:


C-/C++-Quelltext
class World {
public:
void forEachTown(std::function<void(Town&)>);
void forEachVillage(std::function<void(Village&)>);
}

Beide Methoden könnten hier auf demselben Container operieren, ihn aber unterschiedlich filtern. Allerdings habe ich so etwas bisher noch nicht benötigt.


Interessanter Ansatz! Dürfte dot am ehesten gefallen, weil man die Container nicht herausreciht. In meinem Fall möchte ich die Collection aber in der Reihenfolge verändern und dann nacheinander bearbeiten, das geht ja damit nicht direkt. Außer man macht sich eine Kopie, und dann kann ich mir auch die Liste komplett herausreichen und kopieren.

Zitat von »dot«

Ich frag mich in solchen Fällen immer erst einmal, wieso genau die Collection im Interface exposed werden sollte, denn meistens ist so eine Collection meiner Erfahrung nach eher ein Implementierungsdetail, welches besser nicht nach draußen geht. Anstatt den internen vector weiterzureichen, damit andere drüber iterieren können, sollte nicht besser das Iterieren etc. Methode des Besitzers der Collection sein?


Auf die Frage hab ich von dir schon gewartet :). Ganz einfach: Es gibt ein Objekt, welches Objekte besitzt und einem anderen Objekt bereitstellen soll, um sie zu verarbeiten, weil die Verarbeitung gar nichts mit dem Besitzer der Objekte in der Collection zu tun hat und vor allem die Verarbeitung prinzipiell erstmal in einem Interface abgebildet ist - hoffe ich hab mich korrekt und deutlich ausgedrückt. Vor allem stellt sich mir die Frage, ob sich der Aufwand einer Verhüllung der herausgabe von internen Daten sich überhaupt lohnt, wenn das "gefährliche" das Verändern der Objekte in der Collection ist. Aber genau das brauche ich nunmal. Objekte kommunizieren miteinander und tauschen Daten aus.

Zitat von »dot«

IEnumerable tut ebenfalls nichts Anderes, als die Tatsache, dass es unter der Haube eine Collection gibt, nach außen zu tragen. Außerdem ist es nicht effizient... ;)


Das stimmt so direkt nicht. IEnumerable hat nichts Listen oder dergleichen zu tun sondern definiert nur ähnlich wie ein Iterator, dass man von einem Element zum nächsten kommt und irgendwann Schluss ist. Und das ist doch eine sehr gute Schnittstelle. Wobei ich mich gerade Frage ob tatsächlich die Variante von Endgegner dann nicht ein guter Kompromiss ist.
Und dass IEnumerable<> prinzipiell langsam ist ist quatsch, oftmals ist sogar schneller, weil IEnumerables lazy arbeiten. Somit besteht die potenzielle Chance, dass die Menge an zu verarbeitenden Objekten mit jedem Weiterverarbeiten kleiner wird und somit einiges an Zeit gespart wird. Besonders gut merkt man das an Linq-Ausdrücken, die sehr elegant und einfach zu bedienen sind und trotzdem ziemlich fix sind. Und der Vergleich mit C++ bzgl. Performance ist sowieso immer so eine Sache mein Bester :)

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

12

26.02.2014, 10:48

Zitat von »dot«

Ich frag mich in solchen Fällen immer erst einmal, wieso genau die Collection im Interface exposed werden sollte, denn meistens ist so eine Collection meiner Erfahrung nach eher ein Implementierungsdetail, welches besser nicht nach draußen geht. Anstatt den internen vector weiterzureichen, damit andere drüber iterieren können, sollte nicht besser das Iterieren etc. Methode des Besitzers der Collection sein?


Auf die Frage hab ich von dir schon gewartet :). Ganz einfach: Es gibt ein Objekt, welches Objekte besitzt und einem anderen Objekt bereitstellen soll, um sie zu verarbeiten, weil die Verarbeitung gar nichts mit dem Besitzer der Objekte in der Collection zu tun hat und vor allem die Verarbeitung prinzipiell erstmal in einem Interface abgebildet ist [...]

Deine Antwort ist also: "Es muss so sein, weil es so sein muss."!? ;)

Wieso besitzt dieses Objekt weitere Objekte, die dann aber von anderen Objekten verarbeitet werden? Was sind das für andere Objekte und von welcher Art von "Verarbeitung" reden wir hier? Wieso besitzt ersteres Objekt überhaupt Objekte, mit denen es nichts zu tun hat?

Ich sage nicht, dass es sowas wirklich absolut niemals gibt. Aber auf einer Ebene, wo man mit OOP arbeitet, sind solche reinen Collection Klassen meiner Erfahrung nach jedenfalls meistens eher fehl am Platz. Eine Schicht drunter, in der Implementierung der OOP Ebene, sind Dinge wie Containerklassen dagegen allgegenwärtig. An die Kapselung roher Datenstrukturen sollte man imo allerdings nicht mit strikter OOP Denkweise herangehen. Wie es z.B. die Containter der C++ Standardbibliothek demonstrieren, funktionieren andere Ansätze da besser...


Zitat von »dot«

IEnumerable tut ebenfalls nichts Anderes, als die Tatsache, dass es unter der Haube eine Collection gibt, nach außen zu tragen. Außerdem ist es nicht effizient... ;)


Das stimmt so direkt nicht. IEnumerable hat nichts Listen oder dergleichen zu tun sondern definiert nur ähnlich wie ein Iterator, dass man von einem Element zum nächsten kommt und irgendwann Schluss ist. Und das ist doch eine sehr gute Schnittstelle.

Wenn du mich fragst, ist das in den wenigsten Fällen eine gute Schnittstelle (siehe oben).

Wie du richtig erkannt hast, repräsentiert IEnumerable effektiv nichts anderes als eine sequentielle Collection die forward Iteration unterstützt. Indem du IEnumerable<T> implementierst, drückst du aus: "Das hier ist eine sequentielle Collection, durch die von hinten nach vorne iteriert werden kann."


Und dass IEnumerable<> prinzipiell langsam ist ist quatsch, oftmals ist sogar schneller, weil IEnumerables lazy arbeiten. Somit besteht die potenzielle Chance, dass die Menge an zu verarbeitenden Objekten mit jedem Weiterverarbeiten kleiner wird und somit einiges an Zeit gespart wird. Besonders gut merkt man das an Linq-Ausdrücken, die sehr elegant und einfach zu bedienen sind und trotzdem ziemlich fix sind. Und der Vergleich mit C++ bzgl. Performance ist sowieso immer so eine Sache mein Bester :)

Ich versuche hier nicht, C# und C++ zu vergleichen. Ich dachte, deine Frage war, wie man gewisse Dinge in C++ lösen würde!? Meine Kritik richtet sich nicht gegen C#, sondern dagegen, den C# Ansatz 1:1 auf C++ zu übertragen. Ineffizient ist das insofern, als jeder Iterationsschritt über virtual Function Calls abgewickelt wird. Im Vergleich zu z.B. einer generischen Lösung wie std::vector, ist das sogar lächerlich ineffizient, so lange nicht die in jedem Iterationsschritt stattfindende Verarbeitung vergleichsweise teuer ist...

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (26.02.2014, 10:59)


DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

13

26.02.2014, 11:39

Hättest du mal ein Beispiel dot?
Würde mich interessieren wie du sowas löst, am besten irgendwas mit Daten anlegen/löschen und verwalten (z.B. eine GUI mit Tabelle).

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

14

26.02.2014, 11:43

Was genau lösen? Wo genau muss im Falle eines GUI mit Tabelle von irgendjemandem außer der Tabelle selbst über den "Inhalt" der Tabelle iteriert werden? Das wäre doch genau dann und nur dann der Fall, wenn jemand das GUI als Datenspeicher missbrauchen würde!? Und in dem Fall ist die ordentliche Lösung ganz einfach: Das GUI eben nicht als Datenspeicher missbrauchen...

Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

15

26.02.2014, 11:57

Ich hab eine Directory Klasse, die mir ein Dateiverzeichnis abbildet. Ich will jetzt mit jeder Datei in dem Verzeichnis irgendwas machen.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

16

26.02.2014, 12:05

Dann willst du sowas wie STL-Style Iteratoren oder Ranges. Siehe Boost.Filesystem, wird voraussichtlich auch bald Teil der Standardbibliothek sein...

TrommlBomml

Community-Fossil

  • »TrommlBomml« ist der Autor dieses Themas

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

17

26.02.2014, 13:49

Zitat von »dot«

Deine Antwort ist also: "Es muss so sein, weil es so sein muss."!? ;)

Wieso besitzt dieses Objekt weitere Objekte, die dann aber von anderen Objekten verarbeitet werden? Was sind das für andere Objekte und von welcher Art von "Verarbeitung" reden wir hier? Wieso besitzt ersteres Objekt überhaupt Objekte, mit denen es nichts zu tun hat?


Natürlich würde ich so etwas nicht tun, wenn das nicht notwendig wäre. In meinen Fall habe ich konkret zwei Objekte. Eines verwaltet Objekte in einer Baumstruktur und dient zur Aktualisierung der Daten, in diesem Fall ein SceneGraph. Der SceneGraph besitzt diese ganzen Objekte und aktualisiert diese. Es ist aber nicht seine Aufgabe, die Objekte auch zu rendern. Dafür gibt es das SceneRenderer-Interface, der einen SceneGraph kennt und seine Objekte sequentiell zum Rendern erhalten muss, um ggf. optimierungen in der Zeichenreihenfolge etc. vorzunehmen. Es ist ja wohl nicht gut, wenn der SceneGraph auch rendert. Und das sortieren ist abhängig vom Renderverfahren (Deferred, Forward, verschiedene Qualitätsabstufungen, mit Ohne Postprocessing, etc.) und somit ist diese Aufgabe extra in einem Interface angebildet. Daher muss SceneRenderer irgendwie die Objekte aus dem SceneGraphen erhalten und verarbeiten.

Zitat von »dot«

Wenn du mich fragst, ist das in den wenigsten Fällen eine gute Schnittstelle (siehe oben).

Wie du richtig erkannt hast, repräsentiert IEnumerable effektiv nichts anderes als eine sequentielle Collection die forward Iteration unterstützt. Indem du IEnumerable<T> implementierst, drückst du aus: "Das hier ist eine sequentielle Collection, durch die von hinten nach vorne iteriert werden kann."


Dann sagst du damit, dass Microsoft in .NET Framework ziemlich sinnloserweise Schnittstellen definiert hat. Und soweit man das überall so lesen kann schätzen viele die sehr durchdachte und saubere (natürlich aber auch nicht überall) Architektur. Damit hat man doch die möglichkeit, den Zugriff nach außen zu beschränken. Du kannst, wenn du eine IEnumerable herausgibst, die Original-Collection nicht verändern, sondern nur lesen und iterieren - das ist doch Klasse! In C++ hält dich bei der Herausgabe eines vektors keiner davon ab, delete aufzurufen. Das finde ich sehr schade. Dafür kann man "nur" Iteratoren herausreichen, find ich personlich aber auch alles andere als schön, irgendwo einen begin eines iterators herauszureichen. Aber noch mit die beste Möglichkeit.

Zitat von »dot«

Ich versuche hier nicht, C# und C++ zu vergleichen. Ich dachte, deine Frage war, wie man gewisse Dinge in C++ lösen würde!? Meine Kritik richtet sich nicht gegen C#, sondern dagegen, den C# Ansatz 1:1 auf C++ zu übertragen. Ineffizient ist das insofern, als jeder Iterationsschritt über virtual Function Calls abgewickelt wird. Im Vergleich zu z.B. einer generischen Lösung wie std::vector, ist das sogar lächerlich ineffizient, so lange nicht die in jedem Iterationsschritt stattfindende Verarbeitung vergleichsweise teuer ist...


Verwechslke bitte nicht C# mit Java, in C# ist eine Funktion nur virtual wenn du es angibst. Ausserdem optimiert die VM ggf. funktionsaufrufe durch direkte Calls, wodurch die nahezu identische Laufzeit hast zu nativen Code. Versichern tut dir das keiner, aber es scheitert performance ja nicht in der Regel am durchiterieren einer Liste, sondern weil zu viele Elemente iteriert werden^^. Für genau letzteren Punkt, der iterationsweisen Abarbeitung langsamer berechnungen ist IEnumerable<> stark. Besonders gut merkt man das bei DB-Queries über LINQ-Ausdrücke, weil die Ebend lazy ausgewertet werden.
Ja, und du hast recht, C# und C++ wollen wir gar nicht vergleichen sondern uns auf C++ beschränken. Der Vergleich kam nur, weil ich gerne auf Schnittstellen-Ebene solch eine Abbildung suche bzw. eine vergleichbare.

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

18

26.02.2014, 14:14

Ich meinte das eher so, dass man eine Klasse hat die einen std Container verwaltet zusätlich aber auch außerhalb gebraucht wird.
Aber dabei nicht aus diesem abgeleitet werden soll/kann.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

19

26.02.2014, 14:45

Zitat von »TrommlBomml«

Damit hat man doch die möglichkeit, den Zugriff nach außen zu beschränken. Du kannst, wenn du eine IEnumerable herausgibst, die Original-Collection nicht verändern, sondern nur lesen und iterieren - das ist doch Klasse! In C++ hält dich bei der Herausgabe eines vektors keiner davon ab, delete aufzurufen.

Das geht doch bei C++ auch... ?
Einfach einen (konstanten) Iterator nehmen oder direkt ("const std::vector<...>") . Wo gibt es da bei C++ konkret einen Nachteil?
Außerdem ist es eine bisschen unsichere Methode, denn theoretisch steht es jedem frei dein "IEnumerable<T>" wieder in einen schreibbaren Container umzucasten.

Zitat von »TrommlBomml«

Das finde ich sehr schade.

Was ich persönlich eher immer schon Sache gefunden habe, ist das bei C#
  1. das der Zugriff über das Iteratorinterface(Also "IEnumerable<T>") wesentlich Performance zieht weil alles zur Laufzeit aufgelöst wird und
  2. das in C# kein vereinheitlichtes Interface zb. für bidirektionale Iteratoren oder Random Access existiert. Geschweige denn ein Äquivalent für das C++ "const" oder gar reverse Iteratoren.

Zitat von »TrommlBomml«

Dafür kann man "nur" Iteratoren herausreichen, find ich personlich aber auch alles andere als schön

Warum findest du das unschön?
Ich sehe da wie schon gesagt bei C++ viele Vorteile und "IEnumerable<T>" ist im Prinzip auch nur ein Iteratorinterface.

Zitat von »TrommlBomml«

Versichern tut dir das keiner, aber es scheitert Performance ja nicht in der Regel am durchiterieren einer Liste

Die Frage ist eher, warum hier Performance verschenken wenn es auch ohne ginge. Und zwei virtual Calls bei jeder Iteratoren ist schon relativ heftig. Da geht es nicht mehr um Prozente sondern Vielfache.
Das es einigen Anwendungen in der Praxis nicht der absolute Performancekiller ist, ist klar, in der Regel kommen auch noch andere Operationen dazwischen.
In dem Fall meiner Ansicht nach doch für einige Anwendungsfällen zu heftig um ihn zu ignorieren.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Spiele Programmierer« (26.02.2014, 14:55)


TrommlBomml

Community-Fossil

  • »TrommlBomml« ist der Autor dieses Themas

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

20

26.02.2014, 15:15

Zitat von »TrommlBomml«
Damit hat man doch die möglichkeit, den Zugriff nach außen zu beschränken. Du kannst, wenn du eine IEnumerable herausgibst, die Original-Collection nicht verändern, sondern nur lesen und iterieren - das ist doch Klasse! In C++ hält dich bei der Herausgabe eines vektors keiner davon ab, delete aufzurufen.

Das geht doch bei C++ auch... ?
Einfach einen (konstanten) Iterator nehmen oder direkt ("const std::vector<...>") . Wo gibt es da bei C++ konkret einen Nachteil?


Das stimmt allerdings. Dumm ist leider nur, dass du erst beim kompilieren siehst. Durch extra Interfaces wie IEnumerable kann dir das gar nicht erst passieren. Und das finde ich persönlich sehr gut.

Zitat von »Spiele Programmierer«


das in C# kein vereinheitlichtes Interface zb. für bidirektionale Iteratoren oder Random Access existiert. Geschweige denn ein Äquivalent für das C++ "const" oder gar reverse Iteratoren.


Random Access: IList
Reverse iteratoren: ToReveresed()-Linq-Funktion auf jegliche IEnumerable anwendbar.
bidrektionale Iteratoren Gibt es aber soweit ich weiß tatsächlich nicht. Ich glaube aber das ist auch problemlos über Random Access abbildbar.


Const-Correctness wird in C# halt durch Interfaces weitesgehend abgebildet. Ausserdem ist es für die meisten (mich eingeschlossen) ziemilch nervig, überall auf const zu achten. Irgendwo vergessen, wenn nicht richtig durchgezogen, kriegt man ziemilch viele Fehler. Ist aber denke ich auch Geschmackssache :) Ich finde interfaces expliziter als Iteratoren.

Zitat von »Spiele Programmierer«

Die Frage ist eher, warum hier Performance verschenken wenn es auch ohne ginge. Und zwei virtual Calls bei jeder Iteratoren ist schon relativ heftig.


Ich muss meine Anmerkung von vorhin revidieren. Es sind tatsächlich virtual Function calls. Naja, dass ich bin bis heute nicht überzeugt dass das die Performance-Killer sein sollen. Das hält sich doch im Nanosekundenbereich. Und ich finde für Übersichtlichkeit in der Entwicklung ist das doch vollkommen ok. Ich vermute nicht, dass die ganzen Programme auf dem Markt wegen virtuellen Methoden so langsam sind.

Werbeanzeige