Menu

Struct trong C++: gom dữ liệu liên quan vào một kiểu

Giải thích struct trong C++: cách gom các biến liên quan thành một kiểu duy nhất, khai báo và khởi tạo đối tượng struct, thêm hàm thành viên và hàm khởi tạo, và struct thực sự khác class ở điểm nào.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Vì sao cần gom dữ liệu lại với nhau

Từ đầu đến giờ, mỗi biến đều đứng riêng: một int ở chỗ này, một string ở chỗ kia. Nhưng chương trình thực tế làm việc với những thứ gồm nhiều phần: một điểm có x và y, một sinh viên có tên, tuổi và điểm GPA. Truyền chúng đi dưới dạng các biến rời rạc rất dễ sai, vì không có gì ràng buộc chúng với nhau, và một hàm cần cả ba giá trị thì phải nhận cả ba tham số.

Struct giải quyết chuyện này. Struct định nghĩa một kiểu mới gom các biến liên quan, tức các thành viên (member), thành một khối. Sau khi định nghĩa, bạn xem cả khối đó như một giá trị duy nhất để lưu, sao chép và truyền vào hàm.

Bạn truy cập từng thành viên bằng toán tử chấm (s.name, s.age). Dấu chấm phẩy sau dấu } đóng định nghĩa struct là bắt buộc: quên nó là một trong những lỗi biên dịch phổ biến nhất của người mới học C++.

Khởi tạo struct

Gán từng trường bằng tay vẫn được, nhưng dài dòng và dễ quên một thành viên. Cách gọn hơn là aggregate initialization: liệt kê các giá trị trong dấu ngoặc nhọn, theo đúng thứ tự khai báo thành viên.

Cẩn thận với cách khai báo trống: Point p; (không có ngoặc nhọn) khiến x và y chứa giá trị rác, vì các thành viên kiểu có sẵn không tự được gán về 0. Point p{}; thì khởi tạo chúng về 0. Hãy ưu tiên dùng ngoặc nhọn. Bạn cũng có thể đặt giá trị mặc định ngay trong định nghĩa, để ngay cả Point p; cũng bắt đầu sạch sẽ:

Struct làm tham số hàm

Struct là một giá trị duy nhất, nên hàm chỉ cần nhận một tham số thay vì ba. Chỉ cần nhớ rằng truyền struct theo giá trị sẽ sao chép mọi thành viên. Với bất cứ thứ gì lớn hơn vài biến int, hãy truyền bằng tham chiếu const để tránh sao chép, đúng quy tắc bạn đã thấy ở trang tham chiếu.

Trả về cả một struct từ hàm là cách viết chuẩn để trả về nhiều giá trị cùng lúc, gọn hơn nhiều so với việc xoay xở với nhiều tham chiếu đầu ra.

Thêm hàm thành viên và hàm khởi tạo

Struct không chỉ chứa dữ liệu. Nó có thể chứa hàm thành viên (member function) làm việc trên chính các thành viên của nó, và một hàm khởi tạo (constructor) khởi tạo đối tượng ngay khi được tạo ra. Đến đây, struct bắt đầu trông như một đối tượng nhỏ có hành vi riêng.

Bên trong hàm thành viên, các thành viên được truy cập bằng tên (width, height) và chúng trỏ đến bản sao của chính đối tượng này. Đánh dấu area() const cho trình biên dịch biết hàm chỉ đọc đối tượng, nhờ vậy bạn cũng gọi được nó trên các giá trị const Rectangle. Hàm khởi tạo là một chủ đề riêng, bao gồm cả cú pháp danh sách khởi tạo : width(w) kia; trang về hàm khởi tạo sẽ đi sâu hơn.

Struct và class: khác biệt thực sự

Có thể bạn từng nghe "struct dành cho dữ liệu, class dành cho đối tượng". Đó là một quy ước, không phải quy tắc của ngôn ngữ. Trong C++, struct và class gần như là một: khác biệt có sẵn duy nhất là mức truy cập mặc định.

struct S {
    int x;        // public by default
};

class C {
    int x;        // private by default
};

Thành viên của struct là public trừ khi bạn chỉ định khác; thành viên của class mặc định là private. Chỉ vậy thôi: cả hai đều có thể có hàm khởi tạo, hàm thành viên, kế thừa và mọi thứ khác. Bạn thậm chí có thể thêm access specifier tường minh vào struct để ẩn thành viên:

Bài học thực tế: dùng struct cho những gói dữ liệu minh bạch, nơi mọi trường đều được phép truy cập tự do (một Point, một Color, một bản ghi cấu hình), và dùng class khi bạn muốn bảo vệ trạng thái bên trong sau một giao diện public. Trình biên dịch xử lý chúng như nhau; từ khóa chỉ thể hiện ý định của bạn.

Mảng và vector chứa struct

Vì struct là một kiểu bình thường, bạn có thể đặt nhiều struct vào một mảng hoặc một vector và duyệt qua chúng như mọi giá trị khác.

Chú ý các cặp ngoặc nhọn lồng nhau: mỗi {"Keyboard", 49.99} khởi tạo một Product theo kiểu aggregate, còn cặp ngoặc ngoài cùng tạo ra vector. Dùng const Product& trong vòng lặp range-based for giúp tránh sao chép từng struct ở mỗi lần lặp; bỏ dấu & đi là bạn sẽ nhân bản mọi phần tử một cách vô ích.

Tiếp theo: enum

Struct cho phép bạn tạo một kiểu từ nhiều giá trị gom lại với nhau. Chương tiếp theo đi theo hướng ngược lại: enum định nghĩa một kiểu chỉ chứa đúng một giá trị trong một tập nhỏ các giá trị có tên, rất hợp cho những thứ như Color::Red, Direction::North hay trạng thái của một máy trạng thái. Bạn sẽ thấy enum class mang lại các hằng số dễ đọc, an toàn kiểu, kết hợp tự nhiên với struct và class mà bạn đang xây dựng.

Câu hỏi thường gặp

Struct trong C++ là gì?

struct là kiểu do người dùng tự định nghĩa, gom nhiều biến liên quan (gọi là thành viên) dưới một cái tên. Thay vì phải quản lý riêng lẻ các biến string name, int age và double gpa, bạn gom chúng thành một kiểu Student và chỉ cần truyền một đối tượng đó đi khắp nơi. Struct cũng có thể chứa hàm thành viên và hàm khởi tạo: trong C++ hiện đại, struct là một class đầy đủ có các thành viên public theo mặc định.

Struct và class trong C++ khác nhau thế nào?

Về mặt kỹ thuật, chỉ khác ở mức truy cập mặc định: thành viên của struct là public nếu bạn không chỉ định khác, còn thành viên của class mặc định là private. Quy tắc này cũng áp dụng cho kế thừa. Mọi thứ khác như hàm khởi tạo, hàm thành viên, phương thức, kế thừa đều hoạt động giống hệt nhau. Theo quy ước, lập trình viên dùng struct cho những gói dữ liệu đơn thuần và class cho những kiểu có ràng buộc bất biến và phần bên trong cần che giấu.

Khởi tạo struct trong C++ như thế nào?

Cách đơn giản nhất là aggregate initialization bằng dấu ngoặc nhọn theo đúng thứ tự thành viên: Point p{3, 4};. Bạn cũng có thể gán từng trường bằng toán tử chấm (p.x = 3;), đặt giá trị mặc định cho thành viên ngay trong định nghĩa (int x = 0;), hoặc viết hàm khởi tạo để struct tự thiết lập. Nên ưu tiên khởi tạo bằng ngoặc nhọn vì cách này không âm thầm bỏ sót trường chưa khởi tạo.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu