Wann ein Zeiger als Parameter wann eine Referenz?



  • nein, wenn du dir sicher sein kannst, dass die zeiger nicht null sind,musst du nicht prüfen.

    eine frage hab ich aber:
    dürfen funktionen, die einen pointer als parameter erwarten die aufgabe des "auf 0 testen" auf den rest des programms auslagern? oder soll in den fällen, wo der funktion kein nullpointer übergeben werden darf,immer innerhalb der funktion getestet werden?



  • am besten ist es wenn die funktion den null-test selber macht. Übersichtlicher und besser gekapselt, weil unsichtbar. das sieht nicht gut aus, wenn vor jeder Funktion, die pointer erwartet, mehrere if-Abfragen stehen.



  • otze schrieb:

    nein, wenn du dir sicher sein kannst, dass die zeiger nicht null sind,musst du nicht prüfen.

    eine frage hab ich aber:
    dürfen funktionen, die einen pointer als parameter erwarten die aufgabe des "auf 0 testen" auf den rest des programms auslagern? oder soll in den fällen, wo der funktion kein nullpointer übergeben werden darf,immer innerhalb der funktion getestet werden?

    Ich halte es für sehr schwer von außen sicherzustellen, dass ein Pointer nie 0 ist. Selbst wenn dies am Anfang der Fall ist, wird durch Erweiterungen und Wartung immer wahrscheinlicher, dass mal ein 0 Pointer übergeben wird. Ein Parameter, der einen Zeiger erwartet, hat die semantische Aussage: "Da kannst du auch 0 reinstecken."

    Dagegen verhindert bei einer Referenz schon der Compiler, dass eine 0 reingesteckt wird. (Mit den bekannten Ausnahmen) Die semantische Aussage ist jedoch: "Da muß ein gültiges Objekt rein."

    Jemand, der das Programm wartet oder erweitert, wird bei Referenzen sicherstellen, dass gültige Objekte übergeben werden. Bei Zeigern wird er aber annehmen, dass 0 übergeben werden kann.



  • wie entstehen überhaupt nullpointer? wie kommen die in die funktion?
    die alten wege sind doch zumeist verschüttet. new wirft ne exception, statt 0 zurückzugeben. wie kommen sonst de nullen rein?

    otze schrieb:

    eine frage hab ich aber:
    dürfen funktionen, die einen pointer als parameter erwarten die aufgabe des "auf 0 testen" auf den rest des programms auslagern? oder soll in den fällen, wo der funktion kein nullpointer übergeben werden darf,immer innerhalb der funktion getestet werden?

    nee, nicht innerhalb testen. und außerhalb auch nicht. *grins*
    im debug-modus dürfen naürlich tests sein. ach, zum glück sind die eingebaut. der debugger springt ja im falle der schutzverletzung an.
    im release-code würde ich mich nicht damit belasten. da darf es einfach nicht mehr vorkommen, daß ne funktion, die keine 0 essen mag, eine zu essen kriegt.
    es kommt auch in der tat nicht mehr vor. meistens hat man ja wenn man schon mit vielen zeigern hantiert, sowas wie einen vector v<User*>. der steckt wiederum immer als attribut in ner meist simplen wrapperklasse drum, die aus v.push_back(new User()) ein users.add(new User()) macht (und manches wie clear() versteckt), also sprechendere namen. außerdem macht er ohne große kosten des weghauen der Users im destruktor. und da isses selten noch notwendig, mitten drin nullzeiger zu behalten. die kamen ja vor, wenn die user-id zugleich arrayindex war. heute macht man gerne ne hashtable oder ne map, um die id auf den arrayindex abzubilden und erkauft sich damit die möglichkeit, auf nollzeiger zu verzichten. mitten rauslöschen geht ja dann mit swappen von zu löschendem mit letztem und löschen des letzten.
    normalerweise sien die arrays, liste, bäume oder hastables satt und ohne einen nullzeiger drin. in den seltenen fällen, daß man mal die alte version mit nullzeigern benutzt, tut ein einziges if(a[i]) dazwichen auch nicht sonderlich weh.



  • Ponto schrieb:

    Jemand, der das Programm wartet oder erweitert, wird bei Referenzen sicherstellen, dass gültige Objekte übergeben werden. Bei Zeigern wird er aber annehmen, dass 0 übergeben werden kann.

    eben nicht.
    bei

    void eingabeStudent(Student* stud);
    

    würdest du da erwarten, daß man nen nullzeiger übergeben darf? irgendwie kann ich mich gerade an gar keine funktion erinnern, die nen nullzeiger essen mag (außer dem operator delete, aber dafür bin ich nicht verantwortlich zu machen!).



  • otze schrieb:

    eine frage hab ich aber:
    dürfen funktionen, die einen pointer als parameter erwarten die aufgabe des "auf 0 testen" auf den rest des programms auslagern? oder soll in den fällen, wo der funktion kein nullpointer übergeben werden darf,immer innerhalb der funktion getestet werden?

    Erstens) Wieso überhaupt einen Zeiger und keine Referenz? Darum geht doch der ganze Thread hier und ich habe im zweiten Post schon gesagt, dass man eigentlich nie Zeiger zu Übergabe braucht. Habe aber ehrlich gesagt nicht wirlich verstanden, ob Volkard mir da jetzt wiedersprochen oder zugestimmt hat 😕

    Zweitens) Wenn eine Funktion einen Zeiger erwartet der nicht null sein darf, muss da _keine_ Abfrage rein. Du schreibst ja selbst, dass man nur prüfen muss wenn man sich nicht sicher sein kann. Die Funktion kann aber nicht wissen, ob ich mir sicher bin oder nicht. Man könnte höchsten ein Assert einbauen um eventuelle "Flüchtigkeits"-Fehler zu vermeiden.

    // ach mist, nun war er schon wieder schneller als ich



  • DrGreenthumb schrieb:

    Erstens) Wieso überhaupt einen Zeiger und keine Referenz? Darum geht doch der ganze Thread hier und ich habe im zweiten Post schon gesagt, dass man eigentlich nie Zeiger zu Übergabe braucht. Habe aber ehrlich gesagt nicht wirlich verstanden, ob Volkard mir da jetzt wiedersprochen oder zugestimmt hat 😕

    da widerspreche ich energisch (wobei ich aber manchmal das gegenteil von dem schreibe, was ich meine), weil mich nicht die implementierung der funktion interessiert. die mache ich einmal und gut ists.
    mich interessiert die imlementierung des aufrufers.
    ob ich da

    cout<<a<<endl;//ok, a ist korrekt
    f(a,b,c);
    g(a,b);
    h(b,c);
    i(a,b,c);
    cout<<a<<endl;//ups, a ist kaput gegangen, wieso nur???
    

    oder

    cout<<a<<endl;//ok, a ist korrekt
    f(a,b,c);
    g(&a,b);
    h(b,c);
    i(a,b,c);
    cout<<a<<endl;//ups, a ist kaput gegangen, es lag offensichtlich an g
    

    stehen habe.
    die fehlersuchzeit beim aufrufenden code sinkt, wenn man konsequent zeiger für non-const-objekte und const-referenzen oder kopien für const-objekte benutzt.



  • volkard schrieb:

    mich interessiert die imlementierung des aufrufers.
    ...
    die fehlersuchzeit beim aufrufenden code sinkt, wenn man konsequent zeiger für non-const-objekte und const-referenzen oder kopien für const-objekte benutzt.

    Der Meinung war ich auch immer, bis ich mich dann doch von der einfacheren Syntax hab überzeugen lassen. Und in der Praxis fahre ich damit gut. Solche Debug-Situationen sind nach meiner Erfahrung nicht wirklich realistisch. Normalerweise haben die Funktionen dann ja doch sprechendere Namen und man sieht anhand des Kontextes auf Anhieb, wer da irgendwo was verändern möchte und wer nicht.
    Jedenfalls sollte man das.



  • volkard schrieb:

    void eingabeStudent(Student* stud);
    

    würdest du da erwarten, daß man nen nullzeiger übergeben darf? irgendwie kann ich mich gerade an gar keine funktion erinnern, die nen nullzeiger essen mag (außer dem operator delete, aber dafür bin ich nicht verantwortlich zu machen!).

    Das kommt auf den Kontext an. Wenn ich jedoch auf keinen Fall einen 0 Zeiger haben will, nehme ich eine Referenz und fertig. Da bleibt nicht viel Interpretationsspielraum.



  • volkard schrieb:

    warum verwende ich std::swap? aus historischen gründen. das ding hat sich in dieser form fest eingebürgert, bevor man erkannte, wie falsch es eigentlich ist.

    Was ist denn am std::swap falsch und wer hat erkannt, wie falsch dies eigentlich ist?



  • DrGreenthumb schrieb:

    Der Meinung war ich auch immer, bis ich mich dann doch von der einfacheren Syntax hab überzeugen lassen.

    also ist der grund die einfacher syntax? nicht daß man echt zwischen null-sein-könnern und echten objekten unerscheidet, um irgendwelche sicherheut oder dokumentation dazuzugewinnen? das argument konnte ich nämlich nie nachvollziehen.
    einfachere schreibe ist natürlich ein argument. und daß man, wenn man sich sagt: ich will so oft we möglich refs benutzen, dann einfach nach null oder nicht-null trennen muß, ich auch klar. da null-sein-könner echt selten vorkommt, solltest du kaum noch zeiger benutzen. als übergabeparameter fast nie. gerademal innerhalb von dynamischen datenstrukturen für private hilfsfunktionen. als attribute auch nur bei manchen verkettungen. jeder mitarbeiter hat sicher nen chef, also dort kein zegier zum chef, sondern ne ref.

    Und in der Praxis fahre ich damit gut. Solche Debug-Situationen sind nach meiner Erfahrung nicht wirklich realistisch. Normalerweise haben die Funktionen dann ja doch sprechendere Namen und man sieht anhand des Kontextes auf Anhieb, wer da irgendwo was verändern möchte und wer nicht.
    Jedenfalls sollte man das.

    stimmt. mir scheinen sie auch selten. eigentlich nur in code, den ich nicht schreiben würde.

    beide argumente sind schwach. geradezu windig. auf sand gebaut. irrelevant.

    mir geistert eher irgendwas tieferes im sinne. irgendwie *sind* zeiger dinge, die auf was anderes zeigen, während refs irgendwie bloß vrtuelle namen sind. mein chef-attribut ist für mch ein realer zeiger auf ne andere person. den zeiger kann ich als verkettngspfeilchen in allen meinen diagrammen sehen. dieses verkettngspfeilchen hat substanz. es *ist* der zeiger. zeiger sind verkettngspfeilchen und verkettngspfeilchen sind zeiger. also da würde ich mir endlos schwer tun, aus referenzen umzustellen.

    bei lokalen variablen benutze ich gerne refs als alias.

    also funktionsparameter mag ich es, auszudrücken, ob was const oder non-const ist. mir machts's den code irgendwie klarer. auch innerhalb fasse ich den zeiger ja mit * an, udn dann sehe ich gleich: aha, das ist jetzt passiert ein seiteneffekt. nix bloß lokles. kann mir schwer vorstellen, wie es wäre, wenn ich ganz darauf verzichten würde. kann sein, daß es mir hete ar nicht mehr weh tun würde, weil meine funktionen nzwischen recht klein sind und meist recht gute naen tragen. schweirige sache das. aber an den normalen funktionen es zu messen ist eh nicht gut. an den schlimmen muß man messen. und die nächste zeit habe ich gar nicht vor, was schwieriges zu schreiben.



  • Ponto schrieb:

    Was ist denn am std::swap falsch und wer hat erkannt, wie falsch dies eigentlich ist?

    es sollte nicht als

    swap(a,b);
    

    sondern als

    swap(&a,&b);
    

    augerufen werden.
    ich hab's erkannt.



  • volkard schrieb:

    ...
    sondern als

    swap(&a,&b);
    

    ...

    Das ist aber höchst mißverständlich. Was wird hier vertauscht? a und b? Die Adressen von a und b? Zeiger auf a und b? Die Speicherbereiche, wo a und b liegen?

    Das ursprüngliche swap sagt klar und deutlich, was getauscht wird.



  • volkard schrieb:

    also ist der grund die einfacher syntax?

    ja, für mich schon.
    Ich stimm dir aber zu, was die Betrachtung eines Zeigers angeht. Wenn Klasse 1 einen Verweis zu Klasse 2 hat, speicher ich den auch als Zeiger. Selbst wenn ein Referenz möglich wäre. Nur gehe ich nicht soweit das auf jede Funktion zu übertragen. Ist vielleicht ein wenig unkonsequent.

    nicht daß man echt zwischen null-sein-könnern und echten objekten unerscheidet, um irgendwelche sicherheut oder dokumentation dazuzugewinnen? das argument konnte ich nämlich nie nachvollziehen.

    Das wird einfach von C kommen. Dort ist das Gang und Gebe, wegen fehlender Überladungsmöglichkeiten.



  • Ponto schrieb:

    Das ist aber höchst mißverständlich. Was wird hier vertauscht? a und b? Die Adressen von a und b? Zeiger auf a und b? Die Speicherbereiche, wo a und b liegen?

    ist eindeutig, wenn man immer zeiger verwendet. natürlich ist es mißverständlich, wenn mal mal so und mal anders programmiert.
    solhe uneindeutigkeiten hat man oft.
    bei

    void print(char* ch);
    

    was wird ausgegeben? der zeiger oder der dahinterleigende string?
    ist irgendwie historisch gewachsene vereinbarung, daß man normale zeiger als hexzahl ausgibt, aber zeiger auch char als dahinterliegenden string.
    so ist auch gewachsen, was man mit swap meint. es hat keine weitere klarheit als allein die, daß wir uns drauf geeinigt haben.



  • @Volkard
    Herrlich wie du wieder aus einer reinen Style-Sache eine Absoulte-Wahrheits-Sache machst 😉

    Das Thema Reference vs. Pointer bei Funktionsparametern wird in C++ Kreisen seit bestimmt 10 Jahren heftig diskutiert. Jedesmal bringen beide Seiten gute Argumente (die Sache mit der 0, die Sache mit Fortran, die Sache mit Information Hiding, die Sache mit den Pointer-to-const und viele Sachen mehr...) und jedesmal bleibt das Ergebnis: Just a matter of style.
    -> Sei konsistent. Passe dich an deine Umgebung an.

    Du trommelst natürlich sehr laut und das führt, zumindest bei mir, schnell dazu, dass man deshalb meint, du hättest die Richtige-Lösung(TM) parat.

    Ich möchte zum Ausgleich nur auf comp.lang.c++.moderated hinweisen.
    Eine Suche nach "reference vs pointer" bringt einem unzähligen Diskussionen. Dort findet man auch genügend Leute, die genauso laut wie Volkard trommeln, dabei aber genau in die entgegengesetzte Richtung rudern.
    Nur mal so als Beispiel:

    Some brick-headed
    old C programmers would even tell you that the pointer form is superior,
    that it is more self documenting because the passing of the pointer is
    visible, or some such hogwash. This is just low level thinking, and a
    crutch that should be thrown away. Many programming languages use pass
    by reference and have a lot less trouble with it than C beginners have
    with pointers! So don't let some old fart beat you over the head with
    his crutch.

    Ich persönlich ziehe daraus den Schluss, dass es auf beiden Seiten Heißsporne gibt die die andere Seite missionieren wollen. Wenn ich aber genauer hinschaue finde ich auf beiden Seiten gute Argumente, die man wohl am Besten immer wieder im konkreten Kontext berücksichtigt.



  • HumeSikkins schrieb:

    Du trommelst natürlich sehr laut und das führt, zumindest bei mir, schnell dazu, dass man deshalb meint, du hättest die Richtige-Lösung(TM) parat.

    haste meinen beitrag vom 01 Aug 2004 22:37 gelesen? dann hab ich doch eindeutig bewiesen, die einzig richtige lösung parat zu haben, oder?



  • Interessante Beweisführung. So in etwa: "Mir gefällt es besser und ich finde es leichter. QED"


Anmelden zum Antworten