Tôi nên đặt các hàm không liên quan đến một lớp ở đâu?


47

Tôi đang làm việc trong một dự án C ++, nơi tôi có một loạt các hàm toán học mà ban đầu tôi đã viết để sử dụng như một phần của lớp học. Tuy nhiên, khi tôi đã viết nhiều mã hơn, tôi nhận ra rằng tôi cần các hàm toán học này ở mọi nơi.

Đâu là nơi tốt nhất để đặt chúng? Hãy nói rằng tôi có điều này:

class A{
    public:
        int math_function1(int);
        ...
}

Và khi tôi viết một lớp khác, tôi không thể (hoặc tại leat tôi không biết làm thế nào) sử dụng nó math_function1trong lớp khác. Thêm vào đó, tôi đã nhận ra một số chức năng này không thực sự liên quan đến lớp A. Chúng có vẻ như ban đầu, nhưng bây giờ tôi có thể thấy chúng chỉ là các hàm toán học.

Thực hành tốt trong tình huống này là gì? Ngay bây giờ tôi đã sao chép chúng vào các lớp mới, điều mà tôi chắc chắn là cách thực hành tồi tệ nhất.


11
Bạn đã học về statictừ khóa?
S.Lott

30
Trong C ++, các hàm miễn phí hầu như luôn được ưa thích hơn các hàm thành viên.
Pubby

4
Không có quy tắc nào nói rằng mọi thứ phải ở trong một lớp. Ít nhất là không có trong C ++.
tdammers

2
Tôi thích một không gian tên cho một lớp với một loạt các phương thức tĩnh
Nick Keighley

Câu trả lời:


70

C ++ có thể có các hàm phi phương thức tốt, nếu chúng không thuộc về một lớp thì không đặt chúng trong một lớp, chỉ cần đặt chúng ở phạm vi không gian tên toàn cục hoặc khác

namespace special_math_functions //optional
{
    int math_function1(int arg)
    {
         //definition 
    }
}

6
+1 Đây là giải pháp hợp lý nhất, mặc dù không gian tên bổ sung dường như không cần thiết.
Pubby

1
không, nó không cần thiết
jk.

27
Một số không gian tên hữu ích để giảm xung đột tên tiềm năng với các thư viện khác.
Cửa Bill

11
Sử dụng một không gian tên cũng tốt vì nó phân biệt xem cuộc gọi là phương thức hay hàm. ( math_function1(42)có thể gọi một thành viên của lớp hiện tại; special_math_functions::math_function1(42)rõ ràng đang gọi một hàm độc lập). Điều đó đang được nói, ::math_function(42)cung cấp sự định hướng tương tự.
ipeet

2
Không gian tên không cần thiết nhưng chúng cũng không bị cấm. Do đó, tại sao câu trả lời này nói // optional. Mùa theo khẩu vị.
user253751

6

Phụ thuộc vào cách tổ chức dự án và loại mẫu thiết kế bạn đang sử dụng, giả sử đây là mã tiện ích nghiêm ngặt, bạn có các tùy chọn sau:

  • Nếu bạn không phải sử dụng các đối tượng cho tất cả mọi thứ bạn có thể làm một cái gì đó đơn giản như chỉ cần đặt tất cả chúng vào một tệp mà không có lớp bao bọc xung quanh chúng. Điều này có thể có hoặc không có không gian tên mặc dù không gian tên được khuyến nghị để ngăn chặn mọi sự cố trong tương lai.
  • Đối với C ++ được quản lý, bạn có thể tạo một lớp tĩnh để chứa tất cả chúng; tuy nhiên, điều này không thực sự hoạt động giống như một lớp thực tế và tôi hiểu rằng đó là một mô hình chống C ++.
  • Nếu bạn không sử dụng C ++ được quản lý thì bạn chỉ có thể sử dụng các hàm tĩnh để cho phép bạn truy cập chúng và có tất cả chúng trong một lớp duy nhất. Điều này có thể hữu ích nếu còn có các chức năng khác mà bạn muốn có một đối tượng được khởi tạo thích hợp cho cũng có thể là một mô hình chống.
  • Nếu bạn muốn đảm bảo rằng chỉ có một phiên bản của đối tượng chứa các hàm tồn tại thì bạn có thể sử dụng Mẫu Singleton cho một lớp tiện ích cũng cho phép bạn linh hoạt trong tương lai vì bây giờ bạn có quyền truy cập vào các thuộc tính không tĩnh. Điều này sẽ được sử dụng hạn chế và chỉ thực sự áp dụng nếu bạn cần một đối tượng vì một số lý do. Điều lạ lùng là nếu bạn làm điều này bạn sẽ biết tại sao.

Lưu ý rằng tùy chọn đầu tiên sẽ là đặt cược tốt nhất và ba tùy chọn sau đây là hữu ích hạn chế. Mặc dù vậy, bạn có thể gặp phải sau đó chỉ do các lập trình viên C # hoặc Java thực hiện một số công việc C ++ hoặc nếu bạn từng làm việc với mã C # hoặc Java trong đó việc sử dụng các lớp là bắt buộc.


Tại sao bỏ phiếu xuống?
rjzii

10
Tôi không phải là người hạ cấp, nhưng có lẽ vì bạn đang tư vấn cho một lớp có chức năng tĩnh hoặc đơn lẻ, trong khi chức năng miễn phí có thể sẽ ổn trong trường hợp này (và có thể chấp nhận và hữu ích cho nhiều thứ trong C ++).
Anton Golov

@AntonGolov - Các chức năng miễn phí là điều đầu tiên mà tôi đề cập trong danh sách. :) Phần còn lại của chúng là các cách tiếp cận theo định hướng OOP hơn cho các tình huống mà bạn đang xử lý "Mọi thứ phải là một lớp học!" môi trường.
rjzii

9
@Rob Z: Tuy nhiên, C ++ không phải là một trong những "Mọi thứ phải là một lớp học!" môi trường.
David Thornley

1
Kể từ khi nào OOP để buộc các hàm thuần túy vào một lớp? Có vẻ giống như OOP-Freight-Cult.
Ded

1

Như bạn đã nói, sao chép mã là hình thức tái sử dụng mã tồi tệ nhất. Nếu bạn có các hàm không thuộc về bất kỳ lớp nào của bạn hoặc có thể được sử dụng trong một số trường hợp, thì nơi tốt nhất để đặt chúng là lớp trợ giúp hoặc lớp tiện ích. Nếu họ không sử dụng bất kỳ dữ liệu cá thể nào, họ có thể được tạo tĩnh, vì vậy bạn không cần tạo một thể hiện của lớp tiện ích để sử dụng nó.

Xem ở đây để thảo luận về các hàm thành viên tĩnh trong C ++ gốc và ở đây để biết các lớp tĩnh trong C ++ được quản lý. Sau đó, bạn có thể sử dụng lớp tiện ích này bất cứ nơi nào bạn đã dán mã của mình.

Ví dụ, trong .NET, những thứ như Min()Max()được cung cấp dưới dạng thành viên tĩnh trên System.Mathlớp .

Nếu tất cả các hàm của bạn đều liên quan đến toán học và bạn sẽ có một Mathlớp khổng lồ , bạn có thể muốn chia nhỏ hơn nữa và có các lớp như thế TrigonometryUtilities, EucledianGeometryUtilitiesv.v.

Một lựa chọn khác là đưa chức năng chia sẻ vào một lớp cơ sở của các lớp yêu cầu chức năng nói trên. Điều này hoạt động tốt, khi các hàm trong câu hỏi cần hoạt động trên dữ liệu cá thể, tuy nhiên, cách tiếp cận này cũng kém linh hoạt hơn nếu bạn muốn tránh nhiều kế thừa và chỉ bám vào một lớp cơ sở, bởi vì bạn sẽ "sử dụng" một cơ sở của mình lớp chỉ để có quyền truy cập vào một số chức năng được chia sẻ.


18
IMHO, các lớp tiện ích không có gì ngoài các thành viên tĩnh là một mô hình chống trong C ++. Bạn đang sử dụng một lớp để tái tạo hoàn hảo hành vi của một không gian tên, điều này thực sự vô nghĩa.
ipeet

+1 để đề cập đến các lớp tiện ích. Các ngôn ngữ như C # yêu cầu mọi thứ phải có trong một lớp, vì vậy việc tạo ra một số lớp tiện ích cho các mục đích khác nhau là khá phổ biến. Việc triển khai các lớp này dưới dạng Tĩnh làm cho các tiện ích trở nên thân thiện hơn với người dùng và tránh những vấn đề đau đầu mà sự kế thừa đôi khi có thể tạo ra, đặc biệt là khi các lớp cơ sở đang trở nên cồng kềnh với mã chỉ có thể được sử dụng bởi một hoặc hai hậu duệ. Các kỹ thuật tương tự có thể được áp dụng trong các ngôn ngữ khác để cung cấp ngữ cảnh có ý nghĩa cho các chức năng tiện ích của bạn, thay vì để chúng nổi trong phạm vi toàn cầu.
S.Robins

5
@ S.Robins: Không cần bất cứ thứ gì như thế trong C ++, bạn chỉ cần đặt chúng vào một không gian tên, có tác dụng chính xác như vậy.
DeadMG

0

Định hướng thuật ngữ "Hàm trợ giúp". Một định nghĩa là một chức năng tiện lợi mà bạn sử dụng tất cả thời gian chỉ để hoàn thành công việc. Chúng có thể sống trong không gian tên chính và có các tiêu đề riêng, v.v ... Định nghĩa hàm trợ giúp khác là một hàm tiện ích cho một lớp hoặc một lớp gia đình.

// a general helper 
template <class T>
bool isPrinter(T& p){
   return (dynamic_cast<Printer>(p))? true: false;
}

    // specific helper for printers
namespace printer_utils {    
  namespace HP {
     print_alignment_page() { printAlignPage();}
  }

  namespace Xerox {
     print_alignment_page() { Alignment_Page_Print();}
  }

  namespace Canon {
     print_alignment_page() { AlignPage();}
  }

   namespace Kyocera {
     print_alignment_page() { Align(137,4);}
   }

   namespace Panasonic {
      print_alignment_page() { exec(0xFF03); }
   }
} //namespace

Bây giờ isPrintercó sẵn cho bất kỳ mã bao gồm tiêu đề của nó, nhưng print_alignment_pageyêu cầu một lệnh using namespace printer_utils::Xerox;. Người ta cũng có thể tham khảo nó như là

Canon::print_alignment_page();

để rõ ràng hơn.

C ++ STL có std::không gian tên bao gồm hầu hết tất cả các lớp và hàm của nó, nhưng nó chia chúng thành hơn 17 tiêu đề khác nhau để cho phép người viết mã lấy tên lớp, tên hàm, v.v. của riêng họ.

Trong thực tế, KHÔNG nên sử dụng using namespace std;trong tệp tiêu đề hoặc, như thường được thực hiện, như dòng đầu tiên bên trong main(). std::là 5 chữ cái và thường có vẻ như là một việc vặt để nói trước chức năng mà người ta muốn sử dụng (đặc biệt std::coutstd::endl!) nhưng nó phục vụ một mục đích.

C ++ 11 mới có một số không gian tên phụ trong đó cho các dịch vụ đặc biệt như

std::placeholders,
std::string_literals,
std::chrono,
std::this_thread,
std::regex_constants

có thể được đưa vào sử dụng.

Một kỹ thuật hữu ích là thành phần không gian tên . Người ta định nghĩa một không gian tên tùy chỉnh để giữ các không gian tên bạn cần cho .cpptệp cụ thể của bạn và sử dụng không gian đó thay vì một loạt các usingcâu lệnh cho mỗi thứ trong một không gian tên mà bạn có thể cần.

#include <iostream>
#include <string>
#include <vector>

namespace Needed {
  using std::vector;
  using std::string;
  using std::cout;
  using std::endl;
}

int main(int argc, char* argv[])
{
  /*  using namespace std; */
      // would avoid all these individual using clauses,
      // but this way only these are included in the global
      // namespace.

 using namespace Needed;  // pulls in the composition

 vector<string> str_vec;

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 str_vec.push_back(s);
 str_vec.push_back(t);

 cout << s << "\n" << t << endl;
 // ...

Kỹ thuật này hạn chế tiếp xúc với toàn bộ std:: namespace( nó lớn! ) Và cho phép một người viết mã sạch hơn cho các dòng mã phổ biến nhất mà mọi người viết thường xuyên nhất.


-2

Bạn có thể muốn đặt nó trong một hàm mẫu để làm cho nó có sẵn cho các loại số nguyên và / hoặc số float khác nhau:

template <typename T>
T math_function1(T){
 ..
}

Bạn cũng có thể tạo các loại tùy chỉnh gọn gàng đại diện cho số lượng lớn hoặc số phức bằng cách nạp chồng các toán tử có liên quan cho loại tùy chỉnh của bạn để làm cho chúng thân thiện với mẫu.

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.