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ở?
Nó 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.