2 Stimmen

Java und C++: Wertübergabe und Referenzübergabe

Es wird gesagt, dass in Java Methodenargumente als Wert übergeben werden, was für Primitive zutrifft und für Objekte wird die Referenz des Objekts als Wert übergeben. Zur Veranschaulichung betrachten Sie den Code:

class Test {
 private static void dateReplace (Date arg) {
        arg = new Date (arg.getYear(), arg.getMonth(), arg.getDate() +1);
        System.out.println ("arg in dateReplace: " + arg);
    }

 public static void main(String[] args) {

      Date d1 = new Date ("1 Apr 2010");
        dateReplace(d1);
        System.out.println ("d1 after calling dateReplace: " + d1);
    }
}

this will print:
arg in dateReplace: Fri Apr 02 00:00:00 2010
d1 after calling dateReplace: Thu Apr 01 00:00:00 2010.

Was ist das C++-Äquivalent, das die gleichen Ergebnisse liefern würde?

Was ist C++-Äquivalent, das den Wert von d1 nach dem Aufruf der Methode auf den gleichen Wert wie innerhalb der Methode, dh der Aufrufer sehen den geänderten Wert geben würde?

6voto

zneak Punkte 129366

C++ hat nicht die gleiche Semantik wie Java für Werte und Referenzen. Zunächst hat jeder Typ die Möglichkeit, entweder per Kopie oder per Referenz oder per Adresse übergeben zu werden (Sie können jedoch verhindern, dass Typen per Kopie übergeben werden, indem Sie den Kopierkonstruktor ausblenden).

Die Übergabeart, die am engsten mit der "by reference"-Übergabe in Java verwandt ist, ist die Übergabe per Zeiger. Hier ein Beispiel für die drei:

void foo(std::string bar); // by copy
void foo(std::string& bar); // by reference
void foo(std::string* bar); // by address

Nebenbei bemerkt ist die Übergabe per Kopie immer teurer als die Übergabe per Referenz oder Zeiger für Typen, die größer sind als die Größe eines Zeigers. Aus diesem Grund werden Sie oft die Übergabe per const Referenz, die es Ihnen ermöglicht, ein Objekt zu lesen, ohne es kopieren zu müssen.

void foo(const std::string& bar); // by const reference

Dies ist jedoch alles sehr kompliziert, und Sie müssen die Feinheiten von Java kennen, um richtig zu entscheiden, was Sie in C++ wollen. In Java werden Objekte nicht durch Referenzen übergeben, sondern durch Kopieren. Das heißt, wenn Sie einem Argument ein neues Objekt zuweisen, ändert sich das Objekt des übergeordneten Bereichs nicht. In C++ entspricht dies eher der Übergabe von Objekten per Adresse als per Referenz. Hier ein Beispiel, das zeigt, wie wichtig dies ist:

// Java using "object references":
public static void foo(String bar)
{
    bar = "hello world";
}

public static void main(String[] argv)
{
    String bar = "goodbye world";
    foo(bar);
    System.out.println(bar); // prints "goodbye world"
}

// C++ using "references":
void foo(std::string& bar)
{
    bar = "hello world";
}

int main()
{
    std::string bar = "goodbye world";
    foo(bar);
    std::cout << bar << std::endl; // prints "hello world"
}

// C++ using pointers:
void foo(std::string* bar)
{
    bar = new std::string("goodbye world");
    delete bar; // you don't want to leak
}

int main()
{
    std::string bar = "goodbye world";
    foo(&bar);
    std::cout << bar << std::endl; // prints "hello world"
}

Mit anderen Worten: Wenn Sie in C++ Referenzen verwenden, haben Sie es in Wirklichkeit mit der gleiche Variable Sie haben bestanden. Alle Änderungen, die Sie daran vornehmen, auch Zuweisungen, werden im übergeordneten Bereich berücksichtigt. (Dies ist zum Teil darauf zurückzuführen, wie C++ mit dem Zuweisungsoperator umgeht.) Bei der Verwendung von Zeigern erhalten Sie ein Verhalten, das dem von Java-Referenzen ähnlicher ist, allerdings um den Preis, dass Sie die Objektadressen möglicherweise über den unären & Operator (siehe foo(&bar) in meinem Beispiel), wobei die Verwendung der -> Operator, um auf Mitglieder zuzugreifen, und einige zusätzliche Komplexität für die Verwendung von Operatorüberladungen.

Ein weiterer bemerkenswerter Unterschied ist, dass, da die Verwendung von by-reference-Argumenten syntaktisch eng mit der Verwendung von by-copy-Argumenten verwandt ist, Funktionen in der Lage sein sollten, davon auszugehen, dass die Objekte, die Sie per Referenz übergeben, gültig sind. Auch wenn es normalerweise möglich ist, eine Referenz an NULL ist davon dringend abzuraten, da die einzige Möglichkeit, dies zu tun, die Dereferenzierung NULL was zu einem undefinierten Verhalten führt. Wenn Sie also in der Lage sein müssen, eine NULL als Parameter verwenden, werden Sie es vorziehen, Argumente per Adresse statt per Verweis zu übergeben.

Meistens werden Sie per Referenz statt per Adresse übergeben wollen, wenn Sie ein Argument aus einer Funktion ändern wollen, weil das "C++-freundlicher" ist (es sei denn, Sie brauchen die NULL Wert), auch wenn dies nicht genau dem entspricht, was Java tut.

1voto

Ephphatha Punkte 586

C++ übergibt standardmäßig nach Wert. Sie können implizit per Referenz übergeben, indem Sie die Funktion so schreiben, dass sie eine Referenz akzeptiert, oder explizit, indem Sie einen Zeiger übergeben.

Betrachten Sie die folgenden drei Beispiele:

FooByValue(Foo arg)
{
  //arg is passed by value.
}

FooByReference(Foo & arg)
{
  //arg is passed by reference.
  //changes made to arg will be seen by the caller.
}

FooByPointer(Foo * arg)
{
  //arg is passed by reference with pointer semantics.
  //changes made to the derefenced pointer (*arg) will be seen by the caller.
}

Y Foo anObject;

FooByValue(anObject); //anObject will not be modified.
FooByRefence(anOBject); //anOBject may be modified.
FooByPointer(&anObject); //anObject may be modified.

Das Symbol &, wenn es auf eine Variable wie &anOBject wird verwendet, um die Adresse der Variablen zu erhalten, d. h. einen Zeiger auf den Speicherplatz, in dem die Variable gespeichert ist.

FooByValue() funktioniert wie Ihr Beispiel, und FooByReference() behält alle Änderungen, die nach dem Funktionsaufruf an den Argumenten vorgenommen werden, da das Argument nicht kopiert wird, sondern seine tatsächliche Position im Speicher gemeinsam genutzt wird.

0voto

ralphtheninja Punkte 116647
class Test {
 void dateReplaceReference(Date& arg) {
   // arg is passed by reference, modifying arg 
   // will modify it for the caller
 }
 void dateReplaceCopiedArgument(Date arg) {
   // arg is passed by value, modifying arg 
   // will not modify data for the caller
 }
 void dateReplaceByPointer(Date* arg) {
   // arg is passed as pointer, modifying *arg 
   // will modify it for the caller
 }
};

Beachten Sie, dass die Übergabe als Referenz oder per Zeiger dasselbe ist. Der einzige Unterschied besteht darin, wie Sie auf die Daten zugreifen wollen, durch ein "." oder durch "->". Sie können auch keinen Nullwert an eine Methode übergeben, die eine Referenz nimmt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X