Fehler während der laufzeit!

@Cromon: Deine bisher aufgezählten Nachteile von Pointern
beziehen sich ausschließlich auf Unfähigkeit der Verwender.

Sicher sind Pointer komplexer als reine Valueübergabe,
und mehr Komplexität erhöht das Fehlerrisiko.
Aber das heißt ja nicht, dass man mit Pointern keinen fehlerfreien Code schreiben kann.


Die Aussagen zu Geschwindigkeit und großen Daten kann ich auch nur nochmal bestätigen.
 
Hallo Sheel,

Die Nachteile der Zeiger die ich erwähnt habe beziehen sich nicht auf die Unfähigkeit der Verwender sondern auf die Tatsache, dass Programmierer Menschen sind und daher Fehler am laufenden Band erzeugen. C++ bietet dir Möglichkeiten, dass du dich nicht mehr darum kümmern musst und daher auch keine Fehler mehr in dem Bereich machen kannst. Es gibt keine Grund weiterhin ein unsicheres und gefährliches Konzept zu verwenden wenn man eine sichere Alternative hat.

Bezüglich by value und by reference gibt es verschiedene Dinge:
1. By reference hat erstmal überhaupt rein gar nichts mit einem Zeiger zu tun.
C++:
void foo(ComplexType& t) {
}

void foo(ComplexType* t) {
}

Beides ist by reference, die erste Variante kommt jedoch ohne Zeiger aus. Es gibt also auch für Übergaben by reference keinen ersichtlichen Grund einen Zeiger zu verwenden.

2. Diese ganze Performancesache bezieht sich auf eine theoretische Umgebung. In der Realität hast du move-Semantics, Optimierer und viele weitere nette Dinge modifizieren dein Code gerade bei Valueübergaben so extrem, dass du den nicht mehr wiedererkennen würdest.
C++:
void foo(ComplexType t) {
      int a = t.a;
      int b = t.b;

      int c = t.calculate(a * 2, b);
      someOtherFunction(c);
}

Angenommen calculate sei const, dann optimiert der Optimierer t komplett weg und legt vor dem Funktionsaufruf die Adresse des Parameters in ein Register mit welchem foo dann arbeitet. Egal wie gross der Kopieraufwand für ComplexType ist, er wird da nie aufgerufen, genau so wenig wie der Destruktor.

Wenn es darum geht, dass sich jemand Gedanken darüber macht, dass die Übergabe eines shared_ptr ein Performanceproblem ist weil da by value übergeben wird und daher lieber darauf verzichten und raw Pointer verwenden optimieren völlig an der Realität vorbei. Es gibt dafür den bekannten Audruck: Mikrooptimierung. Es ist vergleichbar damit, dass du die Stabilität deines Gebäudes verbessern willst und mal damit beginnst alle Schrauben eine Umdrehung mehr anzuziehen während an diversen Stellen das Gebäude noch nicht mal richtig im Fundament verankert ist. Das Resultat ist, dass du überhaupt keinen Unterschied merkst.

Grüsse
Cromon
 
Hallo,

irgendwie blicke ich da gerade nicht ganz entlang. Also wenn ich das richtig verstehe meinst du es ist ziemlich egal, ob ich die Funktion foo als "foo(ComplexType t)" oder "foo(ComplexType *t)" deklariere, weil der Optimierer eh in beiden Fällen das so optimiert, dass im Endeffekt nur ein Objekt "t" bestehen bleibt? Das kann ich nicht ganz nachvollziehen, da es eventuell die Semantik meines Programms verändern würde. In dem einen Fall würde ich ja auf einer Kopie meines Objektes weiterarbeiten und das Original unverändert lassen (by Value) in dem anderen Fall würde ich das Original verändern (by reference).

Wenn es dir nur im die shared_ptr Sache geht, dann denke ich muss man sich da wirklich keine Sorgen um die Performance machen. Das Layout einer shared_ptr instance wird, denke ich mal eh kaum so sein, dass der Kopier-Aufwand für "by-value" Aufrufe kaum bis garnicht ins Gewicht fällt. Und für native Datentypen wie deine "a und b" fallen sie auch kaum ins Gewicht.

Abgesehen davon, maskiert man mit den shared_ptr doch eh nur raw-pointer und erweitert das Pointer-Konzept um einige weitere Funktionen, so dass das Speicher-Aufräumen automatisch passiert.

Trotzdem sollte man sich mit den Pointern an sich schon auskennen und wissen was man tut. Und ich meine auch, dass die STL Libraries nicht immer auf allen Systemen verfügbar sind.
 
irgendwie blicke ich da gerade nicht ganz entlang. Also wenn ich das richtig verstehe meinst du es ist ziemlich egal, ob ich die Funktion foo als "foo(ComplexType t)" oder "foo(ComplexType *t)" deklariere, weil der Optimierer eh in beiden Fällen das so optimiert, dass im Endeffekt nur ein Objekt "t" bestehen bleibt? Das kann ich nicht ganz nachvollziehen, da es eventuell die Semantik meines Programms verändern würde. In dem einen Fall würde ich ja auf einer Kopie meines Objektes weiterarbeiten und das Original unverändert lassen (by Value) in dem anderen Fall würde ich das Original verändern (by reference).

Hallo Der Wolf,

Der entscheidende Teil ist folgender:
Angenommen calculate sei const[...]

In diesem Fall wird t nicht verändert, ergo ist es auch nicht nötig eine Kopie anzulegen (ausser der Copy-Konstruktor hat wahnsinnig komplexe Nebeneffekte). Aus diesem Grund sollte man sich bei jeder Funktion überlegen, ob man sie nicht const-wahrend implementieren kann.

Grüsse
Cromon
 
Hey,

ich habe im Moment den Eindruck wir reden aneinander vorbei. Ich meine, "calculate" ist ja eh ein Member von t, benötigt also keine "direkte" übergabe der eigenen Instanz (auch wenn intern, beim Abarbeiten von calculate ja ein this-pointer mit übergeben wird). Das heisst, t wird eh nicht kopiert und das kopieren der paar Byte großen int Werte "a" und "b" und "2" ist egal.

Aber nehmen wir jetzt mal an, der ComplexType sei ein Bild im Speicher von ein paar MB Größe und ich habe jetzt ein Klasse "Tracker" mit der Instanz "tracker" die auf diesem Bild arbeitet. Dann will ich das Bild doch wahrscheinlich eher nicht mit "Call-By-Value" an eine Methode von "tracker" weitergeben, sondern nur eine Referenz auf das Bild. Und ich denke, wenn du ein Szenario hast in dem die Bilder vielleicht mit 30 Hz kommen und du einen RL-Tracker haben willst, dann wird man den Unterschied zwischen Call-By-Value und Call-By-Referenz schon merken, oder nicht?

Gruß,
Wolf

Edit: Ah Moment. Jetzt glaube ich, weiss ich worauf die hinaus willst. Das "foo(ComplexType t)" spielt ja auch noch eine Rolle, da findet ja das Kopieren statt. Ok. Aber ist das nicht schon ein sehr spezielles Szenario? Ich meine es kann ja durchaus sein, dass deine Funktion foo wieder intern eine Funktion aufruft, die die Instanz t verändern soll.
 
Zuletzt bearbeitet:
Hallo Der Wolf,

Genau so wie in deinem Edit beschrieben. Diese Situation ist gar nicht so selten. In vielen Fällen sind Parameter hauptsächlich informativ und werden gar nicht manipuliert.

Daher kann man auch einfach alle mal by value übergeben und zwar so:
C++:
void foo(const ComplexType t) {
}

überall wo ein Fehler kommt überlegt man sich, ob da wirklich non-const gefordert ist und wenn ja macht man by reference.

Oder man sagt sich einfach: Ich nehm immer ein & und habs by reference.

Grüsse
Cromon
 
Hallo Cromon,

ok, jetzt verstehe ich worauf du hinaus wolltest und ich habe auch wieder was neues gelernt. Also vielen Dank für die interessante Diskussion. :)

Gruß,
Wolf

P.S.: Ich persönlich mag Pointer auch wenn ich schon Scherereien mit ihnen hatte ;)
 
also meiner meinung nach soll man die smartpointer bei arbeiten mit dem heap benutzen und ansonsten die normalen pointer oder mit referenzen! ich würde nicht darauf verzichten! Ist meine meinung dazu, aber ich bin erst anfänger und kenne mich nicht so aus :P :*
 
Zurück