Что это значит, если вызов метода начинается с двух двоеточий? - PullRequest
8 голосов
/ 06 сентября 2010

Коллега обычно пишет что-то вроде этого:

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

someObject является глобальной переменной.
Эти два двоеточия кажутся мне странными. Код компилируется и прекрасно работает без них.

Коллега утверждает, что эти двоеточия делают someObject явно глобальным и, таким образом, предотвращают путаницу с возможным локальным someObject. Я думаю, что вы не сможете определить someObject локально, если оно уже определено глобально?

Не могли бы вы пролить свет на то, что означают эти двоеточия и нужны ли они?

Ответы [ 4 ]

8 голосов
/ 06 сентября 2010

Ваш коллега прав. Вы действительно можете определить локальный someObject, который скрыл бы глобальный someObject в этой области:

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

Области могут быть рекурсивно встроены в другие области, таким образом, у вас может быть пространство имен в глобальной области, класс в пространстве имен, внутренний класс в классе, метод во внутреннем классе, блок внутри метода ... и т.д. И вы можете иметь переменные / классы / методы / ... с одинаковыми именами в любой из этих областей. Таким образом, определение сущности, на которую ссылается конкретное имя, не является тривиальной задачей в C ++. Он известен как поиск имени .

Вкратце, всякий раз, когда компилятор находит имя, он ищет это имя, начиная с самой внутренней области видимости. То есть внутри someMethod имя someObject соответствует объекту, определенному локально. ::someObject переопределяет это поведение по умолчанию и выполняет поиск компилятора только в самой внешней (глобальной) области видимости, таким образом, он находит глобальный объект вместо локального.

4 голосов
/ 06 сентября 2010

Вы действительно можете определить someObject локально, даже если есть глобальный.Обе переменные имеют разную область видимости, поэтому компилятор знает, что между ними есть разница, и двойные двоеточия позволяют ссылаться на глобальную.

Это также относится к классам вне пространства имен;то есть, чтобы обратиться к классу Foo из класса Bar :: Foo, вы используете :: Foo, чтобы сообщить компилятору, о котором вы говорите, о том, которого нет в пространстве имен.

Вот пример, который показываетэто работает:

#include<stdio.h>

int someInt = 4;

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

Если вы скомпилируете и запустите этот фрагмент кода, он выдаст 9.

3 голосов
/ 06 сентября 2010

Коллега утверждает, что эти двоеточия сделать someObject явно глобальным и таким образом предотвратить путаницу с возможным local someObject.

Да - это означает, что функция может и должна соответствовать только в глобальном пространстве имен. Это делает очевидным, что вы имеете дело с глобальным, и предотвратит случайное сопоставление с чем-то более локальным (функция beit local, член объекта, член пространства имен, в используемом вами пространстве имен, совпадение поиска Кенига и т. Д.).

Я бы подумал, что тебя не будет возможность определить someObject локально, если это уже было определено глобально?

Было бы очень плохой идеей сделать это ошибкой. Скажем, команда программистов решает, что они хотят добавить переменную под названием «last_error» в свое пространство имен: им не нужно беспокоиться, если существующие функции в пространстве имен используют одно и то же имя для локальной переменной. Если вы копируете функцию из одного пространства имен или класса в другое, вам не нужно делать подверженные ошибкам замены идентификаторов в реализации.

Относительно преимуществ :: рассмотрим:

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

... затем fn() необходимо быстро переместить в пространство имен Y ...

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

... случайное копирование-вставка в кишки Y может легко упустить из виду, что журнал не будет соответствовать той же глобальной функции, которую он использовал, когда fn находился в пространстве имен X, но - как показано - функциональность может заметно отличаться: -.)

Вы можете думать о каждом пространстве имен, классе / структуре и функциях, образующих дерево, где каждый уровень должен иметь уникальный ключ (то есть, нет классов с одинаковыми именами в одном и том же пространстве имен), но потомки всегда независимы друг от друга и их предков , Расширение свободы самостоятельного выбора важно для того, чтобы позволить многим людям одновременно работать над большой проблемой.

Не могли бы вы пролить свет на то, что эти двоеточия означают и являются ли они необходимо

В этом конкретном использовании ::, вероятно, не является строго необходимым Это добавляет немного защиты, но затрудняет перемещение переменной в более локальную область видимости позже - хотя это не так важно, потому что компилятор расскажет вам о местах, которые продолжают ссылаться на :: x после перемещения x.

1 голос
/ 06 сентября 2010

Небольшое добавление ко всем остальным приятным моментам, которые появились в ответе.

В основном :: вызывает поиск подходящего имени в глобальном пространстве имен.Однако не гарантируется отсутствие проблем, особенно в условиях неразборчивого использования директив.

Показанный код иллюстрирует важное свойство поиска квалифицированного имени.Дело в том, что в пространствах имен, назначенных с помощью директив в глобальном пространстве имен, производится поиск.Однако использование директив в пространствах имен, для которых выполняется поиск имени, пропускается.

    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
    }

В показанном коде, если строки с директивой #if включены, существует неопределенность в разрешении имени 'f'.

...