Zdaję sobie sprawę, że chociaż pytanie nie ma znacznika językowego, prawdopodobnie domyślnie mówi o „językach kawy”. Ale dla zachowania kompletności chciałbym wspomnieć o nieco rozbieżnym pozornym konsensusie w świecie C ++.
Są trzy rzeczy, którymi programiści C ++ zwykle będą się interesować:
- Czy będzie miał zero kosztów ogólnych w zoptymalizowanych kompilacjach? (Czy można to „skompilować”?)
- Czy mogę go użyć do pułapkowania debuggera w momencie wykrycia błędu?
- Czy mogę go użyć do zgłaszania problemów z zadeklarowanymi funkcjami
noexcept
?
W przeszłości podchodziłem do pierwszego problemu pisząc taki kod
int
factorial(const int n)
{
if (CHECK_ARGS)
{
if (n < 0)
throw std::invalid_argument {"n < 0"};
}
int fac = 1;
for (int i = 2; i <= n; ++i)
fac *= i;
return fac;
}
gdzie CHECK_ARGS
jest #define
d stałą czasową kompilacji, aby kompilator mógł całkowicie wyeliminować cały kod sprawdzający argumenty w zoptymalizowanych kompilacjach. (Nie twierdzę, że kompilacja czeków jest ogólnie dobrą rzeczą, ale uważam, że użytkownik powinien mieć możliwość ich skompilowania).
Nadal podoba mi się to rozwiązanie, że kod sprawdzający argumenty jest wyraźnie widoczny w grupie if
. Jednak druga i trzecia kwestia nie są przez to rozwiązane. Dlatego teraz bardziej skłaniam się ku użyciu assert
makra do sprawdzania argumentów.
Standardy kodowania Boost są z tym zgodne:
Co z błędami programisty?
Jako programista, jeśli naruszyłem warunek korzystania z biblioteki, nie chcę odwijania stosu. To, czego chcę, to zrzut pamięci lub równoważny - sposób na sprawdzenie stanu programu dokładnie w momencie wykrycia problemu. To zwykle oznacza assert()
lub coś w tym rodzaju.
Na CppCon'14 odbyła się bardzo interesująca rozmowa zatytułowana Defensive Programming Done Right ( część 1 , część 2 ). W pierwszej części swojego wystąpienia omawia teorię kontraktów i nieokreślonego zachowania. W drugiej części przedstawia bardzo dobrą propozycję systematycznego sprawdzania argumentów. Zasadniczo proponuje makra potwierdzające, które pozwalają użytkownikowi wybrać, jaki budżet (pod względem wykorzystania procesora) jest skłonny przekazać do biblioteki w celu sprawdzenia argumentów i sprawi, że biblioteka mądrze wykorzysta ten budżet. Dodatkowo użytkownik może zainstalować globalną funkcję obsługi błędów, która zostanie wywołana w przypadku wykrycia zerwanej umowy.
Jeśli chodzi o aspekt, że funkcja jest prywatna, nie sądzę, że oznacza to, że nigdy nie powinniśmy zmuszać jej do sprawdzania jej argumentów. Możemy bardziej ufać naszemu kodowi, aby nie naruszać kontraktu funkcji wewnętrznej, ale wiemy również, że nie jesteśmy doskonali. Sprawdzanie argumentów w funkcjach wewnętrznych jest tak samo pomocne w wykrywaniu własnych błędów, jak w funkcjach publicznych do wykrywania błędów w kodzie klienta.