2011-02-05 29 views
11

tôi có một thừa kế struct A : public B, tôi muốn ẩn các hàm riêng lẻ khỏi B, điều này có khả thi không?một cách trong c + + để ẩn một hàm cụ thể

tôi biết điều ngược lại là có thể sử dụng using BMethod trong tuyên bố A.

cổ vũ

+0

Hiệu ứng chính xác nào bạn muốn thực hiện bằng cách ẩn này? – sth

Trả lời

22

Có một vấn đề ở đây: đây sẽ là một vi phạm trực tiếp Liskov Substitution Nguyên tắc, cụ thể là A sẽ không đóng vai trò như mộtB nữa.

Nếu bạn muốn sử dụng lại B thực hiện, giải pháp là đơn giản chỉ để làm như vậy:

class A 
{ 
public: 
    void foo() { return b.foo(); } 
    void bar() { return b.bar(); } 
    // ... 

private: 
    B b; 
}; 

Đừng lạm dụng thừa kế, thành phần sử dụng thay vì

+4

Chỉ cần viết một lớp là 99% của một lớp khác, nhưng xử lý mọi thứ khác nhau và có 1 hàm, chỉ một hàm không thể tồn tại B. – rxantos

1

Nếu phương thức là riêng tư trong B, khi đó chúng sẽ vẫn ẩn cho dù bạn sử dụng thừa kế công khai.

5

Bạn không thể "ẩn" nó, nhưng bạn có thể làm cho nó trở thành một lỗi thời gian biên dịch để gọi nó. Ví dụ:

struct A 
{ 
    void AMethod() {} 
}; 

class B : public A 
{ 
    void AMethod() {} //Hides A::AMethod 
}; 

int main() 
{ 
    B myB; 
    myB.AMethod(); //Error: AMethod is private 
    static_cast<A*>(&myB)->AMethod(); //Ok 
    return 0; 
} 

Ví dụ về codepad with the errorwithout.

Điều đó tất cả đã nói, mặc dù điều này là có thể, bạn thực sự không nên làm điều đó. Bạn sẽ nhầm lẫn địa ngục của khách hàng.

EDIT: Lưu ý rằng bạn cũng có thể thực hiện việc này với virtual functions (Và with the error).

+0

Tôi không nghĩ rằng có bất kỳ cần phải xác định (tư nhân) AMethod trong kế thừa lớp B. Tuyên bố, tôi nghĩ rằng, đủ. Tôi muốn nói phương pháp ban đầu là "ẩn", nếu đó là một lỗi biên dịch thời gian cố gắng sử dụng nó (ngay cả khi thông báo lỗi khác với trường hợp phương pháp không tồn tại ở tất cả). –

+0

@eq: Đúng vậy, không cần phải định nghĩa nó trừ khi phương thức trong lớp cha là 'virtual'. (Tôi chỉ lười biếng và sử dụng sao chép/dán để tạo 'B') –

0

Không thể thay đổi mức hiển thị của phương thức gốc.

Bạn có thể tạo một phương thức trong cấu trúc A với cùng tên và có phương thức đó là riêng tư, nhưng điều đó không ngăn cản phương thức được gọi khi một thể hiện của cấu trúc A đang được tham chiếu bởi một biến loại B.

0

Tại sao bạn không biến nó thành ảo trong lớp cơ sở và ghi đè nó trong Trẻ em? (more help)

34

Nếu bạn muốn ẩn có chọn lọc các chức năng từ B, không có ý nghĩa gì nhiều khi sử dụng thừa kế công khai ngay từ đầu.
Sử dụng thừa kế riêng một cách chọn lọc & mang lại phương pháp từ B vào phạm vi của A:

struct B{ 
    void method1(){}; 
    void method2(){}; 
}; 
struct A : private B{ 
    using B::method1; 
}; 

A a; 
a.method1(); 
a.method2(); //error method2 is not accesible 
3

Để những người được cho thấy thành phần .. Điều này có thể không phải là cách tốt nhất có thể để đi về mọi thứ. Sự hiểu biết của tôi là Nguyên tắc thay thế Liskov chỉ nói rằng có khả năng các hàm từ lớp cơ sở đang được sử dụng trên đứa trẻ, không phải là chúng nhất thiết phải là. Ví dụ, đối với một lớp cơ sở cụ thể, bạn có thể có nhiều hàm về cơ bản thực hiện cùng một thao tác, nhưng đối với các trường hợp cụ thể khác nhau. Trong lớp dẫn xuất, bạn có thể muốn trừu tượng hóa các hàm công cộng này để ủng hộ việc đơn giản hóa giao diện của người dùng. Đây là nơi thừa kế riêng có thể được sử dụng. Thừa kế riêng cũng có thể là một điều cần thiết, nếu chúng ta có các hàm được bảo vệ trong lớp cơ sở mà chúng ta không muốn người dùng của lớp cơ sở gọi, nhưng sẽ là vô giá đối với lớp dẫn xuất.

Tóm lại, nếu bạn CÓ, hãy sử dụng quyền thừa kế riêng tư, nhưng bố cục được ưu tiên trong hầu hết các trường hợp.

8

Bên cạnh những cách mô tả trong các câu trả lời-phần trước, thừa kế riêng, và thừa kế không tin nhưng với phương pháp di truyền khai báo là private-một cách khác là một cách rõ ràng delete phương pháp kế thừa:

#include <iostream> 

struct A { 
    void foo() { std::cout << "foo\n"; } 
}; 

struct B : A { 
    void foo() = delete; 
}; 

int main() { 
    B b; 
    b.foo(); // COMPILER ERROR 
} 

Mặc dù cuộc gọi b.foo() tạo ra một lỗi biên dịch, mã khách hàng vẫn có thể gọi phiên bản của lớp cơ sở bằng cách đủ điều kiện với tên lớp cơ sở A:

b.A::foo(); // compiles, outputs 'foo' to console 

expli này cách xóa cit hoạt động khi fookhông phương pháp không xóa ảo trong A. Bởi C++ 11 Standard §10.3/16, việc xóa rõ ràng này không đúng khi phương thức đã xóa trong lớp dẫn xuất ghi đè một phương thức ảo không xóa của lớp cơ sở. Để biết thêm thông tin về hạn chế này, hãy xem câu trả lời cho câu hỏi SO C++11 Delete Overriden Method.

1

Vẫn còn cách tiếp cận khác.

class A{ 
    void f1(); 
    void f2(); 
    void f3(); 
} 

class BInterface{ 
    void f2(); 
    void f3(); 
} 

class B : public A, BInterface 
{ 
} 

BInterface b = new B(); 
b->f1(); //doesn't work since f1 is not declared in BInterface 
b->f2(); //should work 
b->f3(); //should work 
delete(b); 

Sử dụng BInterface làm bộ lọc cho lớp kế thừa để loại trừ các phương pháp không mong muốn. Nguyên lý thay thế Liskov không bị vi phạm trong trường hợp này vì đối tượng lớp BInterface không phải là đối tượng của lớp A mặc dù đối tượng lớp B là đối tượng của lớp BInterface.

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