2010-06-15 35 views
5

Tôi đang cố ngăn một lớp không thể chuyển đổi con trỏ 'this' thành con trỏ của một trong các giao diện của nó. Tôi làm điều này bằng cách sử dụng thừa kế riêng thông qua một lớp proxy trung gian. Vấn đề là tôi tìm thấy sự thừa kế riêng làm cho tất cả các thành viên tĩnh công cộng và các kiểu của lớp cơ sở không thể tiếp cận được với tất cả các lớp trong lớp kế thừa trong hệ thống phân cấp.C++ private inheritance và static members/types

class Base 
{ 
public: 
    enum Enum 
    { 
     value 
    }; 
}; 

class Middle : private Base 
{ 
}; 

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     Base::Enum e = Base::value; // doesn't compile BAD!  
     Base* base = this; // doesn't compile GOOD! 
    } 
}; 

Tôi đã thử điều này trong cả hai phiên bản VS2008 (phiên bản bắt buộc) và VS2010, không hoạt động.

Có ai nghĩ đến giải pháp thay thế không? Hoặc một cách tiếp cận khác để dừng chuyển đổi?

Ngoài ra tôi là curios của hành vi, nó chỉ là một tác dụng phụ của việc thực hiện trình biên dịch, hoặc là nó bằng cách thiết kế? Nếu theo thiết kế thì tại sao? Tôi luôn luôn suy nghĩ về sự thừa kế riêng tư có nghĩa là không ai biết được kế thừa của Middle từ Base. Tuy nhiên, hành vi trưng bày ngụ ý sự thừa kế riêng có nghĩa là nhiều hơn thế, trên thực tế, Child có ít quyền truy cập vào Base hơn bất kỳ vùng tên nào không có trong hệ thống phân cấp lớp!

+0

+1: Thật là một câu hỏi rất thú vị. –

Trả lời

6

Bạn sẽ có thể truy cập vào Base::Enum bởi đủ điều kiện đầy đủ về nó:

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     ::Base::Enum e = ::Base::value; 
    } 
}; 

Đây là hành vi theo quy định của ngôn ngữ (C++ 03 §11.2/3):

Lưu ý: Thành viên của một lớp cơ sở riêng có thể không truy cập được như một tên thành viên được kế thừa, nhưng có thể truy cập trực tiếp.

Tiếp theo là ví dụ mở rộng có hiệu quả tương tự như mã mẫu của bạn. Tuy nhiên, có vẻ như Visual C++ 2008 cũng không phải Visual C++ 2010 thực hiện chính xác điều này, vì vậy trong khi bạn có thể sử dụng loại ::Base::Enum, bạn vẫn không thể truy cập ::Base::value. (Trên thực tế, Visual C++ dường như đã nhận được rất nhiều điều này sai, vì nó không chính xác cho phép bạn sử dụng không đủ điều kiện Base::Enum).

Để "đi lại" vấn đề, bạn có thể thêm bằng tờ khai đến lớp Middle:

class Middle : private Base 
{ 
protected: 

    using Base::Enum; 
    using Base::value; 
}; 

này sẽ không cho phép bạn sử dụng Base::Enum hoặc Base::value trong lớp Child của bạn, nhưng nó sẽ cho phép bạn sử dụng số Enumvalue hoặc Middle::EnumMiddle::value.

+1

Tôi đã gửi báo cáo lỗi về Microsoft Connect liên quan đến lỗi Visual C++: https://connect.microsoft.com/VisualStudio/feedback/details/567693/ –

+0

Cảm ơn câu trả lời chi tiết của bạn. Làm phiền nó gây ra bởi một lỗi. Thật không may khi thêm vào việc sử dụng lớp Middle không phải là một lựa chọn trong trường hợp thế giới thực của tôi, vì Middle là một lớp được tạo khuôn mẫu nhận lớp cơ sở làm đối số Type. Lựa chọn duy nhất của tôi là di chuyển enum ra ngoài giao diện. – WearyMonkey

+0

Tôi sẽ lưu ý rằng "ghi chú" từ tiêu chuẩn mà tôi trích dẫn thực sự không chỉ định bất cứ điều gì, nó chỉ giải thích hành vi này. Tôi đoán là đặc điểm kỹ thuật thực tế của hành vi này được lan truyền qua các phần khác nhau mô tả cách phân giải tên diễn ra. –

1

Tôi chỉ có một câu hỏi: tại sao bạn lại thừa kế riêng tư?

Thừa kế là khá một khái niệm bị hỏng, theo ý kiến ​​của tôi, bởi vì nó vi phạm nguyên tắc Một Trách nhiệm:

  • bạn thừa hưởng giao diện
  • bạn thừa kế thực hiện

Đáng tiếc là thừa kế là cần thiết cho đa hình trong mã hướng đối tượng, vì vậy bạn không thể tránh xa nó trong trường hợp này.Tuy nhiên, ở đây bạn rõ ràng muốn KHÔNG sử dụng đa hình, vì vậy tôi thấy mình tự hỏi tại sao sử dụng thừa kế ở tất cả, vì nó là sử dụng thú vị duy nhất của nó (imo).

Thay vào đó, trong C++, bạn có thể sử dụng:

  • Thành phần, cho tái sử dụng mã
  • chức năng miễn phí (được định nghĩa trong namespace của riêng mình)
  • using, typedef vv ... để đưa các đối tượng từ bên ngoài lớp học

Ví dụ của bạn dường như bị ràng buộc ở đây, nhưng tôi cũng bao bọc enums của mình vào struct để ngăn chặn ô nhiễm không gian tên bởi hàng nghìn ký hiệu (và vì struct có thể được sử dụng làm thông số mẫu trong đó không gian tên không thể).

struct MyEnum 
{ 
    enum type 
    { 
    value 
    }; 
}; 

class Child 
{ 
public: 
    typedef MyEnum::type Enum; 

    Child(Enum e = MyEnum::value); 

private: 
}; 

tôi không thấy bất cứ điều gì sai trái với đủ điều kiện tên, thay vào đó tôi cảm thấy nó làm cho nó dễ dàng hơn để đọc một lần nữa, vì bạn biết đó là enum chúng ta đang nói về ...

Thực sự, private thừa kế tốt nhất nên tránh (và thường được thay thế bằng Thành phần). Trường hợp hợp lệ duy nhất (imo) là để Tối ưu hóa cơ sở trống ... và thẳng thắn, không thường xuyên bạn cần nó (như thường lệ với tối ưu hóa).

+0

Cách sử dụng khác để thừa kế riêng là nếu bạn cần quyền truy cập vào các thành viên được bảo vệ của một lớp. Đó không phải là một tình huống lý tưởng, nhưng nó xuất hiện trong những dịp hiếm hoi. Tôi chắc chắn đồng ý tốt nhất nên tránh. –

+0

Tôi thường thích tách ra ở điểm này: Tôi tạo một tầng lớp trung lưu kế thừa công khai và sử dụng bố cục. –

+0

Tôi chắc chắn đồng ý rằng bố cục thích hợp hơn với kế thừa khi có thể. Tuy nhiên lớp cơ sở trong vấn đề thế giới thực của tôi là lớp lõi trong một cơ sở mã lớn cũ, làm một công việc refactor đúng nghĩa là viết lại hầu hết mã. – WearyMonkey