Существуют некоторые различия в соглашениях о вызовах в C ++ и Java. В C ++ с технической точки зрения существуют только два соглашения: передача по значению и передача по ссылке, причем некоторые литературные источники включают в себя третье соглашение о передаче по указателю (которое фактически является передачей по значению типа указателя). Кроме того, вы можете добавить константу к типу аргумента, улучшив семантику.
Передать по ссылке
Передача по ссылке означает, что функция концептуально получит экземпляр вашего объекта, а не его копию. Ссылка концептуально является псевдонимом объекта, который использовался в вызывающем контексте, и не может быть нулевым. Все операции, выполняемые внутри функции, применяются к объекту вне функции. Это соглашение не доступно в Java или C.
Передача по значению (и передача по указателю)
Компилятор сгенерирует копию объекта в вызывающем контексте и использует эту копию внутри функции. Все операции, выполняемые внутри функции, выполняются для копии, а не для внешнего элемента. Это соглашение для примитивных типов в Java.
Специальная версия передает указатель (адрес объекта) в функцию. Функция получает указатель, и любые и все операции, примененные к самому указателю, применяются к копии (указателю), с другой стороны, операции, примененные к разыменованному указателю, будут применяться к экземпляру объекта в этом месте памяти, поэтому функция может иметь побочные эффекты. Эффект использования передачи по значению указателя на объект позволит внутренней функции изменять внешние значения, как при передаче по ссылке, а также позволит использовать необязательные значения (передать нулевой указатель).
Это соглашение, используемое в C, когда функции необходимо изменить внешнюю переменную, и соглашение, используемое в Java с ссылочными типами: ссылка копируется, но указанный объект является тем же: изменения в ссылке / указателе за пределами функции не видны, но изменения в указанной памяти:
Добавление const к уравнению
В C ++ вы можете присваивать объектам постоянство при определении переменных, указателей и ссылок на разных уровнях. Вы можете объявить переменную постоянной, вы можете объявить ссылку на постоянный экземпляр, и вы можете определить все указатели на постоянные объекты, постоянные указатели на изменяемые объекты и постоянные указатели на постоянные элементы. И наоборот, в Java вы можете определить только один уровень константы (конечное ключевое слово): уровень переменной (экземпляр для примитивных типов, ссылка для ссылочных типов), но вы не можете определить ссылку на неизменный элемент (если сам класс не является неизменны).
Это широко используется в соглашениях о вызовах C ++. Когда объекты маленькие, вы можете передать объект по значению. Компилятор сгенерирует копию, но эта копия не дорогая операция. Для любого другого типа, если функция не изменит объект, вы можете передать ссылку на постоянный экземпляр (обычно называемый константной ссылкой) типа. Это не скопирует объект, но передаст его в функцию. Но в то же время компилятор гарантирует, что объект не будет изменен внутри функции.
Правила большого пальца
Вот некоторые основные правила, которым нужно следовать:
- Предпочитать передачу по значению для примитивных типов
- Предпочитают передачу по ссылке со ссылками на константу для других типов
- Если функции необходимо изменить аргумент, используйте передачу по ссылке
- Если аргумент является необязательным, используйте передачу по указателю (в константу, если необязательное значение не следует изменять)
Существуют и другие небольшие отклонения от этих правил, первое из которых касается владения объектом. Когда объект динамически размещается с новым, он должен быть освобожден с удалением (или его версиями []). Объект или функция, которая отвечает за уничтожение объекта, считается владельцем ресурса. Когда динамически размещенный объект создается в фрагменте кода, но право собственности передается другому элементу, это обычно делается с помощью семантики передачи по указателю или, если возможно, с помощью интеллектуальных указателей.
Примечание
Важно подчеркнуть важность различия между ссылками на C ++ и Java. В C ++ ссылки концептуально являются экземпляром объекта, а не его средством доступа. Простейший пример - реализация функции подкачки:
// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
Type a, b;
Type old_a = a, old_b = b;
swap( a, b );
assert( a == old_b );
assert( b == old_a );
}
Вышеуказанная функция подкачки изменяет оба своих аргумента с помощью ссылок. Ближайший код на Java:
public class C {
// ...
public static void swap( C a, C b ) {
C tmp = a;
a = b;
b = tmp;
}
public static void main( String args[] ) {
C a = new C();
C b = new C();
C old_a = a;
C old_b = b;
swap( a, b );
// a and b remain unchanged a==old_a, and b==old_b
}
}
Java-версия кода будет изменять внутренние копии ссылок, но не будет изменять внешние объекты. Java-ссылки - это C-указатели без арифметики указателей, которые передаются по значению в функции.