2011-05-09 39 views
5

Tôi có một lớp chứa đơn giản, cấp thấp được sử dụng bởi một lớp tệp cấp cao hơn. Về cơ bản, lớp tệp sử dụng vùng chứa để lưu trữ sửa đổi cục bộ trước khi lưu phiên bản cuối cùng vào tệp thực. Một số phương thức, do đó, chuyển trực tiếp từ lớp container sang lớp tệp. (Ví dụ: Resize().)Phương pháp sao chép từ thành viên

Tôi vừa xác định các phương thức trong lớp tệp để gọi các biến thể lớp chứa của chúng. Ví dụ:

void FileClass::Foo() 
{ 
    ContainerMember.Foo(); 
} 

Điều này, tuy nhiên, ngày càng trở thành một mối phiền toái. Có cách nào tốt hơn để làm điều này?

Dưới đây là một ví dụ đơn giản:

class MyContainer 
{ 
    // ... 

    public: 

    void Foo() 
    { 
     // This function directly handles the object's 
     // member variables. 
    } 
} 

class MyClass 
{ 
    MyContainer Member; 

    public: 

    void Foo() 
    { 
     Member.Foo(); 

     // This seems to be pointless re-implementation, and it's 
     // inconvenient to keep MyContainer's methods and MyClass's 
     // wrappers for those methods synchronized. 
    } 
} 
+0

Tôi thấy thành phần ở đây, không phải là thừa kế. Tui bỏ lỡ điều gì vậy? –

+0

@Doug Tôi hỏi liệu có một cách để kế thừa từ lớp container hay 'MyContainer' trong ví dụ của tôi, chỉ với một vài phương thức. (Hoặc một giải pháp tốt hơn, nếu có một.) Tôi sẽ chỉnh sửa câu hỏi để rõ ràng hơn. – Maxpm

+1

Trong trường hợp đó, có vẻ như bạn nên chia MyContainer thành một lớp có các phương thức bạn muốn (và sau đó kế thừa từ đó) và các phương thức còn lại của MyContainer trong một lớp khác. – dlev

Trả lời

7

Vâng, tại sao không chỉ kế thừa privatly từ MyContainer và hiển thị những chức năng mà bạn muốn chuyển tiếp với tuyên bố using? Đó được gọi là "Thực hiện MyClassvềMyContainer.

class MyContainer 
{ 
public: 
    void Foo() 
    { 
     // This function directly handles the object's 
     // member variables. 
    } 

    void Bar(){ 
     // ... 
    } 
} 

class MyClass : private MyContainer 
{ 
public: 
    using MyContainer::Foo; 

    // would hide MyContainer::Bar 
    void Bar(){ 
     // ... 
     MyContainer::Bar(); 
     // ... 
    } 
} 

Bây giờ 'bên ngoài' sẽ có thể trực tiếp gọi Foo, trong khi Bar chỉ truy cập bên trong MyClass. Nếu bây giờ bạn thực hiện một chức năng với cùng tên, nó ẩn các chức năng cơ bản và bạn có thể quấn các chức năng cơ bản như thế. Tất nhiên, bây giờ bạn cần phải hội đủ điều kiện đầy đủ các cuộc gọi đến các chức năng cơ bản, hoặc bạn sẽ đi vào một đệ quy vô tận.


Ngoài ra, tôi thừa kế f bạn muốn cho phép (không polymorphical) subclassing của MyClass, hơn đây là một trong những nơi hiếm, được bảo vệ là thực sự hữu ích:

class MyClass : protected MyContainer{ 
    // all stays the same, subclasses are also allowed to call the MyContainer functions 
}; 

Non-polymorphical nếu MyClass của bạn không có destructor ảo.

1

Vâng, việc duy trì một lớp proxy như thế này là rất khó chịu. IDE của bạn có thể có một số công cụ để làm cho nó dễ dàng hơn một chút. Hoặc bạn có thể tải xuống một phần bổ sung IDE.

Nhưng thường không khó, trừ khi bạn cần hỗ trợ hàng tá chức năng và ghi đè và mẫu.

Tôi thường viết chúng thích:

void Foo()  { return Member.Foo(); } 
int Bar(int x) { return Member.Bar(x); } 

Đó là tốt đẹp và đối xứng. C++ cho phép bạn trả về các giá trị void trong các hàm void vì nó làm cho các khuôn mẫu hoạt động tốt hơn. Nhưng bạn có thể sử dụng cùng một thứ để làm cho mã khác đẹp hơn.

1

Đó là delegation inheritance và tôi không biết rằng C++ cung cấp bất kỳ cơ chế nào để trợ giúp điều đó.

1

Hãy xem xét những gì có ý nghĩa trong trường hợp của bạn - thành phần (có a) hoặc thừa kế (là một) mối quan hệ giữa MyClass và MyContainer.

Nếu bạn không muốn có mã như thế này nữa, bạn bị hạn chế khá nhiều đối với thừa kế triển khai (MyContainer làm lớp cơ sở/trừu tượng cơ sở). Tuy nhiên bạn phải chắc chắn rằng điều này thực sự có ý nghĩa trong ứng dụng của bạn, và bạn không kế thừa hoàn toàn cho việc thực hiện (thừa kế để thực hiện là xấu).

Nếu nghi ngờ, những gì bạn có là có thể tốt.

EDIT: Tôi quen với việc suy nghĩ trong Java/C# và bỏ qua thực tế là C++ có tính linh hoạt thừa kế lớn hơn Xeo sử dụng trong câu trả lời của mình. Điều đó chỉ cảm thấy như giải pháp tốt đẹp trong trường hợp này.

0

Tính năng này bạn cần viết một lượng lớn mã thực sự là tính năng cần thiết. C++ là ngôn ngữ tiết tú, và nếu bạn cố gắng tránh viết mã bằng C++, thiết kế của bạn sẽ không bao giờ tốt lắm.

Nhưng vấn đề thực sự với câu hỏi này là lớp học không có hành vi. Nó chỉ là một wrapper mà không có gì. Mỗi lớp cần phải làm một cái gì đó khác hơn là chỉ truyền dữ liệu xung quanh.

Điều quan trọng là mỗi lớp đều có giao diện chính xác. Yêu cầu này làm cho nó cần thiết để viết các chức năng chuyển tiếp. Mục đích chính của mỗi chức năng thành viên là phân phối tác phẩm được yêu cầu cho tất cả thành viên dữ liệu. Nếu bạn chỉ có một thành viên dữ liệu, và bạn chưa quyết định những gì lớp được cho là phải làm, thì tất cả những gì bạn có là chức năng chuyển tiếp. Khi bạn thêm các đối tượng thành viên khác và quyết định lớp nào được yêu cầu làm, thì các hàm chuyển tiếp của bạn sẽ thay đổi thành một cái gì đó hợp lý hơn.

Một điều giúp bạn thực hiện điều này là giữ cho lớp học của bạn nhỏ gọn. Nếu giao diện nhỏ, mỗi lớp proxy sẽ chỉ có giao diện nhỏ và giao diện sẽ không thay đổi thường xuyên.

+0

Lớp * làm * làm điều gì đó. Ví dụ của tôi đã được đơn giản hóa vì lợi ích của câu hỏi. – Maxpm

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