Hàm không gian tên + so với các phương thức tĩnh trên một lớp


290

Hãy nói rằng tôi có, hoặc sẽ viết, một tập hợp các chức năng liên quan. Hãy nói rằng chúng liên quan đến toán học. Có tổ chức, tôi nên:

  1. Viết các hàm này và đặt chúng vào MyMathkhông gian tên của tôi và tham khảo chúng quaMyMath::XYZ()
  2. Tạo một lớp được gọi MyMathvà làm cho các phương thức này tĩnh và tham chiếu tương tựMyMath::XYZ()

Tại sao tôi lại chọn cái này làm phương tiện tổ chức phần mềm của mình?


4
đối với một điều, không gian tên là sự bổ sung gần đây hơn cho ngôn ngữ, so với các lớp và các phương thức tĩnh, có trong ngôn ngữ từ thời điểm nó được gọi là "C với các lớp". Một số lập trình viên có thể thoải mái hơn với các tính năng cũ hơn. Một số lập trình viên khác có thể đang sử dụng trình biên dịch cũ. Chỉ cần $ 0,02 của tôi
Rom

21
@Rom: Bạn nói đúng về "lập trình viên cũ", nhưng sai về "trình biên dịch cũ". Không gian tên được biên dịch chính xác từ các eons (Tôi đã làm việc với chúng với Visual C ++ 6, có từ năm 1998!). Đối với "C với các lớp", một số người trong diễn đàn này thậm chí không được sinh ra khi điều đó xảy ra: Sử dụng điều này như một đối số để tránh một tính năng C ++ tiêu chuẩn và phổ biến là sai lầm. Tóm lại, chỉ các trình biên dịch C ++ lỗi thời không hỗ trợ các không gian tên. Đừng sử dụng lý lẽ đó như một cái cớ để không sử dụng chúng.
paercebal

@paercebal: một số trình biên dịch cổ vẫn đang được sử dụng trong thế giới nhúng. Không hỗ trợ không gian tên có lẽ là một trong những bất tiện nhỏ nhất mà người ta cần phải khắc phục trong khi viết mã cho các CPU nhỏ khác nhau mà mọi người tương tác với nhau mỗi ngày: âm thanh nổi, lò vi sóng, bộ điều khiển động cơ trong xe hơi, đèn giao thông, v.v. hãy rõ ràng: Tôi không ủng hộ việc không sử dụng trình biên dịch mới hơn, tốt hơn ở mọi nơi. Au conrare: Tôi là tất cả các tính năng ngôn ngữ mới nhất (ngoại trừ RTTI;)). Tôi chỉ chỉ ra rằng một xu hướng như vậy tồn tại
Rom

13
@Rom: Trong trường hợp hiện tại, tác giả câu hỏi có quyền lựa chọn, vì vậy, rõ ràng, không có trình biên dịch nào của anh ta / cô ta không biên dịch mã được đặt tên. Và vì đây là câu hỏi về C ++, nên phải có câu trả lời C ++, bao gồm đề cập đến không gian tên và giải pháp RTTI cho vấn đề nếu cần. Đưa ra một câu trả lời C, hoặc một câu trả lời C-with-class-for-lỗi thời-trình biên dịch là không có chủ đề.
paercebal

2
"Chỉ $ 0,02 của tôi" - sẽ đáng giá hơn nếu bạn đã cung cấp bất kỳ bằng chứng nào về trình biên dịch C ++ còn tồn tại không hỗ trợ không gian tên. "một số trình biên dịch cổ vẫn còn được sử dụng trong thế giới nhúng" - điều này thật ngớ ngẩn; "thế giới nhúng" là một sự phát triển gần đây hơn các không gian tên C ++. "C with class" đã được giới thiệu vào năm 1979, rất lâu trước khi bất kỳ ai nhúng mã C vào bất cứ thứ gì. "Tôi chỉ chỉ ra rằng một xu hướng như vậy tồn tại" - ngay cả khi như vậy, điều đó không liên quan gì đến câu hỏi này.
Jim Balter

Câu trả lời:


243

Theo mặc định, sử dụng các chức năng được đặt tên.

Các lớp là để xây dựng các đối tượng, không phải để thay thế các không gian tên.

Trong mã hướng đối tượng

Scott Meyers đã viết toàn bộ Mục cho cuốn sách C ++ hiệu quả của mình về chủ đề này, "Thích các chức năng không phải là thành viên không phải là thành viên". Tôi tìm thấy một tài liệu tham khảo trực tuyến về nguyên tắc này trong một bài viết từ Herb Sutter:http://www.gotw.ca/gotw/084.htm

Điều quan trọng cần biết là: Trong các hàm C ++ trong cùng một không gian tên với một lớp thuộc giao diện của lớp đó (vì ADL sẽ tìm kiếm các hàm đó khi giải quyết các lệnh gọi hàm).

Các hàm được đặt tên, trừ khi được khai báo là "bạn bè", không có quyền truy cập vào bên trong của lớp, trong khi các phương thức tĩnh thì có.

Điều này có nghĩa là, ví dụ, khi duy trì lớp của bạn, nếu bạn cần thay đổi nội bộ của lớp, bạn sẽ cần tìm kiếm các tác dụng phụ trong tất cả các phương thức của nó, bao gồm cả các phương thức tĩnh.

Gia hạn tôi

Thêm mã vào giao diện của lớp.

Trong C #, bạn có thể thêm các phương thức vào một lớp ngay cả khi bạn không có quyền truy cập vào nó. Nhưng trong C ++, điều này là không thể.

Nhưng, vẫn trong C ++, bạn vẫn có thể thêm một hàm được đặt tên, ngay cả với một lớp mà ai đó đã viết cho bạn.

Nhìn từ phía bên kia, điều này rất quan trọng khi thiết kế mã của bạn, bởi vì bằng cách đặt các chức năng của bạn vào một không gian tên, bạn sẽ ủy quyền cho người dùng của mình tăng / hoàn thành giao diện của lớp.

Gia hạn II

Một tác dụng phụ của điểm trước đó, không thể khai báo các phương thức tĩnh trong nhiều tiêu đề. Mọi phương thức phải được khai báo trong cùng một lớp.

Đối với không gian tên, các hàm từ cùng một không gian tên có thể được khai báo trong nhiều tiêu đề (hàm hoán đổi gần như tiêu chuẩn là ví dụ tốt nhất về điều đó).

Gia hạn III

Sự mát mẻ cơ bản của một không gian tên là trong một số mã, bạn có thể tránh đề cập đến nó, nếu bạn sử dụng từ khóa "bằng cách sử dụng":

#include <string>
#include <vector>

// Etc.
{
   using namespace std ;
   // Now, everything from std is accessible without qualification
   string s ; // Ok
   vector v ; // Ok
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Và bạn thậm chí có thể giới hạn "ô nhiễm" trong một lớp:

#include <string>
#include <vector>

{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

"Mẫu" này là bắt buộc để sử dụng đúng thành ngữ hoán đổi gần như tiêu chuẩn.

Và điều này là không thể thực hiện với các phương thức tĩnh trong các lớp.

Vì vậy, không gian tên C ++ có ngữ nghĩa riêng.

Nhưng nó đi xa hơn, vì bạn có thể kết hợp các không gian tên theo cách tương tự như thừa kế.

Ví dụ: nếu bạn có không gian tên A có chức năng AAA, không gian tên B có chức năng BBB, bạn có thể khai báo không gian tên C và đưa AAA và BBB vào không gian tên này bằng từ khóa.

Phần kết luận

Không gian tên dành cho không gian tên. Các lớp học dành cho các lớp học.

C ++ được thiết kế để mỗi khái niệm là khác nhau và được sử dụng khác nhau, trong các trường hợp khác nhau, như là một giải pháp cho các vấn đề khác nhau.

Đừng sử dụng các lớp khi bạn cần không gian tên.

Và trong trường hợp của bạn, bạn cần không gian tên.


Câu trả lời này có thể được áp dụng cho các chủ đề không, nghĩa là tốt hơn là sử dụng các không gian tên hơn là các phương thức tĩnh cho các chủ đề?
bảnh bao

3
@dashesy: không gian tên so với phương thức tĩnh không liên quan gì đến chủ đề, vì vậy, không gian tên tốt hơn vì không gian tên hầu như luôn luôn tốt hơn phương thức tĩnh. Nếu một điều, các phương thức tĩnh có quyền truy cập vào các biến thành viên lớp, do đó chúng bằng cách nào đó có giá trị đóng gói thấp hơn không gian tên. Và cách ly dữ liệu thậm chí còn quan trọng hơn trong thực thi luồng.
paercebal

@ paercebal- cảm ơn, tôi đã sử dụng các phương thức lớp tĩnh cho các hàm luồng. Bây giờ tôi hiểu rằng tôi đã lạm dụng lớp làm không gian tên, vậy bạn nghĩ đâu là cách tiếp cận tốt nhất để có nhiều luồng trong một đối tượng? Tôi cũng đã hỏi câu hỏi này trên SO, tôi đánh giá cao nếu bạn có thể làm sáng tỏ (ở đây hoặc trong chính câu hỏi)
bảnh bao

1
@dashesy: ​​Bạn đang yêu cầu rắc rối. Những gì bạn muốn với các luồng khác nhau là cô lập dữ liệu không được chia sẻ, do đó, việc có nhiều luồng có quyền truy cập đặc quyền vào dữ liệu riêng tư của một lớp là một ý tưởng tồi. Tôi sẽ ẩn một luồng trong một lớp và đảm bảo cách ly dữ liệu cho luồng đó khỏi dữ liệu cho luồng chính. Tất nhiên, dữ liệu được cho là được chia sẻ sau đó có thể là thành viên của lớp đó, nhưng chúng vẫn phải được đồng bộ hóa (khóa, nguyên tử, v.v.). Tôi không chắc chắn về số lượng lib bạn có quyền truy cập, nhưng sử dụng tác vụ / async thậm chí còn tốt hơn.
paercebal

Câu trả lời của paercebal nên được chấp nhận! Chỉ cần thêm một liên kết cho hoán đổi gần như tiêu chuẩn () thông qua không gian tên + ADL -> stackoverflow.com/questions/6380862/
Kẻ

54

Có rất nhiều người sẽ không đồng ý với tôi, nhưng đây là cách tôi thấy:

Một lớp về cơ bản là một định nghĩa của một loại đối tượng nhất định. Các phương thức tĩnh sẽ định nghĩa các hoạt động được liên kết chặt chẽ với định nghĩa đối tượng đó.

Nếu bạn sắp có một nhóm các hàm liên quan không liên quan đến một đối tượng cơ bản hoặc định nghĩa của một loại đối tượng , thì tôi sẽ nói chỉ đi với một không gian tên. Đối với tôi, về mặt khái niệm, điều này hợp lý hơn rất nhiều.

Ví dụ, trong trường hợp của bạn, hãy tự hỏi: "MyMath là gì?" Nếu MyMathkhông định nghĩa một loại đối tượng, thì tôi sẽ nói: đừng biến nó thành một lớp.

Nhưng như tôi đã nói, tôi biết có rất nhiều người sẽ (thậm chí kịch liệt) không đồng ý với tôi về điều này (đặc biệt là các nhà phát triển Java và C #).


3
Bạn có một quan điểm rất thuần túy về điều này. Nhưng thực tế mà nói, một lớp với các phương thức toàn tĩnh có thể có ích: bạn có thể typedefsử dụng chúng, sử dụng chúng làm tham số mẫu, v.v.
Shog9

56
Đó là becuase Jave và C # mọi người không có lựa chọn nào khác.
Martin York

7
@ sh9. Bạn có thể templatize chức năng là tốt!
Martin York

6
@Dan: có lẽ, một trong số đó cần các thói quen toán học và muốn hỗ trợ "cắm vào" các triển khai khác nhau.
Shog9

1
@Dan: "Tôi nghĩ rằng nếu ai đó quan tâm đến việc sử dụng một lớp làm tham số mẫu, thì lớp đó gần như chắc chắn xác định một số đối tượng cơ bản." Không hoàn toàn không. Hãy nghĩ về những đặc điểm. (Tuy nhiên, tôi hoàn toàn đồng ý với câu trả lời của bạn.)
sbi

18
  • Nếu bạn cần dữ liệu tĩnh, sử dụng các phương thức tĩnh.
  • Nếu chúng là các hàm mẫu và bạn muốn có thể chỉ định một tập các tham số mẫu cho tất cả các hàm với nhau thì hãy sử dụng các phương thức tĩnh trong một lớp mẫu.

Mặt khác, sử dụng các hàm được đặt tên.


Đáp lại các ý kiến: có, các phương thức tĩnh và dữ liệu tĩnh có xu hướng được sử dụng quá mức. Đó là lý do tại sao tôi chỉ đưa ra hai kịch bản liên quan mà tôi nghĩ chúng có thể hữu ích. Trong ví dụ cụ thể của OP (một tập các thói quen toán học), nếu anh ta muốn có khả năng chỉ định các tham số - giả sử, kiểu dữ liệu cốt lõi và độ chính xác đầu ra - sẽ được áp dụng cho tất cả các thói quen, anh ta có thể làm một việc như:

template<typename T, int decimalPlaces>
class MyMath
{
   // routines operate on datatype T, preserving at least decimalPlaces precision
};

// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;

Nếu bạn không cần điều đó, thì bằng mọi cách hãy sử dụng một không gian tên.


2
cái gọi là dữ liệu tĩnh có thể là dữ liệu mức không gian tên trong tệp triển khai của không gian tên, điều này làm giảm sự ghép đôi hơn nữa vì nó không phải hiển thị trong tiêu đề.
Motti

Dữ liệu tĩnh không tốt hơn toàn cầu phạm vi không gian tên.
coppro

@coppro. Họ ít nhất là một bước trong chuỗi tiến hóa từ các thế giới ngẫu nhiên vì chúng có thể được đặt ở chế độ riêng tư (nhưng nếu không thì đồng ý).
Martin York

@Motti: OTOH, nếu bạn muốn nó trong tiêu đề (chức năng nội tuyến / mẫu), bạn sẽ trở lại xấu xí về nó.
Shog9

1
Ví dụ thú vị, sử dụng một lớp làm tốc ký để tránh lặp lại các templateđối số!
gạch dưới

13

Bạn nên sử dụng một không gian tên, bởi vì một không gian tên có nhiều ưu điểm so với một lớp:

  • Bạn không phải xác định mọi thứ trong cùng một tiêu đề
  • Bạn không cần phải phơi bày tất cả việc triển khai của mình trong tiêu đề
  • Bạn không thể usinglà thành viên trong lớp; bạn có thể usinglà thành viên không gian tên
  • Bạn không thể using class, mặc dù đó using namespacekhông phải là một ý tưởng hay
  • Sử dụng một lớp ngụ ý rằng có một số đối tượng được tạo khi thực sự không có

Các thành viên tĩnh, theo tôi, rất rất lạm dụng. Chúng không phải là một điều cần thiết thực sự trong hầu hết các trường hợp. Các hàm thành viên tĩnh có lẽ tốt hơn là các hàm phạm vi tệp và các thành viên dữ liệu tĩnh chỉ là các đối tượng toàn cầu với danh tiếng tốt hơn, không được bảo vệ.


3
"Bạn không cần phải phơi bày tất cả việc triển khai của mình trong tiêu đề" bạn cũng không làm gì khi sử dụng một lớp.
Vanuan

Thậm chí nhiều hơn: Nếu bạn đang sử dụng các không gian tên, bạn không thể hiển thị tất cả việc triển khai của mình trong tiêu đề (bạn sẽ kết thúc với nhiều định nghĩa biểu tượng). Các hàm thành viên lớp nội tuyến cho phép bạn làm điều đó.
Vanuan

1
@Vanuan: Bạn có thể trưng ra các cài đặt không gian tên trong tiêu đề. Chỉ cần sử dụng inlinetừ khóa để đáp ứng ODR.
Thomas Eding

@ThomasEding không cần! =
Can

3
@Vanuan: Chỉ có một điều được trình biên dịch đảm bảo khi sử dụng inlinevà nó KHÔNG "nội tuyến" cơ thể của hàm. Mục đích thực sự (và được đảm bảo bởi tiêu chuẩn) inlinelà ngăn chặn nhiều định nghĩa. Đọc về "Quy tắc một định nghĩa" cho C ++. Ngoài ra, câu hỏi SO được liên kết không được biên dịch do các vấn đề tiêu đề được biên dịch trước thay vì các vấn đề ODR.
Thomas Eding

3

Tôi thích các không gian tên, theo cách đó bạn có thể có dữ liệu riêng tư trong một không gian tên ẩn danh trong tệp triển khai (vì vậy nó hoàn toàn không phải hiển thị trong tiêu đề trái ngược với privatecác thành viên). Một lợi ích khác là bởi usingkhông gian tên của bạn, các máy khách của các phương thức có thể từ chối chỉ địnhMyMath::


2
Bạn cũng có thể có dữ liệu riêng tư trong một không gian tên ẩn danh trong tệp triển khai với các lớp. Không chắc chắn tôi làm theo logic của bạn.
Patrick Johnmeyer

0

Thêm một lý do để sử dụng lớp - Tùy chọn để sử dụng các chỉ định truy cập. Sau đó, bạn có thể phá vỡ phương thức tĩnh công khai của mình thành các phương thức riêng tư nhỏ hơn. Phương thức công khai có thể gọi nhiều phương thức riêng tư.


6
Các công cụ sửa đổi truy cập rất tuyệt, nhưng ngay cả privatephương thức nhất cũng dễ truy cập hơn phương thức mà nguyên mẫu không được công bố trong một tiêu đề (và do đó, vẫn không nhìn thấy được). Tôi thậm chí không đề cập đến việc đóng gói tốt hơn được cung cấp bởi các hàm được đặt tên ẩn danh.
paercebal

2
Các phương thức riêng là IMO, kém hơn so với việc ẩn chính hàm đó trong tệp thực thi (tệp cpp) và không bao giờ để lộ nó trong tệp tiêu đề. Hãy giải thích điều này trong câu trả lời của bạn và lý do tại sao bạn muốn sử dụng các thành viên tư nhân . Cho đến lúc đó -1.
vô nghĩa

@nonsensickle Có lẽ ông có nghĩa là một chức năng voi ma mút với nhiều phần lặp đi lặp lại có thể bị phá vỡ một cách an toàn trong khi che giấu các phần phụ vi phạm phía sau riêng tư, ngăn chặn người khác xâm nhập nếu chúng nguy hiểm / cần sử dụng rất cẩn thận.
Troyseph

1
@Troyseph thậm chí như vậy, bạn có thể ẩn thông tin này trong một không gian tên chưa được đặt tên trong .cpptệp để đặt nó ở chế độ riêng tư cho đơn vị dịch mà không cung cấp bất kỳ thông tin thừa nào cho bất kỳ ai đọc tệp tiêu đề. Thực tế, tôi đang cố gắng ủng hộ cho thành ngữ PIMPL.
vô nghĩa

Bạn không thể đặt nó trong một .cpptệp nếu bạn muốn sử dụng các mẫu.
Yay295

0

Cả không gian tên và phương thức lớp đều có cách sử dụng. Không gian tên có khả năng được trải rộng trên các tệp tuy nhiên đó là một điểm yếu nếu bạn cần thực thi tất cả các mã liên quan để đi trong một tệp. Như đã đề cập ở trên lớp cũng cho phép bạn tạo các thành viên tĩnh riêng trong lớp. Bạn có thể có nó trong không gian tên ẩn danh của tệp triển khai tuy nhiên nó vẫn là một phạm vi lớn hơn so với việc có chúng trong lớp.


"[lưu trữ mọi thứ] trong không gian tên ẩn danh của tệp triển khai [là] một phạm vi lớn hơn so với việc có chúng trong lớp" - không, không phải vậy. trong trường hợp không yêu cầu quyền truy cập vào các thành viên, các công cụ được đặt tên ẩn danh sẽ riêng tư hơn các công cụ private:. và trong nhiều trường hợp dường như yêu cầu truy cập đặc quyền , điều đó có thể được thực hiện. chức năng 'riêng tư' nhất là chức năng không xuất hiện trong tiêu đề. private:phương pháp không bao giờ có thể tận hưởng lợi ích này.
gạch dưới
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.