Postskriptum 2017/3/1: Ich habe einen zusätzlichen Artikel geschrieben, weil die Erklärung von Python etwas ungenau war → [Ein bisschen mehr über Referenz ~ Verwenden von Python und Java als Beispiel ~](http://qiita.com/ur_kinsk/items / 7055ec8b50622289eeb2)
Es ist ein häufiges Thema, aber ich habe beschlossen, das Verhalten beim Übergeben von Variablen an Funktionen zusammenzufassen, z. B. das Übergeben nach Wert oder Referenz.
In den meisten Sprachen verhalten sich ** primitive Typen ** und andere unterschiedlich. Obwohl es je nach Sprache leicht unterschiedlich ist, sieht der primitive Typ so aus.
Andere Arrays, Klassen, Strukturen usw. werden hier als ** Objekte ** bezeichnet.
Es wird eine neue Variable erstellt, die den Wert kopiert, wenn er an die Funktion übergeben wird, und die zu einem formalen Argument wird. Durch Ändern des formalen Arguments in der Funktion wird die aufrufende Variable nicht geändert. Beispiele für C ++, Java und Python.
//C++
#include <iostream>
void foo(int x){
x = 42;
}
int main(){
int x = 334;
std::cout << x << "\n";
foo(x);
std::cout << x << "\n";
return 0;
}
//Java
public class Val{
public static void foo(int x){
x = 42;
}
public static void main(String[] args){
int x = 334;
System.out.println(x);
foo(x);
System.out.println(x);
}
}
#Python
def foo(x):
x = 42
x = 334
print(x)
foo(x)
print(x)
Die Ergebnisse sind wie folgt.
334
334
Primitive Typen werden in den meisten Sprachen wie diesen als Wert übergeben. In Sprachen, in denen zwischen Strukturen und Klassen unterschieden wird, werden Strukturen nach Wert übergeben, z. B. C # und Swift, und Klassen werden als Referenz oder Referenz definiert.
Die tatsächlichen und formalen Argumente, die an die Funktion übergeben werden, verweisen jetzt auf dieselbe Stelle im Speicher. Es fühlt sich sozusagen an, als würde man einen "Alias" für dasselbe erstellen. Wenn Sie das formale Argument in der Funktion ändern, wird es im Aufrufer wiedergegeben. C ++, C #, Swift Beispiele.
//C++
#include <iostream>
class Box{
public:
int value;
Box(int value) : value(value){}
};
//Beim Definieren einer Funktion&Wenn Sie hinzufügen, wird es als Referenz übergeben
void foo(Box& box){
box.value = 42;
}
void bar(Box& box){
box = Box(42);
}
int main(){
Box box1(334);
std::cout << "foo: \n";
std::cout << box1.value << "\n";
foo(box1);
std::cout << box1.value << "\n";
Box box2(334);
std::cout << "bar: \n";
std::cout << box2.value << "\n";
bar(box2);
std::cout << box2.value << "\n";
return 0;
}
//C#
using System;
class Ref{
public class Box{
public int value;
public Box(int value){
this.value = value;
}
}
//Wenn ref zum Zeitpunkt der Funktionsdefinition und des Aufrufs hinzugefügt wird, wird es als Referenz übergeben.
public static void Foo(ref Box box){
box.value = 42;
}
public static void Bar(ref Box box){
box = new Box(42);
}
public static void Main(string[] args){
Box box1 = new Box(334);
Console.WriteLine("foo: ");
Console.WriteLine(box1.value);
Foo(ref box1);
Console.WriteLine(box1.value);
Box box2 = new Box(334);
Console.WriteLine("bar: ");
Console.WriteLine(box2.value);
Bar(ref box2);
Console.WriteLine(box2.value);
}
}
//Swift
class Box{
public var value:Int
init(_ value:Int){
self.value = value
}
}
func foo(_ box:Box){
box.value = 42
}
//Beim Zuweisen eines neuen zu einem formalen Argument inout beim Definieren einer Funktion, beim Aufrufen&Anziehen
func bar(_ box:inout Box){
box = Box(42)
}
var box1 = Box(334)
print("foo: ")
print(box1.value)
foo(box1)
print(box1.value)
var box2 = Box(334)
print("bar: ")
print(box2.value)
bar(&box2)
print(box2.value)
Das Ergebnis sieht so aus.
foo:
334
42
bar:
334
42
Auch geteilte Lieferung. Die meisten Sprachen nehmen diese Form an, wenn ein Objekt übergeben wird. Dies wird manchmal als Referenzübergabe bezeichnet, ist jedoch tatsächlich etwas anders, wie unten gezeigt. Java, C #, Python Beispiele.
//Java
class Ptr{
public static class Box{
public int value;
public Box(int value){
this.value = value;
}
}
public static void foo(Box box){
box.value = 42;
}
public static void bar(Box box){
box = new Box(42);
}
public static void main(String[] args){
Box box1 = new Box(334);
System.out.println("foo: ");
System.out.println(box1.value);
foo(box1);
System.out.println(box1.value);
Box box2 = new Box(334);
System.out.println("bar: ");
System.out.println(box2.value);
bar(box2);
System.out.println(box2.value);
}
}
//C#
using System;
class Ptr{
public class Box{
public int value;
public Box(int value){
this.value = value;
}
}
//Ohne ref wird die Klasse als Referenz übergeben
public static void Foo(Box box){
box.value = 42;
}
public static void Bar(Box box){
box = new Box(42);
}
public static void Main(string[] args){
Box box1 = new Box(334);
Console.WriteLine("foo: ");
Console.WriteLine(box1.value);
Foo(box1);
Console.WriteLine(box1.value);
Box box2 = new Box(334);
Console.WriteLine("bar: ");
Console.WriteLine(box2.value);
Bar(box2);
Console.WriteLine(box2.value);
}
}
#Python
class Box:
def __init__(self, value):
self.value = value
def foo(box):
box.value = 42
def bar(box):
box = Box(42)
box1 = Box(334)
print("foo: ")
print(box1.value)
foo(box1)
print(box1.value)
box2 = Box(334)
print("bar: ")
print(box2.value)
bar(box2)
print(box2.value)
Das Ergebnis ist dies.
foo:
334
42
bar:
334
334
Auf diese Weise unterscheidet sich das Verhalten beim Zuweisen eines neuen Objekts zu einem formalen Argument vom Übergeben als Referenz. Wenn Sie in C ++ einen Zeiger wie unten gezeigt übergeben, ist das Verhalten dasselbe wie oben.
//C++
#include <iostream>
class Box{
public:
int value;
Box(int value) : value(value){}
};
void foo(Box *box){
box->value = 42;
}
void bar(Box *box){
box = new Box(42);
delete box;
}
int main(){
Box *box1 = new Box(334);
std::cout << "foo: \n";
std::cout << box1->value << "\n";
foo(box1);
std::cout << box1->value << "\n";
Box *box2 = new Box(334);
std::cout << "bar: \n";
std::cout << box2->value << "\n";
bar(box2);
std::cout << box2->value << "\n";
delete box1;
delete box2;
return 0;
}
~~ Praktisch sollten Sie std :: shared_ptr anstelle des Rohzeigers verwenden, aber dieses Mal wage ich es, dies zum Vergleich zu verwenden. ~~ Es mag verwirrend sein, "Referenzübergabe" zu sagen, aber kurz gesagt bedeutet "** Referenz " hier " Zeiger **". Das heißt, den Zeiger als Wert übergeben. In diesem Sinne ist die von "bar" durchgeführte Verarbeitung
・ Beim Übergeben als Referenz
box
und die callerbox2
zeigen auf den gleichen Ort, so dass sich auch dieser ändert・ Beim Übergeben als Referenz
Es stellt sich heraus, dass. In der Abbildung sieht es so aus.
Wenn Sie das Verhalten der Referenzübergabe in Code reproduzieren möchten, der Zeiger verwendet,
void bar(Box *box){
box = new Box(42);
delete box;
}
Teil ist
void bar(Box *box){
*box = Box(42);
}
werden. Übrigens wird im Objective-C-Code klarer, dass dieses "Objekt von einem Zeiger behandelt wird".
//Objective-C
#import <Foundation/Foundation.h>
@interface Box : NSObject
@property(nonatomic) int value;
- (id)initWithValue:(int)value;
@end
@implementation Box
- (id)initWithValue:(int)value{
if(self = [super init]){
_value = value;
}
return self;
}
@end
void foo(Box *box){
box.value = 42;
}
void bar(Box *box){
box = [[Box alloc] initWithValue:42];
}
int main(){
Box *box1 = [[Box alloc] initWithValue:334];
printf("foo:\n");
printf("%d\n", box1.value);
foo(box1);
printf("%d\n", box1.value);
Box *box2 = [[Box alloc] initWithValue:334];
printf("bar:\n");
printf("%d\n", box2.value);
bar(box2);
printf("%d\n", box2.value);
return 0;
}
・ Nach Wert Eine neue Variable wird mit dem Wert des tatsächlich kopierten Arguments erstellt. Dies ist, wenn Sie den primitiven Typ übergeben. Das Ändern des formalen Arguments wirkt sich nicht auf den Anrufer aus. Abhängig von der Sprache können Strukturen auch als Wert übergeben werden.
・ Als Referenz übergeben Die tatsächlichen und formalen Argumente verweisen jetzt auf dieselbe Stelle im Speicher. Wenn Sie das formale Argument ändern, wird es im Aufrufer angezeigt. Viele Sprachen unterstützen dieses Formular nicht.
-Passing per Referenz Übergeben eines Zeigers in C oder C ++. Indem Sie einem formalen Argument einen Speicherort im Speicher zuweisen, können Sie dasselbe Objekt wie das eigentliche Argument über diesen Speicherort ausführen. Ähnlich wie beim Übergeben als Referenz, aber achten Sie auf das Verhalten, wenn der formale Argumentwert selbst neu geschrieben wird. Viele Sprachen nehmen diese Form an, wenn Objekte übergeben werden.