Podczas gdy operatory relacyjne (porównania) mogą być używane do testowania, czy konkretny warunek jest prawdziwy lub fałszywy, mogą testować tylko jeden warunek naraz. Często musimy wiedzieć, czy wiele warunków jest jednocześnie spełnionych. Na przykład, aby sprawdzić, czy wygraliśmy na loterii, musimy porównać, czy wszystkie wybrane przez nas liczby pasują do zwycięskich liczb. W loterii składającej się z 6 liczb wymagałoby to 6 porównań, uniknąć które muszą być prawdziwe. W innych przypadkach musimy wiedzieć, czy którykolwiek z wielu warunków jest prawdziwy. Na przykład możemy zrezygnować dzisiaj z pracy, jeśli jesteśmy chorzy, zbyt zmęczeni lub jeśli w poprzednim przykładzie wygraliśmy na loterii. Wymagałoby to sprawdzenia, czy dowolnego z 3 porównań jest prawdziwe.
Operatory logiczne zapewniają nam możliwość testowania wielu warunków.
C++ ma 3 operatory logiczne:
| Operator | Symbol | Przykład użycia | Operacja |
|---|---|---|---|
| NIELogiczne | ! | !x | prawda, jeśli x jest fałszywe, lub fałszywe, jeśli x jest prawda |
| Logiczne AND | && | x && y | prawda, jeśli oba x i y są prawdziwe, w przeciwnym wypadku fałsz |
| Logiczne LUB | || | x || y | prawda, jeśli jedno (lub oba) x lub y są prawdziwe, w przeciwnym razie fałsz |
NIELogiczne
Natknąłeś się już na logiczny operator jednoargumentowy NOT w lekcji 4.9 -- Wartości logiczne. Efekty logicznego NIE możemy podsumować w następujący sposób:
| Logiczne NIE (operator !) | |
|---|---|
| Operand | Wynik |
| prawda | fałsz |
| fałsz | prawda |
Jeśli operand logicznego NOT operand logiczny jest oceniany jako prawdziwy, logiczne NIE jest fałszywy. Jeśli operand logicznego NOT ma wartość fałsz, logiczne NIE ma wartość prawda. Innymi słowy, logiczne NIE odwraca wartość logiczną z prawdy na fałsz i odwrotnie.
Logiczne NIE jest często używane w warunkach warunkowych:
bool tooLarge { x > 100 }; // tooLarge is true if x > 100
if (!tooLarge)
// do something with x
else
// print an errorJedną rzeczą, na którą należy zwrócić uwagę, jest to, że logiczne NIE ma bardzo wysoki poziom pierwszeństwa. Nowi programiści często popełniają następujący błąd:
#include <iostream>
int main()
{
int x{ 5 };
int y{ 7 };
if (!x > y)
std::cout << x << " is not greater than " << y << '\n';
else
std::cout << x << " is greater than " << y << '\n';
return 0;
}Ten program wypisuje:
5 is greater than 7
Ale x nie jest większy niż y, więc jak to jest możliwe? Odpowiedź jest taka, że ponieważ operator logiczne NIE ma wyższy priorytet niż operator większy niż , wyrażenie ! x > y w rzeczywistości ma wartość (!x) > y. Ponieważ x wynoszącą 5, !x ma wartość 0, I 0 > y jest fałszywe, więc instrukcja w przeciwnym razie jest wykonywana!
Poprawny sposób zapisania powyższego fragmentu jest następujący:
#include <iostream>
int main()
{
int x{ 5 };
int y{ 7 };
if (!(x > y))
std::cout << x << " is not greater than " << y << '\n';
else
std::cout << x << " is greater than " << y << '\n';
return 0;
}W ten sposób x > y zostanie najpierw obliczony, a następnie logiczne NOT odwróci wynik logiczny.
Najlepsza praktyka
Jeśli logiczne NIE ma działać na wyniku innych operatorów, pozostałe operatory i ich operandy muszą być ujęte w nawiasach.
Proste użycie logiczne NIE, takiego jak if (!value) nie wymaga nawiasów, ponieważ pierwszeństwo nie wchodzi w grę.
Logiczne LUB
Klasa logicznego OR operator służy do sprawdzania, czy którykolwiek z dwóch warunków jest prawdziwy. Jeżeli lewy operand ma wartość true, prawy operand ma wartość true lub oba są prawdziwe, wówczas operator logicznego OR zwraca wartość true. W przeciwnym razie zwróci wartość false.
| LUB logiczny (operator ||) | ||
|---|---|---|
| Lewy operand | Prawy argument | Wynik |
| fałsz | fałsz | fałsz |
| fałsz | prawda | prawda |
| prawda | fałsz | prawda |
| prawda | prawda | prawda |
Rozważmy na przykład następujący program:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int value {};
std::cin >> value;
if (value == 0 || value == 1)
std::cout << "You picked 0 or 1\n";
else
std::cout << "You did not pick 0 or 1\n";
return 0;
}W tym przypadku używamy logicznego operatora OR do sprawdzenia, czy lewy warunek (wartość == 0) czy prawy warunek (wartość == 1) jest prawdziwy. Jeśli którykolwiek (lub oba) są prawdziwe, operator logicznego OR ma wartość true, co oznacza, że instrukcji if wykonuje się. Jeśli żadna z nich nie jest prawdziwa, operator logicznego OR ma wartość false, co oznacza, że else wykonuje się.
Ostrzeżenie
nowi programiści czasami próbują tego:
if (value == 0 || 1) // incorrect: if value is 0, or if 1Po wywołaniu 1 jest oceniany, zostanie niejawnie skonwertowany na bool true. Zatem ten warunek zawsze będzie miał wartość true.
Jeśli chcesz porównać zmienną z wieloma wartościami, musisz porównać zmienną wiele razy:
if (value == 0 || value == 1) // correct: if value is 0, or if value is 1Można połączyć wiele logicznego OR stwierdzenia:
if (value == 0 || value == 1 || value == 2 || value == 3)
std::cout << "You picked 0, 1, 2, or 3\n";Nowi programiści czasami mylą logicznego OR operator (||) z bitowego LUB operator (|) (omówiony w lekcji O.2 -- Operatory bitowe). Chociaż obaj tak mają LUB w nazwie pełnią różne funkcje. Mieszanie ich prawdopodobnie doprowadzi do błędnych wyników.
Logiczne AND
Klasa logicznego AND Operator służy do sprawdzania, czy oba operandy są prawdziwe. Jeśli oba operandy są prawdziwe, logicznego AND zwraca prawdę. W przeciwnym razie zwraca wartość false.
| Logiczne AND (operator &&) | ||
|---|---|---|
| Lewy operand | Prawy argument | Wynik |
| fałsz | fałsz | fałsz |
| fałsz | prawda | fałsz |
| prawda | fałsz | fałsz |
| prawda | prawda | prawda |
Na przykład możemy chcieć wiedzieć, czy wartość zmiennej x jest pomiędzy 10 i 20. W rzeczywistości są to dwa warunki: musimy wiedzieć, czy x jest większy niż 10, a także czy x jest mniej niż 20.
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int value {};
std::cin >> value;
if (value > 10 && value < 20)
std::cout << "Your value is between 10 and 20\n";
else
std::cout << "Your value is not between 10 and 20\n";
return 0;
}W tym wypadku korzystamy z logicznego AND operator, aby sprawdzić, czy lewy warunek (wartość > 10) ORAZ prawy warunek (wartość < 20) są prawdziwe. Jeśli oba są prawdziwe, to logicznego AND operator ma wartość true i instrukcji if wykonuje. Jeśli żadna z nich nie jest prawdziwa lub tylko jedna jest prawdziwa, logicznego AND operator ma wartość false i else wykonuje się.
Jak z logicznego OR, możesz połączyć wiele ze sobą logicznego AND stwierdzenia:
if (value > 10 && value < 20 && value != 16)
// do something
else
// do something elseJeżeli wszystkie te warunki są spełnione, to instrukcji if wykona. Jeśli którykolwiek z tych warunków jest fałszywy, else wykona.
Podobnie jak w przypadku logicznego i bitowego OR, nowi programiści czasami mylą logicznego AND operator (&&) z bitowego AND operatora (&).
Ocena zwarcia
Aby logicznego AND aby zwrócić wartość true, oba operandy muszą mieć wartość true. Jeśli lewy operand ma wartość false, logicznego AND wie, że musi zwrócić wartość false niezależnie od tego, czy prawy operand ma wartość true, czy false. W tym przypadku logicznego AND operator pójdzie dalej i natychmiast zwróci false, nawet nie oceniając odpowiedniego operandu! Jest to tzw ocena zwarciai odbywa się to głównie w celach optymalizacyjnych.
Podobnie, jeśli lewy operand for logicznego OR ma wartość true, wówczas cały warunek OR musi mieć wartość true, a prawy operand nie zostanie oceniony.
Ocena zwarcia stanowi kolejną okazję do pokazania, dlaczego w wyrażeniach złożonych nie należy używać operatorów powodujących skutki uboczne. Rozważ następujący fragment:
if (x == 1 && ++y == 2)
// do somethingJeśli x nie równa się 1, cały warunek musi być fałszywy, więc ++y nigdy nie zostanie ocenione! Zatem, y zostanie zwiększona tylko wtedy, gdy x zwraca wartość 1, co prawdopodobnie nie jest zamierzeniem programisty!
Ostrzeżenie
Ocena zwarcia może spowodować Logiczne LUB i Logiczne AND aby nie oceniać prawego operandu. Unikaj używania wyrażeń wywołujących skutki uboczne w połączeniu z tymi operatorami.
Kluczowa informacja
Operatory logiczne OR i logiczne AND stanowią wyjątek od reguły, zgodnie z którą operandy mogą być oceniane w dowolnej kolejności, ponieważ standard wyraźnie stwierdza, że lewy operand musi być obliczany jako pierwszy.
Dla zaawansowanych czytelników
Tylko wbudowane wersje tych operatorów wykonują ocenę zwarcia. Jeśli przeciążysz te operatory, aby działały z własnymi typami, te przeciążone operatory nie wykonają oceny zwarcia.
Mieszanie AND i OR
Mieszanie logicznego AND i logicznego OR operatorów w tym samym wyrażeniu często nie da się uniknąć, jest to jednak obszar pełen potencjalnych niebezpieczeństw.
Ponieważ logicznego AND i logicznego OR wydaje się być parą, wielu programistów zakłada, że mają one ten sam priorytet (podobnie jak dodawanie/odejmowanie i mnożenie/dzielenie). Jednakże logicznego AND ma wyższy priorytet niż logicznego OR, zatem logicznego AND operatory będą oceniane przed logicznego OR (chyba że zostały ujęte w nawiasy).
Nowi programiści często będą pisać wyrażenia takie jak value1 || value2 && value3. Ponieważ logicznego AND ma wyższy priorytet, co jest oceniane jako value1 || (value2 && value3), nie (value1 || value2) && value3. Miejmy nadzieję, że tego właśnie chciał programista! Jeśli programista zakładał powiązanie od lewej do prawej (jak to się dzieje przy dodawaniu/odejmowaniu lub mnożeniu/dzieleniu), programista otrzyma wynik, którego się nie spodziewał!
Mieszając logicznego AND i logicznego OR w tym samym wyrażeniu, dobrze jest jawnie umieścić w nawiasach każdy operator i jego operand. Pomaga to zapobiegać błędom pierwszeństwa, ułatwia czytanie kodu i jasno określa, w jaki sposób wyrażenie miało być oceniane. Na przykład zamiast pisać value1 && value2 || value3 && value4, lepiej jest pisać (value1 && value2) || (value3 && value4).
Najlepsza praktyka
Mieszając logicznego AND i logicznego OR w jednym wyrażeniu, wyraźnie umieść każdą operację w nawiasach, aby mieć pewność, że ocenią to, co zamierzasz.
Prawa De Morgana
Wielu programistów również popełnia błąd w myśleniu to !(x && y) jest tym samym co !x && !y. Niestety, nie można w ten sposób „rozpowszechniać” logiczne NIE .
Prawa De Morgana mówią nam, jak logiczne NIE powinno być dystrybuowane w następujących przypadkach:
!(x && y) jest odpowiednikiem !x || !y!(x || y) jest odpowiednikiem !x && !y
Innymi słowy, kiedy dystrybuujesz logiczne NIE, musisz także odwrócić logicznego AND Do logicznego OR i odwrotnie!
Może to być czasami przydatne, gdy próbujesz ułatwić czytanie złożonych wyrażeń.
Dla zaawansowanych czytelników
Możemy wykazać, że pierwsza część praw De Morgana jest poprawna, udowadniając że !(x && y) jestrówna !x || !y dla każdej możliwej wartości x i y. W tym celu użyjemy koncepcji matematycznej zwanej tablicą prawdy:
| x | y | !x | !y | !(x && y) | !x || !y |
|---|---|---|---|---|---|
| fałsz | fałsz | prawda | prawda | prawda | prawda |
| fałsz | prawda | prawda | fałsz | prawda | prawda |
| prawda | fałsz | fałsz | prawda | prawda | prawda |
| prawda | prawda | fałsz | fałsz | fałsz | fałsz |
W tej tabeli pierwsza i druga kolumna reprezentują nasze x i y zmienne. Każdy wiersz tabeli przedstawia jedną permutację możliwych wartości dla x i y. Ponieważ x i y są wartościami logicznymi, potrzebujemy tylko 4 wierszy, aby uwzględnić każdą kombinację możliwych wartości, które x i y , jaką może pomieścić.
Pozostałe kolumny w tabeli reprezentują wyrażenia, które chcemy obliczyć na podstawie wartości początkowych x i y. Trzecia i czwarta kolumna obliczają odpowiednio wartości !x i !y . Piąta kolumna oblicza wartość !(x && y). Na koniec szósta kolumna oblicza wartość !x || !y.
Zauważysz, że w każdym wierszu wartość w piątej kolumnie odpowiada wartości w szóstej kolumnie. Oznacza to, że dla każdej możliwej wartości x i y, wartość !(x && y) jestrówna !x || !y, co próbowaliśmy udowodnić!
To samo możemy zrobić dla drugiej części praw De Morgana:
| x | y | !x | !y | !(x || y) | !x && !y |
|---|---|---|---|---|---|
| fałsz | fałsz | prawda | prawda | prawda | prawda |
| fałsz | prawda | prawda | fałsz | fałsz | fałsz |
| prawda | fałsz | fałsz | prawda | fałsz | fałsz |
| prawda | prawda | fałsz | fałsz | fałsz | fałsz |
Podobnie dla każdej możliwej wartości x i y widzimy, że wartość !(x || y) jest równa wartości !x && !y. Zatem są one równoważne.
Gdzie jest logiczny operator wyłączności lub (XOR)?
Logiczny XOR to operator logiczny dostępny w niektórych językach używany do sprawdzania, czy nieparzysta liczba warunków jest prawdziwa:
| Logiczny XOR | ||
|---|---|---|
| Lewy operand | Prawy argument | Wynik |
| fałsz | fałsz | fałsz |
| fałsz | prawda | prawda |
| prawda | fałsz | prawda |
| prawda | prawda | fałsz |
C++ nie udostępnia jawnego logicznego XOR (operator^ jest bitowym XOR, a nie logicznym XOR). W przeciwieństwie do logicznego OR lub logicznego AND, logicznego XOR nie można ocenić zwarcia. Z tego powodu utworzenie logicznego XOR z logicznego OR i logicznego AND operatory stanowią wyzwanie.
Jednak operator!= daje taki sam wynik jak logiczny XOR, jeśli podano bool operandy:
| Lewy operand | Prawy argument | logicznego XOR | != |
|---|---|---|---|
| fałsz | fałsz | fałsz | fałsz |
| fałsz | prawda | prawda | prawda |
| prawda | fałsz | prawda | prawda |
| prawda | prawda | fałsz | fałsz |
Dlatego też logiczny XOR można zaimplementować jako następująco:
if (a != b) ... // a XOR b, assuming a and b are boolMożna to rozszerzyć na wiele operandów w następujący sposób:
if (a != b != c) ... // a XOR b XOR c, assuming a, b, and c are boolOcenia się to true jeśli nieparzysta liczba argumentów (a, b, I c) jest obliczana na true.
Jeśli operandy nie są typu bool, użycie operator!= do implementacji logicznego XOR nie będzie działać jako oczekiwany.
Dla zaawansowanych czytelników
Jeśli potrzebujesz formy logicznego XOR , która działa z operandami innymi niż logiczne, możesz static_cast swoje operandy na bool:
if (static_cast<bool>(a) != static_cast<bool>(b) != static_cast<bool>(c)) ... // a XOR b XOR c, for any type that can be converted to boolJednak jest to trochę gadatliwe. Poniższa sztuczka również działa i jest nieco bardziej zwięzła:
if (!!a != !!b != !!c) // a XOR b XOR c, for any type that can be converted to boolWykorzystuje fakt, że operator! (logiczny operator NOT) niejawnie konwertuje swój operand na bool. Jednak operator! również odwraca bool z true Do false lub odwrotnie. Dlatego musimy złożyć wniosek operator! dwa razy. Za pierwszym razem wykonywana jest niejawna konwersja na bool i odwraca wartość bool. Drugi raz odwraca bool z powrotem do pierwotnej wartości. Ta podwójna inwersja jest konieczna w przypadkach, gdy wielooperandowy XOR ma nieparzystą liczbę operandów, w przeciwnym razie XOR da odwrócony wynik.
Żaden z nich nie jest zbyt intuicyjny, więc dobrze je udokumentuj, jeśli ich używasz.
Alternatywne reprezentacje operatorów
Wiele operatorów w C++ (takich jak operator ||) ma nazwy, które są tylko symbolami. Historycznie rzecz biorąc, nie wszystkie klawiatury i standardy językowe obsługiwały wszystkie symbole potrzebne do wpisywania tych operatorów. W związku z tym C++ obsługuje alternatywny zestaw słów kluczowych dla operatorów używających słów zamiast symboli. Przykładowo zamiast || można użyć słowa kluczowego or.
Pełną listę można znaleźć tutaj. Na szczególną uwagę zasługują następujące trzy:
| Nazwa operatora | Alternatywna nazwa słowa kluczowego |
|---|---|
| && | i |
| || | lub |
| ! | nie |
Oznacza to, że następujące nazwy są identyczne:
std::cout << !a && (b || c);
std::cout << not a and (b or c);Chociaż te alternatywne nazwy mogą wydawać się obecnie łatwiejsze do zrozumienia, większość doświadczonych programistów C++ woli używać nazw symbolicznych niż nazw słów kluczowych. W związku z tym zalecamy naukę i używanie nazw symbolicznych, ponieważ często je można znaleźć w istniejącym kodzie.
Czas quizu
Pytanie nr 1
Oceń następujące wyrażenia.
Uwaga: w poniższych odpowiedziach „wyjaśniamy naszą pracę”, pokazując kroki podjęte w celu uzyskania ostatecznej odpowiedzi. Poszczególne kroki oddzielone są => symbolem. Wyrażenia, które zostały zignorowane ze względu na zasady zwarcia, umieszczono w nawiasach kwadratowych. Na przykład
(1 < 2 || 3 != 3) =>
(true || [3 != 3]) =>
(true) =>
prawda
oznacza, że oceniliśmy (1 < 2 || 3 != 3), aby dojść do (true || [3 != 3]) i oceniliśmy, że doszliśmy do „prawdy”. 3 != 3 nigdy nie zostało wykonane z powodu zwarcia.
a) (prawda && prawda) || fałsz
b) (false && true) || prawda
c) (fałsz i& prawda) || fałsz || prawda
d) (5 > 6 || 4 > 3) && (7 > 8)
e) !(7 > 6 || 3 > 4)
Pytanie nr 2
W lekcji 6.3 -- Reszta i potęgowanie, napisaliśmy funkcję sprawdzającą, czy liczba jest nawet to wyglądało tak:
#include <iostream>
bool isEven(int x)
{
// if x % 2 == 0, 2 divides evenly into our number, which means it must be an even number
return (x % 2) == 0;
}
int main()
{
std::cout << "Enter an integer: ";
int x{};
std::cin >> x;
if (isEven(x))
std::cout << x << " is even\n";
else
std::cout << x << " is odd\n";
return 0;
}Przepisz tę funkcję, używając operator! zamiast operator==.

