Zeiger als Parameter von Funktionen



  • mawilab schrieb:

    Bischen gemein, da ich es mit dem Rückgabewert gemacht habe 🙂

    Naja, gemein ist das nicht, und jeder hier würde die Funktion auch genau so schreiben. Das ist in Java so und in C++ nicht anders.

    mawilab schrieb:

    Nun zu der zweiten Lösung:

    void verdoppel(int *popZeiger)
    {
      *popZeiger = *popZeiger * 2;
    }
    

    Ja. Das war eine Lösung, die ich mir vorgestellt habe: Du willst den Wert von population ändern, also musst Du einen Zeiger auf population benutzen.

    mawilab schrieb:

    Das ist eigtl. klar, die Frage ist warum man es so machen sollte:

    void verdoppel(int **popZeiger)
    {
      **popZeiger = **popZeiger * 2;
    }
    

    Also mit einem Zeiger auf einen Zeiger 🙂

    Gibt keinen Grund. Zeiger auf Zeiger sind eher selten. Wie Sone schon angemerkt hat kommen sie bei mehrdimensionalen (ragged) Arrays vor. Manchmal wird argv so deklariert:

    int main(int argc, char** argv){
      // argv[0] bis argv[argc-1] enthalten die Kommandozeilenparameter
    }
    

    Aber um beim Beispiel mit verdoppel zu bleiben: Du brauchst keinen int** um einen int zu ändern - das ist einfach unnötig kompliziert. Siehe mein früheres Beispiel mit bieg_zeiger_um() - da war ein Zeiger auf Zeiger "nötig".

    Um die Referenz hast Du Dich gedrückt - dabei ist das noch "die natürlichste" Art, verdoppel in C++ zu schreiben.



  • Doppelzeiger "braucht" man in C auch noch, wenn man eine Funktion hat, die einen Zeiger auf ein gültiges Objekt erzeugen soll.

    err_code_t create_some_type_obj(some_type ** p)
    {
      some_type * zeiger_auf_gueltiges_some_type_objekt = malloc(...);
      *zeiger_auf_gueltiges_some_type_objekt = irgendwelche_daten;
      //ggf. mehr zeugs
      *p = zeiger_auf_gueltiges_some_type_objekt;
    }
    
    //...
    
    some_type * p = 0;
    err_code_t err = create_some_type_obj(&p); //hier wird ein some_type Objekt erzeugt
    //wenn bei create_some_type_obj alles gut gegangen ist, zeigt p nun nicht mehr auf 0, sondern auf ein gueltiges Objekt
    

    Ich finde das zwar nicht schön, und würde sofern möglich, mit Rückgabewerten arbeiten, aber in vielen C-APIs sieht man sowas. In C++ braucht man das aber nicht mehr. Da kann man Referenzen benutzen.



  • Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.



  • Ethon schrieb:

    Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.

    Und wobei sind Dir da bis jetzt Probleme entstanden?



  • Vermutlich da nicht, abgesehen davon, dass man ein Ampersand mehr braucht und der DAU da aus Versehen einen nullptr übergeben kann.
    Das Problem ist eher bei dynamischer Speicherallozierung, dass das u.U. Speicherlecks enstehen können.

    foo *f = new foo;
    f->bar();
    delete foo;
    

    Sieht zwar gut aus, aber was machen, wenn bar() wirft?

    foo *f = new foo;
    try
    {
        f->bar();
    }
    catch(...)
    {
        delete f;
        throw;
    }
    delete f;
    

    Ein std::unique_ptr, erledigt das Löschen automatisch.
    Anderer Fall:

    foo::foo()
    : bar1(new bar), bar2(new bar2) {}
    

    Was machen wenn die Allokierung von bar2 fehlschlägt?
    bar1 verbleibt im Speicher.
    Und noch viele änliche Situationen.



  • Tachyon schrieb:

    Ethon schrieb:

    Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.

    Und wobei sind Dir da bis jetzt Probleme entstanden?

    Naja, ich habe beispielsweise eine Klasse, die aus Performancegründen Strings nicht selbst speichert, die gehören in einen Stringpool.

    Sieht bei mir so aus:

    class Foo
    {
    private:
        std::string const& bar;
    
    public:
        Foo(std::string const* bar)
            : bar(*bar)
        { }
    };
    

    Den String per Referenz entgegenzunehmen könnte je nach Benutzer meines Codes zu dem Irrtum führen, hier würde ein String kopiert (was meistens auch zu erwarten ist).



  • Ethon schrieb:

    Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.

    Und das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.



  • Sone schrieb:

    Ethon schrieb:

    Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.

    Und das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.

    Welche Nachteile bitte?



  • Den String per Referenz entgegenzunehmen könnte je nach Benutzer meines Codes zu dem Irrtum führen, hier würde ein String kopiert (was meistens auch zu erwarten ist).

    Hä? Was schert dich was der Benutzer denken könnte oder nicht?



  • Naja, ich nutze immer noch rohe Zeiger.
    Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.



  • Ethon schrieb:

    Sone schrieb:

    Ethon schrieb:

    Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.

    Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.

    Und das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.

    Welche Nachteile bitte?

    Beispielsweise die hohe Fehleranfälligkeit durch falsche Initialisierung.
    Bei einem so trivialen Fall wie deinem macht es allerdings wohl keinen Unterschied.
    P.S.: Was, wenn durch deinen Code der Eindruck entsteht, deine Klasse führt im Destruktor delete auf dem Zeiger aus (managt also den Speicher, der dynamisch allokiert sein sollte)?

    Nathan schrieb:

    Naja, ich nutze immer noch rohe Zeiger.
    Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.

    Ja, da wirst du zu sowas gezwungen. Das ist in Ordnung.



  • Ethon schrieb:

    Welche Nachteile bitte?

    dass du immer auf NULL prüfen musst?



  • Sone schrieb:

    Den String per Referenz entgegenzunehmen könnte je nach Benutzer meines Codes zu dem Irrtum führen, hier würde ein String kopiert (was meistens auch zu erwarten ist).

    Hä? Was schert dich was der Benutzer denken könnte oder nicht?

    Wenn ich eine Klasse schreibe ist es in meinem Interesse dass man damit nicht ganz so viel Scheiße anstellt.

    Bar getBar()
    {
        string foo = makeFoo();
        return Bar(foo);
    }
    

    vs

    Bar getBar()
    {
        string foo = makeFoo();
        return Bar(&foo);
    }
    

    Bei Zweiterem wird jeder, der moderneren C++ Code gewöhnt ist stutzig.
    Bei Ersterem würde ich erwarten dass hier kopiert wird.



  • Bei Ersterem würde ich erwarten dass hier kopiert wird.

    Ja, aber da du gesunden Menschenverstand hast, wirst du immer zuerst die Dokumentation lesen, wo groß und fett steht, wie das ganze Implementiert wurde, um Missverständnisse zu vermeiden.



  • edit: Mist



  • daddy_felix schrieb:

    Ethon schrieb:

    Welche Nachteile bitte?

    dass du immer auf NULL prüfen musst?

    Muss ich das? In den allermeisten Fällen wird ein nullptr absichtlich übergeben und wer absichtlich eine API falls benutzt ... naja.

    Beispielsweise die hohe Fehleranfälligkeit durch falsche Initialisierung.

    Ich kann einen Pointer genau wie eine Referenz verwenden, mit syntaktischen Unterschieden.



  • Sone schrieb:

    Nathan schrieb:

    Naja, ich nutze immer noch rohe Zeiger.
    Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.

    Ja, da wirst du zu sowas gezwungen. Das ist in Ordnung.

    Aber selbst da nutze ich zur Initialisierung eine Referenz, damit mir kein DAU da einen nullptr reinfrimmelt...



  • Nathan schrieb:

    Sone schrieb:

    Nathan schrieb:

    Naja, ich nutze immer noch rohe Zeiger.
    Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.

    Ja, da wirst du zu sowas gezwungen. Das ist in Ordnung.

    Aber selbst da nutze ich zur Initialisierung eine Referenz, damit mir kein DAU da einen nullptr reinfrimmelt...

    Natürlich. Oder könnte der User deiner Klasse vielleicht denken, es wird kopiert? Dann nimm doch lieber Zeiger. Unsicherer, aber Hauptsache keine falschen Implikationen...



  • Ethon schrieb:

    daddy_felix schrieb:

    dass du immer auf NULL prüfen musst?

    Muss ich das? In den allermeisten Fällen wird ein nullptr absichtlich übergeben und wer absichtlich eine API falls benutzt ... naja.

    lol

    wenn du wüsstest, wie oft wir uns hier mit Nullpointern rumschlagen, die eben *nicht* absichtlich übergeben wurden...

    void MyFunction(std::string* str)
    {
      // mach was mit str ohne Überprüfung
    }
    
    std::string* myString = GetSomeString();
    MyFunction(myString);
    

    Wenn jetzt GetSomeString() aus irgendeinem Grund NULL zurückliefert, knallt es.



  • Ethon schrieb:

    daddy_felix schrieb:

    Ethon schrieb:

    Welche Nachteile bitte?

    dass du immer auf NULL prüfen musst?

    Muss ich das? In den allermeisten Fällen wird ein nullptr absichtlich übergeben und wer absichtlich eine API falls benutzt ... naja.

    Der hat seinen gesunden Menschenverstand nicht benutzt und nicht in die Doku geschaut, s.o.

    Ethon schrieb:

    Beispielsweise die hohe Fehleranfälligkeit durch falsche Initialisierung.

    Ich kann einen Pointer genau wie eine Referenz verwenden, mit syntaktischen Unterschieden.

    Ich kann auch ein Fahrrad wie ein Auto verwenden, mit Straßenverkehrsordnungsspezifischen Unterschieden.


Anmelden zum Antworten