Làm thế nào để bạn sử dụng đúng không gian tên trong C ++?


231

Tôi đến từ một nền Java, nơi các gói được sử dụng, không phải là không gian tên. Tôi đã quen với việc đặt các lớp làm việc cùng nhau để tạo thành một đối tượng hoàn chỉnh thành các gói và sau đó sử dụng lại chúng sau gói đó. Nhưng bây giờ tôi đang làm việc trong C ++.

Làm thế nào để bạn sử dụng không gian tên trong C ++? Bạn có tạo một không gian tên duy nhất cho toàn bộ ứng dụng hay bạn tạo không gian tên cho các thành phần chính? Nếu vậy, làm thế nào để bạn tạo các đối tượng từ các lớp trong các không gian tên khác?

Câu trả lời:


167

Không gian tên là gói cơ bản. Chúng có thể được sử dụng như thế này:

namespace MyNamespace
{
  class MyClass
  {
  };
}

Sau đó, trong mã:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

Hoặc, nếu bạn muốn luôn sử dụng một không gian tên cụ thể, bạn có thể làm điều này:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

Chỉnh sửa: Theo những gì bernhardrusch đã nói, tôi có xu hướng không sử dụng cú pháp "sử dụng không gian tên x", tôi thường chỉ định rõ ràng không gian tên khi khởi tạo các đối tượng của mình (ví dụ đầu tiên tôi đã hiển thị).

Và như bạn đã hỏi bên dưới , bạn có thể sử dụng bao nhiêu không gian tên tùy thích.


25
IMO tốt hơn là chỉ nên làm quen với tiền tố stdkhông gian tên cho các biểu tượng thay vì sử dụng usingtất cả. Vì vậy, tôi luôn luôn viết std::couthoặc std::stringbây giờ bởi vì đó là những gì tôi gọi chúng bây giờ. Tôi sẽ không bao giờ chỉ viết cout.
Tom Savage

5
Mặc dù điều này rất đúng std, nhưng cá nhân tôi thấy điều này ít quan trọng hơn khi bạn làm việc với các thư viện nhỏ hơn. Thường thì bạn chỉ có thể sử dụng using namespace FooBario;, đặc biệt nếu bạn đang sử dụng một số lượng đáng kể các loại từ thư viện.
jkerian

4
@jkerian, tôi thấy quan điểm của bạn, nhưng tôi không đồng ý vì các xung đột tên là (trong tâm trí của tôi) nhiều khả năng đến từ các thư viện nhỏ như vậy. Hầu hết mọi người cẩn thận không đặt tên lớp / hàm giống như trong STL. Điều đó nói rằng, tôi đồng ý rằng using namespace X;nên tránh trong các tệp tiêu đề nếu có thể.
Alan Turing

12
@LexFridman "Hầu hết mọi người cẩn thận không đặt tên các lớp / hàm giống như trong STL" - đó là KHÔNG ĐÚNG. Ví dụ, nếu tôi viết một số mã I / O rất chuyên biệt cho một số phần cứng kỳ lạ, tôi sẽ không bao giờ sử dụng bất cứ thứ gì khác ngoài mylibrary::endlviệc thể hiện chuỗi dòng mới đặc biệt của riêng tôi. Ý tôi là, tại sao lại phát minh ra tên?

Trình biên dịch của tôi vẫn không nhận ra không gian tên, mặc dù tôi muốn chỉ định rõ ràng và tôi bao gồm tệp được khai báo.
bgenchel

116

Để tránh nói tất cả mọi thứ, Mark Ingram đã nói một mẹo nhỏ khi sử dụng không gian tên:

Tránh chỉ thị "sử dụng không gian tên" trong các tệp tiêu đề - điều này sẽ mở ra không gian tên cho tất cả các phần của chương trình nhập tệp tiêu đề này. Trong các tệp thực hiện (* .cpp), điều này thường không có vấn đề gì lớn - tôi thích sử dụng chỉ thị "sử dụng không gian tên" ở cấp độ chức năng.

Tôi nghĩ rằng không gian tên chủ yếu được sử dụng để tránh xung đột đặt tên - không nhất thiết phải tổ chức cấu trúc mã của bạn. Tôi muốn tổ chức các chương trình C ++ chủ yếu với các tệp tiêu đề / cấu trúc tệp.

Đôi khi các không gian tên được sử dụng trong các dự án C ++ lớn hơn để ẩn chi tiết triển khai.

Lưu ý bổ sung cho chỉ thị sử dụng: Một số người thích sử dụng "sử dụng" chỉ cho các yếu tố duy nhất:

using std::cout;  
using std::endl;

2
Một lợi thế của "sử dụng không gian tên" ở cấp độ chức năng như bạn đề xuất thay vì ở cấp tệp .cpp hoặc cấp khối {} trong phạm vi .cpp là nó giúp ích rất nhiều cho các bản dựng đơn vị biên dịch đơn. "Sử dụng không gian tên" mang tính bắc cầu và áp dụng cho không gian tên A trên các không gian tên riêng biệt A {} trong cùng một đơn vị, do đó, đối với đơn vị biên dịch đơn, bạn sẽ nhanh chóng kết thúc việc sử dụng mọi thứ nếu chúng được thực hiện ở cấp độ khối tệp hoặc không gian tên.
idij

using std::cout; là một tuyên bố sử dụng
Konstantin

3
Là nó có thể sử dụng nhiều tên từ một đơn namespace trong một tuyên bố đơn? Một cái gì đó như using std::cout, std::endl;hoặc thậm chí , using std::cout, endl;.
AlQuemist

Có thể sử dụng using namespace xtiêu đề trong tiêu đề nếu nó nằm trong không gian tên khác. Nói chung, đây không phải là thứ tôi muốn giới thiệu nhưng nó không gây ô nhiễm không gian tên toàn cầu.
Praxeolitic

79

Vincent Robert đã đúng trong nhận xét của mình Làm thế nào để bạn sử dụng đúng không gian tên trong C ++? .

Sử dụng không gian tên

Không gian tên được sử dụng ít nhất để giúp tránh va chạm tên. Trong Java, điều này được thi hành thông qua thành ngữ "org.domain" (vì người ta cho rằng người ta sẽ không sử dụng bất cứ thứ gì khác ngoài tên miền của chính mình).

Trong C ++, bạn có thể cung cấp một không gian tên cho tất cả mã trong mô-đun của mình. Ví dụ, đối với mô-đun MyModule.dll, bạn có thể cung cấp cho mã của nó không gian tên MyModule. Tôi đã thấy người khác sử dụng MyCompany :: MyProject :: MyModule. Tôi đoán điều này là quá mức cần thiết, nhưng tất cả trong tất cả, nó có vẻ đúng với tôi.

Sử dụng "sử dụng"

Việc sử dụng nên được sử dụng hết sức cẩn thận vì nó nhập hiệu quả một (hoặc tất cả) các ký hiệu từ một không gian tên vào không gian tên hiện tại của bạn.

Điều này là xấu khi thực hiện nó trong tệp tiêu đề vì tiêu đề của bạn sẽ gây ô nhiễm mọi nguồn bao gồm cả nó (nó làm tôi nhớ về macro ...) và ngay cả trong tệp nguồn, kiểu xấu bên ngoài phạm vi chức năng vì nó sẽ nhập ở phạm vi toàn cầu các ký hiệu từ không gian tên.

Cách an toàn nhất để sử dụng "sử dụng" là nhập các ký hiệu chọn:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

Bạn sẽ thấy rất nhiều "sử dụng không gian tên std;" trong mã hướng dẫn hoặc ví dụ. Lý do là để giảm số lượng các biểu tượng để làm cho việc đọc dễ dàng hơn, không phải vì đó là một ý tưởng tốt.

"sử dụng không gian tên std;" Scott Meyers không khuyến khích (tôi không nhớ chính xác cuốn sách nào, nhưng tôi có thể tìm thấy nó nếu cần thiết).

Thành phần không gian tên

Không gian tên là nhiều hơn gói. Một ví dụ khác có thể được tìm thấy trong "Ngôn ngữ lập trình C ++" của Bjarne Stroustrup.

Trong "Phiên bản đặc biệt", tại 8.2.8 Thành phần không gian tên , anh mô tả cách bạn có thể hợp nhất hai không gian tên AAA và BBB vào một tên gọi khác là CCC. Do đó, CCC trở thành bí danh cho cả AAA và BBB:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

Bạn thậm chí có thể nhập các biểu tượng được chọn từ các không gian tên khác nhau, để xây dựng giao diện không gian tên tùy chỉnh của riêng bạn. Tôi vẫn chưa tìm thấy một ứng dụng thực tế của việc này, nhưng trên lý thuyết, nó thật tuyệt.


Bạn có thể làm rõ, xin vui lòng "cung cấp một không gian tên cho tất cả các mã trong mô-đun của bạn"? Thực hành tốt để đóng gói cho mô-đun là gì. Ví dụ tôi có lớp số phức và các hàm ngoài liên quan đến số phức. Lớp này và hai hàm đó nên ở trong một không gian tên?
yanpas

74

Tôi không thấy bất kỳ đề cập nào về nó trong các câu trả lời khác, vì vậy đây là 2 xu Canada của tôi:

Trong chủ đề "sử dụng không gian tên", một câu lệnh hữu ích là bí danh không gian tên, cho phép bạn "đổi tên" một không gian tên, thông thường để đặt cho nó một tên ngắn hơn. Ví dụ: thay vì:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

bạn có thể viết:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;

55

Đừng nghe mọi người nói với bạn rằng không gian tên chỉ là không gian tên.

Chúng rất quan trọng vì chúng được trình biên dịch xem xét để áp dụng nguyên tắc giao diện. Về cơ bản, nó có thể được giải thích bằng một ví dụ:

namespace ns {

class A
{
};

void print(A a)
{
}

}

Nếu bạn muốn in một đối tượng A, mã sẽ là đối tượng này:

ns::A a;
print(a);

Lưu ý rằng chúng tôi đã không đề cập rõ ràng đến không gian tên khi gọi hàm. Đây là nguyên tắc giao diện: C ++ coi một hàm lấy một loại làm đối số là một phần của giao diện cho loại đó, do đó không cần chỉ định không gian tên vì tham số đã ngụ ý không gian tên.

Bây giờ tại sao nguyên tắc này là quan trọng? Hãy tưởng tượng rằng tác giả lớp A không cung cấp hàm print () cho lớp này. Bạn sẽ phải tự cung cấp một cái. Vì bạn là một lập trình viên giỏi, bạn sẽ xác định hàm này trong không gian tên của riêng bạn hoặc có thể trong không gian tên toàn cục.

namespace ns {

class A
{
};

}

void print(A a)
{
}

Và mã của bạn có thể bắt đầu gọi hàm print (a) bất cứ nơi nào bạn muốn. Bây giờ hãy tưởng tượng rằng những năm sau đó, tác giả quyết định cung cấp hàm print (), tốt hơn của bạn bởi vì anh ta biết nội bộ của lớp mình và có thể tạo ra một phiên bản tốt hơn của bạn.

Sau đó, các tác giả C ++ đã quyết định rằng phiên bản hàm print () của anh ta nên được sử dụng thay vì phiên bản được cung cấp trong không gian tên khác, để tôn trọng nguyên tắc giao diện. Và việc "nâng cấp" chức năng print () này phải dễ dàng nhất có thể, điều đó có nghĩa là bạn sẽ không phải thay đổi mọi cuộc gọi thành chức năng print (). Đó là lý do tại sao "các hàm giao diện" (hàm trong cùng một không gian tên với một lớp) có thể được gọi mà không chỉ định không gian tên trong C ++.

Và đó là lý do tại sao bạn nên coi một không gian tên C ++ như một "giao diện" khi bạn sử dụng nó và ghi nhớ nguyên tắc giao diện.

Nếu bạn muốn giải thích rõ hơn về hành vi này, bạn có thể tham khảo cuốn sách C ++ đặc biệt từ Herb Sutter


23
Bạn thực sự phải thay đổi mọi cuộc gọi thành print () nếu ns :: Print được thêm vào, nhưng trình biên dịch sẽ gắn cờ mỗi cuộc gọi là mơ hồ. Âm thầm chuyển sang chức năng mới sẽ là một ý tưởng khủng khiếp.
Nhật thực

Bây giờ tôi đang tự hỏi, có những gì @Vincent đã nói rằng bạn sẽ phải thay đổi tất cả các cuộc gọi để in, nếu autor sẽ cung cấp hàm ns :: Print (), bạn đang cố nói gì? Rằng khi tác giả thêm hàm ns :: Print (), bạn có thể loại bỏ việc thực hiện của riêng mình không? Hoặc bạn sẽ chỉ cần thêm bằng cách sử dụng khai báo ns :: print ()? Hay cái gì khác? Cảm ơn
Vaska el gato

36

Các dự án C ++ lớn hơn mà tôi thấy hầu như không sử dụng nhiều hơn một không gian tên (ví dụ: thư viện boost).

Trên thực tế boost sử dụng vô số không gian tên, thông thường mỗi phần của boost đều có không gian tên riêng cho hoạt động bên trong và sau đó chỉ có thể đặt giao diện công cộng trong phần tăng không gian tên cấp cao nhất.

Cá nhân tôi nghĩ rằng một cơ sở mã càng lớn thì các không gian tên càng trở nên quan trọng, ngay cả trong một ứng dụng (hoặc thư viện). Trong công việc, chúng tôi đặt từng mô-đun ứng dụng của chúng tôi vào không gian tên riêng của nó.

Một cách sử dụng khác (không có ý định chơi chữ) của các không gian tên mà tôi sử dụng nhiều là không gian tên ẩn danh:

namespace {
  const int CONSTANT = 42;
}

Điều này về cơ bản giống như:

static const int CONSTANT = 42;

Tuy nhiên, sử dụng một không gian tên ẩn danh (thay vì tĩnh) là cách được khuyến nghị để mã và dữ liệu chỉ hiển thị trong đơn vị biên dịch hiện tại trong C ++.


13
Cả hai ví dụ của bạn đều tương đương với const int CONSTANT = 42;vì const cấp cao nhất trong phạm vi không gian tên đã ngụ ý liên kết nội bộ. Vì vậy, bạn không cần không gian tên ẩn danh trong trường hợp này.
sellibitze

19

Ngoài ra, lưu ý rằng bạn có thể thêm vào một không gian tên. Điều này rõ ràng hơn với một ví dụ, ý tôi là bạn có thể có:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

trong một tập tin square.h, và

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

trong một tập tin cube.h. Điều này xác định một không gian tên duy nhất MyNamespace(nghĩa là bạn có thể xác định một không gian tên duy nhất trên nhiều tệp).


11

Trong Java:

package somepackage;
class SomeClass {}

Trong C ++:

namespace somenamespace {
    class SomeClass {}
}

Và sử dụng chúng, Java:

import somepackage;

Và C ++:

using namespace somenamespace;

Ngoài ra, tên đầy đủ là "somepackge.SomeClass" cho Java và "somenamespace :: someClass" cho C ++. Sử dụng các quy ước đó, bạn có thể tổ chức giống như bạn đã quen với Java, bao gồm tạo các tên thư mục phù hợp cho các không gian tên. Mặc dù vậy, thư mục-> gói và tệp-> yêu cầu lớp không có, vì vậy bạn có thể đặt tên độc lập cho các thư mục và lớp của mình khỏi các gói và không gian tên.


6

@ Bảo Bình

Có, bạn có thể sử dụng một số không gian tên cùng một lúc, ví dụ:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[Tháng Hai 2014 - (Đã thực sự lâu chưa?): Ví dụ cụ thể này hiện không rõ ràng, như Joey chỉ ra dưới đây. Boost và std :: bây giờ mỗi cái đều có shared_ptr.]


2
Lưu ý rằng hiện tại stdcũng có shared_ptr, vì vậy sử dụng cả hai booststdkhông gian tên sẽ xung đột khi bạn cố gắng sử dụng a shared_ptr.
Joey

2
Đây là một ví dụ tốt về lý do tại sao nhiều nhà phần mềm sẽ không khuyến khích nhập toàn bộ không gian tên theo cách này. Sẽ không hại khi luôn chỉ định không gian tên và nếu chúng quá dài thì hãy tạo bí danh hoặc chỉ các lớp cụ thể quan trọng từ không gian tên.
thóc

5

Bạn cũng có thể chứa "sử dụng không gian tên ..." trong một hàm chẳng hạn:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

3

Nói chung, tôi tạo một không gian tên cho một bộ mã nếu tôi tin rằng có thể có thể có chức năng hoặc loại xung đột tên với các thư viện khác. Nó cũng giúp mã thương hiệu, ala boost :: .


3

Tôi thích sử dụng một không gian tên cấp cao nhất cho ứng dụng và không gian tên phụ cho các thành phần.

Cách bạn có thể sử dụng các lớp từ các không gian tên khác rất giống với cách trong java. Bạn có thể sử dụng "sử dụng NnamPACE" tương tự như câu lệnh "nhập GÓI", ví dụ: sử dụng tiêu chuẩn. Hoặc bạn chỉ định gói là tiền tố của lớp được phân tách bằng "::", ví dụ std :: string. Điều này tương tự như "java.lang.String" trong Java.


3

Lưu ý rằng một không gian tên trong C ++ thực sự chỉ là một không gian tên. Họ không cung cấp bất kỳ sự đóng gói nào mà các gói thực hiện trong Java, vì vậy bạn có thể sẽ không sử dụng chúng nhiều như vậy.


2

Tôi đã sử dụng các không gian tên C ++ giống như cách tôi làm trong C #, Perl, v.v. Nó chỉ là một sự phân tách ngữ nghĩa giữa các thư viện tiêu chuẩn, nội dung của bên thứ ba và mã của riêng tôi. Tôi sẽ đặt ứng dụng của riêng mình trong một không gian tên, sau đó là thành phần thư viện có thể sử dụng lại trong không gian tên khác để phân tách.


2

Một điểm khác biệt giữa java và C ++ là trong C ++, hệ thống phân cấp không gian tên không cần phải bố trí hệ thống tập tin. Vì vậy, tôi có xu hướng đặt toàn bộ thư viện có thể sử dụng lại trong một không gian tên duy nhất và các hệ thống con trong thư viện trong các thư mục con:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

Tôi sẽ chỉ đặt các hệ thống con trong các không gian tên lồng nhau nếu có khả năng xảy ra xung đột tên.

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.