Was bedeutet der vorangestellte Doppelpunkt "::"?


410

Ich habe diese Codezeile in einer Klasse gefunden, die ich ändern muss:

::Configuration * tmpCo = m_configurationDB;//pointer to current db

und ich weiß nicht genau, was der Doppelpunkt vor dem Klassennamen bedeutet. Ohne das würde ich lesen: Deklaration tmpCoals Zeiger auf ein Objekt der Klasse Configuration... aber der vorangestellte Doppelpunkt verwirrt mich.

Ich fand auch:

typedef ::config::set ConfigSet;

7
Ich glaube nicht, dass es eine Antwort ist, also werde ich Folgendes kommentieren: en.wikipedia.org/wiki/Scope_resolution_operator . In diesem Zusammenhang ::bezieht sich das nackte Mittel auf die Variable aus dem globalen / anonymen Namespace.
wkl

Antworten:


489

Dadurch wird sichergestellt, dass die Auflösung über den globalen Namespace erfolgt, anstatt mit dem Namespace zu beginnen, in dem Sie sich gerade befinden. Wenn Sie beispielsweise zwei verschiedene Klassen Configurationals solche aufgerufen haben :

class Configuration; // class 1, in global namespace
namespace MyApp
{
    class Configuration; // class 2, different from class 1
    function blah()
    {
        // resolves to MyApp::Configuration, class 2
        Configuration::doStuff(...) 
        // resolves to top-level Configuration, class 1
        ::Configuration::doStuff(...)
    }
}

Grundsätzlich können Sie bis zum globalen Namespace wechseln, da Ihr Name in diesem Fall möglicherweise durch eine neue Definition in einem anderen Namespace überlastet wird MyApp.


Was ist der Grund für das Setzen von 2 Sätzen Doppelpunkt? In diesem:::Configuration::doStuff(...)
Azurespot

@NoniA. Fragen Sie sich, was der zweite Satz Doppelpunkte bewirkt?
FCo

1
@ WyattAnderson, nein der 1. Satz. Ich glaube, ich verstehe, dass sich die ::beiden Begriffe zwischen dem Namespace oder der Klasse und ihrem Mitglied beziehen. Aber was ist mit dem ersten?
Azurespot

6
@ Azurespot, das stellt OP, das ist die Frage, die dieser Beitrag beantwortet. Es wird sichergestellt, dass ein Bezeichner aus dem globalen Namespace verwendet wird. Schauen Sie sich das Beispiel noch einmal an
hungriger Wolf

193

Der ::Operator wird als Scope-Resolution-Operator bezeichnet und löst genau das, er löst den Scope auf. Wenn Sie diesem Typnamen einen Präfix voranstellen, wird Ihr Compiler angewiesen, im globalen Namespace nach dem Typ zu suchen.

Beispiel:

int count = 0;

int main(void) {
  int count = 0;
  ::count = 1;  // set global count to 1
  count = 2;    // set local count to 2
  return 0;
}

122

Viele vernünftige Antworten schon. Ich werde mich mit einer Analogie befassen, die einigen Lesern helfen kann. ::funktioniert ähnlich wie das Dateisystem-Verzeichnis-Trennzeichen ' /', wenn Sie Ihren Pfad nach einem Programm durchsuchen, das Sie ausführen möchten. Erwägen:

/path/to/executable

Dies ist sehr explizit - nur eine ausführbare Datei an genau dieser Stelle im Dateisystembaum kann mit dieser Spezifikation übereinstimmen, unabhängig vom gültigen PATH. Ähnlich...

::std::cout

... ist im C ++ - Namespace "tree" gleichermaßen explizit.

Im Gegensatz zu solchen absoluten Pfaden können Sie gute UNIX-Shells (z. B. zsh ) konfigurieren , um relative Pfade unter Ihrem aktuellen Verzeichnis oder einem beliebigen Element in Ihrer PATHUmgebungsvariablen aufzulösen. Wenn also PATH=/usr/bin:/usr/local/binund Sie "in" waren /tmp, dann ...

X11/xterm

... würde gerne rennen, /tmp/X11/xtermwenn gefunden, sonst /usr/bin/X11/xterm, sonst /usr/local/bin/X11/xterm. Angenommen, Sie befanden sich in einem Namespace namens Xund hatten ein " using namespace Y" in Kraft, dann ...

std::cout

... könnte in einem gefunden werden ::X::std::cout, ::std::cout, ::Y::std::coutund möglicherweise an anderen Orten aufgrund Argument abhängiger Lookup (ADL, auch bekannt als Koenig - Lookup). Es ist also nur ::std::coutwirklich explizit, welches Objekt Sie genau meinen, aber zum Glück würde niemand, der bei klarem Verstand ist, jemals eine eigene Klasse / Struktur oder einen eigenen Namespace mit dem Namen " std" oder etwas mit dem Namen " cout" erstellen. In der Praxis std::coutist es also in Ordnung , nur zu verwenden .

Bemerkenswerte Unterschiede :

1) Shells verwenden in der Regel die erste Übereinstimmung mit der Reihenfolge in PATH, während C ++ einen Compilerfehler ausgibt, wenn Sie mehrdeutig waren.

2) In C ++ Namen ohne führende Bereich kann in den aktuellen Namensraum angepasst werden, während die meisten UNIX - Shells nur das tun , wenn Sie setzen .in der PATH.

3) C ++ durchsucht immer den globalen Namespace (wie /implizit Ihren PATH).

Allgemeine Diskussion über Namespaces und Explizite von Symbolen

Die Verwendung absoluter ::abc::def::..."Pfade" kann manchmal nützlich sein, um Sie von anderen Namespaces zu isolieren, die Sie verwenden, die Teil des Inhalts oder sogar anderer Bibliotheken sind, die der Clientcode Ihrer Bibliothek ebenfalls verwendet, aber nicht wirklich kontrolliert. Andererseits werden Sie auch enger an die vorhandene "absolute" Position des Symbols gekoppelt, und Sie vermissen die Vorteile des impliziten Abgleichs in Namespaces: weniger Kopplung, einfachere Mobilität des Codes zwischen Namespaces und präziserer, lesbarer Quellcode .

Wie bei vielen Dingen ist es ein Balanceakt. Die C ++ Standard - Puts vielen Kennungen unter , std::dass weniger „einzigartig“ als cout, dass Programmierer für etwas ganz anderes in ihrem Code verwenden kann (zB merge, includes, fill, generate, exchange, queue, toupper, max). Zwei nicht verwandte Nicht-Standard-Bibliotheken haben eine weitaus höhere Wahrscheinlichkeit, dieselben Bezeichner zu verwenden, da sich die Autoren im Allgemeinen nicht oder weniger bewusst sind. Und Bibliotheken - einschließlich der C ++ Standard-Bibliothek - ändern ihre Symbole im Laufe der Zeit. All dies führt möglicherweise zu Mehrdeutigkeiten beim Neukompilieren von altem Code, insbesondere wenn using namespaces häufig verwendet wird: Das Schlimmste, was Sie in diesem Bereich tun können, ist Zulassenusing namespaces in Headern, um den Gültigkeitsbereichen der Header zu entgehen, sodass eine beliebig große Menge direkten und indirekten Client-Codes nicht in der Lage ist, selbst zu entscheiden, welche Namespaces verwendet werden sollen und wie Mehrdeutigkeiten verwaltet werden sollen.

Ein Leading ::ist also ein Werkzeug in der Toolbox des C ++ - Programmierers, um einen bekannten Konflikt aktiv zu disambiguieren und / oder die Möglichkeit zukünftiger Mehrdeutigkeiten auszuschließen.


8
+1 für eine gute Analogie. Analogien werden IMO bei weitem nicht genug als Lehrmittel verwendet.
Trevor Boyd Smith

38

::ist der Operator für die Bereichsauflösung. Es wird verwendet, um den Umfang von etwas anzugeben.

Zum Beispiel ist ::allein der globale Bereich außerhalb aller anderen Namespaces.

some::thing kann auf eine der folgenden Arten interpretiert werden:

  • someist ein Namespace (im globalen Bereich oder ein äußerer Bereich als der aktuelle) und thingist ein Typ , eine Funktion , ein Objekt oder ein verschachtelter Namespace ;
  • someist eine Klasse, die im aktuellen Bereich verfügbar thingist und ein Mitgliedsobjekt , eine Funktion oder ein Typ der someKlasse ist;
  • in einer Elementfunktion , somekann eine sein , Basistyp des aktuellen Typs (oder den aktuellen Typs selbst) , und thingist dann ein Mitglied dieser Klasse, eine Art , Funktion oder Objekt .

Sie können auch einen verschachtelten Bereich haben, wie in some::thing::bad. Hier kann jeder Name ein Typ, ein Objekt oder ein Namespace sein. Darüber hinaus könnte der letzte badauch eine Funktion sein. Die anderen konnten dies nicht, da Funktionen nichts innerhalb ihres internen Bereichs verfügbar machen können.

Zurück zu Ihrem Beispiel ::thingkann also nur etwas im globalen Bereich sein: ein Typ, eine Funktion, ein Objekt oder ein Namespace.

Die Art und Weise, wie Sie es verwenden, legt nahe (in einer Zeigerdeklaration verwendet), dass es sich um einen Typ im globalen Bereich handelt.

Ich hoffe, diese Antwort ist vollständig und korrekt genug, um Ihnen das Verständnis der Bereichsauflösung zu erleichtern.


2
@obounaim Betrachten Sie diesen Code liveworkspace.org/code/3Wabw0$5 class some { protected: int thing; }; class some_ext : public some { float thing; void action(){ some::thing = 42; thing = 666; } }; Hier someist eine Basisklasse von some_extund wenn Sie some::thingin Mitgliedsfunktionen von some_ext schreiben , bedeutet dies das thingObjekt in den Basistyp some. Ohne some::, thingallein mittels der thingim engsten Umfang, dh some_ext::thing. Ist es klarer?
Klaim

17

:: wird verwendet, um etwas (eine Variable, eine Funktion, eine Klasse, ein typedef usw.) mit einem Namespace oder einer Klasse zu verknüpfen.

Wenn es vorher keine linke Seite gibt ::, unterstreicht dies die Tatsache, dass Sie den globalen Namespace verwenden.

z.B:

::doMyGlobalFunction();


10

Der als Bereichsauflösungsoperator bezeichnete versteckte globale Name kann mit dem Bereichsauflösungsoperator referenziert werden:
Zum Beispiel;

int x;
void f2()
{
   int x = 1; // hide global x
   ::x = 2; // assign to global x
   x = 2; // assign to local x
   // ...
}

10

(Diese Antwort ist hauptsächlich für Googler gedacht, da OP sein Problem bereits gelöst hat.) Die Bedeutung des vorangestellten ::Operators für die Bereichsauflösung wurde in anderen Antworten beschrieben, aber ich möchte hinzufügen, warum die Benutzer sie verwenden.

Die Bedeutung ist "Name aus globalem Namespace übernehmen, nichts anderes". Aber warum sollte dies explizit geschrieben werden müssen?

Anwendungsfall - Namespace-Konflikt

Wenn Sie im globalen Namespace und im lokalen / verschachtelten Namespace denselben Namen haben, wird der lokale verwendet. Wenn Sie also die globale Version möchten, stellen Sie sie voran ::. Dieser Fall wurde in der Antwort von @Wyatt Anderson beschrieben, siehe sein Beispiel.

Anwendungsfall - Hervorheben der Nichtmitgliedsfunktion

Wenn Sie eine Member-Funktion (eine Methode) schreiben, sehen Aufrufe an andere Member-Funktionen und Aufrufe an Nicht-Member-Funktionen (kostenlos) ähnlich aus:

class A {
   void DoSomething() {
      m_counter=0;
      ...
      Twist(data); 
      ...
      Bend(data);
      ...
      if(m_counter>0) exit(0);
   }
   int m_couner;
   ...
}

Aber es kann vorkommen, dass Twistes sich um eine Schwesterfunktion der Klasse Ahandelt und Bendes sich um eine freie Funktion handelt. Das heißt, Twistkann verwenden und ändern m_counerund Bendkann nicht. Wenn Sie also sicherstellen möchten, dass m_counter0 bleibt, müssen Sie dies überprüfen Twist, müssen es jedoch nicht überprüfen Bend.

Um dies deutlicher hervorzuheben, kann man entweder schreiben this->Twist, um dem Leser zu zeigen, dass Twistes sich um eine Mitgliedsfunktion handelt, oder schreiben ::Bend, um zu zeigen, dass Bendes kostenlos ist. Oder beides. Dies ist sehr nützlich, wenn Sie ein Refactoring durchführen oder planen.


5

:: ist ein Operator zum Definieren des Namespace.

Wenn Sie beispielsweise cout verwenden möchten, ohne dies using namespace std;in Ihrem Code zu erwähnen , schreiben Sie Folgendes:

std::cout << "test";

Wenn kein Namespace erwähnt wird, heißt es, dass die Klasse zum globalen Namespace gehört.


1

"::" steht für den Bereichsauflösungsoperator. Funktionen / Methoden mit demselben Namen können in zwei verschiedenen Klassen definiert werden. Für den Zugriff auf die Methoden eines bestimmten Klassenbereichs wird der Auflösungsoperator verwendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.