2011-05-07 29 views
2

Говорят, что аргументы метода Java передаются по значению, которое истинно для примитивов и для объектов, ссылка объекта передается по значению. Для иллюстрации рассмотрим код:Java и C++ передаются по значению и передаются по ссылке

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. 

Что такое эквивалент C++, который даст такие же результаты?

Что такое эквивалент C++, который даст значение d1 после вызова метода как того же значения, что и в методе, то есть вызывающий объект видит измененное значение?

+0

Это домашнее задание? Вероятно, вы захотите ознакомиться с операторами C * (косвенное) и & (ссылкой). – eggyal

+3

@eggyal Ни C, ни C++ не имеют ссылочного оператора - & является адресом оператора. –

+0

@unapersson: какая разница? : s – eggyal

ответ

5

C++ не имеет той же семантики, что и Java, для значений и ссылок. Сначала каждый тип может быть передан либо копией, либо ссылкой или по адресу (однако вы можете предотвратить передачу типов копиями путем скрытия конструктора копирования).

Проходной тип, наиболее тесно связанный с передачей «по ссылке» Java, является указателем. Вот пример из трех:

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

Как примечание стороны, проходя мимо копии всегда дороже, чем передача по ссылке или указателю для типов, которые больше, чем размер указателя. По этой причине вы также можете часто ссылаться на ссылку const, что позволит вам читать объект без необходимости его копирования.

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

Однако, это все сложно, и вы должны быть осведомлены о Java тонкостях правильно, что вы хотите в C++ решить. В Java вы фактически не передаете объекты по ссылкам: вы передаете ссылки на объекты по копии. То есть, если вы назначаете новый объект аргументу, объект родительской области не изменится. В C++ это более близко соответствует прохождению объектов по адресу, чем по ссылке. Вот пример того, как это важно:

// 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" 
} 

Другими словами, при использовании ссылки на C++, вы действительно имеем дело с же переменной вы прошли.Любые изменения, которые вы делаете с ним, даже присваивания, отражаются в родительской области. (Отчасти это связано с тем, как C++ имеет дело с оператором присваивания.) Используя указатели, вы получаете более тесное отношение к тому, которое у вас есть с ссылками Java, за счет того, что возможно получить адреса объектов через унарный оператор & (см. foo(&bar) в моем примере), необходимо использовать оператора -> для доступа к элементам и некоторую дополнительную сложность для использования перегрузок операторов.

Другая заметная разница заключается в том, что, поскольку использование аргументов by-reference синтаксически тесно связано с использованием аргументов by-copy, функции должны иметь возможность предположить, что объекты, которые вы передаете по ссылке, действительны. Несмотря на то, что обычно можно передать ссылку на NULL, это очень сильно обескураживает, так как единственный способ сделать это - разыменовать NULL, который имеет неопределенное поведение. Поэтому, если вам нужно передать NULL в качестве параметра, вы предпочитаете передавать аргументы по адресу, а не по ссылке.

В большинстве случаев вы захотите перейти по ссылке вместо адреса, если хотите изменить аргумент из функции, потому что он больше «C++ дружественный» (если вам не нужно значение NULL), хотя это не совсем так, как это делает Java.

+1

Java может называть их объектными ссылками. Но они больше похожи на указатели C++, чем ссылки, поскольку они могут быть NULL. Поэтому я просто думаю о Java-объектах как об убранных мусором указателях, что в основном эквивалентно общему указателю на C++ (который представляет собой гораздо более тонкий сборщик мусора, собранный мусором). –

0
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 
} 
}; 

Обратите внимание, что передача как ссылки или указателем - это то же самое. Единственное различие заключается в том, как вы хотите получить доступ к данным, используя «.». или "->". Кроме того, вы не можете передать значение null методу, использующему ссылку.

+0

Фактически, модификация '* arg' будет изменять данные в вызывающем. Изменение 'arg' не будет. – nos

+0

Да, это неправильно. Доступ к чему-либо через указатель ИЗМЕНИТЬ объект для всех пользователей. Единственный способ сделать изменения невидимыми - это второй метод, в котором конструктор копирования вызывается для arg и только копия, предоставленная вызываемому лицу. – Voo

+0

Вы абсолютно правы! :) – ralphtheninja

1

C++ по умолчанию по умолчанию. Вы можете неявно передавать по ссылке, записывая функцию, чтобы принять ссылку, или явно, передав указатель.

Рассмотрим следующие три примера:

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. 
} 

Вы можете затем вызвать выше, следующим Foo anObject;

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

& символ, когда применяется к переменной, как &anOBject используется, чтобы получить адрес переменной, в основном дает указатель на место в памяти, что переменная хранится в.

FooByValue() работает как ваш пример, и FooByReference() сохранит любые изменения, внесенные в аргументы после вызова функции, поскольку аргумент не будет скопирован, его фактическое местоположение в памяти будет общим.

+0

Неправильно. В C++ ничего не передается по ссылке. Это распространенное заблуждение. Вы передаете ссылку/указатель по значению. Если он был передан по ссылке, вы можете изменить сам параметр (а не только объект, который он указывает на/ссылки). –

+5

@Tamas У вас, похоже, есть свое личное значение для термина «пройти по ссылке». –

+0

Если вы передаете объект указателем, можете ли вы изменить указатель внутри функции, чтобы он указывал в другом месте, а изменение отражалось снаружи? Вы можете сделать это в Паскале, например, потому что на самом деле это [семантика передачи по ссылке] (http://www.hkbu.edu.hk/~bba_ism/ISM2110/pas045.htm). Вы не можете сделать это на C++. –

Смежные вопросы