Trình bao bọc tổng quát
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 50 trên 61.
Bản thân void* có thể lưu trữ mọi loại dữ liệu, nhưng có một nhược điểm nghiêm trọng: bạn không biết nó đang chứa kiểu dữ liệu nào. Để tạo ra các vùng chứa tổng quát thực sự hữu ích, chúng ta cần ghép con trỏ dữ liệu với thông tin kiểu dữ liệu. Đây là lúc mẫu Generic Wrapper phát huy tác dụng.
Ý tưởng rất đơn giản: tạo một struct gộp void* với một enum để xác định kiểu dữ liệu được lưu trữ. Nhờ vậy, wrapper luôn biết cách diễn giải nội dung bên trong.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Khi lưu trữ dữ liệu, bạn cấp phát bộ nhớ cho giá trị thực tế và ghi lại kiểu dữ liệu của nó. Khi truy xuất, bạn kiểm tra kiểu dữ liệu trước khi ép kiểu:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}
void print_wrapper(Wrapper* w) {
if (w->type == TYPE_INT) {
printf("%d\n", *(int*)w->data);
} else if (w->type == TYPE_STRING) {
printf("%s\n", (char*)w->data);
}
}Mẫu này là nền tảng để xây dựng các vùng chứa generic an toàn kiểu trong C. enum đóng vai trò như một thẻ kiểu khi chạy, giúp code đưa ra quyết định an toàn về cách xử lý dữ liệu được lưu trữ.
Thử thách
DễHãy xây dựng một hệ thống bao bọc tổng quát, an toàn về kiểu dữ liệu, có thể lưu số nguyên hoặc chuỗi, đồng thời theo dõi kiểu dữ liệu mà nó chứa để ta có thể truy xuất và in dữ liệu một cách an toàn sau này.
Bạn sẽ sắp xếp code trong ba tệp:
wrapper.h: Định nghĩa một enumDataTypevới các giá trịTYPE_INTvàTYPE_STRING. Sau đó, định nghĩa một structWrapperchứa con trỏ dữ liệuvoid*cùng một trườngDataType. Khai báo nguyên mẫu các hàm để tạo wrapper và in nội dung của chúng.wrapper.c: Triển khai chức năng của wrapper:wrap_int: nhận một số nguyên, cấp phát bộ nhớ cho số đó, lưu giá trị và trả về một Wrapper có thẻ kiểu dữ liệu phù hợpwrap_string: nhận một chuỗi (char*), cấp phát bộ nhớ và sao chép chuỗi, rồi trả về một Wrapper có thẻ kiểu chuỗiprint_wrapper: kiểm tra thẻ kiểu dữ liệu và in dữ liệu phù hợp (số nguyên giữ nguyên, chuỗi giữ nguyên)free_wrapper: giải phóng dữ liệu được cấp phát động bên trong wrapper
main.c: Kết hợp tất cả. Đọc ký hiệu kiểu dữ liệu (icho số nguyên,scho chuỗi), rồi đọc giá trị. Tạo wrapper phù hợp, in nội dung và giải phóng bộ nhớ.
Chương trình của bạn sẽ nhận hai đầu vào:
- Ký hiệu kiểu dữ liệu:
icho số nguyên hoặcscho chuỗi - Giá trị cần bao bọc
Ví dụ đầu ra khi đầu vào là i và 42:
42Ví dụ đầu ra khi đầu vào là s và Hello:
HelloVí dụ đầu ra khi đầu vào là i và -100:
-100Ví dụ đầu ra khi đầu vào là s và Generic Programming:
Generic ProgrammingNhớ dùng include guard trong tệp tiêu đề. Khi sao chép chuỗi, hãy cấp phát đủ chỗ cho chuỗi và ký tự kết thúc null bằng strlen và strcpy.
Tự làm thử
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // Đọc bỏ ký tự xuống dòng
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Tạo wrapper cho số nguyên bằng wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Xóa ký tự xuống dòng ở cuối nếu có
str[strcspn(str, "\n")] = '\0';
// TODO: Tạo wrapper cho chuỗi bằng wrap_string
}
// TODO: In ra nội dung của wrapper bằng print_wrapper
// TODO: Giải phóng bộ nhớ của wrapper bằng free_wrapper
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ật7Con trỏ hàm
Khai báo con trỏ hàmGọi con trỏ hàmDùng typedef cho con trỏ hàmTruyền hàm làm đối sốÔn tập: điều phối máy tính10Bộ chứa tổng quát
Ôn tập con trỏ voidTrình bao bọc tổng quátHoán đổi tổng quátSo sánh tổng quátÔn tập: mảng tổng quá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