Menu
CoddyTech

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ị.

challenge icon

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úc Student vớ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ên createStudent, nhận tên, mã ID và GPA, rồi trả về một cấu trúc Student.
  • GradeUtils.h: Tạo các hàm tiện ích để xử lý điểm số.

    Định nghĩa một hàm getGradeInfo nhận điểm số dạng số nguyên và trả về một std::tuple gồ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 makeScorePair nhận tên khóa học (std::string) và điểm số (int), rồi trả về một std::pair chứa hai giá trị đó.

  • main.cpp: Đọc bốn đầu vào:
    1. Tên học sinh (chuỗi)
    2. Mã ID học sinh (số nguyên)
    3. Tên khóa học (chuỗi)
    4. Đ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:

    1. Gọi createStudent vớ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])
    2. Gọi makeScorePair vớ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]
    3. Gọi getGradeInfo vớ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:
      Grade: [letter]
      Points: [points]
      Passing: [yes/no]
      (In ra "yes" nếu đạt, "no" nếu không đạt)

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: yes

Với các đầu vào Bob, 2002, Physics và 55:

Student: Bob (ID: 2002)
Course: Physics, Score: 55
Grade: F
Points: 0
Passing: no

Nhớ 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;
}
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