Thế nào là sử dụng ô nhiễm không gian tên là gì?


15

Tôi đã xem hướng dẫn mã hóa google [tại đây] và họ không khuyên người ta nên sử dụng using namespacehoặc namespace::function- nếu tôi không hiểu sai về nó.

Điều này có áp dụng cho stdlà tốt? cout<<không hoạt động mà không có nó. Cuốn sách này , đề nghị như vậy. Vì vậy, làm thế nào để tôi đi về sử dụng cout<<mà không using namespace std;hoặc std::cout<<?

Cách được đề nghị là gì? std::cout<<? Hầu hết các sách giáo khoa c ++ dạy cho người mới bắt đầu với việc using namespace std;họ tuyên truyền thực hành mã hóa kém?

Câu trả lời:


18

Khi tôi đọc tiêu chuẩn Google, bạn không thể sử dụng lệnh using namespace foo;này ở bất cứ đâu. Lệnh này mang lại mọi thứ được khai báo trong không gian tên và là nguyên nhân phổ biến của các va chạm và hành vi không mong muốn. Những người khác đã trích dẫn một điều rất phổ biến: bạn có phương thức tối đa hoặc tối thiểu của riêng mình ở đâu đó và nó va chạm vào một tệp src nơi ai đó bao gồm một tiêu đề với phương thức của bạn và sau đó nóiusing namespace std;

Ở một số nơi nhất định, nó được phép có một khai báo sử dụng, có dạng using ::foo::bar;

Mọi người thích sử dụng các lệnh trong mã của họ vì nó tiết kiệm rất nhiều thao tác gõ, nhưng nó đi kèm với rủi ro. Nếu bạn có một tệp có nhiều câu lệnh cout, tôi có thể hiểu không muốn phải nhập std :: cout một trăm lần, nhưng bạn có thể nói đơn giản bằng cách sử dụng :: std :: cout. Tôi coi những điều này giống như khai báo biến: phạm vi chúng ở nơi cần thiết. Nếu một hàm trong tệp 10 cần ghi đầu ra, đừng khai báo cách cout ở trên cùng, hãy đặt nó vào hàm đó đang thực hiện đầu ra thực tế.

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

Đó là một chút cực đoan với chỉ một vài dòng làm đầu ra, nhưng bạn có ý tưởng.

Một điều khác người ta có thể làm là bí danh hoặc typedef để giảm thiểu việc gõ. Tôi không tìm thấy std :: bất cứ điều gì tệ đến thế, nhưng chúng tôi có một bộ nguồn khổng lồ với vài chục mô-đun và đôi khi chúng tôi phải viết mã như thế nào console_gui::command_window::append("text"). Điều đó trở nên tẻ nhạt sau một thời gian và gây ra rất nhiều dòng dài. Tôi là tất cả cho một cái gì đó như

typedef console_gui::command_window cw;
cw::append("text");

miễn là các bí danh được thực hiện trong một phạm vi cục bộ và giữ đủ ngữ cảnh để làm cho mã có thể đọc được.


1
Cảm ơn! Điều này thực sự hữu ích. Bạn không chỉ giải thích tại sao nó xấu với các ví dụ đẹp, mà còn chỉ ra các giải pháp với các ví dụ đẹp. :-)
Chúa Loh.

1
BTW: Sử dụng std::endlcho tuôn ra rõ ràng trên stdout/ stderrthường khá thừa, những luồng đó được gắn với stdout/ stderranyway. Nó thậm chí còn làm mọi thứ chậm lại một chút.
Ded repeatator

8

Điều này là do: 1) nó đánh bại toàn bộ mục đích của không gian tên, nhằm giảm xung đột tên; 2) nó cung cấp cho không gian tên toàn cầu toàn bộ không gian tên được chỉ định bằng lệnh sử dụng.

Ví dụ: nếu bạn bao gồm và xác định hàm max () của riêng mình, nó sẽ va chạm với std :: max ().

http://en.cppreference.com/w/cpp/alacticm/max

Tùy chọn là sử dụng std :: Member_you_wish_to_use vì nó nói rõ không gian tên nào sẽ sử dụng.


Tôi đoán điều này có nghĩa là tôi nên sử dụng std::max()với tiền tố không gian tên. Hay là tôi nhầm?
Chúa ơi.

3
Nó có nghĩa là nếu bạn đặt "bằng cách sử dụng không gian tên std;" trong mã của bạn, bạn sẽ gặp lỗi nếu bạn xác định hàm max của riêng mình (hoặc bất kỳ tên nào khác đã được xác định trong không gian tên std)
Chewy Gumball

1
Điều đó chỉ có nghĩa là bạn nên cẩn thận với các usingchỉ thị vì trong trường hợp này, nó sẽ phá vỡ hàm max () của bạn nếu bạn đã xác định một hàm và bao gồm <thuật toán>. Đây là một trường hợp đơn giản nhưng bạn không bao giờ biết những gì bạn có thể phá vỡ. Bạn cần biết toàn bộ thư viện để chắc chắn rằng bạn đã không phá vỡ nó nhưng bạn không thể biết liệu mã của mình có bị phá vỡ (tức là xung đột tên) trong tương lai hay không.
ApplePie

6

Trích dẫn liên kết bạn cung cấp:

Bạn có thể sử dụng khai báo sử dụng bất cứ nơi nào trong tệp .cc và trong các hàm, phương thức hoặc lớp trong tệp .h.

// OK trong tệp .cc.

// Phải ở trong một hàm, phương thức hoặc lớp trong các tệp .h.

sử dụng :: foo :: bar;

Phong cách Google cấm bạn sử dụng nhập không gian tên trong ngữ cảnh toàn cầu, nhưng cho phép làm như vậy trong các địa phương.

Ở mọi nơi mà việc sử dụng khai báo chỉ ảnh hưởng đến một phần mã được giới hạn và có thể nhìn thấy rõ ràng, nó hoàn toàn có thể chấp nhận được.

Khi bạn gây ô nhiễm bối cảnh toàn cầu, mã không liên quan sẽ bị ảnh hưởng (bằng cách sử dụng tiêu đề của bạn). Không có gì xảy ra khi bạn làm như vậy trong bối cảnh địa phương.


Chúng tôi có cùng tiêu chuẩn. Một số người của chúng tôi sẽ gõ địa phương một không gian tên dài. ví dụ typedef dại dột :: barbious fb; fb :: uống d;
Michael Mathews

1

họ không khuyến nghị người ta sử dụng không gian tên ornamespace: function` - nếu tôi không hiểu sai về nó.

Bạn đã làm. Việc không tham gia chỉ áp dụng cho using namespacechỉ thị (thường được gọi là abusing namespace, không hoàn toàn hài hước). Bạn nên sử dụng tên đủ điều kiện của một chức năng hoặc đối tượng, chẳng hạn như std::cout.


1

Mặc dù câu hỏi đã có câu trả lời hữu ích, nhưng một chi tiết dường như quá ngắn.

Hầu hết các lập trình viên ban đầu hơi bối rối với usingtừ khóa và các mô tả về namespacecách sử dụng, ngay cả khi họ cố gắng học nó bằng cách tra cứu tài liệu tham khảo, bởi vì khai báochỉ thị đọc phần nào tương đương, cả hai đều là những từ dài tương đối trừu tượng bắt đầu bằng d .

Mã định danh trong không gian tên có thể truy cập bằng cách đặt tên rõ ràng không gian tên:

myNamespace::myIdent

Đây có thể là rất nhiều phím để gõ. Nhưng nó cũng có thể làm giảm tầm quan trọng của mã của bạn, nếu hầu hết các mã định danh đều có tiền tố theo cùng một cách. Các usingtừ khóa giúp ngăn chặn những nhược điểm không gian tên. Từusing hoạt động ở cấp độ trình biên dịch (không phải là macro), nên hiệu ứng của nó tồn tại trong toàn bộ phạm vi mà nó được sử dụng. Đó là lý do tại sao kiểu Google hạn chế sử dụng đối với phạm vi được xác định rõ, tức là các lớp trong tệp tiêu đề hoặc hàm trong tệp cpp.

... tất nhiên có một sự khác biệt giữa việc sử dụng khai báo

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

sử dụng chỉ thị

using myNamespace; // make all identifiers of myNamespace directly accessible

Nếu được sử dụng trong phạm vi lớn, sau này dẫn đến nhiều nhầm lẫn.


1

Ở đây bạn đi:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

Bằng cách viết theo cách này, chúng tôi tránh được ADL dễ bị lỗi cùng với việc sử dụng các chỉ thị và khai báo.

Điều này có nghĩa là một câu trả lời mỉa mai. : -D

Tôi với Herb Sutter trên Google về điều này. Từ các tiêu chuẩn mã hóa C ++:

Bạn có thể và nên sử dụng không gian tên bằng cách sử dụng các khai báo và chỉ thị một cách tự do trong các tệp triển khai của mình sau các lệnh #incoide và cảm thấy tốt về nó. Mặc dù nhiều lần khẳng định ngược lại, không gian tên sử dụng các khai báo và chỉ thị không phải là xấu xa và chúng không đánh bại mục đích của các không gian tên. Thay vào đó, chúng là những gì làm cho không gian tên có thể sử dụng .

Bạn có thể ám ảnh về các xung đột không gian tên tiềm năng có thể sẽ không bao giờ xuất hiện và có thể sẽ không khó khắc phục trong một sự kiện thiên văn hiếm gặp như vậy bằng cách cẩn thận tránh các usingchỉ thị và chỉ định rõ ràng từng điều bạn sử dụng (cho các nhà khai thác) bằng các usingtuyên bố, hoặc chỉ cần đi trước và bắt đầu using namespace std. Tôi đề nghị sau này từ một quan điểm năng suất.

Hầu hết các sách văn bản c ++ dạy người mới bắt đầu với việc sử dụng không gian tên std; Có phải họ tuyên truyền thực hành mã hóa kém?

Ngược lại nếu bạn hỏi tôi, và tôi tin Sutter ở trên đồng ý.

Bây giờ, trong suốt sự nghiệp của mình, tôi đã gặp phải khoảng 3 xung đột không gian tên do kết quả trực tiếp của các usingchỉ thị trong các cơ sở mã hóa kéo dài hàng chục triệu LỘC. Tuy nhiên, trong cả 3 trường hợp, chúng đều nằm trong các tệp nguồn kéo dài hơn 50.000 dòng mã kế thừa, ban đầu được viết bằng C và sau đó được bastard hóa thành C ++, thực hiện một danh sách các hàm phân tách lớn, bao gồm các tiêu đề từ hàng tá thư viện khác nhau và có một danh sách sử thi #includeskéo dài trên một trang. Mặc dù có sự lộn xộn sử thi, nhưng chúng không quá khó để sửa vì chúng gây ra lỗi xây dựng trên OSX (một hệ điều hành không thể xây dựng mã), không phải lỗi thời gian chạy. Đừng tổ chức mã của bạn theo cách ác mộng này và bạn sẽ ổn thôi.

Điều đó nói rằng, tránh cả using chỉ thị và khai báo trong các tệp tiêu đề. Đó chỉ là chậm phát triển. Nhưng đối với các tệp nguồn và đặc biệt là các tệp không có toàn bộ trang chứa đầy các #includechỉ thị, tôi sẽ nói đừng đổ mồ hôi nếu bạn không làm việc cho Google.

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.