Bài 35.2: Đối tượng, lớp, thuộc tính, phương thức trong C++

Bài 35.2: Đối tượng, lớp, thuộc tính, phương thức trong C++
Chào mừng trở lại với hành trình chinh phục C++ cùng FullhouseDev! Nếu bạn đã theo dõi đến đây, chắc hẳn bạn đã sẵn sàng để khám phá một trong những mô hình lập trình mạnh mẽ và phổ biến nhất: Lập trình hướng đối tượng (Object-Oriented Programming - OOP). OOP không chỉ là một kỹ thuật, nó là một cách tư duy mới về cách chúng ta cấu trúc và tổ chức code, giúp giải quyết các vấn đề phức tạp một cách hiệu quả và có tổ chức.
Trọng tâm của OOP xoay quanh việc xem xét thế giới lập trình như một tập hợp các đối tượng tương tác với nhau. Để có thể làm việc hiệu quả với OOP trong C++, việc đầu tiên và quan trọng nhất là phải nắm vững bốn khái niệm cốt lõi: Đối tượng, Lớp, Thuộc tính và Phương thức.
Hãy cùng nhau "mổ xẻ" từng khái niệm này để thấy chúng hoạt động như thế nào trong C++ nhé!
1. Lớp (Class) - Bản Thiết Kế Của Vạn Vật
Hãy tưởng tượng bạn là một kiến trúc sư và bạn được yêu cầu thiết kế một khu đô thị với hàng trăm ngôi nhà có cấu trúc tương tự nhau. Bạn sẽ không vẽ từng ngôi nhà một từ đầu. Thay vào đó, bạn sẽ tạo ra một bản thiết kế chi tiết cho một loại nhà mẫu, mô tả rõ ràng: ngôi nhà có bao nhiêu tầng, bao nhiêu phòng, vị trí cửa sổ, loại vật liệu chính, v.v. Bản thiết kế này không phải là ngôi nhà thực, nhưng nó chứa tất cả thông tin cần thiết để xây dựng nên những ngôi nhà thực tế.
Trong lập trình hướng đối tượng, Lớp (Class) đóng vai trò như bản thiết kế, khuôn mẫu hoặc nguyên mẫu cho một kiểu dữ liệu do người dùng định nghĩa. Một lớp định nghĩa cấu trúc dữ liệu (những thông tin mà một "thực thể" sẽ lưu trữ) và hành vi (những hành động mà thực thể đó có thể thực hiện).
Nói một cách khác, lớp là một khái niệm trừu tượng mô tả những đặc điểm chung và những hành động chung của một nhóm các đối tượng có cùng bản chất.
Ví dụ: Lớp XeHoi
(Car) sẽ định nghĩa rằng một chiếc xe hơi nói chung có các đặc điểm như màu sắc, hãng sản xuất, số chỗ ngồi, và các hành động như khởi động, chạy, phanh.
Trong C++, bạn định nghĩa một lớp bằng từ khóa class
:
class TenCuaLop {
// Khu vực này chứa định nghĩa các thành viên của lớp
// Bao gồm thuộc tính (data members) và phương thức (member functions)
}; // Đừng quên dấu chấm phẩy kết thúc định nghĩa lớp!
Giải thích code:
class
là từ khóa bắt buộc để khai báo một lớp.TenCuaLop
là tên mà bạn đặt cho lớp của mình (nên bắt đầu bằng chữ cái in hoa theo quy ước phổ biến).- Cặp dấu ngoặc nhọn
{}
chứa thân lớp, nơi bạn định nghĩa các thành viên. - Dấu chấm phẩy
;
sau dấu ngoặc nhọn cuối cùng là bắt buộc khi định nghĩa lớp.
2. Đối tượng (Object) - Thực Thể Cụ Thể Từ Bản Thiết Kế
Tiếp tục với ví dụ kiến trúc sư: sau khi có bản thiết kế nhà mẫu, người ta sẽ dựa vào đó để thực sự xây dựng những ngôi nhà trên thực địa. Mỗi ngôi nhà được xây dựng xong là một thực thể độc lập, có vị trí cụ thể trên mặt đất, có thể được sơn màu khác với ngôi nhà bên cạnh, mặc dù chúng đều tuân theo cùng một bản thiết kế.
Trong OOP, Đối tượng (Object) là một thực thể cụ thể, một thể hiện (instance) của một lớp. Khi bạn tạo một đối tượng từ một lớp, bạn đang cấp phát bộ nhớ cho thực thể đó và nó sẽ mang tất cả các thuộc tính và phương thức đã được định nghĩa trong lớp đó.
Nếu lớp XeHoi
là bản thiết kế chung, thì "chiếc xe VinFast Fadil màu trắng, đời 2021" là một đối tượng cụ thể của lớp XeHoi
. "Chiếc xe Toyota Camry màu đen, đời 2023" cũng là một đối tượng khác của lớp XeHoi
. Mỗi chiếc xe này là độc lập, có màu sắc, đời xe riêng và bạn có thể "khởi động" hoặc "chạy" chúng riêng biệt.
Để tạo một đối tượng trong C++, bạn khai báo nó giống như khai báo một biến thông thường, với tên lớp đóng vai trò là kiểu dữ liệu:
TenCuaLop tenDoiTuong; // Tạo một đối tượng có tên tenDoiTuong từ lớp TenCuaLop
// Ví dụ:
XeHoi xeCuaToi; // Tạo một đối tượng XeHoi tên là xeCuaToi
XeHoi xeCuaBan; // Tạo một đối tượng XeHoi khác tên là xeCuaBan
Giải thích code:
XeHoi
là tên lớp (kiểu dữ liệu).xeCuaToi
vàxeCuaBan
là tên của hai đối tượng khác nhau thuộc lớpXeHoi
. Mỗi đối tượng này tồn tại độc lập trong bộ nhớ.
3. Thuộc tính (Attributes) - Đặc Điểm Riêng Của Mỗi Đối tượng
Một ngôi nhà thực tế được xây từ bản thiết kế sẽ có những đặc điểm riêng của nó: địa chỉ, màu sơn cụ thể, số lượng người ở... dù cấu trúc chung giống bản thiết kế.
Trong OOP, Thuộc tính (Attributes), còn được gọi là biến thành viên (data members), là những biến được khai báo bên trong một lớp. Chúng dùng để lưu trữ dữ liệu trạng thái (state) hoặc đặc điểm riêng của một đối tượng cụ thể. Mỗi đối tượng được tạo ra từ cùng một lớp sẽ có một bản sao riêng của tất cả các thuộc tính đã được định nghĩa trong lớp đó.
Ví dụ với lớp XeHoi
, các thuộc tính có thể là mauSac
(kiểu string), hangSanXuat
(kiểu string), soChoNgoi
(kiểu int). Đối tượng xeCuaToi
có thể có mauSac = "Trang"
, trong khi đối tượng xeCuaBan
có mauSac = "Den"
.
Chúng ta khai báo thuộc tính bên trong phần định nghĩa lớp. Theo quy tắc đóng gói (encapsulation - sẽ nói sâu hơn sau), thuộc tính thường được đặt ở chế độ riêng tư (private
), và được truy cập hoặc thay đổi thông qua các phương thức công khai (public
). Tuy nhiên, trong ví dụ đơn giản này để minh họa, chúng ta tạm thời đặt chúng ở chế độ public
để dễ truy cập:
#include <string> // Can them thu vien string cho string
#include <iostream> // Can them thu vien iostream cho cout
class XeHoi {
public: // Cac thanh vien duoi day co the truy cap tu ben ngoai lop
// Thuoc tinh (Attributes)
string mauSac;
string hangSanXuat;
int soChoNgoi;
};
int main() {
XeHoi xeA; // Tao doi tuong xeA
xeA.mauSac = "Do"; // Gan gia tri cho thuoc tinh 'mauSac' cua xeA
xeA.hangSanXuat = "Ferrari";
xeA.soChoNgoi = 2;
XeHoi xeB; // Tao doi tuong xeB
xeB.mauSac = "Xanh"; // Gan gia tri cho thuoc tinh 'mauSac' cua xeB
xeB.hangSanXuat = "Porsche";
xeB.soChoNgoi = 4;
// In ra thong tin cac thuoc tinh cua tung doi tuong
cout << "Xe A: Mau " << xeA.mauSac << ", Hang " << xeA.hangSanXuat << ", So cho " << xeA.soChoNgoi << endl;
cout << "Xe B: Mau " << xeB.mauSac << ", Hang " << xeB.hangSanXuat << ", So cho " << xeB.soChoNgoi << endl;
return 0;
}
Giải thích code:
- Chúng ta định nghĩa lớp
XeHoi
với ba thuộc tính:mauSac
,hangSanXuat
(kiểustring
) vàsoChoNgoi
(kiểuint
). - Trong
main
, chúng ta tạo hai đối tượngxeA
vàxeB
từ lớpXeHoi
. - Mỗi đối tượng có bộ thuộc tính riêng của nó. Chúng ta dùng toán tử
.
(chấm) để truy cập và gán/đọc giá trị cho các thuộc tính của từng đối tượng (xeA.mauSac
,xeB.mauSac
, v.v.). - Kết quả in ra cho thấy
xeA
vàxeB
có các giá trị thuộc tính khác nhau, mặc dù chúng cùng thuộc một lớp.
4. Phương thức (Methods) - Hành Động Mà Đối Tượng Có Thể Thực Hiện
Một ngôi nhà không chỉ có cấu trúc và đặc điểm (số phòng, màu sơn) mà còn có những chức năng: bạn có thể mở cửa, bật đèn, mở vòi nước.
Trong OOP, Phương thức (Methods), hay còn gọi là hàm thành viên (member functions), là những hàm được khai báo bên trong một lớp. Chúng định nghĩa hành vi hoặc các thao tác mà các đối tượng của lớp đó có thể thực hiện. Phương thức thường làm việc với các thuộc tính của đối tượng đó, có thể là đọc giá trị thuộc tính, thay đổi chúng, hoặc thực hiện một hành động dựa trên trạng thái của đối tượng.
Với lớp XeHoi
, các phương thức có thể là khoiDong()
, chay(tocDo)
, phanh()
. Khi bạn gọi phương thức khoiDong()
trên đối tượng xeA
, chỉ có xe A khởi động, không ảnh hưởng đến xe B. Phương thức chay(tocDo)
có thể làm thay đổi trạng thái (ví dụ: tốc độ hiện tại) của chính đối tượng đó.
Chúng ta khai báo phương thức bên trong phần định nghĩa lớp, thường ở chế độ public
để có thể gọi từ bên ngoài:
#include <iostream>
#include <string>
class XeHoi {
public:
string mauSac;
string hangSanXuat;
int soChoNgoi;
bool dangChay = false; // Them mot thuoc tinh de theo doi trang thai
// Phuong thuc khoi dong xe
void khoiDong() {
if (!dangChay) {
dangChay = true; // Thay doi trang thai cua doi tuong
cout << hangSanXuat << " mau " << mauSac << " da khoi dong." << endl;
} else {
cout << hangSanXuat << " mau " << mauSac << " da dang chay roi." << endl;
}
}
// Phuong thuc tat may xe
void tatMay() {
if (dangChay) {
dangChay = false; // Thay doi trang thai cua doi tuong
cout << hangSanXuat << " mau " << mauSac << " da tat may." << endl;
} else {
cout << hangSanXuat << " mau " << mauSac << " da tat may san roi." << endl;
}
}
};
int main() {
XeHoi xeC;
xeC.mauSac = "Den";
xeC.hangSanXuat = "Audi";
xeC.soChoNgoi = 5;
// Su dung cac phuong thuc tren doi tuong xeC
xeC.khoiDong(); // Goi phuong thuc khoiDong() cua xeC
xeC.khoiDong(); // Goi lai de xem ket qua
xeC.tatMay(); // Goi phuong thuc tatMay() cua xeC
xeC.tatMay(); // Goi lai
xeC.khoiDong(); // Goi lai
xeC.tatMay(); // Goi lai lan cuoi
return 0;
}
Giải thích code:
- Chúng ta thêm một thuộc tính
dangChay
(kiểubool
) để theo dõi trạng thái của xe. - Định nghĩa hai phương thức
khoiDong()
vàtatMay()
bên trong lớpXeHoi
. - Các phương thức này truy cập và thay đổi thuộc tính
dangChay
của chính đối tượng gọi phương thức. - Trong
main
, chúng ta tạo đối tượngxeC
và lần lượt gọi các phương thứckhoiDong()
vàtatMay()
trên nó bằng toán tử.
(chấm). - Mỗi lần gọi phương thức sẽ thực hiện hành động được định nghĩa và có thể in ra thông báo hoặc thay đổi trạng thái của
xeC
.
5. Mối Quan Hệ Giữa Bốn Khái Niệm (Ví Dụ Tổng Hợp)
Để thấy rõ sự kết hợp của cả bốn khái niệm, hãy xem xét một ví dụ đơn giản về lớp SinhVien
:
- Lớp (Class):
SinhVien
- Bản thiết kế chung cho tất cả sinh viên. - Thuộc tính (Attributes):
maSinhVien
(string),ten
(string),diemTrungBinh
(double) - Những đặc điểm riêng của mỗi sinh viên. - Phương thức (Methods):
hienThiThongTin()
(in ra thông tin của sinh viên đó),capNhatDiem(diemMoi)
(thay đổi điểm trung bình) - Những hành động/thao tác có thể thực hiện trên một sinh viên. - Đối tượng (Objects):
sinhVienA
("SV001", "Nguyen Van A", 8.5),sinhVienB
("SV002", "Tran Thi B", 9.0) - Hai thực thể sinh viên cụ thể, mỗi thực thể có bộ thuộc tính riêng và có thể thực hiện các phương thức.
Đây là code minh họa:
#include <iostream>
#include <string>
#include <iomanip> // Can cho fixed va setprecision
// Dinh nghia lop SinhVien
class SinhVien {
public:
// Thuoc tinh
string maSinhVien;
string ten;
double diemTrungBinh;
// Phuong thuc hien thi thong tin
void hienThiThongTin() {
cout << "Ma SV: " << maSinhVien << endl;
cout << "Ten: " << ten << endl;
cout << fixed << setprecision(2); // Dinh dang diem 2 chu so thap phan
cout << "Diem TB: " << diemTrungBinh << endl;
cout << "---" << endl;
}
// Phuong thuc cap nhat diem
void capNhatDiem(double diemMoi) {
if (diemMoi >= 0 && diemMoi <= 10) {
diemTrungBinh = diemMoi;
cout << "Da cap nhat diem cho sinh vien " << ten << "." << endl;
} else {
cout << "Diem khong hop le." << endl;
}
}
};
int main() {
// Tao doi tuong sinhVienA
SinhVien sinhVienA;
sinhVienA.maSinhVien = "SV001";
sinhVienA.ten = "Nguyen Van A";
sinhVienA.diemTrungBinh = 8.5;
// Tao doi tuong sinhVienB
SinhVien sinhVienB;
sinhVienB.maSinhVien = "SV002";
sinhVienB.ten = "Tran Thi B";
sinhVienB.diemTrungBinh = 9.0;
// Hien thi thong tin ban dau
cout << "Thong tin sinh vien ban dau:" << endl;
sinhVienA.hienThiThongTin(); // Goi phuong thuc cua sinhVienA
sinhVienB.hienThiThongTin(); // Goi phuong thuc cua sinhVienB
// Cap nhat diem cho sinhVienA
sinhVienA.capNhatDiem(9.2);
// Hien thi lai thong tin de kiem tra
cout << "Thong tin sau khi cap nhat:" << endl;
sinhVienA.hienThiThongTin();
sinhVienB.hienThiThongTin(); // Diem cua sinhVienB khong thay doi
return 0;
}
Giải thích code:
- Lớp
SinhVien
được định nghĩa với các thuộc tínhmaSinhVien
,ten
,diemTrungBinh
. - Hai phương thức
hienThiThongTin()
vàcapNhatDiem()
được thêm vào để xử lý dữ liệu của sinh viên. - Trong
main
, chúng ta tạo hai đối tượngsinhVienA
vàsinhVienB
. - Mỗi đối tượng được gán giá trị thuộc tính riêng.
- Khi gọi
sinhVienA.hienThiThongTin()
, phương thức này chỉ sử dụng dữ liệu (maSinhVien
,ten
,diemTrungBinh
) của đối tượngsinhVienA
. Tương tự vớisinhVienB
. - Khi gọi
sinhVienA.capNhatDiem(9.2)
, chỉ có thuộc tínhdiemTrungBinh
của đối tượngsinhVienA
bị thay đổi. Thuộc tính củasinhVienB
vẫn giữ nguyên.
Comments