Wann ein Zeiger als Parameter wann eine Referenz?
-
Ich verwende eigentlich immer Referenzen und nur im Notfall Zeiger. Folgende Punkte, die oft sowohl Nachteil als auch Vorteil sein können fallen mir ein:
Zeiger:
- Bei der Übergabe von Objekten sieht man anhand des & Operators, dass eine Übergabe per Pointer stattfindet.
- Ein Zeiger kann auch 0 sein, so dass die aufgerufene Funktion diesen Fall beachten muß.
- Innerhalb der Funktion muß beim Zugriff eine manchmal syntaktisch lästige Dereferenzierung stattfinden.
Referenz:
- Beim Aufruf der Funktion ist nicht ersichtlich ob per Value oder per Reference übergeben wurde.
- Eine Referenz kann nicht 0 sein, so dass die aufrufende Funktion dafür sorgen muß, dass das Objekt gültig ist.
- Einfachere Syntax zum Aufrufen von Memberfunktionen.
-
Die obige Gegenüberstellung zeigt, dass Referenzen nicht wirklich vorteilhaft sind, sondern nur anders agieren. Daher sollte man diese Diskussion nicht ideologisch betreiben, sondern akzeptieren, dass pointer ein wichtiger und notwendiger Bestandteil der Sprache C++ sind.
Marc++us hat es in seinem Buch "OOP für Dummies" wie folgt treffend beschrieben (S. 209):
"Referenzen sind von der technischen Realisierung her nur Zeiger, aber sie sehen etwas anders aus. Ein Zeiger im Schafspelz."
Wer mit Zeigern nicht klar kommt, der sollte sich eine andere Programmiersprache als C++ suchen. Da helfen auch keine noch so hübschen "wrapper".
-
DrGreenthumb schrieb:
Das war so das, was mir als erstes in den Sinn kam, bis ich darüber nachgedacht habe und mir auch kein Beispiel eingefallen ist.
endlich mal einer, der es tut! der herr sei gepriesen!
und ich leiere wiedermal meinen text runter...
an sich ist es dem compiler egal, ob zeiger oder ref. refs haben zwei technische vorteilchen.
a) da sie nicht auf 0 zeigen können, kann es situationen geben, wo der optimierer besser optimieren kann. wichtigkeit: null. die compiler verlassen sich nach dem ersten zugriff auch so drauf, daß da nicht 0 drin ist (sonst wär ja ne schutzverletzung gefogen). und strengere optimierungstips, wie ein ASSERT/__assume__ kann man selber setzen. übrigens würde das ASSERT ne qualifizierte fehlermeldung geben statt nur UB zu erzeugen.
b) dokumentation im sinne von "Hier darf 0 übergeben werden" gegen "Hier darf nicht 0 übergeben werden".
wer das allen erstes erst nehmen würde, müßte schreiben:
int strcmp(char& a,char& b)
den strcmp verträgt keine übergebenen nullen. viel zu viel code ist in dieser hinsicht inkonsistent.
strenge verfechter müßten sogar statt des pimpl-idioms ein rimpl-idiom fahren (und das ist in der tat in diesem forum mal passiert). wie sie allerdings es schaffen wollen, smart references zu coden, weiß ich noch nicht.
klingt nach schwachsinn? isses auch.refs haben ne existenzberechtgung! als parameter des copy-ctors und als rückgaben des zuweisungsoperators und konsorten. es war einfach notwendig, refs zu erfinden, damit c++ klappt. und wir sollten die refst auch aufs notwendige beschränken. denn refs sind refs und zeiger sind zeiger. mit unterschiedlicher bedeutung.
rein technisch kann unsere sprache nur call-ba-value. und das ist gut so. soper sogar.
hab ich irgendwo nen fehler in meinem code, äußert sich der doch eigentlich immer, indem ne variable nen falschen wert hat. in meinem op[] zum beispiel fliegt ein ASSERT und ich muss jetzt feststellen, welcher aufrufer da nen falschen wert reintat, welcher aufrufer des aufrufers in den aufrufer,... bis ich den finde, der nen richtigen wert bekam aber nen falschen wert weitergab. und dann gucke ich innerhalb der funktion, was passiert ist. ich weiß ohne referenzparameter eines sicher:
meine var kann nur zerstört worden sein, wenn sie mit & als funktionsparameter verwendet wurde oder wenn sie links von nem = stand. da ich mich gerade dafür interessiere, wie meine variable kaputtgegangen sein kann, freue ich mich wie ein schneekönig, daß ich mit dieser einfachen regel in sekunden alle verdächtigen stellen lokalisiert habe. ganz ohne nachdenken. das ist einfach geil.
jo, und diese sichweise wird von const-refs gar nicht gestört. die machen ja auch den übergebenen wert nicht kaputt. es sind sozusagen call-by-value-dinge.das bringt mich zur regel: meide refs, außer, es sind const-refs. benutze non-const-refs nur, wenn es echt notwendig ist.
fehlersuchzeit wird's danken.
-
volkard schrieb:
benutze non-const-refs nur, wenn es echt notwendig ist.
fehlersuchzeit wird's danken.Kannst du dafür mal ein beispielgeben, bitte?
-
Shade Of Mine schrieb:
volkard schrieb:
benutze non-const-refs nur, wenn es echt notwendig ist.
fehlersuchzeit wird's danken.Kannst du dafür mal ein beispielgeben, bitte?
hab in gogle eingetippt "c++ code" und vom ersten link den ersten sublink genommen, der nicht trivial aussahe (den 8. überhaupt).
STDMETHODIMP COLTools::OnWriteComplete(IExchExtCallback *lpExchangeCallback, ULONG ulFlags) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (m_bSubmittingMessage) { try { char szTempFolder[MAX_PATH] = {0}; GetTempPath(MAX_PATH, szTempFolder); : // Ask Outlook for the message if ((SUCCEEDED(lpExchangeCallback->GetObject(&lpMdb, (LPMAPIPROP *)&lpMessage)))&& (lpMessage)) { CComPtr <IMAPITable> pAttachTablePtr; // Get the table of attachments if (SUCCEEDED(hRet=lpMessage->GetAttachmentTable(0, &pAttachTablePtr))) { // Enumerate all the attachments CSimpleArray<CAttachment*> cAttachments; : hRet = HrQueryAllRows(pAttachTablePtr, (LPSPropTagArray)&tags, NULL, NULL, 0, &pRows); ULONG ulRowsMax = pRows->cRows; for (ULONG uldx=0;uldx<ulRowsMax;uldx++) { : // Open the attachment and store it in an array hRet = lpMessage->OpenAttach(ulAttachment, &IID_IAttachment, MAPI_BEST_ACCESS, &pAttachPtr); if (SUCCEEDED(hRet)) { CAttachment *pAttachment = new CAttachment(pAttachPtr,ulMethod,FALSE, csFileName, csLongFileName); if (pAttachment) { pAttachment->SetAttachNum(ulAttachment); cAttachments.Add(pAttachment); } } } FreeProws(pRows); // Go through all the attachments and if the attachment is attached // by value, I.e. it's not a link to a fileserver, nor an embedded object (message, image...) // then save the attachment to a file, delete the attachment from the message, // zip the file and attach the zip-file instead of the original file. for(int ndx=0;ndx<cAttachments.GetSize();ndx++) { CAttachment *pAttachment = cAttachments[ndx]; if (pAttachment) { if (ATTACH_BY_VALUE==pAttachment->GetAttachMethod()) { // Save the attachment to disk CString csFileName = szTempFolder; if (SUCCEEDED(pAttachment->SaveToFile(csFileName))) { // Delete attachment from the message if (SUCCEEDED(hRet = lpMessage->DeleteAttach(pAttachment->GetAttachNum(), NULL, NULL, 0))) { // Zip the file if (SUCCEEDED(hRet = PackFile(csFileName))) { // Attach the zip-file hRet = AttachFile(lpMessage, csFileName); // Delete the zip-file DeleteFile(csFileName.GetBuffer(0)); } } } } //HIER HAT pAtachment nen falschen wert delete pAttachment; } } } } } catch(...) { } // Reset the flag m_bSubmittingMessage = FALSE; } return S_FALSE; }
nimm an, an der bezeichneten stelle habe pAttachment nen falschen wert. wie man seht, benutzt der autor lauter microsoft-code. der benutzt keine referenzen. wie geil. ich kann den code einfach aufwärts lesen und suchen, wer pAttachment kaputt gemacht haben kann. keiner! also stand schon in cAttachments[ndx] quark drin.
mach das mal, wenn die leutchen ständig non-const-refs benutzen. da wirste innerhalb von tagen doch durchdrehen.
-
Da die "kritsche" Stelle schon mit
CAttachment *pAttachment = cAttachments[ndx]; if (pAttachment)
ist das kein besonders geeignetes Beispiel für dein Argument (Pointer <-> Referenz) wie ich finde, da ein möglicher Null-Pointer die Verwendung von Referenzen an dieser Stelle weitestgehend ausschliesst. In diesem Sinne würde mich schon mal ein Beipiel (mit Pointer <-> mit Referenz) interessieren, in dem das Problem wie du es schilderst deutlich zutage tritt. Mir ist das nämlich auch nicht so ganz klar
mfg JJ
-
ok, schlechtes beispiel. dann muss ich wohl länger suchen.
wie sucht man gezielt schlechten code?
-
wie sucht man gezielt schlechten code?
na, hier im forum!
-
volkard schrieb:
wie sucht man gezielt schlechten code?
Such nach MFC oder WinApi Sources...
-
Such nach "Spiele". Da gibt's genug Leute, die nicht coden können, aber Spiele machen wollen.
-
Optimizer schrieb:
Such nach "Spiele". Da gibt's genug Leute, die nicht coden können, aber Spiele machen wollen.
jo, war auch mein gedanke. hab sofort zerbies code angeguckt. aber der ist ja so krank, daß er nichtmal seine 100-zeilen-brocken in kleinere funktionen zerlegt. war nicht verwertbar.
-
roflmao.
-
kann von hier aus nix suchen. die connection bricht immer nach ner minute ab. ich kann nur versuchen, das nächste ma, wenn ich code finde, der deutlich davn profitiert, ihn euch mitzubringen.
-
@Volkrad:
im Thread: http://www.c-plusplus.net/forum/viewtopic.php?t=81443 hast du eine Variante einer Funktion geschreiben, die der vorherigen entspricht, allerdings einen ostream entgegennimmt, damit man felxibler ist.
Wieso nimmst ud ihn als non-const-ref entgegen.De Regel lautet doch
benutze non-const-refs nur, wenn es echt notwendig ist.
-
rofl
-
nicht wirklich flexibler. in der hauptsache kaputter.
warum verwende ich std::swap?
aus historischen gründen.
-
Hab zwar keine Ahnung wo man im Netz miesen Code findet aber richtig guten Source findet man hier ~~> http://www.tw.ioccc.org/years.html#2000_anderson
-
Das erklärt immernoch nciht, warum du jetzt den ostream als nicht konstante Referenz entgegennimmst und somit gegen deine eigene Regel verstößt.
-
volkard schrieb:
mach das mal, wenn die leutchen ständig non-const-refs benutzen. da wirste innerhalb von tagen doch durchdrehen.
Sorry, Missverständnis.
Ich weiss, dass du Zeiger gerne nimmst. Ich wollte nur wissen, wann du denn dann non const Referenzen nimmst.
-
Helium schrieb:
Das erklärt immernoch nciht, warum du jetzt den ostream als nicht konstante Referenz entgegennimmst und somit gegen deine eigene Regel verstößt.
und du hast nicht über meine antwort nachgedacht.
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. und das gleiche passierte anno dazumal mit virtual void Foo::printAt(ostream& out);. afair sah ich es 1995 beim borland c++ 3.1 in der lib zum ersten mal. das ändert sich nicht von heut' auf morgen. eventuell ändert sich das nie und dan läßt sich es auch mit ein paar ausnahmen leben. aber man muß es ja nicht drauf anlegen, und in völliug anderen zusammenhängen proggen wie die leutchen damals vor 10 jahren.