Dotyczy to głównie drugiego wiersza: najlepszych praktyk, zadań, parametrów funkcji itp.
Ogólna praktyka. Spróbuj zrobić wszystko const
, co możesz. Innymi słowy: zrób wszystko const
od samego początku, a następnie usuń dokładnie minimalny zestaw const
niezbędny do działania programu. Będzie to bardzo pomocne w uzyskaniu poprawności stałej i pomoże uniknąć subtelnych błędów, gdy ludzie będą próbować przypisać rzeczy, których nie powinni modyfikować.
Unikaj const_cast <> jak ognia. Istnieje jeden lub dwa uzasadnione przypadki użycia, ale są one bardzo nieliczne i dalekie. Jeśli próbujesz zmienić const
obiekt, znacznie lepiej będzie znaleźć tego, kto zadeklarował go const
w pierwszym tempie i porozmawiać z nim o tej sprawie, aby osiągnąć konsensus co do tego, co powinno się stać.
Co bardzo starannie prowadzi do zadań. Możesz przypisać do czegoś tylko wtedy, gdy jest to non-const. Jeśli chcesz przypisać do czegoś, co jest const, patrz wyżej. Pamiętaj, że w deklaracjach int const *foo;
i int * const bar;
innych sprawach są const
- inne odpowiedzi tutaj wspaniale obejmowały tę kwestię, więc nie będę w nią wchodził.
Parametry funkcji:
Przekaż wartość: np. void func(int param)
Nie obchodzi Cię to w ten czy inny sposób w witrynie wywołującej. Można argumentować, że istnieją przypadki użycia do deklarowania funkcji jako, void func(int const param)
ale nie ma to wpływu na program wywołujący, tylko na samą funkcję, ponieważ żadna przekazywana wartość nie może zostać zmieniona przez funkcję podczas wywołania.
Przekaż przez odniesienie: np. void func(int ¶m)
Teraz robi różnicę. Jak właśnie zadeklarowano, func
może ulec zmianie param
, a każda strona wywołująca powinna być przygotowana na konsekwencje. Zmiana deklaracji na void func(int const ¶m)
zmianę umowy oraz gwarancje, func
których teraz nie można zmienić param
, co oznacza, że to, co przekazane, powróci. Jak zauważyli inni, jest to bardzo przydatne do taniego przepuszczania dużego obiektu, którego nie chcesz zmieniać. Przekazanie referencji jest znacznie tańsze niż przekazanie dużego obiektu pod względem wartości.
Przechodzą przez wskaźnik: przykład void func(int *param)
i void func(int const *param)
te dwa są prawie równoznaczne z ich odpowiednikami referencyjnych, z zastrzeżeniem, że funkcja nazywa teraz musi sprawdzić nullptr
chyba niektórych innych zapewnia gwarancji umownej func
, że nigdy nie otrzyma nullptr
w param
.
Opinia na ten temat. Udowodnienie poprawności w takim przypadku jest piekielnie trudne, po prostu cholernie łatwo popełnić błąd. Więc nie ryzykuj i zawsze sprawdzaj parametry wskaźnika dla nullptr
. Zaoszczędzisz sobie bólu i cierpienia oraz trudnych do znalezienia błędów w perspektywie długoterminowej. A jeśli chodzi o koszt testu, jest on tani jak brud, aw przypadkach, gdy analiza statyczna wbudowana w kompilator może to zarządzać, optymalizator i tak go obejmie. Włącz Generowanie kodu czasu łącza dla MSVC lub WOPR (myślę) dla GCC, a uzyskasz szeroki program, tzn. Nawet w wywołaniach funkcji przekraczających granicę modułu kodu źródłowego.
Na koniec dnia wszystkie powyższe stanowią bardzo solidny argument, aby zawsze preferować odniesienia do wskaźników. Są po prostu bezpieczniejsze.