Upcasting
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 31 trên 61.
Upcasting là việc xem một kiểu cụ thể (kiểu dẫn xuất) như kiểu tổng quát hơn (kiểu cơ sở). Trong mô hình kết hợp của C, điều này có nghĩa là truyền con trỏ đến một struct chứa kiểu cơ sở cho hàm đang chờ kiểu cơ sở đó.
Xét một cấu trúc cơ sở Vehicle và một cấu trúc Car chứa nó:
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // Phần cơ sở là thành viên đầu tiên
int doors;
} Car;Bây giờ, hãy hình dung một hàm tổng quát có thể hoạt động với mọi loại phương tiện:
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}Nhờ quy tắc thành viên đầu tiên, ta có thể truyền Car* cho hàm này bằng cách ép kiểu thành Vehicle*:
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Đầu ra: 50Đây là upcasting: chúng ta chuyển từ một kiểu cụ thể “lên” một kiểu tổng quát hơn. Hàm accelerate không biết mình đang làm việc với một Car; nó chỉ thấy một Vehicle. Nhờ vậy, bạn có thể viết code có thể tái sử dụng, hoạt động với kiểu cơ sở và chấp nhận mọi kiểu dẫn xuất có chứa kiểu đó.
Thử thách
DễHãy xây dựng một hệ thống giao thông đơn giản để minh họa cách upcasting hoạt động. Bạn sẽ tạo kiểu cơ sở Vehicle và kiểu cụ thể hơn Motorcycle, sau đó cho thấy cách một hàm tổng quát có thể làm việc với cả hai thông qua việc ép kiểu con trỏ.
Ưu điểm của upcasting là bạn có thể viết hàm cho kiểu cơ sở, và các hàm đó sẽ hoạt động trơn tru với mọi kiểu dẫn xuất chứa kiểu cơ sở này. Xe máy của bạn “là một” phương tiện, vì vậy mọi hàm nhận Vehicle* đều có thể xử lý nó.
Bạn sẽ tổ chức code trong ba tệp:
vehicle.h: Định nghĩa cấu trúc phân cấp của bạn cùng với các include guard. Tạo cấu trúcVehiclevới hai trường:speed(một số nguyên biểu thị tốc độ hiện tại) vàmax_speed(một số nguyên biểu thị tốc độ tối đa của phương tiện). Sau đó, định nghĩa cấu trúcMotorcyclechứaVehiclelàm thành viên đầu tiên và thêm trườnghas_sidecar(một số nguyên, trong đó 1 nghĩa là có và 0 nghĩa là không). Khai báo hai hàm:boostnhậnVehicle*cùng lượng cần tăng tốc độ, vàprint_motorcyclenhậnMotorcycle*.vehicle.c: Cài đặt cả hai hàm. Hàmboostcần tăng tốc độ của phương tiện theo lượng đã cho, nhưng giới hạn ởmax_speed. Nếu tốc độ mới vượt quá mức tối đa, hãy đặt tốc độ thànhmax_speed. Hàmprint_motorcyclecần hiển thị mọi thông tin của xe máy, bao gồm cả dữ liệu phương tiện được chứa bên trong.main.c: Tạo mộtMotorcyclevà khởi tạo tất cả các trường của nó. Sau đó, minh họa upcasting bằng cách gọi hàmboost: ép kiểuMotorcycle*thànhVehicle*để truyền nó vào hàm tổng quát này. Sau khi tăng tốc, hãy in thông tin xe máy để xem kết quả.
Bạn sẽ nhận bốn đầu vào: tốc độ ban đầu của xe máy, tốc độ tối đa, xe có thùng xe bên hay không (1 hoặc 0), và lượng tăng tốc.
Đầu ra sẽ có dạng như sau:
Speed: 75
Max Speed: 120
Sidecar: YesTrong đó, 75 là tốc độ sau khi tăng tốc (ban đầu 50 + tăng 25), 120 là tốc độ tối đa, còn Yes hoặc No cho biết xe máy có thùng xe bên hay không. Nếu tăng tốc khiến tốc độ vượt quá max_speed, cần giới hạn tốc độ ở max_speed.
Bài học chính ở đây là boost không biết gì về xe máy. Hàm này chỉ làm việc với Vehicle. Tuy nhiên, nhờ upcasting, xe máy của bạn có thể dùng hàm tổng quát này để thay đổi dữ liệu phương tiện được chứa bên trong.
Tự làm thử
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// Đọc các giá trị đầu vào
scanf("%d", &initial_speed);
scanf("%d", &max_speed);
scanf("%d", &has_sidecar);
scanf("%d", &boost_amount);
// TODO: Tạo một Motorcycle và khởi tạo tất cả các trường của nó
// Lưu ý: Motorcycle chứa Vehicle làm thành viên đầu tiên
// TODO: Minh họa ép kiểu lên bằng cách gọi boost
// Ép kiểu Motorcycle* thành Vehicle* để truyền vào hàm boost
// TODO: In ra thông tin của xe mô tô
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online