Ý nghĩa của dấu hai chấm được chuẩn bị trước là gì


410

Tôi tìm thấy dòng mã này trong một lớp mà tôi phải sửa đổi:

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

và tôi không biết chính xác nghĩa của dấu hai chấm được đặt trước tên lớp là gì. Nếu không có điều đó tôi sẽ đọc: khai báo tmpConhư một con trỏ tới một đối tượng của lớp Configuration... nhưng dấu hai chấm được đặt trước làm tôi bối rối.

Tôi cũng tìm thấy:

typedef ::config::set ConfigSet;

7
Đừng thực sự cảm thấy đó là một câu trả lời, vì vậy tôi sẽ nhận xét: en.wikipedia.org/wiki/Scope_resolution_operator . Trong ngữ cảnh này, trần trụi ::có nghĩa là tham chiếu biến từ không gian tên toàn cầu / ẩn danh.
wkl

Câu trả lời:


490

Điều này đảm bảo rằng độ phân giải xảy ra từ không gian tên toàn cầu, thay vì bắt đầu từ không gian tên mà bạn hiện đang ở. Ví dụ: nếu bạn có hai lớp khác nhau được gọi Configurationnhư sau:

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(...)
    }
}

Về cơ bản, nó cho phép bạn đi qua không gian tên toàn cầu vì tên của bạn có thể bị ghi đè bởi một định nghĩa mới bên trong một không gian tên khác, trong trường hợp này MyApp.


Lý do của việc đặt 2 bộ dấu hai chấm là gì? Trong này:::Configuration::doStuff(...)
Azurespot

@NoniA. Bạn có hỏi bộ dấu hai chấm thứ hai không?
FCo

1
@WyattAnderson, không có tập 1. Tôi nghĩ rằng tôi hiểu rằng ::hai thuật ngữ ở giữa có nghĩa là không gian tên hoặc lớp và thành viên của nó. Nhưng những gì về người đầu tiên?
Azurespot

6
@Azurespot đó là những gì OP yêu cầu, đó là câu hỏi mà bài đăng này trả lời. Nó đảm bảo sử dụng định danh từ không gian tên toàn cầu. Nhìn vào ví dụ một lần nữa
đóiWolf

193

Các ::nhà điều hành được gọi là toán tử phạm vi độ phân giải và làm điều đó, nó giải quyết phạm vi. Vì vậy, bằng cách thêm tiền tố vào tên loại, nó sẽ cho trình biên dịch của bạn tìm trong không gian tên toàn cục cho loại.

Thí dụ:

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

Rất nhiều câu trả lời hợp lý rồi. Tôi sẽ đưa ra một sự tương tự có thể giúp một số độc giả. ::hoạt động rất giống như trình phân tách thư mục hệ thống tập tin ' /', khi tìm kiếm đường dẫn của bạn cho một chương trình bạn muốn chạy. Xem xét:

/path/to/executable

Điều này rất rõ ràng - chỉ có một tệp thực thi tại vị trí chính xác đó trong cây hệ thống tệp có thể phù hợp với đặc điểm kỹ thuật này, bất kể PATH có hiệu lực. Tương tự ...

::std::cout

... Cũng rõ ràng không kém trong "cây" không gian tên C ++.

Tương phản với các đường dẫn tuyệt đối như vậy, bạn có thể định cấu hình các shell UNIX tốt (ví dụ zsh ) để giải quyết các đường dẫn tương đối trong thư mục hiện tại của bạn hoặc bất kỳ phần tử nào trong PATHbiến môi trường của bạn , vì vậy PATH=/usr/bin:/usr/local/bin, nếu và bạn đã "vào" /tmp, thì ...

X11/xterm

... sẽ vui vẻ chạy /tmp/X11/xtermnếu tìm thấy, khác /usr/bin/X11/xterm, khác /usr/local/bin/X11/xterm. Tương tự, giả sử bạn đang ở trong một không gian tên được gọi Xvà có using namespace Yhiệu lực " ", sau đó ...

std::cout

... có thể được tìm thấy trong bất kỳ ::X::std::cout, ::std::cout, ::Y::std::cout, và những nơi có thể khác do tra cứu luận phụ thuộc (ADL, aka Koenig tra cứu). Vì vậy, chỉ ::std::coutthực sự rõ ràng về chính xác đối tượng của bạn, nhưng may mắn thay, không ai trong tâm trí bên phải của họ sẽ tạo ra lớp / cấu trúc hoặc không gian tên riêng của họ được gọi là " std", hoặc bất cứ thứ gì gọi là " cout", vì vậy trong thực tế chỉ sử dụng std::coutlà tốt.

Sự khác biệt đáng chú ý :

1) shell có xu hướng sử dụng trận đấu đầu tiên bằng cách sử dụng thứ tự trong PATH khi C ++ gây ra lỗi trình biên dịch khi bạn không rõ ràng.

2) Trong C ++, các tên không có bất kỳ phạm vi hàng đầu nào có thể được khớp trong không gian tên hiện tại, trong khi hầu hết các shell UNIX chỉ làm điều đó nếu bạn đặt .vào PATH.

3) C ++ luôn tìm kiếm không gian tên toàn cầu (giống như có /ngầm định của bạn PATH).

Thảo luận chung về không gian tên và nhân chứng của các biểu tượng

Sử dụng tuyệt đối ::abc::def::... "đường dẫn" đôi khi có thể hữu ích để cách ly bạn khỏi mọi không gian tên khác mà bạn đang sử dụng, một phần nhưng không thực sự có quyền kiểm soát nội dung hoặc thậm chí các thư viện khác mà mã máy khách của thư viện của bạn cũng sử dụng. Mặt khác, nó cũng kết nối bạn chặt chẽ hơn với vị trí "tuyệt đối" hiện tại của biểu tượng và bạn bỏ lỡ những lợi thế của việc kết hợp ngầm trong không gian tên: ít khớp nối hơn, di chuyển mã dễ dàng hơn giữa các không gian tên và mã nguồn dễ đọc hơn, ngắn gọn hơn .

Như với nhiều thứ, đó là một hành động cân bằng. C ++ chuẩn puts nhiều định danh dưới std::mà ít "độc đáo" hơn cout, đó là các lập trình viên có thể sử dụng cho một cái gì đó hoàn toàn khác nhau trong mã của họ (ví dụ như merge, includes, fill, generate, exchange, queue, toupper, max). Hai thư viện không liên quan không liên quan có cơ hội sử dụng cùng số định danh cao hơn nhiều vì các tác giả thường không biết hoặc ít biết về nhau. Và các thư viện - bao gồm thư viện C ++ Standard - thay đổi ký hiệu của chúng theo thời gian. Tất cả điều này có khả năng tạo ra sự mơ hồ khi biên dịch lại mã cũ, đặc biệt là khi đã sử dụng nhiều using namespaces: điều tồi tệ nhất bạn có thể làm trong không gian này là cho phépusing namespaces trong các tiêu đề để thoát khỏi phạm vi của các tiêu đề, sao cho một lượng lớn mã khách hàng trực tiếp và gián tiếp không thể tự đưa ra quyết định về việc sử dụng không gian tên nào và cách quản lý sự mơ hồ.

Vì vậy, hàng đầu ::là một công cụ trong hộp công cụ của lập trình viên C ++ để chủ động phân tán một cuộc đụng độ đã biết và / hoặc loại bỏ khả năng mơ hồ trong tương lai ....


8
+1 cho sự tương tự tốt. tương tự không sử dụng gần như đủ IMO như một công cụ giảng dạy.
Trevor Boyd Smith

38

::là toán tử phân giải phạm vi. Nó được sử dụng để xác định phạm vi của một cái gì đó.

Ví dụ, ::một mình là phạm vi toàn cầu, bên ngoài tất cả các không gian tên khác.

some::thing có thể được giải thích theo bất kỳ cách nào sau đây:

  • somelà một không gian tên (trong phạm vi toàn cục hoặc phạm vi bên ngoài so với hiện tại) và thinglà một kiểu , một hàm , một đối tượng hoặc một không gian tên lồng nhau ;
  • somelà một lớp có sẵn trong phạm vi hiện tại và thinglà một đối tượng thành viên , chức năng hoặc loại của somelớp;
  • trong một hàm thành viên lớp , somecó thể là một kiểu cơ sở của kiểu hiện tại (hoặc chính kiểu hiện tại) và thingsau đó là một thành viên của lớp này, một kiểu , hàm hoặc đối tượng .

Bạn cũng có thể có phạm vi lồng nhau, như trong some::thing::bad. Ở đây mỗi tên có thể là một loại, một đối tượng hoặc một không gian tên. Ngoài ra, cái cuối cùng bad, cũng có thể là một chức năng. Những người khác không thể, vì các chức năng không thể phơi bày bất cứ điều gì trong phạm vi nội bộ của họ.

Vì vậy, trở lại ví dụ của bạn, ::thingcó thể chỉ là một cái gì đó trong phạm vi toàn cầu: một loại, một chức năng, một đối tượng hoặc một không gian tên.

Cách bạn sử dụng nó gợi ý (được sử dụng trong khai báo con trỏ) rằng đó là một loại trong phạm vi toàn cầu.

Tôi hy vọng câu trả lời này là đầy đủ và chính xác để giúp bạn hiểu độ phân giải phạm vi.


2
@obounaim Xem xét mã này liveworkspace.org/code/3Wabw0$5 class some { protected: int thing; }; class some_ext : public some { float thing; void action(){ some::thing = 42; thing = 666; } }; Đây somelà một lớp cơ sở some_extvà khi bạn viết some::thingvào các hàm thành viên của some_ext, nó có nghĩa là thingđối tượng vào loại cơ sở some. Không có some::, thingmột mình có nghĩa là thingtrong phạm vi gần nhất, đó là some_ext::thing. Có rõ ràng hơn không?
Klaim

17

:: được sử dụng để liên kết một cái gì đó (một biến, một hàm, một lớp, một typedef, v.v.) với một không gian tên hoặc với một lớp.

nếu không có phía bên trái trước ::, thì nó nhấn mạnh thực tế bạn đang sử dụng không gian tên toàn cầu.

ví dụ:

::doMyGlobalFunction();


10

toán tử phân giải phạm vi được gọi của nó, Một tên toàn cầu ẩn có thể được gọi bằng cách sử dụng toán tử phân giải phạm vi ::
Ví dụ;

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

10

(Câu trả lời này chủ yếu dành cho các nhân viên của Google, vì OP đã giải quyết vấn đề của anh ấy rồi.) Ý nghĩa của ::toán tử phục hồi phạm vi được chuẩn bị trước - đã được mô tả trong các câu trả lời khác, nhưng tôi muốn thêm lý do tại sao mọi người sử dụng nó.

Ý nghĩa là "lấy tên từ không gian tên toàn cầu, không phải bất cứ điều gì khác". Nhưng tại sao điều này cần phải được đánh vần rõ ràng?

Ca sử dụng - xung đột không gian tên

Khi bạn có cùng tên trong không gian tên toàn cầu và trong không gian tên cục bộ / lồng nhau, tên cục bộ sẽ được sử dụng. Vì vậy, nếu bạn muốn một cái toàn cầu, hãy đăng ký trước ::. Trường hợp này đã được mô tả trong câu trả lời của @Wyatt Anderson, xin vui lòng xem ví dụ của anh ấy.

Ca sử dụng - nhấn mạnh chức năng không phải thành viên

Khi bạn đang viết một hàm thành viên (một phương thức), các lệnh gọi đến hàm thành viên khác và các hàm gọi đến các hàm không phải thành viên (miễn phí) trông giống nhau:

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

Nhưng nó có thể xảy ra Twistlà một hàm thành viên chị em của lớp ABendlà một hàm miễn phí. Đó là, Twistcó thể sử dụng và sửa đổi m_counerBendkhông thể. Vì vậy, nếu bạn muốn đảm bảo m_countervẫn là 0, bạn phải kiểm tra Twist, nhưng bạn không cần kiểm tra Bend.

Vì vậy, để làm cho điều này nổi bật rõ ràng hơn, người ta có thể viết this->Twistđể hiển thị cho người đọc đó Twistlà một chức năng thành viên hoặc viết ::Bendđể hiển thị đó Bendlà miễn phí. Hoặc cả hai. Điều này rất hữu ích khi bạn đang làm hoặc lên kế hoạch tái cấu trúc.


5

:: là một toán tử xác định không gian tên.

Ví dụ: nếu bạn muốn sử dụng cout mà không đề cập đến using namespace std;mã của mình, bạn hãy viết điều này:

std::cout << "test";

Khi không có không gian tên được đề cập, người ta nói rằng lớp thuộc về không gian tên toàn cầu.


1

"::" đại diện cho toán tử phân giải phạm vi. Hàm / phương thức có cùng tên có thể được định nghĩa trong hai lớp khác nhau. Để truy cập các phương thức của một toán tử phân giải phạm vi lớp cụ thể được sử dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.