2009-01-27 62 views
37

Có bất kỳ lý do nào để thực hiện các quyền trên hàm ảo C++ ghi đè khác với lớp cơ sở không? Có nguy hiểm nào khi làm như vậy không?Ghi đè các chức năng ảo công cộng với các chức năng riêng tư trong C++

Ví dụ:

class base { 
    public: 
     virtual int foo(double) = 0; 
} 

class child : public base { 
    private: 
     virtual int foo(double); 
} 

Các C++ faq nói rằng đó là một ý tưởng tồi, nhưng không nói lý do tại sao.

Tôi đã thấy thành ngữ này trong một số mã và tôi tin rằng tác giả đã cố gắng để làm cho lớp cuối cùng, dựa trên giả định rằng không thể ghi đè lên một hàm thành viên riêng tư. Tuy nhiên, This article hiển thị ví dụ về các hàm riêng tư ghi đè. Tất nhiên another part of the C++ faq khuyên bạn không nên làm như vậy.

câu hỏi cụ thể của tôi:

  1. Có bất kỳ vấn đề kỹ thuật với việc sử dụng một sự cho phép khác nhau cho các phương pháp ảo trong các lớp học có nguồn gốc vs lớp cơ sở?

  2. Có lý do chính đáng nào để làm như vậy không?

+3

phát minh lại "được bảo vệ" phải không? –

Trả lời

27

Vấn đề là phương pháp lớp cơ sở là cách khai báo giao diện của lớp. Đó là, trong bản chất nói rằng, "Đây là những điều bạn có thể làm cho các đối tượng của lớp này."

Khi ở trong lớp Có nguồn gốc, bạn tạo thứ gì đó mà Cơ sở đã tuyên bố là riêng tư, bạn đang lấy cái gì đó đi. Bây giờ, mặc dù một đối tượng có nguồn gốc" là một "đối tượng cơ sở, một thứ mà bạn có thể làm đối tượng lớp Cơ sở mà bạn không thể làm đối tượng đối tượng Lớp gốc, hãy phá vỡ Liskov Substitution Prinicple

Điều này có gây ra sự cố "kỹ thuật" trong chương trình của bạn không? Nhưng có thể điều đó có nghĩa là đối tượng của lớp học sẽ không hoạt động theo cách người dùng của bạn mong đợi họ hành xử.

Nếu bạn thấy mình trong tình huống mà đây là những gì bạn muốn (ngoại trừ trường hợp phương pháp không dùng nữa được đề cập trong câu trả lời khác) s là bạn có một mô hình thừa kế mà thừa kế không thực sự là mô hình "is-a" (ví dụ: Ví dụ của Scott Myers Square kế thừa từ Rectangle, nhưng bạn không thể thay đổi chiều rộng của Square độc ​​lập với chiều cao của nó như bạn có thể cho một hình chữ nhật) và bạn có thể cần phải xem xét lại các mối quan hệ lớp của bạn.

6

Không có vấn đề kỹ thuật, nhưng bạn sẽ kết thúc với một tình huống mà các chức năng công khai sẽ phụ thuộc vào việc bạn có con trỏ cơ bản hay có nguồn gốc hay không.

Điều này theo ý kiến ​​của tôi sẽ lạ và khó hiểu.

35

Bạn nhận được kết quả đáng ngạc nhiên rằng nếu bạn có con, bạn không thể gọi foo, nhưng bạn có thể truyền nó đến một cơ sở và sau đó gọi foo.

child *c = new child(); 
c->foo; // compile error (can't access private member) 
static_cast<base *>(c)->foo(); // this is fine, but still calls the implementation in child 

Tôi cho rằng bạn có thể có một ví dụ mà bạn không muốn hàm bị lộ, ngoại trừ khi bạn đang xử lý nó làm thể hiện của lớp cơ sở. Nhưng thực tế là tình huống đó bật lên sẽ gợi ý một thiết kế OO xấu ở đâu đó dọc theo dòng mà có lẽ nên được tái cấu trúc.

+0

Qt đã làm điều đó và đưa tôi đến đây :) Cảm ơn. – mlvljr

+3

Ngoài ra: là một IDE, bạn hút! – mlvljr

+21

@mlvljr: Nhưng như một sự kiện thiên văn, tôi khá tuyệt vời! – Eclipse

3

Có thể thực hiện và đôi khi sẽ mang lại lợi ích. Ví dụ, trong codebase của chúng ta, chúng ta đang sử dụng một thư viện có chứa một lớp với một hàm public mà chúng ta sử dụng, nhưng bây giờ không khuyến khích sử dụng do các vấn đề tiềm ẩn khác (có các phương thức an toàn hơn để gọi). Chúng tôi cũng xảy ra để có một lớp học bắt nguồn từ lớp đó mà rất nhiều mã của chúng tôi sử dụng trực tiếp. Vì vậy, chúng tôi đã thực hiện các chức năng cho riêng tư trong lớp dẫn xuất để giúp mọi người nhớ không sử dụng nó nếu họ có thể giúp nó. Nó không loại bỏ khả năng sử dụng nó, nhưng nó sẽ nắm bắt một số sử dụng khi mã cố gắng biên dịch, thay vì sau này trong các đánh giá mã.

1
  1. Không có vấn đề kỹ thuật, nếu bạn có ý nghĩa kỹ thuật vì có chi phí thời gian chạy ẩn.
  2. Nếu bạn kế thừa cơ sở công khai, bạn không nên làm điều này. Nếu bạn kế thừa thông qua bảo vệ hoặc riêng tư, thì điều này có thể giúp ngăn chặn việc sử dụng các phương thức không có ý nghĩa trừ khi bạn có một con trỏ cơ sở.
4

Có thể rất hữu ích nếu bạn đang sử dụng quyền thừa kế riêng - nghĩa là bạn muốn sử dụng lại chức năng (tùy chỉnh) của một lớp cơ sở, nhưng không sử dụng giao diện.

1

Trường hợp sử dụng tốt cho kế thừa riêng tư là giao diện sự kiện Trình lắng nghe/Quan sát viên.

Ví dụ mã cho đối tượng cá nhân:

class AnimatableListener { 
    public: 
    virtual void Animate(float delta_time); 
}; 

class BouncyBall : public AnimatableListener { 
    public: 
    void TossUp() {} 
    private: 
    void Animate(float delta_time) override { } 
}; 

Một số người dùng của đối tượng muốn các chức năng phụ huynh và một số muốn các chức năng con:

class AnimationList { 
    public: 
    void AnimateAll() { 
     for (auto & animatable : animatables) { 
     // Uses the parent functionality. 
     animatable->Animate(); 
     } 
    } 
    private: 
    vector<AnimatableListener*> animatables; 
}; 

class Seal { 
    public: 
    void Dance() { 
     // Uses unique functionality. 
     ball->TossUp(); 
    } 
    private: 
    BouncyBall* ball; 
}; 

Bằng cách này, AnimationList có thể giữ một tham chiếu đến cha mẹ và sử dụng chức năng của cha mẹ. Trong khi các Seal giữ một tham chiếu đến đứa trẻ và sử dụng chức năng con độc đáo và bỏ qua của cha mẹ. Trong ví dụ này, số Seal không được gọi là Animate. Bây giờ, như đã đề cập ở trên, Animate có thể được gọi bằng cách truyền tới đối tượng cơ sở, nhưng điều đó khó khăn hơn và thường không được thực hiện.

+0

Giao diện người nghe/Quan sát viên, nhận cuộc gọi lại. – dashesy

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