Làm thế nào để C ++ xử lý nhiều kế thừa với một tổ tiên chung được chia sẻ?


13

Tôi không phải là một anh chàng C ++, nhưng tôi buộc phải suy nghĩ về điều này. Tại sao nhiều kế thừa có thể có trong C ++, nhưng không có trong C #? (Tôi biết về vấn đề kim cương , nhưng đó không phải là điều tôi đang hỏi ở đây). Làm thế nào để C ++ giải quyết sự mơ hồ của các chữ ký phương thức giống hệt nhau được kế thừa từ nhiều lớp cơ sở? Và tại sao thiết kế tương tự không được tích hợp vào C #?


3
Để hoàn thiện, "vấn đề kim cương" là gì?
jcolebrand

1
@jcolebrand en.wikipedia.org/wiki/Mult Môn_inherribution xem vấn đề Kim cương
l46kok

1
Chắc chắn, tôi đã googled nó, nhưng làm thế nào để tôi biết đó là ý nghĩa của Sandeep? Nếu bạn định tham chiếu một cái gì đó bằng một cái tên tối nghĩa, khi "Đa thừa kế với một tổ tiên chung được chia sẻ" trực tiếp hơn ...
jcolebrand

1
@jcolebrand Tôi đã chỉnh sửa nó để phản ánh những gì tôi nhận được từ câu hỏi. Tôi giả sử anh ta có nghĩa là vấn đề kim cương được tham chiếu trên wikipedia từ ngữ cảnh. Lần tới hãy bỏ bình luận thân thiện, VÀ sử dụng công cụ chỉnh sửa được đề xuất mới sáng bóng :)
Earlz

1
@Earlz chỉ hoạt động khi tôi hiểu tài liệu tham khảo. Nếu không, tôi đang thực hiện một chỉnh sửa xấu, và đó chỉ là juju xấu.
jcolebrand

Câu trả lời:


24

Tại sao nhiều kế thừa có thể có trong C ++, nhưng không có trong C #?

Tôi nghĩ (không có tài liệu tham khảo khó khăn), rằng trong Java họ muốn hạn chế tính biểu cảm của ngôn ngữ để làm cho ngôn ngữ dễ học hơn và bởi vì mã sử dụng nhiều kế thừa thường quá phức tạp đối với chính nó. Và vì thực hiện nhiều kế thừa đầy đủ phức tạp hơn rất nhiều để thực hiện, do đó, nó đã đơn giản hóa máy ảo rất nhiều (nhiều kế thừa tương tác rất tệ với trình thu gom rác, vì nó yêu cầu giữ con trỏ vào giữa đối tượng (ở đầu cơ sở) )

Và khi thiết kế C # Tôi nghĩ rằng họ đã xem xét Java, đã thấy rằng nhiều kế thừa thực sự không bị bỏ sót nhiều và được chọn để giữ mọi thứ đơn giản.

Làm thế nào để C ++ giải quyết sự mơ hồ của các chữ ký phương thức giống hệt nhau được kế thừa từ nhiều lớp cơ sở?

không . Có một cú pháp để gọi phương thức lớp cơ sở từ cơ sở cụ thể một cách rõ ràng, nhưng không có cách nào để ghi đè chỉ một trong các phương thức ảo và nếu bạn không ghi đè phương thức trong lớp con, bạn không thể gọi nó mà không chỉ định cơ sở lớp học.

Và tại sao thiết kế tương tự không được tích hợp vào C #?

Không có gì để kết hợp.


Vì Giorgio đã đề cập đến các phương thức mở rộng giao diện trong các bình luận, tôi sẽ giải thích các mixin là gì và cách chúng được triển khai bằng nhiều ngôn ngữ khác nhau.

Các giao diện trong Java và C # chỉ giới hạn cho các phương thức khai báo. Nhưng các phương thức phải được thực hiện trong mỗi lớp kế thừa giao diện. Tuy nhiên, có một lớp giao diện lớn, nơi sẽ hữu ích khi cung cấp các cài đặt mặc định của một số phương thức theo các phương thức khác. Ví dụ phổ biến là so sánh (bằng ngôn ngữ giả):

mixin IComparable {
    public bool operator<(IComparable r) = 0;
    public bool operator>(IComparable r) { return r < this; }
    public bool operator<=(IComparable r) { return !(r < this); }
    public bool operator>=(IComparable r) { return !(r > this); }
    public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
    public bool operator!=(IComparable r) { return r < this || r > this; }
};

Sự khác biệt so với lớp đầy đủ là điều này không thể chứa bất kỳ thành viên dữ liệu nào. Có một số lựa chọn để thực hiện điều này. Rõ ràng nhiều thừa kế là một. Nhưng nhiều kế thừa là khá phức tạp để thực hiện. Nhưng nó không thực sự cần thiết ở đây. Thay vào đó, nhiều ngôn ngữ thực hiện điều này bằng cách tách mixin trong một giao diện, được thực hiện bởi lớp và một kho lưu trữ các phương thức triển khai, được đưa vào chính lớp đó hoặc một lớp cơ sở trung gian được tạo ra và chúng được đặt ở đó. Điều này được triển khai trong Ruby và D , sẽ được triển khai trong Java 8 và có thể được triển khai thủ công trong C ++ bằng cách sử dụng mẫu khuôn mẫu định kỳ tò mò . Ở trên, ở dạng CRTP, trông giống như:

template <typename Derived>
class IComparable {
    const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
    bool operator>(const IComparable &r) const { r._d() < _d(); }
    bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
    ...
};

và được sử dụng như:

class Concrete : public IComparable<Concrete> { ... };

Điều này không yêu cầu bất cứ điều gì được khai báo ảo như lớp cơ sở thông thường, vì vậy nếu giao diện được sử dụng trong các mẫu sẽ mở các tùy chọn tối ưu hóa hữu ích. Lưu ý rằng trong C ++, điều này có thể vẫn được kế thừa như là cha mẹ thứ hai, nhưng trong các ngôn ngữ không cho phép nhiều thừa kế, nó được chèn vào chuỗi thừa kế duy nhất, vì vậy nó giống như

template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };

Việc thực hiện trình biên dịch có thể hoặc không thể tránh các công văn ảo.

Một triển khai khác đã được chọn trong C #. Trong C #, các triển khai là các phương thức tĩnh của lớp hoàn toàn riêng biệt và cú pháp gọi phương thức được trình biên dịch giải thích hợp nếu một phương thức của tên đã cho không tồn tại, nhưng một "phương thức mở rộng" được định nghĩa. Điều này có lợi thế là các phương thức mở rộng có thể được thêm vào lớp đã được biên dịch và nhược điểm là các phương thức đó không thể bị ghi đè, ví dụ như để cung cấp phiên bản tối ưu hóa.


Người ta có thể muốn đề cập rằng nhiều kế thừa sẽ được đưa vào Java 8 với các phương thức mở rộng giao diện.
Giorgio

@Giorgio: Không, nhiều kế thừa chắc chắn sẽ không được giới thiệu trong Java. Mixins sẽ là một điều rất khác biệt, mặc dù nó bao gồm nhiều lý do còn lại để sử dụng nhiều kế thừa và hầu hết các lý do để sử dụng mẫu khuôn mẫu định kỳ (CRTP) và hoạt động chủ yếu như CRTP, không giống như nhiều kế thừa.
Jan Hudec

Tôi nghĩ rằng nhiều kế thừa không yêu cầu con trỏ vào giữa một đối tượng. Nếu có, kế thừa nhiều giao diện cũng sẽ yêu cầu nó.
Svick

@svick: Không, không. Nhưng giải pháp thay thế kém hiệu quả hơn nhiều vì nó yêu cầu công văn ảo để truy cập thành viên.
Jan Hudec

+1 cho câu trả lời đầy đủ hơn nhiều so với của tôi.
Nathan C. Tresch

2

Câu trả lời là nó không hoạt động chính xác trong C ++ trong trường hợp xung đột không gian tên. Xem này . Để tránh xung đột không gian tên, bạn phải thực hiện tất cả các loại điều hướng với con trỏ. Tôi đã làm việc tại MS trong nhóm Visual Studio và ít nhất một phần lý do họ phát triển phái đoàn là để tránh xung đột không gian tên hoàn toàn. Trước đây tôi đã nói rằng họ cũng coi Giao diện là một phần của giải pháp đa thừa kế, nhưng tôi đã nhầm. Các giao diện rất tuyệt vời và có thể hoạt động trong C ++, FWIW.

Phái đoàn đặc biệt giải quyết xung đột không gian tên: Bạn có thể ủy quyền cho 5 lớp và tất cả 5 lớp sẽ xuất phương thức của chúng sang phạm vi của bạn với tư cách là thành viên của lớp đầu tiên. Ở bên ngoài tìm kiếm trong IS thừa kế này.


1
Tôi thấy rất khó có thể đây là lý do chính để không đưa MI vào C #. Hơn nữa, bài đăng này không trả lời câu hỏi tại sao MI hoạt động trong C ++ khi bạn không có "xung đột không gian tên".
Doc Brown

@DocBrown Tôi đã làm việc tại MS trong nhóm Visual Studio và tôi đảm bảo với bạn rằng ít nhất một phần lý do họ đã phát triển ủy quyền và giao diện, để tránh xung đột không gian tên hoàn toàn. Đối với chất lượng của câu hỏi, meh. Sử dụng downvote của bạn, những người khác dường như nghĩ rằng nó hữu ích.
Nathan C. Tresch

1
Tôi không có ý định hạ thấp câu trả lời của bạn vì tôi nghĩ đó là một câu trả lời đúng. Nhưng câu trả lời của bạn giả vờ MI hoàn toàn không sử dụng được trong C ++ và đó là lý do họ không giới thiệu nó trong C #. Mặc dù tôi không thích MI rất nhiều trong C ++, tôi nghĩ nó không hoàn toàn không thể hiểu được.
Doc Brown

1
Đó là một trong những lý do và tôi không có ý ám chỉ rằng nó không hoạt động. Khi một cái gì đó không mang tính quyết định trong điện toán, tôi có xu hướng nói nó "bị hỏng" hoặc nó "không hoạt động" khi tôi có ý định "nó giống như voodoo, nó có thể hoặc không hoạt động, thắp nến và cầu nguyện." : D
Nathan C. Tresch

1
"va chạm không gian tên" là không xác định?
Doc Brown
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.