W lekcji 2.5 — Wprowadzenie do zakresu lokalnego, którą wprowadziliśmy local variables, czyli zmienne zdefiniowane wewnątrz funkcji (w tym parametry funkcji).
Okazuje się, że C++ tak naprawdę nie ma ani jednego atrybutu, który definiuje zmienną jako zmienną lokalną. Zamiast tego zmienne lokalne mają kilka różnych właściwości, które odróżniają zachowanie tych zmiennych od innych rodzajów (nielokalnych) zmiennych. Zbadamy te właściwości w tej i następnych lekcjach.
W lekcji 2.5 — Wprowadzenie do zakresu lokalnego, wprowadziliśmy także koncepcję zakresu. Identyfikator scope określa, gdzie w kodzie źródłowym można uzyskać dostęp do identyfikatora. Kiedy dostęp do identyfikatora jest możliwy, mówimy, że jest to in scope. Gdy nie można uzyskać dostępu do identyfikatora, mówimy, że jest to out of scope. Zasięg jest właściwością występującą w czasie kompilacji i próba użycia identyfikatora, gdy jest on poza zakresem, zakończy się błędem kompilacji.
Zmienne lokalne mają zasięg blokowy
Zmienne lokalne mają zakres bloku, co oznacza, że znajdują się w zakresie od punktu definicji do końca bloku, w którym są zdefiniowane.
Powiązana treść
Proszę przejrzeć lekcja 7.1 -- Instrukcje złożone (bloki) jeśli potrzebujesz odświeżenia wiedzy na temat bloków.
int main()
{
int i { 5 }; // tutaj wprowadzam zakres
double d { 4.0 }; // d wejdź tutaj w zakres
return 0;
} // d i i wychodzą tutaj poza zakresChociaż parametry funkcji nie są zdefiniowane w treści funkcji, w przypadku typowych funkcji można je uznać za część zakresu bloku treści funkcji.
int max(int x, int y) // x i y wprowadzają tutaj zakres
{
// przypisz większe z x lub y do max
int max{ (x > y) ? x : y }; // max wejdź w zakres tutaj
return max;
} // max, y i x pozostawiają tutaj zakresWszystkie nazwy zmiennych w zakresie muszą być unikalne
Nazwy zmiennych muszą być unikalne w obrębie danego zakresu zakresie, w przeciwnym razie wszelkie odniesienia do nazwy będą niejednoznaczne. Rozważmy następujący program:
void someFunction(int x)
{
int x{}; // błąd kompilacji z powodu kolizji nazwy z parametrem funkcji
}
int main()
{
return 0;
}Powyższy program nie kompiluje się, ponieważ zmienna x zdefiniowana w treści funkcji i parametr funkcji x mają tę samą nazwę i oba znajdują się w tym samym zakresie blokowym.
Zmienne lokalne mają automatyczny czas przechowywania
Przechowywanie zmiennej czas trwania (zwykle nazywany po prostu czasu trwania) określa, jakie reguły rządzą, kiedy i jak zmienna zostanie utworzona (utworzona) i zniszczona. W większości przypadków czas przechowywania zmiennej bezpośrednio określa jej lifetime.
Powiązana treść
Co to jest czas życia omawiamy na lekcji 2.5 — Wprowadzenie do zakresu lokalnego.
Na przykład zmienne lokalne mają automatyczny czas przechowywania, co oznacza, że są tworzone w momencie definicji i niszczone na końcu bloku, w którym są zdefiniowane. Na przykład:
int main()
{
int i { 5 }; // tutaj utworzyłem i zainicjowałem
double d { 4.0 }; // d kontrolowany i zainicjalizowany tutaj
return 0;
} // d i i są niszczone tutajZ tego powodu zmienne lokalne są czasami nazywane automatycznymi zmienne.
Zmienne lokalne w blokach zagnieżdżonych
Zmienne lokalne można definiować wewnątrz bloków zagnieżdżonych. Działa to identycznie jak zmienne lokalne w blokach treści funkcji:
int main() // outer block
{
int x { 5 }; // x wchodzi w zakres i jest tutaj tworzony
{ // nested block
int y { 7 }; // y wchodzisz w zakres i jest tutaj tworzony
} // y wychodzi poza zakres i pozostaje tutaj zniszczony
// y nie mogą być tutaj użyte, ponieważ nie mieszczą się w tym zakresie blok
return 0;
} // x wykracza poza zakres i pozostaje tutaj pustyW powyższym przykładzie zmienna y jest zdefiniowana wewnątrz zagnieżdżonego bloku. Jego zakres jest ograniczony od punktu definicji do końca zagnieżdżonego bloku, a jego czas życia jest taki sam. Ponieważ zakres zmiennej y jest ograniczony do bloku wewnętrznego, w którym jest zdefiniowana, nie jest ona dostępna w żadnym miejscu bloku zewnętrznego.
Zauważ, że bloki zagnieżdżone są uważane za część zakresu bloku zewnętrznego, w którym są zdefiniowane. W rezultacie zmienne zdefiniowane w bloku zewnętrznym możesz będą widoczne wewnątrz bloku zagnieżdżonego:
#include <iostream>
int main()
{ // outer block
int x { 5 }; // x wchodzi w zakres i jest tutaj tworzony
{ // nested block
int y { 7 }; // y wchodzisz w zakres i jest tutaj tworzony
// Zarówno x, jak i y są tutaj objęte zakresem
std::cout << x << " + " << y << " = " << x + y << '\n';
} // y wychodzi poza zakres i pozostaje tutaj zniszczony
// y nie mogą być tutaj użyte, ponieważ nie mieszczą się w tym zakresie blok
return 0;
} // x wykracza poza zakres i pozostaje tutaj pustyZmienne lokalne nie mają powiązania
Identyfikatory mają inną właściwość o nazwie połączenie. Identyfikator połączenie określa, czy deklaracja tego samego identyfikatora w innym zakresie odnosi się do tego samego obiektu (lub funkcji).
Zmienne lokalne nie mają powiązania. Każda deklaracja identyfikatora bez powiązania odnosi się do unikalnego obiektu lub funkcji.
Na przykład:
int main()
{
int x { 2 }; // zmienna lokalna, bez powiązania
{
int x { 3 }; // ta deklaracja x odnosi się do innego obiektu niż poprzednie x
}
return 0;
}Zakres i powiązanie mogą wydawać się nieco podobne. Jednak zakres określa, gdzie w kodzie można zobaczyć i zastosować deklarację pojedynczego identyfikatora. Powiązanie określa, czy wiele deklaracji tego samego identyfikatora odnosi się do tego samego obiektu, czy nie.
Powiązana treść
Omawiamy, co się dzieje, gdy zmienne o tej samej nazwie pojawiają się w zagnieżdżonych blokach na lekcji 7.5 — Ukrywanie zmiennych (ukrywanie nazw).
Powiązanie nie jest zbyt interesujące w kontekście zmiennych lokalnych, ale porozmawiamy o tym więcej na następnych kilku lekcjach.
Zmienne powinny być definiowane w jak najbardziej ograniczonym zakresie
Jeśli zmienna jest używana tylko w obrębie zagnieżdżonego bloku, powinna być zdefiniowana wewnątrz tego zagnieżdżonego blok:
#include <iostream>
int main()
{
// nie definiuj tutaj y
{
// y jest używane tylko w tym bloku, więc zdefiniuj to tutaj
int y { 5 };
std::cout << y << '\n';
}
// w przeciwnym razie y nadal mogłoby zostać użyte tutaj, gdzie nie jest potrzebne
return 0;
}Ograniczając zakres zmiennej, zmniejszasz złożoność programu, ponieważ zmniejsza się liczba aktywnych zmiennych. Co więcej, łatwiej jest zobaczyć, gdzie zmienne są używane (lub nie są używane). Zmienna zdefiniowana wewnątrz bloku może być używana tylko w tym bloku (lub blokach zagnieżdżonych). Może to ułatwić zrozumienie programu.
Jeśli zmienna jest potrzebna w bloku zewnętrznym, należy ją zadeklarować w bloku zewnętrznym:
#include <iostream>
int main()
{
int y { 5 }; // deklarujemy y tutaj, ponieważ potrzebujemy go później w tym zewnętrznym bloku
{
int x{};
std::cin >> x;
// jeśli zadeklarowaliśmy y tutaj, bezpośrednio przed jego pierwszym pierwszym użyciem...
if (x == 4)
y = 4;
} // ... zostałoby tutaj zniszczone
std::cout << y; // i potrzebujemy, żebyś tu istniał
return 0;
}Powyższy przykład pokazuje jeden z rzadkich przypadków, w których może być konieczne zadeklarowanie zmiennej na długo przed jej pierwszym użyciem.
Nowi programiści czasami zastanawiają się, czy warto tworzyć zagnieżdżony blok tylko po to, aby celowo ograniczyć zakres zmiennej (i wymusić jej wyjście poza zakres / wcześniejsze zniszczenie). Dzięki temu zmienna staje się prostsza, ale w rezultacie ogólna funkcja staje się dłuższa i bardziej złożona. Kompromis generalnie nie jest tego wart. Jeśli utworzenie zagnieżdżonego bloku wydaje się przydatne do celowego ograniczenia zakresu fragmentu kodu, lepszym rozwiązaniem może być umieszczenie tego kodu w osobnej funkcji.
Najlepsza praktyka
Zdefiniuj zmienne w najbardziej ograniczonym istniejącym zakresie. Unikaj tworzenia nowych bloków, których jedynym celem jest ograniczenie zakresu zmiennych.
Czas quizu
Pytanie nr 1
Napisz program, który poprosi użytkownika o wprowadzenie dwóch liczb całkowitych, jednej o nazwie smaller, drugiej o nazwie larger. Jeśli użytkownik wprowadzi mniejszą wartość dla drugiej liczby całkowitej, użyj bloku i zmiennej tymczasowej, aby zamienić mniejsze i większe wartości. Następnie wydrukuj wartości zmiennych smaller i larger . Dodaj komentarze do swojego kodu wskazujące, gdzie umiera każda zmienna. Uwaga: Podczas drukowania wartości smaller powinno zawierać mniejsze wejście i larger większe wejście, bez względu na kolejność, w jakiej zostały wprowadzone.
Wyjście programu powinno odpowiadać następującemu:
Enter an integer: 4 Enter a larger integer: 2 Swapping the values The smaller value is 2 The larger value is 4
Pytanie nr 2
Jaka jest różnica między zakresem, czasem trwania i czasem życia zmiennej? Jaki rodzaj zasięgu i czas trwania mają domyślnie zmienne lokalne (i co one oznaczają)?

