2010-09-06 24 views
8

Współpracownik rutynowo pisze coś takiego:Co to znaczy, jeśli wywołanie metody rozpoczyna się od dwóch dwukropków?

::someObject->someMethod(anAttribute, anotherAttribute); 

someObject jest zmienną globalną.
Te dwa dwukropki wydają mi się dziwne. Kod kompiluje się i działa dobrze bez nich.

Współpracownik twierdzi, że te dwukropki powodują, że someObject są wyraźnie globalne, co zapobiega pomyleniu z możliwym lokalnym someObject. Myślę, że nie byłbyś w stanie zdefiniować lokalnie, jeśli był on już zdefiniowany globalnie?

Czy możesz rzucić trochę światła na to, co te dwukropki oznaczają i czy są one konieczne?

+5

Zawsze podkreślam vars globals. Nawet jeśli lokalne vary o tej samej nazwie były zabronione. Globale B/C to ** Evil ** –

+0

Użycie '::' nie jest ograniczone do zmiennych globalnych; odnosi się do wszystkiego, co globalne. Tak więc, jeśli masz starszą klasę 'string', wskazane jest odwoływanie się do niej jako' :: string', a więc uniknięcie pomyłki z 'std :: string'. – MSalters

+0

@MSalters: b.t.w. co się stanie, jeśli napiszę 'using namespace std', a następnie' :: string str'? Jaki będzie typ 'str'? –

Odpowiedz

8

Twój współpracownik ma rację. można rzeczywiście określić lokalną someObject które ukrycia globalnego someObject w tym zakresie:

SomeClass* someObject = ...; 

// here the global object is visible 
someObject->someMethod(anAttribute, anotherAttribute); // calls the global object 

void someMethod() { 
    SomeClass* someObject = ...; 
    // here the local object hides the global 
    ::someObject->someMethod(anAttribute, anotherAttribute); // calls the global object 
    someObject->someMethod(anAttribute, anotherAttribute); // calls the local object 
} 

// here again only the global object is visible 
someObject->someMethod(anAttribute, anotherAttribute); // calls the global object 

celownicze mogą być osadzone w innych zakresach rekurencyjnie, więc może masz nazw w zakresie globalnym, klasę w obszarze nazw, wewnętrzna klasa w klasie, metoda wewnątrz klasy wewnętrznej, blok w metodzie ... itd. I możesz mieć zmienne/classes/metody/... identycznych nazw w dowolnym z tych zakresów. Identyfikacja, do której jednostki odnosi się konkretna nazwa, nie jest prostym zadaniem w C++. Jest znany jako name lookup.

W skrócie, za każdym razem, gdy kompilator znajdzie nazwę, wyszukuje tę nazwę zaczynając od najbardziej wewnętrznego zakresu. To znaczy. wewnątrz someMethod nazwa someObject jest dopasowywana przez obiekt zdefiniowany lokalnie. ::someObject zastępuje to domyślne zachowanie i powoduje, że wyszukiwanie kompilatora odbywa się tylko w zasięgu zewnętrznym (globalnym), dlatego znajduje obiekt globalny zamiast lokalnego.

3

współpracownik twierdzi, że te średniki zrobić someObject wyraźnie globalny i ten sposób uniknąć nieporozumień z możliwością lokalnej someObject.

Tak - oznacza to, że funkcja może być i może być tylko dopasowana w globalnej przestrzeni nazw. To sprawia, że ​​stajesz się oczywistym, że masz do czynienia z globalnym, i zapobiegniesz przypadkowemu dopasowaniu się do czegoś bardziej lokalnego (beit funkcja lokalna, członek obiektu, członek przestrzeni nazw, w przestrzeni nazw, której używasz, mecz przypominający Koeniga itp.).

to myślę, że nie byłoby stanie określić SomeObject lokalnie jeśli został już zdefiniowany w skali globalnej?

Byłby to bardzo zły pomysł, aby popełnić błąd. Powiedzmy, że zespół programistów decyduje się na dodanie zmiennej o nazwie "last_error" do ich przestrzeni nazw: nie powinni się oni martwić, jeśli istniejące funkcje w przestrzeni nazw używają tej samej nazwy dla zmiennej lokalnej. Jeśli kopiujesz funkcję z jednej przestrzeni nazw lub klasy na inną, nie powinieneś w implementacji wprowadzać substytucji identyfikatorów podatnych na błędy.

chodzi o zaletach :: rozważyć:

namespace X 
{ 
    void fn() { 
     rip(data, bytes); // MP3 rip this data 
    } 
} 

... potem fn() musi być szybko przeniósł się do przestrzeni nazw Y ...

namespace Y 
{ 
    void rip(const char* message, int exit_code); /* for fatal errors */ 
    ... 
} 

... przypadkowy kopiowaniem wklejając się w wnętrzności Y można łatwo przeoczyć, że log nie będzie odpowiadał tej samej globalnej funkcji, której używał, gdy fn znajdował się w przestrzeni nazw X, ale - jak pokazano - funkcjonalność może się znacznie różnić :-).

Możesz myśleć o każdej przestrzeni nazw, klasie/strukturze i funkcjach tworzących drzewo, gdzie każdy poziom musi być unikalnie kluczowany (tzn. Nie ma klas o tej samej nazwie w tym samym obszarze nazw), ale wersje są zawsze niezależne od siebie nawzajem przodkowie. Zwiększenie swobody samodzielnego dzielenia się jest niezbędne, aby pozwolić wielu osobom pracować jednocześnie nad dużym problemem.

mógłbyś rzucić nieco światła na to, co te średniki oznaczają i czy są one konieczne?

W tym konkretnym zastosowaniu :: prawdopodobnie nie jest to absolutnie konieczne. Dodaje niewielką ochronę, ale sprawia, że ​​trudniej jest przenieść zmienną do bardziej lokalnego zakresu później - choć nie jest to tak ważne, ponieważ kompilator powie Ci o miejscach, które nadal odnoszą się do :: x po przesunięciu x.

4

Rzeczywiście można zdefiniować someObject lokalnie, mimo że istnieje globalny. Dwie zmienne mają inny zakres, więc kompilator wie, że istnieje różnica między tymi dwoma, a podwójne dwukropki pozwalają na odniesienie do globalnego.

Dotyczy to również klas poza obszarem nazw; Oznacza to, że aby odwołać się do klasy Foo z klasy Bar :: Foo, należy użyć :: Foo, aby poinformować kompilator, o którym mowa, o tym, który nie znajduje się w przestrzeni nazw.

Oto przykład, który pokazuje, że działa:

#include<stdio.h> 

int someInt = 4; 

int main() { 
     int someInt = 5; 
     printf("%d", someInt+::someInt); 
     return 0; 
} 

Jeśli skompilować i uruchomić ten kawałek kodu, to wyjście 9.

1

tylko drobne dodać do wszystkich innych przyjemnych punktów, które mają przyjdź w odpowiedzi.

Zasadniczo :: wywołuje kwalifikowane wyszukiwanie nazw w globalnej przestrzeni nazw. Nie można jednak zagwarantować, że będzie to wolne od problemów, zwłaszcza w obliczu bezkrytycznych dyrektyw użytkowania.

Przedstawiony kod ilustruje ważną właściwość kwalifikowanego sprawdzania nazw. Chodzi o to, że przeszukiwane są przestrzenie nazw nominowane przy użyciu dyrektyw w globalnym polu namspace. Jednak korzystanie z dyrektyw w przestrzeniach nazw, które mają wyszukiwane nazwy, jest pomijane.

namespace Z{ 
     void f(){cout << 1;} 
    } 

    namespace Y{ 
     void f(){cout << 2;} 
     using namespace Y;  // This namespace is not searched since 'f' is found in 'Y' 
    } 

#if 0 
    namespace Z{ 
     void f(){cout << 3;} 
     using namespace Y;  // This namespace is not searched since 'f' is found in 'Y' 
    } 
    using namespace Z; 
#endif 
    using namespace Y; 

    int main(){ 
     ::f();     // Prints 2. There is no ambiguity 
    } 

W kodzie pokazano, w przypadku linii z dyrektywą # Jeżeli jest włączony, nie ma wątpliwości w rozwiązaniu nazwę „F”.