Trong chương trước, chúng ta đã thảo luận về tính chất của đối tượng trong lập trình OOP (object composition)), trong đó các class phức tạp được xây dựng từ các class và các kiểu dữ liệu đơn giản hơn. Kết hợp đối tượng là một giải pháp hoàn hảo để xây dựng các đối tượng mới mà sở hữu một mối quan hệ “has-a” (có một) với các class thành phần/bộ phận khác của chúng. Tuy nhiên, kết hợp đối tượng chỉ là một trong hai cách chính mà C++ hỗ trợ cho chúng ta để xây dụng được các class phức tạp. Cách thứ hai chính là thông qua sự kế thừa, đại diện cho mối quan hệ “is-a” (là một) giữa hai đối tượng được mô hình hóa trong lập trình OOP.
Không giống như kết hợp các đối tượng, là một quá trình bao gồm việc tạo ra các đối tượng mới bằng cách kết hợp và kết nối các đối tượng khác, kế thừa sẽ tạo ra các đối tượng mới bằng cách sử dụng lại trực tiếp các thuộc tính (biến) và các hành vi (hàm) của các đối tượng khác, và sau đó mở rộng hoặc tiếp tục sử dụng luôn những gì vừa có được thông qua kế thừa. Giống với kết hợp đối tượng, sự kế thừa xuất hiện ở khắp mọi nơi trong thế giới lập trình thực tế. Có thể giải thích tính kế thừa trong lập trình hướng đối tượng bằng một ví dụ thực tế ^^: Khi được thụ thai, thai nhi sẽ thừa hưởng/kế thừa các genes của cha mẹ, và có được các thuộc tính vật lý từ cả hai người — rồi sau đó thai nhi cũng sẽ tự thêm vào các thuộc tính cá nhân của nó, và đặt lên trên cùng với những gì đã được kế thừa. Các sản phẩm công nghệ (máy tính, điện thoại di động, v.v…) kế thừa các tính năng từ các phiên bản tiền nhiệm của chúng (thường được sử dụng để duy trì tính tương thích với các phiên bản cũ – backwards compatibility). Ví dụ, vi xử lý Intel Pentium kế thừa rất nhiều tính năng được định nghĩa bởi vi xử lý Intel 486, mà chính bản thân vi xử lý này cũng kế thừa nhiều tính năng từ các bộ vi xử lý đã ra mắt trước đó. C++ được thừa hưởng nhiều tính năng từ C, ngôn ngữ mà nó đặt làm cơ sở để phát triển tiếp, và C cũng sở hữu nhiều tính năng từ các ngôn ngữ lập trình đã ra đời trước nó.
Thêm một ví dụ thực tế khác để bạn có thể dễ hình dung hơn, đó là về táo và chuối. Mặc dù táo và chuối là các loại trái cây khác nhau, nhưng cả hai đều có điểm chung cùng là trái cây. Và bởi vì táo và chuối đều là trái cây, nên theo logic đơn giản ta có thể thấy được rằng bất cứ điều gì là đúng đối với các loại trái cây thì cũng đều là đúng đối với táo và chuối. Ví dụ, tất cả các loại trái cây đều có tên, màu sắc và kích cỡ. Do đó, táo và chuối cũng sẽ có tên, màu sắc và kích cỡ. Chúng ta có thể nói rằng táo và chuối đã kế thừa (thu thập được) tất cả những đặc tính/đặc điểm/thuộc tính này của trái cây, bởi vì bản thân chúng chính là trái cây. Bạn biết đấy, tất cả các loại trái cây đều sẽ trải qua quá trình chín dần, nhờ đó chúng trở nên có thể ăn được. Bởi vì táo và chuối đều là trái cây, nên chúng ta cũng biết được rằng táo và chuối sẽ kế thừa hành vi “chín dần” của trái cây.
Khi được mô tả bằng hình vẽ, mối quan hệ giữa táo, chuối và trái cây sẽ trông giống như sau:
Bạn có thể thấy rằng, sơ đồ này xác định một hệ thống kế thừa được phân cấp.
Nội dung chính
Mô hình phân cấp
Mô hình phân cấp (hierarchy) là một biểu đồ cho thấy các đối tượng khác nhau có liên quan như thế nào với nhau. Hầu hết các mô hình phân cấp đều cho thấy một sự tiến triển theo thời gian (vi xử lý 386 => vi xử lý 486 => vi xử lý Pentium), hoặc phân loại mọi thứ theo tiêu chí từ tổng quát đến cụ thể (trái cây => táo => táo red delicious).
Dưới đây là một ví dụ khác về mô hình phân cấp: Một hình vuông cũng chính là một hình chữ nhật, mà hình chữ nhật chính là hình tứ giác, và tất cả chúng đều là một hình (shape). Một tam giác vuông là một tam giác, và cũng là một hình (shape). Khi đặt chúng vào trong một mô hình phân cấp, ta sẽ được:
Biểu đồ này đi từ tổng quát (phía trên) đến cụ thể (phía dưới), trong đó mỗi mục trong hệ thống phân cấp sẽ kế thừa các thuộc tính và hành vi của mục nằm ở trên nó.
Ý nghĩa của chương này
Trong chương này, chúng ta sẽ tìm hiểu những kiến thức cơ bản về cách thức hoạt động của tính kế thừa trong C++.
Chương tiếp theo, chúng ta sẽ thảo luận về việc làm thế nào mà tính kế thừa có thể cho phép triển khai tính đa hình (đây là một khái niệm hết sức quan trọng trong lập trình hướng đối tượng) thông qua các hàm ảo (virtual functions).
Trong quá trình học, chúng ta cũng sẽ nói về các lợi ích chính của tính kế thừa, cũng như một số nhược điểm của nó.