Tại sao các không gian tên không tên được sử dụng và lợi ích của chúng là gì?


242

Tôi vừa tham gia một dự án phần mềm C ++ mới và tôi đang cố gắng hiểu thiết kế. Dự án sử dụng thường xuyên các không gian tên không tên. Ví dụ, một cái gì đó như thế này có thể xảy ra trong một tệp định nghĩa lớp:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

Các cân nhắc thiết kế có thể khiến người ta sử dụng một không gian tên không tên là gì? Những lợi thế và bất lợi là gì?

Câu trả lời:


189

Các không gian tên không tên là một tiện ích để tạo một đơn vị dịch thuật định danh cục bộ. Chúng hoạt động như thể bạn sẽ chọn một tên duy nhất cho mỗi đơn vị dịch cho một không gian tên:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

Bước bổ sung sử dụng phần thân trống rất quan trọng, vì vậy bạn có thể đã tham chiếu trong phần thân không gian tên đến các mã định danh giống như ::nameđược xác định trong không gian tên đó, vì lệnh sử dụng đã diễn ra.

Điều này có nghĩa là bạn có thể có các hàm miễn phí được gọi (ví dụ) helpcó thể tồn tại trong nhiều đơn vị dịch thuật và chúng sẽ không xung đột tại thời điểm liên kết. Hiệu ứng này gần giống với việc sử dụng statictừ khóa được sử dụng trong C mà bạn có thể đưa vào khai báo định danh. Các không gian tên không tên là một sự thay thế vượt trội, thậm chí có thể tạo ra một đơn vị dịch thuật kiểu địa phương.

namespace { int a1; }
static int a2;

Cả hai đều alà đơn vị dịch thuật địa phương và sẽ không đụng độ tại thời điểm liên kết. Nhưng sự khác biệt là a1trong không gian tên ẩn danh có một tên duy nhất.

Đọc bài viết xuất sắc tại máy tính comeau Tại sao một không gian tên không tên được sử dụng thay vì tĩnh? ( Gương lưu trữ.org ).


Bạn giải thích mối quan hệ với static. Bạn có thể vui lòng so sánh với __attribute__ ((visibility ("hidden")))?
phinz

74

Có một cái gì đó trong một không gian tên ẩn danh có nghĩa là địa phương của đơn vị dịch thuật này (tệp .cpp và tất cả bao gồm) điều này có nghĩa là nếu một biểu tượng khác có cùng tên được xác định ở nơi khác thì sẽ không vi phạm Quy tắc Một Định nghĩa (ODR).

Điều này giống như cách C có một biến toàn cục tĩnh hoặc hàm tĩnh nhưng nó cũng có thể được sử dụng cho các định nghĩa lớp (và nên được sử dụng thay vì statictrong C ++).

Tất cả các không gian tên ẩn danh trong cùng một tệp được coi là cùng một không gian tên và tất cả các không gian tên ẩn danh trong các tệp khác nhau là khác biệt. Một không gian tên ẩn danh tương đương với:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

Không gian tên không tên giới hạn quyền truy cập của lớp, biến, hàm và đối tượng vào tệp mà nó được định nghĩa. Chức năng không gian tên chưa được đặt tên tương tự như statictừ khóa trong C / C ++.
statictừ khóa giới hạn quyền truy cập của biến toàn cục và chức năng vào tệp mà chúng được xác định.
Có sự khác biệt giữa không gian tên và statictừ khóa không tên vì không gian tên không tên có lợi thế hơn tĩnh. statictừ khóa có thể được sử dụng với biến, hàm và đối tượng nhưng không phải với lớp do người dùng định nghĩa.
Ví dụ:

static int x;  // Correct 

Nhưng,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

Nhưng điều tương tự có thể xảy ra với không gian tên không tên. Ví dụ,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct

13

Ngoài các câu trả lời khác cho câu hỏi này, sử dụng một không gian tên ẩn danh cũng có thể cải thiện hiệu suất. Vì các ký hiệu trong không gian tên không cần bất kỳ liên kết bên ngoài nào, trình biên dịch sẽ tự do hơn để thực hiện tối ưu hóa mã mạnh mẽ trong không gian tên. Ví dụ, một hàm được gọi nhiều lần trong một vòng lặp có thể được nội tuyến mà không có bất kỳ tác động nào đến kích thước mã.

Ví dụ, trên hệ thống của tôi, đoạn mã sau chiếm khoảng 70% thời gian chạy nếu không gian tên ẩn danh được sử dụng (x86-64 gcc-4.6.3 và -O2; lưu ý rằng mã bổ sung trong add_val khiến trình biên dịch không muốn bao gồm nó hai lần).

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}

5
Quá tốt là đúng - Tôi đã thử phân đoạn này trên gcc 4-1-2, sử dụng tối ưu hóa O3, có và không có câu lệnh không gian tên: -> Có cùng thời gian (3 giây, với -O3 và 4sec với -O3)
Theo

2
Mã này đã cố tình phức tạp để cố gắng thuyết phục trình biên dịch không nội tuyến b và add_val thành chính. Tối ưu hóa O3 sử dụng rất nhiều nội tuyến bất kể chi phí để mã hóa phình to. Tuy nhiên, vẫn có các hàm có khả năng trong đó O3 sẽ không bổ sung nội tuyến. Bạn có thể thử làm cho add_val phức tạp hơn hoặc gọi nó nhiều lần từ chính trong các trường hợp khác nhau.
xioxox 4/11/2015

5
@Daniel: tôi đang thiếu gì? như đã đọc, bạn nói bạn so -O3với chính nó, sau đó bạn nói 3 vs 4 giây là "cùng một lúc". không ai trong số này làm cho một chút ý nghĩa. Tôi nghi ngờ lời giải thích thực sự , nhưng nó là gì?
gạch dưới

@underscore_d Các trạng thái câu trả lời -O2 được sử dụng trong cả hai trường hợp, không phải -O3. Mức độ tối ưu hóa khác nhau có thể hành xử khác nhau. Ngoài ra, các phiên bản trình biên dịch khác nhau có thể hoạt động khác nhau (câu trả lời có thể bị lỗi thời, đó là)
Paul Stelian

1
@PaulStelian Tôi biết điều đó, nhưng có vẻ khá rõ ràng rằng tôi đã trả lời không phải câu trả lời của xioxox mà là nhận xét của Theo (mặc dù tên của anh ấy đã thay đổi hoặc bằng cách nào đó tôi đã nhầm lẫn)
underscore_d

12

Ví dụ cho thấy những người trong dự án bạn tham gia không hiểu không gian tên ẩn danh :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Chúng không cần phải ở trong một không gian tên ẩn danh, vì constđối tượng đã có liên kết tĩnh và do đó không thể xung đột với các định danh cùng tên trong một đơn vị dịch thuật khác.

    bool getState(userType*,otherUserType*);
}

Và đây thực sự là một sự bi quan: getState()có liên kết bên ngoài. Thông thường tốt hơn là thích liên kết tĩnh, vì điều đó không gây ô nhiễm bảng biểu tượng. Nó là tốt hơn để viết

static bool getState(/*...*/);

đây. Tôi rơi vào cùng một cái bẫy (có từ ngữ trong tiêu chuẩn cho thấy rằng các thống kê tệp bị phản đối theo cách nào đó có lợi cho các không gian tên ẩn danh), nhưng làm việc trong một dự án C ++ lớn như KDE, bạn có rất nhiều người quay đầu đúng cách xung quanh một lần nữa :)


10
Vì các không gian tên không tên c ++ 11 có liên kết bên trong (phần 3.5 trong tiêu chuẩn hoặc en.cppreference.com/w/cpp/lingu/namespace#Unnamed_namespaces )
Emile Vrijdags

11
"Những điều này không cần phải được trong một không gian tên vô danh" Về mặt kỹ thuật, chắc chắn - nhưng vẫn còn, nó không làm tổn thương để đặt chúng trong một, như một lời nhắc nhở trực quan của các ngữ nghĩa của họ và để làm cho nó (thậm chí hơn) tầm thường để loại bỏ constNess sau này nếu muốn. Tôi nghi ngờ điều đó có nghĩa là nhóm của OP "không hiểu" bất cứ điều gì! Ngoài ra, bit về các hàm trong không gian tên ẩn danh có liên kết ngoài là sai trong C ++ 11 trở đi như đã lưu ý. Theo hiểu biết của tôi, họ đã khắc phục sự cố đối số mẫu trước đây cần liên kết ngoài, do đó có thể cho phép các không gian tên không tên (có thể chứa đối số mẫu) có liên kết nội bộ.
gạch dưới

11

Một không gian tên ẩn danh làm cho các biến, hàm, lớp, v.v. chỉ có sẵn trong tệp đó. Trong ví dụ của bạn, đó là một cách để tránh các biến toàn cục. Không có thời gian chạy hoặc biên dịch hiệu suất thời gian khác nhau.

Không có quá nhiều lợi thế hay bất lợi ngoài "tôi muốn biến này, hàm, lớp, v.v. là công khai hay riêng tư?"


2
Có thể có sự khác biệt về hiệu suất - xem câu trả lời của tôi ở đây. Nó cho phép trình biên dịch tối ưu hóa mã tốt hơn.
xioxox

2
Bạn có một điểm; ít nhất là cho đến ngày nay C ++. Tuy nhiên, C ++ 98 / C ++ 03 những thứ bắt buộc phải có liên kết bên ngoài để được sử dụng làm đối số mẫu. Vì những thứ trong không gian tên ẩn danh có sẵn dưới dạng đối số mẫu, nên chúng sẽ có liên kết ngoài (ít nhất là trong tiền C ++ 11) ngay cả khi không có cách nào để tham chiếu chúng từ bên ngoài tệp. Tôi nghĩ rằng có thể đã có một số khả năng làm mờ đi điều đó, bởi vì tiêu chuẩn chỉ yêu cầu mọi thứ hành động như thể các quy tắc được thi hành; và đôi khi có thể làm điều đó mà không thực sự thực thi các quy tắc.
Max Lybbert
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.