Ràng buộc cấu trúc
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 88 trên 104.
Structured bindings, được giới thiệu trong C++17, cho phép bạn tách nhiều giá trị từ mảng, cặp, tuple hoặc struct thành các biến có tên riêng trong một khai báo. Cách này giúp code gọn gàng và dễ đọc hơn khi làm việc với dữ liệu tổng hợp.
Cú pháp dùng auto với dấu ngoặc vuông chứa tên các biến mới:
#include <iostream>
#include <map>
#include <tuple>
int main() {
// Tách các giá trị từ một cặp
std::pair<std::string, int> person{"Alice", 25};
auto [name, age] = person;
std::cout << name << " is " << age << "\n";
// Tách các giá trị từ một tuple
std::tuple<int, double, char> data{42, 3.14, 'X'};
auto [num, pi, letter] = data;
// Tách các giá trị trong vòng lặp for duyệt theo phạm vi
std::map<std::string, int> scores{{"Bob", 90}, {"Carol", 85}};
for (const auto& [student, score] : scores) {
std::cout << student << ": " << score << "\n";
}
}Phép gán có cấu trúc cũng hoạt động với mảng và các struct tùy chỉnh có thành viên công khai:
struct Point {
double x;
double y;
};
Point getOrigin() { return {0.0, 0.0}; }
int main() {
auto [x, y] = getOrigin();
int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;
}Bạn có thể dùng auto& hoặc const auto& để liên kết bằng tham chiếu, tránh tạo bản sao và cho phép sửa đổi các giá trị ban đầu khi cần. Tính năng này đặc biệt hữu ích khi duyệt qua các vùng chứa STL hoặc xử lý các hàm trả về nhiều giá trị.
Thử thách
DễHãy xây dựng một công cụ phân tích điểm số của học sinh để thể hiện sức mạnh của phép ràng buộc cấu trúc. Bạn sẽ tạo một hệ thống xử lý hồ sơ học sinh và dữ liệu khóa học, dùng phép ràng buộc cấu trúc để giải nén các cặp, tuple và cấu trúc trong toàn bộ code một cách gọn gàng.
Bạn sẽ sắp xếp code trong ba tệp:
Student.h: Định nghĩa một cấu trúcStudentvới các thành viên công khai gồm tên học sinh (std::string), mã ID (int) và GPA (double). Đồng thời tạo một hàm có têncreateStudent, nhận tên, mã ID và GPA, rồi trả về một cấu trúcStudent.GradeUtils.h: Tạo các hàm tiện ích để xử lý điểm số.Định nghĩa một hàm
getGradeInfonhận điểm số dạng số nguyên và trả về mộtstd::tuplegồm ba giá trị: điểm chữ (std::string), điểm quy đổi (double) và trạng thái đạt hay không đạt (bool). Dùng thang điểm sau:- Từ 90 trở lên: "A", 4.0, true
- Từ 80 đến 89: "B", 3.0, true
- Từ 70 đến 79: "C", 2.0, true
- Từ 60 đến 69: "D", 1.0, true
- Dưới 60: "F", 0.0, false
Đồng thời định nghĩa một hàm
makeScorePairnhận tên khóa học (std::string) và điểm số (int), rồi trả về mộtstd::pairchứa hai giá trị đó.main.cpp: Đọc bốn đầu vào:- Tên học sinh (chuỗi)
- Mã ID học sinh (số nguyên)
- Tên khóa học (chuỗi)
- Điểm số (số nguyên)
Dùng phép ràng buộc cấu trúc trong toàn bộ hàm main:
- Gọi
createStudentvới tên, mã ID và GPA giữ chỗ là 0.0. Dùng phép ràng buộc cấu trúc để giải nén cấu trúc trả về thành các biến riêng, sau đó in ra:Student: [name] (ID: [id]) - Gọi
makeScorePairvới tên khóa học và điểm số. Dùng phép ràng buộc cấu trúc để giải nén cặp, sau đó in ra:Course: [course], Score: [score] - Gọi
getGradeInfovới điểm số. Dùng phép ràng buộc cấu trúc để giải nén cả ba phần tử của tuple, sau đó in ra:(In ra "yes" nếu đạt, "no" nếu không đạt)Grade: [letter] Points: [points] Passing: [yes/no]
Ví dụ, với các đầu vào Alice, 1001, Math và 85:
Student: Alice (ID: 1001)
Course: Math, Score: 85
Grade: B
Points: 3
Passing: yesVới các đầu vào Bob, 2002, Physics và 55:
Student: Bob (ID: 2002)
Course: Physics, Score: 55
Grade: F
Points: 0
Passing: noNhớ thêm <tuple> và <utility> ở những nơi cần thiết. Điểm mấu chốt là phép ràng buộc cấu trúc cho phép bạn giải nén các kiểu dữ liệu phức hợp, cặp, tuple và cấu trúc thành các biến có tên trong một khai báo duy nhất, dễ đọc.
Tự làm thử
#include <iostream>
#include <string>
#include "Student.h"
#include "GradeUtils.h"
int main() {
// Đọc dữ liệu đầu vào
std::string studentName;
int studentId;
std::string courseName;
int score;
std::cin >> studentName;
std::cin >> studentId;
std::cin >> courseName;
std::cin >> score;
// TODO: Gọi createStudent với tên, id và GPA tạm là 0.0
// Dùng structured bindings để tách struct: auto [name, id, gpa] = ...
// In ra: Student: [name] (ID: [id])
// TODO: Gọi makeScorePair với tên khóa học và điểm số
// Dùng structured bindings để tách pair: auto [course, sc] = ...
// In ra: Course: [course], Score: [score]
// TODO: Gọi getGradeInfo với điểm số
// Dùng structured bindings để tách tuple: auto [letter, points, passing] = ...
// In ra:
// Grade: [letter]
// Points: [points]
// Đạt: [yes/no] (in ra "yes" nếu passing là true, "no" nếu là false)
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
1Nền tảng về OOP
Tệp bên ngoàiBiên dịch và tạo bản dựng C++Tệp header và tệp nguồnNamespace và phạm viGiới thiệu về OOP trong C++Lớp và đối tượngCon trỏ thisPhương thức (hàm thành viên)Thuộc tính (dữ liệu thành viên)Cơ bản về hàm khởi tạo và hàm hủyÔn tập: máy tính đơn giản4Thuộc tính của lớp
Thành viên đối tượng và thành viên tĩnhHàm truy xuất và hàm thiết lậpHàm thành viên hằngTừ khóa mutablePhương thức và biến tĩnhHàm và lớp bạnÔn tập: Trình quản lý tài khoản ngân hàng7Kế thừa
Kế thừa cơ bảnMức truy cập trong kế thừaThứ tự gọi hàm khởi tạo và hàm hủyGhi đè phương thứcHàm ảo và VTableĐa kế thừaKế thừa ảoÔn tập: hệ phân cấp nhân viên2Quản lý bộ nhớ
Bộ nhớ ngăn xếp và heapCon trỏ và tham chiếuBộ nhớ động (new/delete)Con trỏ thông minh trong C++RAII trong C++Ôn tập: Trình quản lý mảng động5Tính đóng gói
Phạm vi truy cập trong C++Tìm hiểu sâu về phạm vi truy cậpẨn thông tinStruct và lớpLớp lồng nhau và lớp bên trongÔn tập: hệ thống hồ sơ sinh viên8Đa hình
Đa hình lúc biên dịch và khi chạyNạp chồng hàmÔn lại hàm ảoHàm ảo thuần túyLớp trừu tượngThiết kế interface trong C++Ép kiểu động và RTTIÔn tập: Máy tính hình học3Hàm khởi tạo và hàm hủy
Hàm khởi tạo mặc địnhHàm khởi tạo có tham sốHàm khởi tạo sao chépHàm khởi tạo di chuyểnDanh sách khởi tạoHàm khởi tạo ủy quyềnTìm hiểu sâu về hàm hủyQuy tắc 3 / 5 / 0Ôn tập: lớp chuỗi6Nạp chồng toán tử
Giới thiệu về nạp chồng toán tửNạp chồng toán tử số họcNạp chồng toán tử so sánhToán tử luồngNạp chồng toán tử gánNạp chồng toán tử [] và ()Toán tử chuyển đổi kiểuÔn tập: lớp ma trận9Mẫu
Mẫu hàmMẫu lớpChuyên biệt hóa mẫuMẫu biến tham sốCơ bản về SFINAE và đặc điểm kiểuÔn tập: vùng chứa tổng quát12Các tính năng hiện đại của C++
Ngữ nghĩa di chuyển và giá trị rvalueChuyển tiếp hoàn hảoTìm hiểu sâu về biểu thức lambdastd::function và std::bindconstexpr và constevalRàng buộc cấu trúcoptional, variant, anyTự luyện tập: Trình biên dịch C++ online