Menu
CoddyTech

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 đó.

challenge icon

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úc Vehicle vớ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úc Motorcycle chứa Vehicle làm thành viên đầu tiên và thêm trường has_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: boost nhận Vehicle* cùng lượng cần tăng tốc độ, và print_motorcycle nhận Motorcycle*.
  • vehicle.c: Cài đặt cả hai hàm. Hàm boost cầ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ành max_speed. Hàm print_motorcycle cầ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ột Motorcycle và 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àm boost: ép kiểu Motorcycle* thành Vehicle* để 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: Yes

Trong đó, 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;
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch C online