Cách đúng để xác định các phương thức không gian tên C ++ trong tệp .cpp


108

Có thể là một bản sao, nhưng không dễ tìm kiếm ...

Đưa ra một tiêu đề như:

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

Tôi đã thấy method() được định nghĩa theo một số cách trong tệp .cpp:

Phiên bản 1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

Phiên bản 2:

using namespace ns1;

void MyClass::method()
{
 ...
}

Phiên bản 3:

void ns1::MyClass::method()
{
 ...
}

Có cách nào 'đúng' để làm điều đó không? Có cái nào 'sai' ở chỗ chúng không có nghĩa giống nhau không?


Trong hầu hết các mã tôi thường thấy phiên bản thứ ba (nhưng tôi không hiểu tại sao: D), phiên bản thứ hai chỉ đơn giản là đối lập với lý do tại sao không gian tên được giới thiệu, tôi đoán.
Mr.Anubis

1
Thực tế là thú vị, các công cụ tái cấu trúc Visual Assist rất sẵn lòng tạo mã bằng cách sử dụng # 1 hoặc # 3, tùy thuộc vào kiểu nào đã được sử dụng trong tệp đích.
Mr. Boy,

Câu trả lời:


51

Phiên bản 2 không rõ ràng và không dễ hiểu vì bạn không biết vùng tên nào MyClass thuộc về và nó chỉ phi logic (hàm lớp không nằm trong cùng vùng tên?)

Phiên bản 1 đúng vì nó cho thấy rằng trong không gian tên, bạn đang xác định hàm.

Phiên bản 3 cũng đúng vì bạn đã sử dụng ::toán tử phân giải phạm vi để tham chiếu đến MyClass::method ()trong không gian tênns1 . Tôi thích phiên bản 3 hơn.

Xem Không gian tên (C ++) . Đây là cách tốt nhất để làm điều này.


22
Gọi # 2 là "sai" là một sự phóng đại quá lớn. Theo logic này, tất cả các tên biểu tượng đều "sai" vì chúng có thể ẩn các tên biểu tượng khác trong các phạm vi khác.
10/4, 30/12/11

Nó phi logic. Nó sẽ biên dịch tốt (xin lỗi, đã giải thích điều đó trong câu trả lời), nhưng tại sao bạn lại định nghĩa một hàm bên ngoài không gian tên của nó? Nó làm người đọc bối rối. Ngoài ra, khi nhiều không gian tên được sử dụng, nó không hiển thị MyClass không gian tên nào thuộc về. Phiên bản 1 & 3 khắc phục sự cố này. Kết luận, nó không sai, nhưng chỉ là không rõ ràng và khó hiểu.
GILGAMESH

3
Tôi đồng ý với @PhoenicaMacia, thủ thuật sử dụng thật khủng khiếp và có thể dẫn đến nhầm lẫn. Hãy xem xét một lớp triển khai toán tử như một hàm miễn phí, trong tiêu đề bạn sẽ có namespace N {struct X { void f(); }; X operator==( X const &, X const & ); }, bây giờ trong tệp cpp với câu lệnh using, bạn có thể định nghĩa hàm thành viên void X::f() {}, nhưng nếu bạn định nghĩa, X operator==(X const&, X const&)bạn sẽ xác định một toán tử khác với một được xác định trong tiêu đề (bạn sẽ phải sử dụng 1 hoặc 3 cho hàm miễn phí ở đó).
David Rodríguez - dribeas

1
Đặc biệt, tôi thích 1 hơn, và ví dụ trong bài viết được liên kết không thực sự giải quyết bất cứ điều gì, ví dụ đầu tiên sử dụng 1, ví dụ thứ hai sử dụng kết hợp 1 và 3 (các hàm được xác định với đủ điều kiện, nhưng chúng được xác định bên trong không gian tên bên ngoài)
David Rodríguez - dribeas

1
Trong số 3, tôi sẽ nói 1) là tốt nhất tuy nhiên hầu hết các IDE có thói quen khá khó chịu là thụt lề mọi thứ bên trong khai báo không gian tên đó và nó gây ra một số nhầm lẫn.
locka

28

5 năm sau và tôi nghĩ tôi sẽ đề cập đến điều này, cả hai đều trông đẹp và không xấu xa

using ns1::MyClass;

void MyClass::method()
{
  // ...
}

3
Đây là câu trả lời tốt nhất. Nó trông sạch sẽ nhất và tránh các vấn đề với Phiên bản 1 của OP, có thể đưa mọi thứ vào không gian tên một cách vô ý và 2, có thể đưa mọi thứ vào không gian toàn cầu một cách vô ý.
ayane_m

Vâng, đây là sự kết hợp tuyệt vời giữa việc nhập ít hơn 3, trong khi vẫn khai báo rõ ràng ý định.
jb

14

Tôi đang sử dụng phiên bản 4 (bên dưới) vì nó kết hợp hầu hết các ưu điểm của phiên bản 1 (tính ngắn gọn của định nghĩa cộng hưởng) và phiên bản 3 (rõ ràng tối đa). Bất lợi chính là mọi người không quen với nó nhưng vì tôi coi nó về mặt kỹ thuật vượt trội so với các lựa chọn thay thế nên tôi không bận tâm.

Phiên bản 4: sử dụng đủ điều kiện bằng cách sử dụng bí danh không gian tên:

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

Trong thế giới của tôi, tôi thường xuyên sử dụng bí danh không gian tên vì mọi thứ đều đủ điều kiện rõ ràng - trừ khi nó không thể (ví dụ: tên biến) hoặc nó là một điểm tùy chỉnh đã biết (ví dụ: swap () trong một mẫu hàm).


1
Mặc dù tôi nghĩ logic "nó tốt hơn nên tôi không quan tâm nếu nó gây nhầm lẫn cho mọi người" là thiếu sót, tôi phải đồng ý rằng đây một cách tiếp cận tốt cho các không gian tên lồng nhau.
Mr. Boy,

1
+1 cho ý tưởng "tại sao-tôi-không-nghĩ-về điều đó" xuất sắc! (Đối với "mọi người không quen với [những thứ cao cấp mới về mặt kỹ thuật]", họ sẽ quen với điều đó nếu nhiều người làm điều đó.)
wjl

Chỉ để đảm bảo rằng tôi hiểu, cả hai outervà đều innerđược xác định là không gian tên trong các tệp tiêu đề khác chưa?
dekuShrub

4

Phiên bản 3 làm cho mối liên kết giữa lớp và không gian tên rất rõ ràng với chi phí nhập nhiều hơn. Phiên bản 1 tránh điều này nhưng nắm bắt liên kết với một khối. Phiên bản 2 có xu hướng ẩn cái này nên tôi sẽ tránh cái đó.



3

Tôi chọn Num.3 (hay còn gọi là phiên bản dài dòng). Nó phải gõ nhiều hơn, nhưng mục đích là chính xác đối với bạn và trình biên dịch. Vấn đề bạn đăng dưới dạng thực sự đơn giản hơn so với thế giới thực. Trong thế giới thực, có nhiều phạm vi định nghĩa khác, không chỉ các thành viên trong lớp. Các định nghĩa của bạn không phức tạp lắm chỉ với các lớp - bởi vì phạm vi của chúng không bao giờ được mở lại (không giống như không gian tên, phạm vi toàn cục, v.v.).

Num.1 điều này có thể không thành công với các phạm vi khác với các lớp - bất kỳ thứ gì có thể được mở lại. Vì vậy, bạn có thể khai báo một hàm mới trong không gian tên bằng cách sử dụng cách tiếp cận này, hoặc nội tuyến của bạn có thể bị thay thế thông qua ODR. Bạn sẽ cần điều này cho một số định nghĩa (đặc biệt là các chuyên ngành mẫu).

Num.2 Điều này rất dễ hỏng, đặc biệt là trong các cơ sở mã lớn - khi tiêu đề và phần phụ thuộc thay đổi, chương trình của bạn sẽ không biên dịch được.

Không. 3 Đây là lý tưởng, nhưng phải nhập rất nhiều - mục đích của bạn là xác định một cái gì đó . Điều này thực hiện chính xác điều đó và trình biên dịch bắt đầu hoạt động để đảm bảo bạn không mắc lỗi, một định nghĩa không đồng bộ với khai báo của nó, v.v.



2

Tất cả các cách đều đúng, và mỗi cách đều có ưu và nhược điểm.

Trong phiên bản 1, bạn có lợi thế là không cần phải viết vùng tên phía trước mỗi hàm. Điểm bất lợi là bạn sẽ nhận được một danh tính nhàm chán, đặc biệt nếu bạn có nhiều hơn một cấp không gian tên.

Trong phiên bản 2, bạn làm cho mã của mình sạch hơn, nhưng nếu bạn có nhiều hơn một không gian tên đang được triển khai trong CPP, một vùng có thể truy cập trực tiếp vào các hàm và biến của người kia, khiến không gian tên của bạn trở nên vô dụng (đối với tệp cpp đó).

Trong phiên bản 3, bạn sẽ phải nhập nhiều hơn và các dòng chức năng của bạn có thể lớn hơn màn hình, điều này không tốt cho hiệu ứng thiết kế.

Cũng có một cách khác mà một số người sử dụng. Nó tương tự như phiên bản đầu tiên, nhưng không có vấn đề về nhận dạng.

Nó như thế này:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

Tùy bạn chọn cái nào phù hợp hơn cho từng tình huống =]


14
Tôi không thấy lý do gì để sử dụng macro ở đây. Nếu bạn không muốn thụt lề, chỉ cần không thụt lề. Sử dụng macro chỉ làm cho mã ít rõ ràng hơn.
10/4, 30/12/11

1
Tôi nghĩ rằng phiên bản cuối cùng mà bạn đề cập sẽ hữu ích bất cứ khi nào bạn muốn biên dịch mã cho bạn bằng các trình biên dịch cũ không hỗ trợ không gian tên (Có, một số loài khủng long vẫn còn tồn tại). Trong trường hợp đó, bạn có thể đặt macro bên trong một #ifdefmệnh đề.
Luca Martini

Bạn không phải nhận dạng nếu bạn không muốn, nhưng nếu bạn không sử dụng macro, một số IDE sẽ cố gắng làm điều đó cho bạn. Ví dụ, trong Visual Studio, bạn có thể chọn toàn bộ mã và nhấn ALT + F8 để tự động nhận dạng. Nếu bạn không sử dụng các định nghĩa, bạn sẽ mất chức năng đó. Ngoài ra, tôi không nghĩ rằng OPEN_ (không gian tên) và CLOSE_ (không gian tên) ít rõ ràng hơn, nếu bạn có nó trong tiêu chuẩn mã hóa của mình. Lý do mà @LucaMartini đưa ra cũng rất thú vị.
Renan Greinert

Nếu điều này được thực hiện chung chung, tức là #define OPEN_NS(X)tôi nghĩ nó hơi hữu ích, nhưng không thực sự ... Tôi không phản đối macro nhưng điều này có vẻ hơi OTT. Tôi nghĩ cách tiếp cận của Dietmar Kühl tốt hơn cho các không gian tên lồng nhau.
Mr. Boy,
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.