Was sind die Unterschiede zwischen den Parameterdefinitionen als (Typ & name) und (Typ * Name)?

StackOverflow https://stackoverflow.com/questions/948947

  •  09-09-2019
  •  | 
  •  

Frage

Eine sehr einfache Frage, aber immer noch, es wäre gut, von C ++ Gurus da draußen zu hören.

Es gibt zwei ziemlich ähnliche Weise durch Verweisparameter in C ++.

zu erklären,

1) Verwendung von "Stern":

void DoOne(std::wstring* iData);

2) Mit "Ampersand":

void DoTwo(std::wstring& iData);

Was sind Auswirkungen jeder Methode? Gibt es Gotcha die in jedem Fall?

Bonus # 1: Was für eine formale Art und Weise Methode aufzurufen in # 1 und # 2 sein würde? Sind sie beide als "by-reference"?

Bonus # 2: std :: wstring wird bewusst eingesetzt. Was Auswirkungen auf Standard-Bibliotheksklassen in jedem Fall sein würde?

War es hilfreich?

Lösung

# 1 verwendet einen Zeigerparameter ( 'einen Zeiger auf vorbei'), # 2 verwendet einen Referenzparameter ( "passing durch Referenz). Sie sind sehr ähnlich, aber beachten Sie, dass der anrufende Code in den beiden Fällen anders aussieht:

std::wstring s;

DoOne(&s); // pass a pointer to s
DoTwo(s); // pass s by reference

Einige Leute bevorzugen # 1, eine Konvention mit, die angibt, der durch Zeiger vorbei, dass die Funktion könnte den Wert von s ändern (auch wenn entweder Funktion könnte). Andere Leute (mich eingeschlossen) bevorzugen # 2, da durch Bezugnahme vorbei erlaubt nicht NULL übergeben werden.

Es ist ein weiterer wichtiger Unterschied, wenn sie von const Zeiger oder eine Referenz übergeben. Eine temporäre Variable kann nur auf einen konstanten Referenz-Parameter übergeben werden:

void ByConstPointer(const std::wstring&);
void ByConstReference(const std::wstring*);

void test()
{
  ByConstPointer(&std::wstring(L"Hello")); // error: cannot take address of temporary
  ByConstReference(std::wstring(L"Hello")); // fine
}

Andere Tipps

Regel Nummer eins für: Wenn NULL ein gültiger Wert für den Funktionsparameter im Rahmen der Funktion ist, dann gibt sie als Zeiger, sonst wird es als Referenz zu übergeben

.

Rationale, wenn es nicht kann (sollte nicht!) Immer NULL sein, dann setzen Sie sich nicht durch die Mühe für NULL zu überprüfen.

Während Beispiele zu schreiben, kam ich mit meiner eigenen Antwort auf. Alles andere als unten?

Das Ergebnis jeder von ihnen ist ganz ähnlich: ein Verweis auf ein Objekt in dem Speicher innerhalb Methode des Oszilloskops endet. Es scheint keine strengen Speicheranforderungen für jeden von ihnen zu sein. Das Objekt kann entweder auf dem Stapel oder in Haufen sein.

Bei Stapeln jedes der Verfahren wie folgt aufgerufen werden würde:

{
    std::wstring data;
    DoOne(&data);
    DoTwo(data);
}

Doch wenn es um die Halde kommt, würde der zweite Ansatz erfordert, dass das Objekt vor dem Aufruf der Methode vorhanden sein muss. Wenn das Objekt nicht vorhanden ist, würde der Anrufer verursacht Ausnahme, nicht die Angerufenen.

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    DoTwo(*pData);
}

In dem obigen, wenn out-of-memory Bedingung eintritt und pData NULL endet, der Absturz vor DoTwo passieren würde, aber doone würde die NULL schlucken und könnte einige Zeit später zum Absturz bringen.

Ich selbst würde nicht eine C ++ Abbildung nennen (außer in meinem Lebenslauf), aber ich würde sagen; es sei denn, Theres jede Verwendung des Parameters als einen Zeiger vorbei (das heißt die Funktion für null überprüfen möchte), immer eine Referenz verwendet werden.

Das gilt auch für Funktionen Objekte Rückkehr, ein Zeiger Rückkehr irgendwie den Benutzer der Klasse sagen, dass es null sein könnte.

In doone kann iData zugewiesen NULL werden. Wenn Sie, dass nach dem Aufruf doone verwenden, stürzt die Anwendung.

So etwas wie

void DoOne(std::wstring* iData)
{
   //Use iData
   delete iData;
   iData = NULL;
}

und

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    pData->someFunction(); //Crash
}

Ihre Antwort ist völlig falsch, wenn Sie sagen:

  

Das Ergebnis jeder von ihnen ist ganz   ähnliche: eine Referenz auf ein Objekt in   der Speicher endet innerhalb des Verfahrens up   Umfang. Es scheint keine streng zu sein   Speicheranforderungen für jeden von ihnen.

connsider:

void f( int * p1 ) {
   int ** p2 = & p1;
}

hier p1 hat einen bestimmten „Speicherbedarf“ - es muss vorhanden sein, und ich muss seine Adresse übernehmen können. Vergleichen Sie dies mit

void f( int & r ) }
   int * p = & r;
}

hier r hat keine eigene Existenz, es ist lediglich eine Referenz. Whem Ich nehme seine richte ich die Adresse der Sache nehme, dass r bezeichnet.

Ihre Anmerkungen den NULL-Zeiger in Bezug sind auch falsch. den NULL-Zeiger Derefrencing verursacht undefiniertes Verhalten - dies bei einem Crash oder nicht führen kann.

Wenn Sie eine Funktion schreiben, die eine Variable, die durch Zeiger bekommen, werden Sie höchstwahrscheinlich überprüfen müssen, wenn der Zeiger gültig ist (zB nicht NULL), sonst Sie Programmabsturz riskieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top