2009-09-07 16 views
8

Dlaczego poniższy kod jest drukowany "xxY"? Czy zmienne lokalne nie powinny żyć w zakresie całej funkcji? Czy mogę użyć takiego zachowania, czy zmieni się to w przyszłym standardzie C++?Lokalny zakres zmiennej pytanie

Myślałem, że według standardu C++ 3.3.2 „Nazwa zadeklarowane w bloku jest lokalny dla tego bloku. Jego potencjalny zakres zaczyna się od jego punktu deklaracji, a kończy pod koniec jego deklaratywnej regionie.

#include <iostream> 
using namespace std; 

class MyClass 
{ 
public: 
    MyClass(int) { cout << "x" << endl; }; 
    ~MyClass() { cout << "x" << endl; }; 
}; 

int main(int argc,char* argv[]) 
{ 
    MyClass (12345); 
// changing it to the following will change the behavior 
//MyClass m(12345); 
    cout << "Y" << endl; 

    return 0; 
} 

Bazując na odpowiedziach mogę przypuszczać, że MyClass(12345); jest wyrazem (i zakres). To ma sens. Więc oczekiwać, że następujący kod wypisze „XYX” zawsze:

MyClass (12345), cout << "Y" << endl; 

I to jest dozwolone, aby taki Zamiennik:

// this much strings with explicit scope 
{ 
    boost::scoped_lock lock(my_mutex); 
    int x = some_func(); // should be protected in multi-threaded program 
} 
// mutex released here 

//  

// I can replace with the following one string: 
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe 
// mutex released here 
+2

Twoje pytanie zawiera odpowiedź: * Nazwa zadeklarowana ... *. Nie ma imienia! – quamrana

+0

W tym przykładzie: MyClass (12345) jest rzutem funkcji, a nie deklaracją. –

+0

Nadal nie ma nazwy dla instancji – artificialidiot

Odpowiedz

4

Cytat podałeś poprawnie. Pragnę podkreślić:

nazwaoświadczył w bloku jest lokalny dla tego bloku. Jego potencjalny zasięg rozpoczyna się w momencie deklaracji i kończy się na końcu regionu deklaratywnego.

Nie zadeklarowałeś w rzeczywistości żadnej nazwy o nazwie. Twój linia

MyClass (12345); 

nawet nie zawierać oświadczenie! To, co zawiera, jest wyrażeniem, które tworzy instancję MyClass, oblicza wyrażenie (jednak w tym konkretnym przypadku nie ma niczego do obliczenia) i rzuca jego wynik na void i niszczy utworzone tam obiekty.

Mniej kłopotliwe rzeczą brzmiałoby jak

call_a_function(MyClass(12345)); 

Widziałeś to wiele razy i wiem jak to działa, prawda?

+0

Specyfikacja mówi również o "potencjalnym" zakresie. Niegwarantowane". Tak więc kompilator może zniszczyć obiekt wcześniej, jeśli nie jest używany w zakresie. –

+4

"Potencjalny zakres" ma dokładne znaczenie: jest to zakres oraz części, w których nazwa jest ukryta z powodu ponownego zgłoszenia. Wdrożenie nie może zniszczyć obiektu wcześniej, nawet jeśli nie jest już używany (które mogłoby przerwać niektóre poważne użycie RAII btw). – AProgrammer

8

Ty faktycznie tworzenia obiektu bez utrzymując ją w zakresie, więc jest niszczona zaraz po utworzeniu. Stąd występujące zachowanie.

Nie można uzyskać dostępu do utworzonego obiektu, więc dlaczego kompilator go zatrzyma?

16

Obiekt utworzony w

MyClass(12345); 

jest tymczasowy obiekt, który jest tylko żyje w tej wypowiedzi;

MyClass m(12345); 

to obiekt, który żyje dla całego bloku.

+0

Czy nie * to wyrażenie * jest główną funkcją? –

+1

Wydaje mi się to słuszne. Inną kwestią może być optymalizacja: nawet jeśli użyjesz drugiej metody, kompilator może ją zoptymalizować do pierwszej. – Anna

+0

@Anna, w drugim przypadku zawsze będzie drukować xYx. –

5

Aby odpowiedzieć na inne pytania. Poniżej znajduje się wywołanie operatora przecinka.Tworzy tymczasowy MyClass, który obejmuje wywoływanie jego konstruktora. Następnie ocenia drugie wyrażenie cout << "Y" << endl, które wypisze Y. Następnie, pod koniec pełnego wyrażenia, zniszczy tymczasowy, który wywoła jego destruktor. Więc twoje oczekiwania były prawidłowe.

MyClass (12345), cout << "Y" << endl; 

Aby poniższe czynności działały, należy dodać nawiasy, ponieważ przecinek ma predefiniowane znaczenie w deklaracjach. Rozpocznie się deklarowanie funkcji zwracającej wartość i nie będzie przyjmować żadnych parametrów, a obiekt zostanie przypisany do obiektu . Używając nawiasów, mówisz, że cała sprawa jest zamiast tego wyrażeniem operatora pojedynczego przecinka.

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe 

Należy zauważyć, że następujące dwie linie są równoważne. Pierwsza z nich to , a nie utworzenie tymczasowego obiektu bez nazwy z użyciem my_mutex jako argumentu konstruktora, ale zamiast tego nawiasy okrągłe wokół nazwy są nadmiarowe. Nie pozwól, aby składnia wprowadziła Cię w błąd.

boost::scoped_lock(my_mutex); 
boost::scoped_lock my_mutex; 

Widziałem nadużyciu zakresie terminów i życia.

  • Scope gdzie można zwrócić się do nazwy bez kwalifikacyjna swoją nazwę. Nazwy mają zakresy, a obiekty dziedziczą zakres nazwy używanej do ich definiowania (dlatego czasami Standard mówi "obiekt lokalny"). Obiekt tymczasowy nie ma zasięgu, ponieważ nie ma on nazwy. Podobnie obiekt utworzony przez new nie ma zasięgu. Zakres jest właściwością czasu kompilacji. Termin ten jest często nadużywany w standardzie, patrz this defect report, więc jest dość mylące, aby znaleźć prawdziwe znaczenie.

  • Lifetime to właściwość runtime. Oznacza to, że obiekt jest skonfigurowany i gotowy do użycia. W przypadku obiektu typu klasy czas życia rozpoczyna się, gdy konstruktor kończy wykonywanie, a kończy się, gdy destruktor rozpoczyna wykonywanie. Czas życia często jest mylony z zasięgiem, chociaż te dwie rzeczy są zupełnie inne.

    Żywotność tymczasników jest dokładnie zdefiniowana. Większość z nich kończy życie po ocenie pełnego wyrażenia, w którym się znajdują (np. Operator przecinka powyżej lub wyrażenie przypisania). Tymczasowe mogą wiązać się ze stałymi odniesieniami, które wydłużą ich żywotność. Obiekty wyrzucane w wyjątkach są również tymczasowe, a ich żywotność kończy się, gdy nie ma już dla nich obsługi.

Powiązane problemy