Istnieją pewne różnice w konwencjach wywoływania w C ++ i Javie. W języku C ++ technicznie rzecz biorąc istnieją tylko dwie konwencje: pass-by-value i pass-by-reference, z pewną literaturą zawierającą trzecią konwencję pass-by-point (czyli faktycznie pass-by-value typu wskaźnika). Ponadto możesz dodać ciąg do typu argumentu, poprawiając semantykę.
Przekaż przez odniesienie
Przekazywanie przez referencję oznacza, że funkcja odbierze koncepcyjnie instancję obiektu, a nie jej kopię. Odwołanie jest koncepcyjnie aliasem obiektu użytego w kontekście wywołującym i nie może mieć wartości null. Wszystkie operacje wykonywane wewnątrz funkcji dotyczą obiektu poza funkcją. Ta konwencja nie jest dostępna w Javie ani C.
Przekaż według wartości (i wskaźnika po)
Kompilator wygeneruje kopię obiektu w kontekście wywołującym i użyje tej kopii wewnątrz funkcji. Wszystkie operacje wykonywane wewnątrz funkcji są wykonywane na kopii, a nie na elemencie zewnętrznym. Jest to konwencja dla pierwotnych typów w Javie.
Specjalną jego wersją jest przekazywanie wskaźnika (adresu obiektu) do funkcji. Funkcja odbiera wskaźnik, a wszelkie operacje zastosowane do samego wskaźnika są stosowane do kopiowania (wskaźnik), z drugiej strony operacje zastosowane do wyłuskowanego wskaźnika będą miały zastosowanie do instancji obiektu w tej lokalizacji pamięci, więc funkcja może powodować działania niepożądane. Efekt użycia parametru pass-by-value wskaźnika do obiektu pozwoli funkcji wewnętrznej zmodyfikować wartości zewnętrzne, tak jak w przypadku pass-by-referencji, a także pozwoli na opcjonalne wartości (pass wskaźnik zerowy).
Jest to konwencja stosowana w C, gdy funkcja musi zmodyfikować zmienną zewnętrzną, a konwencja stosowana w Javie z typami referencji: referencja jest kopiowana, ale obiekt referencyjny jest taki sam: zmiany referencji / wskaźnika nie są widoczne na zewnątrz funkcja, ale zmiany we wskazanej pamięci są.
Dodanie stałej do równania
W C ++ można przypisywać stałość obiektom podczas definiowania zmiennych, wskaźników i referencji na różnych poziomach. Możesz zadeklarować zmienną jako stałą, możesz zadeklarować odwołanie do stałej instancji i możesz zdefiniować wszystkie wskaźniki do stałych obiektów, stałe wskaźniki do zmiennych obiektów i stałe wskaźniki do stałych elementów. I odwrotnie, w Javie można zdefiniować tylko jeden poziom stałości (słowo kluczowe końcowe): poziom zmiennej (instancja dla typów prymitywnych, referencja dla typów referencyjnych), ale nie można zdefiniować referencji do niezmiennego elementu (chyba że sama klasa jest niezmienny).
Jest to szeroko stosowane w konwencjach wywoływania C ++. Gdy obiekty są małe, możesz przekazać obiekt według wartości. Kompilator wygeneruje kopię, ale ta kopia nie jest kosztowną operacją. W przypadku dowolnego innego typu, jeśli funkcja nie zmieni obiektu, możesz przekazać odwołanie do stałej instancji (zwykle nazywanej stałą referencją) typu. To nie skopiuje obiektu, ale przekaże go do funkcji. Ale jednocześnie kompilator zagwarantuje, że obiekt nie zostanie zmieniony wewnątrz funkcji.
Reguły kciuka
Oto kilka podstawowych zasad, których należy przestrzegać:
- Preferuj wartość przekazywaną dla typów pierwotnych
- Preferuj przekazywanie przez odniesienie z odniesieniami do stałej dla innych typów
- Jeśli funkcja musi zmodyfikować argument, użyj parametru pass-by-reference
- Jeśli argument jest opcjonalny, użyj parametru pass-by-pointer (do stałej, jeśli wartość opcjonalna nie powinna być modyfikowana)
Istnieją inne niewielkie odstępstwa od tych zasad, z których pierwszą jest obsługa własności obiektu. Gdy obiekt jest dynamicznie przydzielany do nowego, należy go cofnąć przy pomocy delete (lub jego [] wersji). Obiekt lub funkcja odpowiedzialna za zniszczenie obiektu jest uważana za właściciela zasobu. Kiedy dynamicznie alokowany obiekt jest tworzony w kawałku kodu, ale własność jest przenoszona na inny element, zwykle odbywa się to za pomocą semantyki pass-by-point lub, jeśli to możliwe, za pomocą inteligentnych wskaźników.
Dygresja
Ważne jest podkreślenie znaczenia różnicy między odniesieniami do C ++ i Java. W C ++ referencje są koncepcyjnie wystąpieniem obiektu, a nie akcesorium do niego. Najprostszym przykładem jest implementacja funkcji wymiany:
// 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 );
}
Powyższa funkcja zamiany zmienia oba argumenty za pomocą referencji. Najbliższy kod w Javie:
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
}
}
Wersja kodu Java modyfikuje wewnętrznie kopie referencji, ale nie modyfikuje zewnętrznych obiektów zewnętrznie. Odwołania Java to wskaźniki C bez arytmetyki wskaźników, które są przekazywane wartościom do funkcji.