"Sử dụng không gian tên" trong tiêu đề c ++


119

Trong tất cả các khóa học c ++ của chúng tôi, tất cả các giáo viên luôn đặt using namespace std;ngay sau #includes trong .htệp của họ . Điều này đối với tôi dường như là nguy hiểm kể từ đó bằng cách đưa tiêu đề đó vào một chương trình khác, tôi sẽ nhận được không gian tên được nhập vào chương trình của mình, có thể mà không nhận ra, dự định hoặc muốn nó (bao gồm tiêu đề có thể được lồng rất sâu).

Vì vậy, câu hỏi của tôi là kép: Tôi có đúng là using namespacekhông nên được sử dụng trong các tệp tiêu đề và / hoặc có cách nào đó để hoàn tác nó, một cái gì đó như:

//header.h
using namespace std {
.
.
.
}

Một câu hỏi nữa dọc theo những dòng tương tự: Liệu một tệp tiêu đề có #includetất cả các tiêu đề mà .cpptệp tương ứng của nó cần, chỉ những tiêu đề cần thiết cho định nghĩa tiêu đề và để .cpptệp #includecòn lại, hay không có và khai báo mọi thứ mà nó cần extern?
Lý do đằng sau câu hỏi tương tự như trên: Tôi không muốn bất ngờ khi bao gồm.h các tệp.

Ngoài ra, nếu tôi đúng, đây có phải là một lỗi phổ biến không? Ý tôi là trong lập trình thế giới thực và trong các dự án "thực" ngoài kia.

Cảm ơn bạn.



3
như một lưu ý phụ, nếu bạn nhận được xung đột tên do các using namespacecâu lệnh thì bạn có thể sử dụng tên đủ điều kiện để giải quyết vấn đề.
Marius Bancila

Câu trả lời:


115

Bạn chắc chắn KHÔNG nên sử dụng using namespacetrong tiêu đề vì chính xác lý do bạn nói, rằng nó có thể bất ngờ thay đổi ý nghĩa của mã trong bất kỳ tệp nào khác bao gồm tiêu đề đó. Không có cách nào để hoàn tác một using namespacelý do khác khiến nó rất nguy hiểm. Tôi thường chỉ sử dụng grephoặc những thứ tương tự để đảm bảo rằngusing namespace nó không được gọi ra trong tiêu đề thay vì thử bất cứ điều gì phức tạp hơn. Có lẽ những người kiểm tra mã tĩnh cũng gắn cờ điều này.

Tiêu đề chỉ nên bao gồm các tiêu đề cần biên dịch. Một cách dễ dàng để thực thi điều này là luôn bao gồm tiêu đề riêng của mỗi tệp nguồn làm điều đầu tiên, trước bất kỳ tiêu đề nào khác. Sau đó, tệp nguồn sẽ không biên dịch được nếu tiêu đề không có sẵn. Trong một số trường hợp, ví dụ đề cập đến các lớp chi tiết triển khai trong thư viện, bạn có thể sử dụng khai báo chuyển tiếp thay #includevì vì bạn có toàn quyền kiểm soát định nghĩa của lớp được khai báo chuyển tiếp đó.

Tôi không chắc mình sẽ gọi nó là phổ biến, nhưng nó chắc chắn xuất hiện một lần, thường được viết bởi các lập trình viên mới không nhận thức được hậu quả tiêu cực. Thông thường, chỉ cần một chút giáo dục về rủi ro sẽ xử lý được mọi vấn đề vì nó tương đối đơn giản để khắc phục.


2
chúng tôi có được tự do sử dụng các usingcâu lệnh trong .cpptệp của mình không? các 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators là cái chết đến đầu ngón tay.
Christopher

1
và làm thế nào chúng ta nên sắp xếp hợp lý các templatechức năng - những chức năng được cho là có trong tiêu đề? typedefs?
Christopher

1
@donlan, có vẻ như bạn không nhận được phản hồi trong một thời gian ... Có, bạn có thể sử dụng usingcâu lệnh trong .cpptệp mà không cần quan tâm nhiều vì phạm vi sẽ bị giới hạn trong tệp đó, nhưng đừng bao giờ làm điều đó trước một #includecâu lệnh. Đối với các hàm mẫu được xác định trong tiêu đề, rất tiếc là tôi không biết giải pháp nào tốt ngoài việc chỉ viết ra không gian tên ... Có lẽ bạn có thể đặt một usingkhai báo trong một phạm vi riêng biệt { /* using statement in between brackets */ }, điều đó ít nhất sẽ ngăn nó thoát khỏi tệp hiện tại .
tjwrona1992,

26

Mục 59 trong "Tiêu chuẩn mã hóa C ++: 101 Quy tắc, Nguyên tắc và Thực tiễn Tốt nhất" của Sutter và Alexandrescu :

59. Không viết không gian tên bằng cách sử dụng trong tệp tiêu đề hoặc trước #include.

Không gian tên usinglà để thuận tiện cho bạn, không phải để bạn xâm phạm người khác: Không bao giờ viết một usingtuyên bố hoặc mộtusing chỉ thị trước một #includechỉ thị.

Hệ quả: Trong tệp tiêu đề, không viết usingchỉ thị hoặc usingkhai báo cấp không gian tên ; thay vào đó, không gian tên rõ ràng đủ điều kiện cho tất cả các tên.

Tệp tiêu đề là một khách trong một hoặc nhiều tệp nguồn. Tệp tiêu đề bao gồmusing chỉ thị và tuyên bố cũng mang những người bạn ồn ào của nó.

Một using khai báo mang lại một người bạn. Một using chỉ thị thu hút tất cả các bạn bè trong không gian tên. Việc giáo viên của bạn sử dụngusing namespace std; là một chỉ thị sử dụng.

Nghiêm trọng hơn, chúng tôi có không gian tên để tránh xung đột tên. Tệp tiêu đề nhằm cung cấp một giao diện. Hầu hết các tiêu đề đều không xác định được mã nào có thể bao gồm chúng, bây giờ hoặc trong tương lai. Việc thêm các usingcâu lệnh để thuận tiện cho nội bộ bên trong tiêu đề sẽ hỗ trợ các tên thuận tiện đó trên tất cả các khách hàng tiềm năng của tiêu đề đó. Điều đó có thể dẫn đến đụng độ tên tuổi. Và nó hoàn toàn thô lỗ.


12

Bạn cần phải cẩn thận khi đưa tiêu đề vào bên trong tiêu đề. Trong các dự án lớn, nó có thể tạo ra một chuỗi phụ thuộc rất rối có thể kích hoạt các bản xây dựng lại lớn hơn / dài hơn mức thực sự cần thiết. Hãy xem bài viết nàyphần tiếp theo của nó để tìm hiểu thêm về tầm quan trọng của cấu trúc vật lý tốt trong các dự án C ++.

Bạn chỉ nên bao gồm tiêu đề bên trong tiêu đề khi thực sự cần thiết (bất cứ khi nào cần định nghĩa đầy đủ về một lớp) và sử dụng khai báo chuyển tiếp bất cứ nơi nào bạn có thể (khi lớp được yêu cầu là một con trỏ hoặc một tham chiếu).

Đối với không gian tên, tôi có xu hướng sử dụng phạm vi không gian tên rõ ràng trong các tệp tiêu đề của mình và chỉ đặt một using namespacetrong các tệp cpp của tôi.


1
làm thế nào để bạn sắp xếp hợp lý templatekhai báo hàm? điều đó phải xảy ra trong tiêu đề, không?
Christopher

6

Kiểm tra các tiêu chuẩn mã hóa của Trung tâm chuyến bay vũ trụ Goddard (cho C và C ++). Điều đó hóa ra khó hơn một chút so với trước đây - hãy xem câu trả lời cập nhật cho các câu hỏi SO:

Tiêu chuẩn mã hóa C ++ của GSFC cho biết:

§3.3.7 Mỗi tệp tiêu đề sẽ #includelà tệp mà nó cần để biên dịch, thay vì buộc người dùng vào #includecác tệp cần thiết. #includessẽ được giới hạn ở những gì tiêu đề cần; khác #includesnên được đặt trong tệp nguồn.

Câu hỏi đầu tiên trong số các câu hỏi được tham chiếu chéo hiện bao gồm phần trích dẫn từ tiêu chuẩn mã hóa GSFC C và cơ sở lý luận, nhưng nội dung cuối cùng lại giống nhau.


5

Bạn đúng rằng using namespacetrong tiêu đề là nguy hiểm. Tôi không biết cách nào để hoàn tác nó. Rất dễ dàng để phát hiện ra nó tuy nhiên chỉ cần tìm kiếm using namespacetrong các tệp tiêu đề. Vì lý do cuối cùng đó, nó không phổ biến trong các dự án thực tế. Những đồng nghiệp có kinh nghiệm hơn sẽ sớm phàn nàn nếu ai đó làm điều gì đó giống như vậy.

Trong các dự án thực, mọi người cố gắng giảm thiểu số lượng tệp được bao gồm, bởi vì bạn càng ít đưa vào thì tệp biên dịch càng nhanh. Điều đó tiết kiệm thời gian của mọi người. Tuy nhiên, nếu tệp tiêu đề giả định rằng một cái gì đó nên được bao gồm trước nó thì nó sẽ bao gồm chính nó. Nếu không, nó làm cho tiêu đề không được khép kín.


4

Bạn đúng rồi. Và bất kỳ tệp nào cũng chỉ nên bao gồm các tiêu đề cần thiết của tệp đó. Đối với "việc làm sai có phổ biến trong các dự án thế giới thực không?" - ồ, vâng!


4

Giống như tất cả những thứ trong lập trình, chủ nghĩa thực dụng sẽ chiến thắng chủ nghĩa giáo điều, IMO.

Miễn là bạn đưa ra quyết định trên toàn dự án ("Dự án của chúng tôi sử dụng STL rộng rãi và chúng tôi không muốn phải thêm mọi thứ với std ::."), Tôi không thấy vấn đề với nó. Rốt cuộc, điều duy nhất bạn đang mạo hiểm là va chạm tên tuổi, và với sự phổ biến của STL, nó không có khả năng là một vấn đề.

Mặt khác, nếu đó là quyết định của một nhà phát triển trong một tệp tiêu đề duy nhất (không riêng tư), tôi có thể thấy nó sẽ tạo ra sự nhầm lẫn giữa nhóm và nên tránh như thế nào.


4

Liên quan đến "Có cách nào để hoàn tác [một usingtuyên bố] không?"

Tôi nghĩ sẽ hữu ích khi chỉ ra rằng các usingkhai báo bị ảnh hưởng bởi phạm vi.

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

Vì vậy, có hiệu quả. Bằng cách giới hạn phạm vi củausing tuyên bố, hiệu lực của nó chỉ kéo dài trong phạm vi đó; nó được 'hoàn tác' khi phạm vi đó kết thúc.

Khi usingkhai báo được khai báo trong một tệp bên ngoài bất kỳ phạm vi nào khác, nó có phạm vi tệp và ảnh hưởng đến mọi thứ trong tệp đó.

Trong trường hợp tệp tiêu đề, nếu usingkhai báo ở phạm vi tệp, điều này sẽ mở rộng đến phạm vi của bất kỳ tệp nào mà tiêu đề được đưa vào.


2
bạn dường như là người duy nhất hiểu câu hỏi thực tế như thế nào ... tuy nhiên, biên dịch của tôi không hài lòng lắm về việc tôi sử dụng bên trong giảm tốc lớp.
giấy gỉ

Câu trả lời này thậm chí có thể được thực hiện tốt hơn bằng cách giải thích vấn đề với ý tưởng của OP về cách phạm vi hoạt động (như namespacenội dung khai báo) so với cách nó thực sự hoạt động (như một biến). {}bao quanh nó giới hạn phạm vi của nó, {}sau khi nó không làm gì liên quan đến nó. Đó là một cách tình cờ mà nó using namespaceđược áp dụng trên toàn cầu.
TafT

2

Tôi tin rằng bạn có thể sử dụng 'using' trong tiêu đề C ++ một cách an toàn nếu bạn viết các khai báo của mình trong một không gian tên lồng nhau như sau:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Điều này sẽ chỉ bao gồm những thứ được khai báo trong 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' mà không sử dụng không gian tên. Tôi đã thử nghiệm nó trên trình biên dịch mingw64.


Đây là một kỹ thuật hữu ích mà tôi chưa từng thấy trước đây; cảm ơn. Thông thường, tôi thấy ổn với việc sử dụng đủ điều kiện phạm vi và đặt các usingkhai báo bên trong các định nghĩa hàm ở nơi tôi có thể để chúng không gây ô nhiễm không gian tên bên ngoài hàm. Nhưng bây giờ tôi muốn sử dụng các ký tự do người dùng định nghĩa trong C ++ 11 trong tệp tiêu đề và theo quy ước thông thường, các toán tử ký tự được bảo vệ bởi một không gian tên; nhưng tôi không thể sử dụng chúng trong danh sách trình khởi tạo phương thức khởi tạo không nằm trong phạm vi mà tôi có thể sử dụng usingkhai báo không gây ô nhiễm . Vì vậy, điều này là tuyệt vời để giải quyết vấn đề đó.
Anthony Hall

Mặc dù một tác dụng phụ không mong muốn của mô hình này là bất kỳ lớp học khai báo trong namespace trong cùng sẽ hiển thị trong các thông báo lỗi biên dịch với tên đầy đủ: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Ít nhất, đó là những gì đang xảy ra với tôi trong g ++.
Anthony Hall
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.