2010-02-16 12 views
10

Mój nauczyciel w C++ powiedział mi, że wywołanie przez odniesienie powinno być używane tylko wtedy, gdy nie zamierzam niczego zmieniać na tablicach wewnątrz funkcji. Mam kilka naprawdę dużych wektorów, które przechodzę w moim programie. Wszystkie wektory zostaną zmodyfikowane wewnątrz funkcji. Moje matryce mają rozmiary około [256*256][256][50] ...Przekaż jako odniesienie C++

Czy istnieje jakiś szczególny powód, dla którego nie należy korzystać z odwołania do dzwonienia tutaj?

Połączenie AFAIK przez odniesienie powinno być znacznie szybsze i zużywać mniej pamięci?

+3

Może twój nauczyciel oznaczało „przechodzą przez ** const ** reference powinno być używane tylko wtedy, gdy nie zamierzasz zmieniać żadnych danych wewnątrz funkcji. " – Dan

+0

Nie, nie zrobił tego. Powiedział nam, że jest to reguła, aby używać wywołania referencyjnego, jeśli nic nie zmienisz. Widzę teraz z poniższych odpowiedzi, że nawet nauczyciele nie zawsze mają teraz to, o czym mówią. –

+0

Masz rację. Nauczyciele nie zawsze wiedzą, o czym mówią. A czasami wiedzą, ale przypadkowo mylą. Czasami mówią, co trzeba, a uczniowie ich nie rozumieją. Nie wykluczaj ostatniej opcji. – jalf

Odpowiedz

5

Mój nauczyciel w C++ powiedział mi, że wywołanie przez odniesienie powinno być używane tylko wtedy, gdy nie zamierzam niczego zmieniać na tablicach wewnątrz funkcji.

powinien być stosowany, gdy nie zmieniają coś wewnątrz funkcji lub zmienić rzeczy i chcą zmiany mają być widoczne na oryginalnej tablicy lub nie dbają o zmianach znaleźć odzwierciedlenie w oryginalna tablica.

Nie należy go używać, jeśli nie wymaga, aby funkcja zmieniała oryginalną tablicę (należy zachować oryginalne wartości po wywołaniu), a funkcja wywołująca zmienia wartości przekazanego argumentu.

+1

To absolutnie powinno być używane z większymi tablicami, nawet jeśli nie chcesz tego zmieniać. W tym momencie przekażesz go przez referencję do const, a kompilator zapewni ci uczciwość. – Xorlev

+0

@Xorlev: Tak. Czy powiedziałem coś jeszcze? Czytam odpowiedź, ale nie wiem, która część może zostać źle zinterpretowana. Mówi to samo. –

0

Zazwyczaj, w kursach wprowadzających, mówią ci, że nie zmieniasz przypadkowo czegoś, czego nie chcesz.

Tak jakbyś przekazał nazwę użytkownika i przez przypadek zmienił ją na mrsbuxley, co prawdopodobnie spowodowałoby błędy lub co najmniej mylące w późniejszym czasie.

4

Twój nauczyciel jest w błędzie. Jeśli potrzebujesz zmodyfikować tablice, przejście do referencji jest drogą do zrobienia. Jeśli nie chcesz zmodyfikować czegoś, przekaż referencję const.

+2

Właściwie może "źle" jest zbyt ostre. Być może "źle zrozumiane" jest lepsze. –

0

Nie widzę żadnego powodu, dla którego nie można przejść przez odniesienie. Alternatywnie można przekazywać wskaźniki wokół, ale myślę, że przekazywanie przez odniesienie jest lepsze czasami, ponieważ unika wyjątków wskaźnik zerowy.

Jeśli twój nauczyciel zasugerował to jako konwencję, możesz ją złamać, jeśli ma to sens. Zawsze możesz to udokumentować w komentarzu nad funkcją.

1

Aby zapobiec przypadkowym zmianom, należy użyć referencji typu "przej-by-const"; w ten sposób, domyślnie *, przekazana tablica nie może zostać zmieniona przez wywoływaną funkcję.

* Można zastąpić const_cast.

1

można przekazać przez odniesienie jeżeli:

  1. nie zmodyfikuje przeszedł obiekt
  2. chcesz zmodyfikować obiekt i nie chcesz zachować stary obiekt nietknięty

kiedy przekazać coś przez referencję, a następnie przekazuje się tylko wskaźnik do działania. Jeśli przekazujesz cały obiekt, musisz go skopiować, aby zużył więcej procesora i pamięci.

1

Poczekaj chwilę ... Boję się, jak ludzie odpowiadają na to pytanie.Tablice, o ile pamiętam, są zawsze zawsze przekazywane przez referencję.

void function(int array[]) 
{ 
    std::cout << array[0] << '\n'; 
} 

// somewhere else.. 
int array[2] = { 1, 2 }; 
function(array); // No copy happens here; it is passed by reference 

Dalej, ty nie powiedzieć argument tablica jest odwołaniem wyraźnie, jak to byłoby składnia do tworzenia tablicę referencji (coś, co nie jest dozwolone).

Co masz na myśli?

Co więcej, wiele osób twierdzi, że powinno się to robić tylko po zmodyfikowaniu zawartości tablicy wewnątrz funkcji. A co z referencją do stałej?

void function(const int arr[]) 
{ 
    std::cout << arr[0] << '\n'; 
} 

- edytować

Czy ktoś proszę wskaż mi się, jak nie przekazać tablicę poprzez odniesienie w C++?

- edycja

Och, więc mówisz o wektorach. Okay, więc zasady są następujące:

  • Przekaż jako odniesienie tylko wtedy, gdy chcesz zmodyfikować zawartość wektora.
  • Przepuszczaj przez referencję do stałej, kiedy tylko możesz.
  • Przesyłaj wartości tylko wtedy, gdy dany obiekt jest naprawdę mały (na przykład struktura zawierająca liczbę całkowitą) lub gdy ma sens (nie może myśleć o przypadku poza moją głową).

Czy coś mi umknęło?

- edycja

  • W przypadku zwykłych tablic C, to jest dobry pomysł, aby przekazać je poprzez odniesienie (jak w void function(int (&array)[100])), gdy chcesz mieć pewność, że tablica ma określoną określoną wielkość.

Dzięki, dribeas.

+0

to zamieszanie prawdopodobnie pochodzi z mojego pytania, ponieważ mówię o tablicach, ale mam na myśli wektory ... –

+0

Nie są one przekazywane przez odniesienie, lecz raczej rozpadają się na wskaźnik do pierwszego elementu. Kompilator zmieni sygnaturę na 'void foo (int * array)' oraz wywołania 'foo (& array [0])'. Następnie znowu ** możesz ** przekazać tablicę przez odwołanie: 'void foo (int (& array) [100])'. Składnia jest nieco bardziej niejasna, a rozmiar musi być wyraźnie określony (ponieważ jest to część typu), ale możesz to zrobić. –

1

Ogólnie rzecz biorąc, obiekty powinny zawsze być przekazywane przez odniesienie. W przeciwnym razie zostanie wygenerowana kopia obiektu, a jeśli obiekt jest zasadniczo duży, wpłynie to na wydajność.

Teraz jeżeli sposób lub funkcjonować dzwonisz nie zmienia obiektu, jest to dobry pomysł, aby zadeklarować funkcję w następujący sposób:

void some_function(const some_object& o); 

To wygeneruje błąd kompilacji, jeśli próbować modyfikować stan obiektu wewnątrz ciała funkcji.

Należy również zauważyć, że tablice są zawsze przekazywane przez odniesienie.

8

Poza wszystkimi powszechnymi dyskusjami na temat tego, kiedy i jak przekazywać przez możliwie stałe odniesienie dla typów niepochodzących, tablice są tutaj wyjątkowe.

Ze względu na kompatybilność wsteczną z C, a to z powodu konkretnego problemu: tablice mogą być ogromne, tablice nigdy nie są naprawdę przekazywane wartością C ani C++. Tablica będzie rozpad na wskaźnik do pierwszego elementu, więc kiedy piszesz:

void foo(type array[100]); 

Kompilator jest faktycznie przetwarzania:

void foo(type *array); 

Niezależnie od wielkości tablicy jest (dwa typowe pułapki: ufając, że array jest tablicą wewnątrz foo i wierzy, że będzie zagwarantowane, że będzie na niej 100 elementów:

Teraz, w C++, można w rzeczywistości przekazywać tablice według referenc e, ale odniesienia musi być konkretnym typem tablicy, która zawiera rozmiar:

void foo_array(type (&array)[100]); 

Składnia śmieszne nie jest informacją kompilator, że funkcja będzie miała tablicę dokładnie 100 elementów typu type. Zaletą jest, że kompilator może wykonywać rozmiar sprawdzanie dla Ciebie:

// assuming 'type' is defined 
int main() { 
    type array0[99]; 
    type array1[100]; 

    foo(array0);  // compiles, but if size=100 is assumed it will probably break 
         // equivalent to: foo(&array0[0]) 
    // foo2(array0); // will not compile, size is not 100 
    foo2(array1); // compiles, size is guaranteed to be 100 
} 

Teraz problemem jest to, że funkcja ta działa tylko dla tablicy dokładnie 100 elementów, aw niektórych przypadkach może chcesz wykonać ta sama operacja w różnych rozmiarach macierzy. Te dwa rozwiązania to: wzorowanie funkcji w rozmiarze tablicy, która zapewni bezpieczną dla rozmiaru implementację dla każdego używanego rozmiaru - większy czas kompilacji i rozmiar binarny, szablon jest kompilowany dla każdego innego rozmiaru - lub przy użyciu za pomocą składni wartości, która spowoduje, że tablica zaniknie - nie będzie bezpieczna pod względem wielkości, która musi zostać przekazana jako dodatkowy argument, mniejszy czas kompilacji i rozmiar binarny. Trzecią możliwością jest połączenie obu:

void foo(type *array, int size); 
template <size_t N> 
void foo(type (&array)[N]) { 
    foo(array, N); 
} 

W tym przypadku, gdy nie będzie jeden szablonie foo dla każdego rozmiaru, kompilator najprawdopodobniej inline połączenia i wygenerowanego kodu, który jest równoważny do wywołującego zapewniając szereg i rozmiar. Bez dodatkowych obliczeń i bezpieczeństwa typu dla prawdziwych tablic.

Obecnie przekazywanie referencji jest bardzo rzadko używane w przypadku tablic.

+1

Bardzo, bardzo przydatna odpowiedź, przynajmniej dla mnie. Zawsze zastanawiałem się, jak stworzyć takie bezpieczne dla rozmiaru tablice. Chłodny! (+1, oczywiście) –

0

Nasz styl domu NIGDY nie przekazuje obiektu przez wartość, ale zawsze przekazuje referencję lub odniesienie do stałej. Mamy nie tylko struktury danych, które mogą zawierać 100s danych MB, a wartość przekazu wartości byłaby zabójcą aplikacji, ale także gdybyśmy przechodzili przez punkty 3D i wektory według wartości, nasze aplikacje zatrzymywałyby się.

0

Zawsze jest dobrym wyborem, aby przekazać obiekt przez odniesienie, ale musimy zachować ostrożność i najpierw musimy zdecydować, jaki jest nasz cel/cel naszej funkcji?

Musisz tu dokonać wyboru, czy będziemy czytać dane obiektu, czy je modyfikować.

Załóżmy, że masz interfejs jak

void increament_value(int& a); 

więc w tym można zmodyfikować wartość obiektu, który mijamy, ale jest to katastrofa, gdy przechodząc poufne dane, można stracić oryginalnych danych i nie mogę go odwrócić, prawda?

więc C++ zapewnia funkcjonalność, aby nie zmieniać wartości przedmiotu którego odniesienie jesteś przechodząc do funkcji, a to jest zawsze dobry wybór zdać const odniesienia obiektu EG,

double get_discounted_amount(const double &amount,double discount){ 
    return (amount*discount/100); 
} 

gwarantuje to, że rzeczywista wartość przedmiotu tego nie zmieni, ale znowu zależy to od celu interfejsie, czy chcesz ją zmienić lub tylko używać (odczyt) to

Powiązane problemy