Di sản
Toàn bộ điểm kế thừa là chia sẻ một giao diện và giao thức chung giữa nhiều triển khai khác nhau sao cho một thể hiện của lớp dẫn xuất có thể được xử lý giống hệt với bất kỳ trường hợp nào khác từ bất kỳ loại dẫn xuất nào khác.
Trong kế thừa C ++ cũng mang đến chi tiết triển khai, đánh dấu (hoặc không đánh dấu) hàm hủy là ảo là một chi tiết triển khai như vậy.
Chức năng đóng sách
Bây giờ khi một hàm, hoặc bất kỳ trường hợp đặc biệt nào của nó như hàm tạo hoặc hàm hủy được gọi, trình biên dịch phải chọn thực hiện hàm nào có nghĩa. Sau đó, nó phải tạo mã máy theo ý định này.
Cách đơn giản nhất để làm việc này là chọn hàm tại thời gian biên dịch và phát ra mã máy vừa đủ sao cho bất kể giá trị nào, khi đoạn mã đó thực thi, nó luôn chạy mã cho hàm. Điều này làm việc tuyệt vời ngoại trừ thừa kế.
Nếu chúng ta có một lớp cơ sở với một hàm (có thể là bất kỳ hàm nào, bao gồm hàm tạo hoặc hàm hủy) và mã của bạn gọi một hàm trên nó, điều này có nghĩa là gì?
Lấy từ ví dụ của bạn, nếu bạn gọi initialize_vector()
trình biên dịch phải quyết định xem bạn thực sự có ý định gọi việc thực hiện được tìm thấy Base
hay thực hiện được tìm thấy trong Derived
. Có hai cách để quyết định điều này:
- Đầu tiên là quyết định rằng vì bạn đã gọi từ một
Base
loại, bạn có nghĩa là việc thực hiện Base
.
- Thứ hai là quyết định rằng vì loại thời gian chạy của giá trị được lưu trữ trong
Base
giá trị được nhập có thể là Base
hoặc Derived
quyết định về cuộc gọi sẽ thực hiện, phải được thực hiện trong thời gian chạy khi được gọi (mỗi lần nó được gọi).
Trình biên dịch tại thời điểm này bị lẫn lộn, cả hai tùy chọn đều có giá trị như nhau. Đây là khi virtual
đi vào hỗn hợp. Khi có từ khóa này, trình biên dịch chọn tùy chọn 2 trì hoãn quyết định giữa tất cả các triển khai có thể cho đến khi mã đang chạy với một giá trị thực. Khi từ khóa này vắng mặt, trình biên dịch chọn tùy chọn 1 vì đó là hành vi bình thường khác.
Trình biên dịch vẫn có thể chọn tùy chọn 1 trong trường hợp gọi hàm ảo. Nhưng chỉ khi nó có thể chứng minh rằng đây luôn là trường hợp.
Nhà xây dựng và phá hủy
Vậy tại sao chúng ta không chỉ định một Nhà xây dựng ảo?
Trực giác hơn làm thế nào trình biên dịch sẽ chọn giữa các triển khai giống hệt của hàm tạo cho Derived
và Derived2
? Điều này khá đơn giản, nó không thể. Không có giá trị tồn tại từ đó trình biên dịch có thể tìm hiểu những gì thực sự được dự định. Không có giá trị trước vì đó là công việc của nhà xây dựng.
Vậy tại sao chúng ta cần chỉ định một hàm hủy ảo?
Trực giác hơn làm thế nào trình biên dịch sẽ chọn giữa các triển khai cho Base
và Derived
? Chúng chỉ là các cuộc gọi chức năng, vì vậy hành vi gọi chức năng xảy ra. Nếu không có hàm hủy ảo được khai báo, trình biên dịch sẽ quyết định liên kết trực tiếp với hàm Base
hủy bất kể loại thời gian chạy giá trị.
Trong nhiều trình biên dịch, nếu dẫn xuất không khai báo bất kỳ thành viên dữ liệu nào, cũng không kế thừa từ các loại khác, hành vi trong ~Base()
sẽ phù hợp, nhưng nó không được bảo đảm. Nó sẽ hoạt động hoàn toàn bằng cách tình cờ, giống như đứng trước súng phun lửa chưa được đốt cháy. Bạn ổn trong một thời gian.
Cách chính xác duy nhất để khai báo bất kỳ loại cơ sở hoặc giao diện nào trong C ++ là khai báo một hàm hủy ảo, để hàm hủy chính xác được gọi cho bất kỳ trường hợp cụ thể nào của hệ thống phân cấp loại đó. Điều này cho phép hàm có kiến thức nhất về thể hiện để dọn sạch thể hiện đó một cách chính xác.
~derived()
đại biểu cho hàm hủy của vec. Ngoài ra, bạn đang giả định rằngunique_ptr<base> pt
sẽ biết hàm hủy có nguồn gốc. Nếu không có một phương thức ảo, đây không thể là trường hợp. Mặc dù unique_ptr có thể được cung cấp chức năng xóa là tham số mẫu mà không có bất kỳ biểu diễn thời gian chạy nào và tính năng đó không được sử dụng cho mã này.