Lập trình C++ – Kiểu cấu trúc

Kiểu cấu trúc trong C++

Kiểu dữ liệu có cấu trúc được dùng khi ta cần nhóm một số biến dữ liệu luôn đi kèm với nhau. 
Khi đó, việc xử lí trên một nhóm các biến được thực hiện như trên các biến cơ bản thông thường. 
Khai báo cấu trúc 
Trong C++, một cấu trúc do người dùng tự định nghĩa được khai báo thông qua từ khoá struct: 

struct <Tên cấu trúc>{ 
         <Kiểu dữ liệu 1> <Tên thuộc tính 1>; 
<Kiểu dữ liệu 2> <Tên thuộc tính 2>; 
         … 
<Kiểu dữ liệu n> <Tên thuộc tính n>; }; 

Trong đó: 

  • struct: là tên từ khoá để khai báo một cấu trúc, bắt buộc phải có khi định nghĩa cấu trúc. 

  • Tên cấu trúc: là tên do người dùng tự định nghĩa, tuân thủ theo quy tắc đặt tên biến trong C++. Tên này sẽ trở thành tên của kiểu dữ liệu có cấu trúc tương ứng. 

  • Thuộc tính: mỗi thuộc tính của cấu trúc được khai báo như khai báo một biến thuộc kiểu dữ liệu thông thường, gồm có kiểu dữ liệu và tên biến tương ứng. Mỗi khai báo thuộc tính phải kết thúc bằng dấu chấm phẩy “;” như một câu lệnh C++ thông thường. 

Ví dụ, để quản lí nhân viên của một công ty, khi xử lí thông tin về mỗi nhân viên, ta luôn phải xử lí các thông tin liên quan như: 

  • Tên 

  • Tuổi 

  • Chức vụ 

  • Lương 

Do đó, ta sẽ dùng cấu trúc để lưu giữ thông tin về mỗi nhân viên bằng cách định nghĩa một cấu trúc có tên là Employeee với các thuộc tính như sau: 

struct Employeee{ 
     char name; // Tên nhân viên 
     int age;      // Tuổi nhân viên 
     string role; // Chức vụ của nhân viên 
     float salary;     // Lương của nhân viên 
}; 

Lưu ý: 
Cấu trúc chỉ cần định nghĩa một lần trong chương trình và có thể được khai báo biến cấu trúc nhiều lần. Khi cấu trúc đã được định nghĩa, việc khai báo biến ở lần khác trong chương trình được thực hiện như khai báo biến thông thường: 

<Tên cấu trúc> <tên biến 1>, <tên biến 2>; 

Ví dụ, sau khi đã định nghĩa cấu trúc Employeee, muốn có biến myEmployeee, ta khai báo như sau: 

Employee myEmployeee; 

Cấu trúc lồng nhau 
Các cấu trúc có thể được định nghĩa lồng nhau khi một thuộc tính của một cấu trúc cũng cần có kiểu là một cấu trúc khác. Khi đó, việc định nghĩa cấu trúc cha được thực hiện như một cấu trúc bình thường, với khai báo về thuộc tính đó là một cấu trúc con: 

struct <Tên cấu trúc cha>{ 
         <Kiểu dữ liệu 1> <Tên thuộc tính 1>; 
         // Có kiểu cấu trúc 
         <Kiểu cấu trúc con> <Tên thuộc tính 2>;  
         … 
         <Kiểu dữ liệu n> <Tên thuộc tính n>; 
}; 

Ví dụ, với kiểu cấu trúc Employee, ta không quan tâm đến tuổi nhân viên nữa, mà quan tâm đến ngày sinh của nhân viên. Vì ngày sinh cần có các thông tin luôn đi với nhau là ngày sinh, tháng sinh, năm sinh. Do đó, ta định nghĩa một kiểu cấu trúc con cho kiểu ngày sinh: 

struct Date{ 
     int day;      int month;      int year; 
}; 

khi đó, cấu trúc Employee trở thành: 

struct Employee{ 
     char name;  // Tên nhân viên 
Date birthDay;      // Ngày sinh của nhân viên 
     string role;  // Chức vụ của nhân viên 
    float salary;    // Lương của nhân viên 
}; 

 Lưu ý: 
Trong định nghĩa các cấu trúc lồng nhau, cấu trúc con phải được định nghĩa trước cấu trúc cha để đảm bảo các kiểu dữ liệu của các thuộc tính của cấu trúc cha là tường minh tại thời điểm nó được định nghĩa. 

Định nghĩa cấu trúc với từ khoá typedef 
Để tránh phải dùng từ khoá struct mỗi khi khai báo biến cấu trúc, ta có thể dùng từ khóa typedef khi định nghĩa cấu trúc: 

typedef struct { 
         <Kiểu dữ liệu 1> <Tên thuộc tính 1>; 
         <Kiểu dữ liệu 2> <Tên thuộc tính 2>; 
         … 
         <Kiểu dữ liệu n> <Tên thuộc tính n>; 
} <Tên kiểu dữ liệu cấu trúc>; 

Trong đó: 
Tên kiểu dữ liệu cấu trúc: là tên kiểu dữ liệu của cấu trúc vừa định nghĩa. Tên này sẽ được dùng như một kiểu dữ liệu thông thường khi khai báo biến cấu trúc. 
Ví dụ, muốn có kiểu dữ liệu có cấu trúc nhân viên, có tên là Employee, ta dùng từ khoá typedef để định nghĩa cấu trúc như sau: 

typedef struct { 
     string name; // Tên nhân viên 
     int age;      // Tuổi nhân viên 
     string role; // Chức vụ của nhân viên 
     float salary;     // Lương của nhân viên 
} Employee; 

Khi đó, muốn có hai biến là myEmployee1 và myEmployee2 có kiểu cấu trúc Employee, ta chỉ cần khai báo như sau mà không cần từ khoá struct: 
Employee myEmployee1, myEmployee2; 
Trong ví dụ khai báo lồng cấu trúc Employee, dùng từ khoá typedef cho kiểu Date: 

typedef struct { 
     int day;      int month;      int year; 
} Date; cấu trúc Employee trở thành:
 
typedef struct { 
         string name; // Tên nhân viên 
Date birthDay; // Ngày sinh của nhân viên      
        string role; // Chức vụ của nhân viên 
        float salary;    // Lương của nhân viên 
} Employee; 

Lưu ý: 

  • Khi không dùng từ khoá typedef, tên cấu trúc (nằm sau từ khoá struct) được dùng để khai báo biến. Trong khi đó, khi có từ khoá typedef, tên kiểu dữ liệu cấu trúc (dòng cuối cùng trong định nghĩa) mới được dùng để khai báo biến. 

  • Khi dùng từ khoá typedef thì không thể khai báo biến đồng thời với định nghĩa cấu trúc. 

Thao tác trên cấu trúc

Các thao tác trên cấu trúc bao gồm: 

  • Khai báo và khởi tạo giá trị ban đầu cho biến cấu trúc 

  • Truy nhập đến các thuộc tính của cấu trúc 

Khởi tạo giá trị ban đầu cho cấu trúc 
Khởi tạo biến có cấu trúc đơn 
Biến cấu trúc được khai báo theo các cách sau: 

<Tên kiểu dữ liệu cấu trúc> <tên biến>; 

Ngoài ra, ta có thể khởi tạo các giá trị cho các thuộc tính của cấu trúc ngay khi khai báo bằng các cú pháp sau: 

<Tên kiểu dữ liệu cấu trúc> <tên biến> = {      
    <giá trị thuộc tính 1>, 
    <giá trị thuộc tính 2>, 
    … 
    <giá trị thuộc tính n> 
}; 

Trong đó: 
Giá trị thuộc tính: là giá trị khởi đầu cho mỗi thuộc tính, có kiểu phù hợp với kiểu dữ liệu của thuộc tính. Mỗi giá trị của thuộc tính được phân cách bằng dấu phẩy “,”. 
Ví dụ, với định nghĩa cấu trúc: 

typedef struct { 
     string name; // Tên nhân viên 
     int age;      // Tuổi nhân viên 
     string role; // Chức vụ của nhân viên 
     float salary;  // Lương của nhân viên 
} Employee; 

thì có thể khai báo và khởi tạo cho một biến như sau: 

Employee myEmployee1 = {
	"Nguyen Van A",
	27,
	"Nhan vien",
	300f
};

Khởi tạo các biến có cấu trúc lồng nhau 
Trong trường hợp các cấu trúc lồng nhau, phép khởi tạo cũng thực hiện như thông thường với phép khởi tạo cho tất cả các cấu trúc con. 
Ví dụ với khai báo cấu trúc như sau: 

typedef struct { 
     int day;      int month;      int year; 
} Date; 

và: 

typedef struct { 
         string name; // Tên nhân viên 
         Date birthDay; // Ngày sinh của nhân viên      
         string role; // Chức vụ của nhân viên 
         float salary;  // Lương của nhân viên 
} Employee; 

Thì khai báo và khởi tạo một biến có kiểu Employee có thể thực hiện như sau: 

Employee myEmployee1 = { 
         "Nguyen Van A", 
         {15, 05, 1980}, // Khởi tạo cấu trúc con 
         "Nhan vien", 
         300f 
}; 

Truy nhập đến thuộc tính của cấu trúc 
Có hai cách để truy cập vào các thành viên cấu trúc:

  1. Bởi . (thành viên hoặc toán tử chấm).

  2. Bởi ->(toán tử con trỏ cấu trúc).

Ví dụ, với một biến cấu trúc kiểu Employee đơn: 

Employee myEmployee1 = { 
         "Nguyen Van A", 
         27, 
         "Nhan vien", 
         300f 
}; 

ta có thể truy xuất như sau: 

cout << myEmployee1.name;  // hiển thị ra “Nguyen Van A” 
myEmployee1.age += 1;      // Tăng số tuổi lên 1 

Đối với kiểu cấu trúc lồng nhau, phép truy nhập đến thuộc tính được thực hiện lần lượt từ cấu trúc cha đến cấu trúc con. 
Ví dụ, với một biến cấu trúc kiểu Employee lồng nhau:  

Employee myEmployee1 = { 
         "Nguyen Van A", 
         {15, 05, 1980}, 
         "Nhan vien", 
         300f 
}; 

 ta có thể truy xuất như sau: 

cout << myEmployee1.name;       // hiển thị ra “Nguyen Van A” 
myEmployee1.birthDay.day = 16;     // Sửa lại ngày sinh thành 16 
myEmployee1.birthDay.month = 07; // Sửa lại tháng sinh thành 07 

Ví dụ về kiểu cấu trúc

Ví dụ chúng ta có lớp Rectangle có 2 thuộc tính width and height.

#include <iostream>  
using namespace std;
struct Rectangle
{
	int width, height;

};
int main(void) {
	struct Rectangle rec;
	rec.width = 8;
	rec.height = 5;
	cout << "Area of Rectangle is: " << (rec.width * rec.height) << endl;
	system("pause");
	return 0;
}

Kết quả:

Area of Rectangle is: 40

Chúng ta hãy xem một ví dụ khác về cấu trúc sử dụng hàm khởi tạo dữ liệu và phương thức để tính diện tích hình chữ nhật

#include <iostream>  
using namespace std;
struct Rectangle
{
	int width, height;
	Rectangle(int w, int h)
	{
		width = w;
		height = h;
	}
	void areaOfRectangle() {
		cout << "Area of Rectangle is: " << (width*height);
	}
};
int main(void) {
	struct Rectangle rec = Rectangle(4, 6);
	rec.areaOfRectangle();
	system("pause");

	return 0;
}

Kết quả:

Area of Rectangle is: 24

<