Virtual dispatch và cast: kiểu handle khác với kiểu object
Tiên quyết
Sau bài này bạn sẽ
- So sánh virtual và nonvirtual dispatch qua base handle
- Dùng super trong override mà vẫn giữ contract
- Kiểm $cast thành công, thất bại và destination không đổi
API chung cần gọi đúng hành vi của object thực
Một array Operation giữ ba loại xử lý. Caller chỉ biết API score, còn từng object quyết định công thức. Nếu quên virtual ở extension point base, môi trường có thể luôn gọi hành vi mặc định dù đã tạo derived object đúng.
Virtual chọn override theo object thực
Operation.score trả x+1; DoubleOp trả 2 x; OffsetOp gọi super.score(x)+3 nên trả x+4. TB lưu cả ba qua Operation handles rồi kiểm từng input 0..7. Việc gán handle không clone hoặc đổi runtime type của object.
Method thường vẫn đi theo kiểu handle ở case này
Tag là nonvirtual ở cả Operation và DoubleOp. Qua ops[1] kiểu Operation, tag trả 1; qua twice kiểu DoubleOp, tag trả 2. Đừng dùng kết quả nonvirtual tag để kết luận score cũng dispatch như thế; qualifier được quyết định theo từng method.
Override có thể dựa trên phần base rõ ràng
OffsetOp giữ phép cộng 1 của base rồi thêm 3. Super call trong implementation không ngăn caller qua base handle tới override của OffsetOp. Thay toàn bộ công thức hoặc dùng lại phần base là lựa chọn thiết kế cần được kiểm bằng oracle độc lập.
Downcast có kiểm tra thay vì tin mọi base handle
Function-form $cast(view, ops[1]) thành công vì object là DoubleOp. Cast ops[0] là Operation object vào cùng destination phải trả 0 và giữ view cũ. Main kiểm cả return value lẫn identity sau thất bại; không chỉ nhìn field tình cờ có giá trị đúng.
Đếm đúng số call được cộng vào tổng
Tám input cho ba operation tạo 24 kết quả được cộng: base 36, double 56, offset 60, tổng 152. TB gọi mỗi operation một lần mỗi input rồi dùng observed cho assertions và cộng tổng; không gọi lặp chỉ để kiểm rồi làm lệch accounting.
Derived method virtual không sửa được mọi base API đã nonvirtual
Mutation bỏ virtual ở score của Operation bị assertion virtual dispatch bắt. Không sửa caller sang kiểu DoubleOp để lấy xanh, vì caller qua base handle là mục tiêu của ví dụ. Ghi qualifier ở base cùng contract mà môi trường thực sự dùng.
Dispatch, nonvirtual tag và hai cast
Expected trước chạy: 24 kết quả cộng sum 152, base_tag1/derived_tag2, cast accept 1/reject 0 và destination giữ identity sau reject. Chỉ kiểm function-form $cast/class hierarchy này; không claim mọi cast, null input hoặc X/Z behavior.
Tự kiểm tra
Kiểm cả call site và dynamic object
Với mỗi call, ghi kiểu khai báo handle, kiểu object và qualifier của method trong base API. Với cast, kiểm return trước khi dùng destination và giữ assertion cho failure path. Đừng lấy class name trong biến làm bằng chứng runtime type.
Nguồn và mức độ đối chiếu
- SystemVerilog Virtual Methods — bản chụp resources.zip, Nguồn đào tạo đã đọc. Trích dẫn; nguồn đã đọc các mục liên quan.
- Thí nghiệm nguyên bản ex.b5.virtual-poly, Bằng chứng thực nghiệm. Trích dẫn; nguồn đã đọc toàn văn.
- IEEE 1800-2023 SystemVerilog LRM, Class handles, inheritance, virtual methods, access and parameterization. Chưa đối chiếu với văn bản nguồn (chưa mở).
- Thí nghiệm nguyên bản ex.b5.virtual-poly, Source, self-checks, verification.json và mutation/capability probes. Trích dẫn; nguồn đã đọc toàn văn.
- SystemVerilog Virtual Methods — bản chụp resources.zip, Without virtual; With virtual. Trích dẫn; nguồn đã đọc các mục liên quan.