2010-04-06 35 views
13

Việc thêm chức năng vào một lớp có thể được thực hiện bằng cách thêm một phương thức hoặc bằng cách xác định một hàm lấy đối tượng làm tham số đầu tiên của nó. Hầu hết các lập trình viên mà tôi biết sẽ chọn giải pháp thêm một phương thức thể hiện.Chọn giữa các phương pháp mẫu và các chức năng miễn phí?

Tuy nhiên, đôi khi tôi muốn tạo một hàm riêng biệt. Ví dụ: trong mã ví dụ bên dưới AreaDiagonal được định nghĩa là các hàm miễn phí thay vì phương pháp. Tôi tìm thấy nó tốt hơn theo cách này bởi vì tôi nghĩ rằng các chức năng này cung cấp các cải tiến hơn là chức năng cốt lõi.

Đây có phải là hành vi tốt/xấu không? Nếu câu trả lời là "nó phụ thuộc", thì các quy tắc để quyết định giữa việc thêm phương thức hoặc xác định một hàm riêng biệt là gì?

class Rect 
{ 
public: 
    Rect(int x, int y, int w, int h) : 
     mX(x), mY(y), mWidth(w), mHeight(h) 
    { 
    } 

    int x() const { return mX; } 

    int y() const { return mY; } 

    int width() const { return mWidth; } 

    int height() const { return mHeight; } 

private: 
    int mX, mY, mWidth, mHeight; 
}; 


int Area(const Rect & inRect) 
{ 
    return inRect.width() * inRect.height(); 
} 


float Diagonal(const Rect & inRect) 
{ 
    return std::sqrt(std::pow(static_cast<float>(inRect.width()), 2) + std::pow(static_cast<float>(inRect.height()), 2)); 
} 
+0

Một lưu ý phụ, sử dụng pow với công suất không đổi của hai cực kỳ không hiệu quả so với việc chỉ viết chính x * x + y * y + z * z. –

+0

@Mark B: Cảm ơn, tôi không biết điều đó. – StackedCrooked

+1

Xem phần _ cổ điển [Các hàm không phải thành viên cải thiện đóng gói] (http://drdobbs.com/cpp/184401197) _. ("Khi bạn nghĩ đóng gói, bạn nên nghĩ rằng các chức năng không phải là thành viên") Xem thêm http://stackoverflow.com/q/1692084/140719. – sbi

Trả lời

14

GoTW #84 đã khám phá câu hỏi này liên quan đến std::string. Ông có khuynh hướng hướng tới ý tưởng ngược lại: hầu hết các chức năng phải là toàn cầu trừ phi họ thực sự cần phải là thành viên.

Tôi muốn nói rằng C++ hiện đại nhất có xu hướng hướng tới cùng một ý tưởng. Ví dụ, nếu bạn xem qua Boost, một chút được thực hiện với các chức năng miễn phí.

+4

Tôi thích trích dẫn này từ Scott Meyers chứa trong bài viết: > Tôi sẽ bắt đầu với cú đấm: Nếu bạn đang viết một hàm có thể được triển khai làm thành viên hoặc là người không phải là thành viên, bạn nên thực hiện nó như một hàm không phải thành viên. Quyết định đó làm tăng sự đóng gói của lớp học. Khi bạn nghĩ rằng đóng gói, bạn nên nghĩ rằng các chức năng không phải là thành viên. –

+2

Chỉ có thể của tôi với điều này là sự ô nhiễm không gian tên. Là hình học nổi :: Diện tích (const Hình học :: Rect & r) tốt hơn so với hình học nổi :: Rect :: Area()? Bạn không thực sự muốn vùng chức năng tự do nằm trong không gian tên toàn cầu, do đó bạn cần phải đặt vào một không gian tên. Và tại thời điểm đó nó trông giống như một hàm lớp tĩnh. – jmucchiello

+2

@jmucchiello: Có, hầu hết các chức năng miễn phí phải nằm trong không gian tên. Tôi không nghĩ rằng như là làm cho trông giống như các chức năng thành viên tĩnh mặc dù - Tôi quen với 'x :: y' có nghĩa là" chức năng y trong không gian tên x ". –

1

Tôi muốn nói rằng điều đó tùy thuộc vào mục tiêu của bạn. Nếu hàm là hàm thành viên, thì bạn có thể sử dụng. và -> ký hiệu với nó, và nó được tích hợp tốt hơn với lớp. Nó cũng cho phép đa hình. Nếu bạn muốn hàm của bạn là ảo, nó cần phải là một hàm thành viên.

Tuy nhiên, có một trường tư tưởng rằng nó đóng gói tốt hơn để làm cho chức năng thành viên chỉ hoạt động nếu chúng cần. Vì vậy, nếu chức năng không thực sự cần truy cập vào các biến thành viên (có lẽ vì nó có thể làm những gì nó cần làm chỉ với các chức năng công cộng), sau đó bạn làm cho nó một chức năng riêng biệt mà không phải là một thành viên của lớp. Bằng cách đó, nó không thể gây rối với nội bộ ngay cả một cách tình cờ.

Một lưu ý khác là nếu đó không phải là chức năng thành viên, bạn phải lo lắng về việc liệu các loại khác có thể hoặc nên được truyền tới loại hiện là thông số đầu tiên của hàm. Đôi khi đó là mong muốn và đôi khi không. Nếu đó là một chức năng thành viên, thì đó không phải là một vấn đề (hoặc một cơ hội, tùy thuộc vào những gì bạn đang cố gắng làm).

Cá nhân, tôi không thích các chức năng riêng biệt với lớp học, nhưng bạn thường bị buộc phải viết các hàm theo cách đó khi bạn không có quyền kiểm soát lớp hoặc bạn không thể thay đổi nó cho tính tương thích lý do hay không.

Thực sự, tôi nghĩ rằng nó đi xuống với những gì quan trọng nhất đối với bạn, và những gì bạn đang thực sự cố gắng làm. Tôi nghĩ rằng việc có chức năng là một phần của lớp giúp dễ sử dụng hơn và hướng đến nhiều đối tượng hơn, nhưng tốt hơn nếu bạn giới hạn mức độ truy cập vào các thành viên riêng trong lớp của bạn.

2

Nếu bạn muốn sử dụng nó với các lớp khác, không liên quan, nó phải là một hàm miễn phí có ghi đè. Ví dụ, bạn có thể ghi đè float Area(Hexagon const &)float Diagonal(Hexagon const &). x, y, widthheight tuy nhiên có nghĩa là những thứ khác nhau cho các hình dạng khác nhau.

Lớp cơ sở trừu tượng (hoặc các phương thức đồng âm không liên quan) là một giải pháp khác, nhưng các chức năng miễn phí có thể mở rộng để người dùng có được giao diện đồng nhất hơn khi họ đã thêm nội dung của riêng họ.

Tránh các chức năng miễn phí có tên mơ hồ, để các hàm không liên quan không trở thành phần ghi đè "cạnh tranh".

Các vấn đề liên quan