Lời khuyên về không gian tên trong C ++


75

Tôi chỉ đang tự học không gian tên C ++ (đến từ nền tảng C #) và tôi thực sự bắt đầu nghĩ rằng ngay cả với tất cả những thứ mà C ++ làm tốt hơn hầu hết các ngôn ngữ khác, không gian tên lồng nhau không phải là một trong số đó!

Tôi có đúng khi nghĩ rằng để khai báo một số không gian tên lồng nhau, tôi phải làm như sau:

namespace tier1
{
    namespace tier2
    {
        namespace tier3
        {
            /* then start your normal code nesting */
        }
    }
}

Như trái ngược với:

namespace tier1::tier2::tier3
{
}

à la C #?

Điều này thậm chí còn trở nên mất trí nhớ hơn khi tôi cần chuyển tiếp khai báo:

namespace tier1
{
    namespace tier2
    {
        namespace forward_declared_namespace
        {
            myType myVar; // forward declare
        }
        namespace tier3
        {
            /* then start your normal code nesting */
            class myClass
            {
                forward_declared_namespace::myType myMember;
            }
        }
    }
}

Lưu ý rằng một hệ thống điển hình mà tôi phát triển bao gồm:

MyCompany::MySolution::MyProject::System::[PossibleSections]::Type

Đây có phải là lý do tại sao bạn không có xu hướng sử dụng nhiều không gian tên trong các ví dụ C ++? Hay thường chỉ có các vùng tên đơn (không lồng nhau)?

CẬP NHẬT

Đối với bất kỳ ai quan tâm, đây là cách tôi giải quyết vấn đề này.


8
Cuối cùng, C ++ 17 sẽ cho phép namespace tier1::tier2::tier3.
underscore_d

7
Liên kết trong bản cập nhật bị hỏng ...
AlwaysLearning

2
Tôi muốn tìm hiểu cách bạn giải quyết vấn đề và nhấp vào liên kết của bạn. Rất tiếc, như AlwaysLearning đã chỉ ra, liên kết đã bị hỏng.
csj

Câu trả lời:


113

Không gian tên C ++ không nhằm mục đích trở thành một cơ chế thiết kế - chúng ở đó chỉ đơn giản là để ngăn chặn xung đột tên. Bạn thực sự không muốn hoặc không cần sử dụng các không gian tên lồng nhau trong 99,99% trường hợp.

Một ví dụ điển hình về việc sử dụng đúng không gian tên trong C ++ là Thư viện chuẩn C ++. Mọi thứ trong thư viện khá lớn này được đặt trong một không gian tên duy nhất được gọi là std - không cần cố gắng hoặc không cần phải chia thư viện thành (ví dụ) không gian tên phụ I / O, không gian tên con toán học, không gian tên phụ vùng chứa Vân vân.

Công cụ cơ bản để tạo mô hình trong C ++ là lớp (và ở một mức độ nào đó là khuôn mẫu), không phải không gian tên. Nếu bạn cảm thấy cần phải lồng nhau, bạn nên cân nhắc sử dụng các lớp lồng nhau, có những ưu điểm sau so với không gian tên:

  • họ có phương pháp
  • họ có thể kiểm soát quyền truy cập
  • chúng không thể được mở lại

Sau khi xem xét những điều này, nếu bạn vẫn muốn sử dụng các không gian tên lồng nhau bằng mọi cách - không có gì sai về mặt kỹ thuật khi sử dụng chúng theo cách này.


18
mẫu không được dự định là một cơ chế mpl. Nhưng Guys sử dụng chúng theo cách đó.
Mykola Golubyev

13
Hở . . . Tôi ghét phải là một kẻ lừa đảo (tất cả chúng ta đều biết đó là một lời nói dối), nhưng <iostream> không thực hiện một không gian tên ios bên trong không gian tên std sao? Tôi không có trình biên dịch C ++ tiện dụng vào lúc này. . .
Binary Worrier

1
Bạn có thể cung cấp các đối số (không phải ví dụ), tại sao không nên sử dụng các bảng tên lồng nhau?
bayda

4
@binary ios là typedef cho basic_ios <char>

7
Không gian tên trong C ++ có các tính năng cụ thể để hỗ trợ các giao diện theo cách mô-đun hơn. Vui lòng tham khảo các quy tắc sau trong cuốn sách tuyệt vời "Tiêu chuẩn mã hóa C ++" của Herb Sutter và Andrei Alexandrescu: 57: Giữ một kiểu và giao diện hàm không phải của nó trong cùng một không gian tên. 58: Giữ các loại và chức năng trong các không gian tên riêng biệt trừ khi chúng được dự định cụ thể để hoạt động cùng nhau.
alexk7

23

Không gian tên C ++ là một cải tiến lớn so với cung cấp trước đó (tức là không có không gian tên nào cả). Không gian tên C # đã mở rộng khái niệm và chạy cùng với nó. Tôi khuyên bạn nên giữ không gian tên của bạn trong một cấu trúc phẳng đơn giản.

CHỈNH SỬA Bạn có khuyên rằng do những điểm ngắn mà tôi đã nêu ở đây không?

Đơn giản là "Có". Không gian tên C ++ không được thiết kế để giúp bạn phân vùng logic & thư viện theo cách chúng làm trong C #.

Mục đích của không gian tên C ++ là để ngăn chặn vấn đề trong thế giới thực mà các nhà phát triển C gặp phải, nơi họ gặp phải xung đột tên khi sử dụng hai lib của bên thứ ba xuất (các) tên hàm giống nhau. Các nhà phát triển C có nhiều cách giải quyết khác nhau cho nó, nhưng nó có thể là một vấn đề nghiêm trọng.

Ý tưởng là STL vv có std::không gian tên, các lib do "XYZ Corp" cung cấp sẽ có một xyz::không gian tên, bạn làm việc cho "ABC corp" sẽ đặt tất cả nội dung của bạn vào một abc::không gian tên duy nhất .


2
Bạn có khuyên rằng do những đường đi ngắn mà tôi đã nêu ra ở đây không?
Adam Naylor

Nếu nhiều người làm việc tại "ABC corp" cho nhiều dự án ở đó, và qua nhiều năm, các dự án hợp nhất hoặc bị chia tách, thì việc có ít nhất abc::PROJECTkhông gian tên phụ sẽ rất hợp lý. Và nếu các dự án lớn hơn có các phần rõ ràng, thì abc::PROJECT::SECTIONcũng có lý. Ngay cả các vị thần C ++ cũng hiểu rằng có các phần và tạo các không gian tên con như std::chronohoặc std::ios.
Kai Petzke

19

những gì tôi làm khi khai báo chuyển tiếp trông như thế này:

 namespace abc { namespace sub { namespace subsub { class MyClass; }}}

Các khai báo chuyển tiếp của tôi được thu gọn thành một dòng. Khả năng đọc của khai báo chuyển tiếp bị hy sinh để đổi lại khả năng đọc của phần còn lại của mã. Và đối với các định nghĩa, tôi cũng không sử dụng thụt lề:

 namespace abc {
 namespace sub {
 namespace subsub {
 
 class MyClass 
 {
    public:
       MyClass();

       void normalIntendationsHere() const;
 };

 }
 }
 }

Sử dụng phong cách này đòi hỏi một chút kỷ luật khi bắt đầu, nhưng đó là sự thỏa hiệp tốt nhất đối với tôi.


Kể từ C ++ 17, bạn có thể khai báo không gian tên với cú pháp do tác giả của câu hỏi đề xuất.

namespace A::B::C { ... }

định nghĩa không gian tên lồng nhau: namespace A::B::C { ... }tương đương với namespace A { namespace B { namespace C { ... } } }.

https://en.cppreference.com/w/cpp/language/namespace


7
Đây không phải là câu trả lời cho câu hỏi đã hỏi.
Paul Merrill,

2
@Jammer: Khi đó tôi mới sử dụng stackoverflow và không có ý tưởng :) Sry.
doc

8

Ít nhất là một trợ giúp nhỏ, trong một số trường hợp, bạn có thể làm điều này:

namespace foo = A::B::C::D;

Và sau đó tham chiếu A :: B :: C :: D như foo. Nhưng chỉ trong một số trường hợp.


2
Những trường hợp nào bạn không thể làm được điều đó?
Michael Dorst

Trường hợp trong câu hỏi là một. Tôi nghĩ?
Các cuộc đua ánh sáng trong quỹ đạo

6

Bạn có thể bỏ qua thụt lề. Tôi thường viết

namespace myLib { namespace details {

/* source code */

} } /* myLib::details */

Mã nguồn C ++ cuối cùng được biên dịch thành nhị phân, không giống như C # / Java vẫn ở trong hệ nhị phân. Do đó, không gian tên chỉ cung cấp một giải pháp tốt cho xung đột đặt tên biến. Nó không dành cho phân cấp lớp.

Tôi thường giữ một hoặc hai mức không gian tên trong mã.


4

Trước hết, bạn có thể tránh thụt đầu dòng không gian tên, bởi vì không có lý do gì cho điều đó.

Sử dụng không gian tên trong các ví dụ sẽ không hiển thị sức mạnh không gian tên. Và quyền lực của họ đối với tôi là phân chia các khu vực Miền với nhau. Phân chia các lớp tiện ích từ các lớp kinh doanh.

Chỉ cần không kết hợp các cấu trúc phân cấp không gian tên khác nhau trong một tệp .h. Không gian tên là một loại bình luận bổ sung cho giao diện khai báo hàm của bạn. Nhìn vào không gian tên và tên lớp sẽ giải thích rất nhiều thứ.

namespace product
{
namespace DAO
{

class Entity
{
};

3
"Trước hết, bạn có thể tránh thụt đầu dòng không gian tên, bởi vì không có lý do gì cho điều đó." đó chính xác là những gì tôi thực sự làm;)
Adam Naylor

Điểm tuyệt vời, và tôi nghĩ rằng sức mạnh của không gian tên còn được khuếch đại hơn nữa khi sử dụng các công cụ CASE để trực quan hóa các mối quan hệ và lớp cơ sở mã. Các công cụ như CppDepend hoặc Visual Studio's Dependency Graph lấy "tay nắm khả năng hiểu mã" do nhà phát triển xây dựng, được cấu trúc thông qua tất cả các tổ hợp không gian tên để thực sự chiếu sáng các lớp và phụ thuộc theo cách có cấu trúc chặt chẽ.
jxramos

0

Bạn đang sử dụng chúng quá mức (và bạn sẽ không nhận được gì).


15
Một cấu trúc phân cấp, có thứ tự. (lại là “bạn sẽ không nhận được gì”) Tôi nghĩ điều này rất lớn và được đánh giá thấp trong C ++. Chỉ cần nghĩ về cách điều này ảnh hưởng đến tài liệu.
Konrad Rudolph

1
@Konrad, tôi tin rằng đây là bản chất của câu hỏi của tôi.
Adam Naylor

Câu hỏi rất cũ, nhưng nó vẫn còn phù hợp với tôi ngày nay. Tôi có cùng quan điểm với @KonradRudolph, nhưng sau đó tôi phát hiện ra rằng các không gian tên lồng nhauvô giá trị . Cách tốt nhất là chỉ có một không gian tên thư viện rộng bằng phẳng ... Thật đáng buồn, nhưng tốt nhất là chúng ta nên có ngay bây giờ. Và điều này tiếp theo với cách cấu trúc std. Và nó được cấu trúc theo cách này là có lý do - hiện tại không có cách nào tốt hơn.
Sahsahae

@Sahsahae Chúng chắc chắn không vô giá trị, thậm chí không phải theo ý kiến ​​(!) Của một thành viên cấp cao của ủy ban tiêu chuẩn C ++. Nhưng ngay cả khi đó, nó chỉ có vậy: một ý kiến ​​(mà tôi không chia sẻ). Tôi chắc chắn không khuyên bạn nên lồng ghép một cách phù phiếm - nhưng việc sử dụng hợp lý các không gian tên lồng nhau được sử dụng rộng rãi và thành công trong các ngôn ngữ khác và không thể hiện những khiếm khuyết mà Titus gây ra.
Konrad Rudolph

@KonradRudolph Đây là vấn đề của bạn: các ngôn ngữ khác . Câu hỏi này hoàn toàn là về không gian tên C ++ và tôi chưa mở rộng phạm vi của nó. Thật thú vị, cũng giống như không gian tên C ++, tôi đã vô tình quản lý bằng cách nào đó để mở rộng nó thành "không gian tên trong tất cả các ngôn ngữ", vì langs::cppvẫn có thể nhìn thấy mọi thứ trong đó langs, không có sự cô lập thực sự cpp... Đây chính xác là vấn đề với không gian tên C ++ và tại sao cấu trúc phẳng được gợi ý. trong mô-đun Rust, chẳng hạn, bạn không thể truy cập langs::*từ cppvà tôi chưa bao giờ nói rằng điều này là xấu, nó tuyệt vời, nhưng trong C ++ thì không phải vậy.
Sahsahae

0

Tôi nhận thấy rằng bạn có thể bắt chước không gian tên c # như thế này;

namespace ABC_Maths{
    class POINT2{};
    class Complex{};
}

namespace ABC_Maths_Conversion{
    ABC_MATHS::Complex ComplexFromPOINT2(ABC_MATHS::POINT2)
    {return new ABC_MATHS::Complex();}

    ABC_MATHS::POINT4 POINT2FromComplex(ABC_MATHS::COMPLEX)
    {return new ABC_MATHS::POINT2();}
}

namespace ABC
{
}

Nhưng mã có vẻ không được gọn gàng cho lắm. và tôi mong đợi sẽ sử dụng lâu dài

Tốt hơn là lồng càng nhiều chức năng vào các lớp như

namespace ABC{
    class Maths{
        public:
        class POINT2{};
        class Complex:POINT2{};
        class Conversion{
            public:
            static Maths.Complex ComplexFromPOINT2(MATHS.POINT2 p)
            {return new MATHS.Complex();}

            static MATHS.POINT2 POINT2FromComplex(MATHS.COMPLEX p)
            {return new ABC::MATHS.POINT2();}// Can reference via the namespace if needed
} /*end ABC namespace*/

Và điều đó vẫn còn hơi dài. nhưng không cảm thấy một chút OO.

Và nghe cách nó có vẻ được thực hiện tốt nhất

namespace ABC
{
    class POINT2{};
    class Complex:POINT2{};
    Complex ComplexFromPOINT2(POINT2 p){return new Complex();}
    POINT2 POINT2FromComplex(Complex){return new POINT2();}
}

nghe cách sử dụng sẽ trông như thế nào

int main()
{
    ABC_Maths::Complex p = ABC_Maths_Conversion::ComplexFromPOINT2(new ABC_MATHS::POINT2());

    // or THE CLASS WAY

    ABC.Maths.Complex p = ABC.Maths.Conversion.ComplexFromPOINT2(new ABC.Maths.POINT2());

    // or if in/using the ABC namespace

    Maths.Complex p = Maths.Conversion.ComplexFromPOINT2(new Maths.POINT2());

    // and in the final case

    ABC::Complex p = ABC::ComplexFromPOINT2(new ABC::POINT2());
}

Thật thú vị khi tìm hiểu lý do tại sao tôi không bao giờ sử dụng không gian tên c ++ như tôi làm với c #. Nó sẽ quá dài và sẽ không bao giờ hoạt động giống như không gian tên c #.

cách sử dụng tốt nhất cho không gian tên trong c ++ là ngăn không cho hàm super cout của tôi (nó hoạt động mỗi khi nó được gọi) khỏi bị lẫn với hàm std :: cout (ít ấn tượng hơn).

Chỉ vì c # và c ++ có không gian tên, không có nghĩa là không gian tên có nghĩa giống nhau. chúng khác nhau nhưng giống nhau. Ý tưởng cho không gian tên c # hẳn đến từ không gian tên c ++. ai đó chắc hẳn đã thấy những gì một thứ tương tự nhưng khác biệt có thể làm, và không còn đủ trí tưởng tượng để đặt cho nó cái tên ban đầu như "ClassPath", điều này sẽ có ý nghĩa hơn khi nó là một đường dẫn đến các lớp thay vì cung cấp đặt tên cho không gian trong đó mỗi không gian có thể có các tên giống nhau

tôi hi vọng điêu nay se giup được ai đo

Tôi quên nói rằng tất cả những cách này đều hợp lệ và việc sử dụng vừa phải hai cách đầu tiên có thể được kết hợp với cách thứ ba để tạo ra một thư viện có ý nghĩa (không phải ví dụ tuyệt vời)

_INT::POINT2{}

_DOUBLE::POINT2{}

vì vậy bạn có thể thay đổi mức độ chính xác bằng cách sử dụng

#define PREC _DOUBLE 
// or #define PREC _INT 

và sau đó tạo một phiên bản của PREC :: POINT2 cho POINT2 chính xác gấp đôi

Đó không phải là điều dễ dàng thực hiện với không gian tên c # hoặc java

rõ ràng đó chỉ là một ví dụ. Hãy suy nghĩ về cách sử dụng đọc.


-1

Đôi khi tôi khai báo không gian tên sâu dưới dạng một vài macro trong tiêu đề riêng biệt

không gian tên.h

#define NAMESPACE_TIER1_TIER2_TIER3 \
    namespace tier1 { \
    namespace tier2 { \
    namespace tier3 {

#define END_NAMESPACE_TIER1_TIER2_TIER3 }}}

Để sử dụng nó ở một nơi khác:

anotherfile.h

#include "./namespace.h"

NAMESPACE_TIER1_TIER2_TIER3;

/* Code here */

END_NAMESPACE_TIER1_TIER2_TIER3;

Dấu chấm phẩy thừa sau macro là để tránh thụt lề thừa.


2
điều đó thực sự lộn xộn và bạn phải xác định nó cho mọi không gian tên lồng nhau. Thêm vào đó, nếu bạn định viết ra NAMESPACE_TIER1_TIER2_TIER3END_NAMESPACE_TEIR1_TIER2_TIER3, tại sao không chỉ viết namespace tier1{namespace tier2{namespace tier3{}}}? Nhìn chung, nó thực sự ngắn hơn.
Michael Dorst

Tôi không đồng ý, nó không ngắn hơn, và nó tạo ra vấn đề thụt lề rất sâu. Tôi đã thử cả hai và tôi thích phương pháp #define hơn. Chỉnh sửa: ok, trừ vấn đề thụt đầu dòng ...
icecream

1
Thích những gì bạn thích, nhưng tôi sẽ không giới thiệu điều đó cho những người dùng khác. Nó không ngắn hơn (như bạn có thể thấy từ nhận xét của tôi nơi cả hai được xếp thẳng hàng) và đó là không tính định nghĩa, và chắc chắn khó theo dõi lỗi hơn theo cách đó. Nếu bạn mắc lỗi đánh máy thì sao? Trình biên dịch sẽ không biết điều gì sai và lỗi của bạn sẽ hiển thị ở vị trí hoàn toàn sai.
Michael Dorst

3
Đối số "ngắn hơn" không thực sự phù hợp vì nó phụ thuộc vào cách các macro được xác định. Và về lập luận "lỗi đánh máy". Viết sai tên không gian tên và bạn sẽ nhận được các thông báo khó hiểu như với giải pháp macro, nhưng với giải pháp macro, nhiều IDE hiện đại có thể giải quyết rằng tên chưa được xác định và giúp bạn xác định lỗi. Tên không gian tên chỉ là văn bản tự do và bạn có thể viết bất cứ thứ gì bạn muốn ở đó, vì vậy thực sự khó tránh các vấn đề lỗi đánh máy hơn khi không sử dụng macro. Và việc “khuyến nghị” giải pháp vĩ mô có hại gì, người dân tự quyết định.
icecream,
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.