[C++ OOP Thực Chiến] Bài 21: Phương thức khởi tạo có tham số (Phần 2) - Cú lừa từ API và kỹ năng Parse chuỗi
Chào anh em! Trong [Bài 20], chúng ta đã thiết kế thành công Class PhanSo nhận vào 2 số nguyên. Code chạy cực mượt, phân số tự động rút gọn.
Nhưng đời không như mơ! Giả sử bạn đang viết một API Backend, Client (Frontend hoặc Mobile) gửi lên một request chứa dữ liệu phân số dưới dạng chuỗi String như sau: "3/4" hoặc "15/-5". Thậm chí, nếu họ truyền một số nguyên, nó sẽ là "7".
Bạn không thể ném cái chuỗi "3/4" đó vào cái Constructor PhanSo(int, int) được. Trình biên dịch sẽ chửi bạn ngay. Chúng ta cần "dạy" Class PhanSo cách tự đọc hiểu một chuỗi String!
1. Nạp chồng Constructor (Overloading)
Trong C++, một Class có thể có vô số hàm khởi tạo (Constructor), miễn là danh sách tham số của chúng khác nhau. Khi bạn tạo Object, C++ sẽ tự động nhìn vào kiểu dữ liệu bạn truyền vào để "khớp lệnh" với Constructor phù hợp nhất.
Chúng ta sẽ giữ nguyên Constructor cũ và viết thêm một Constructor mới chỉ nhận 1 tham số kiểu std::string.
2. Kỹ thuật Parse (Bóc tách) chuỗi trong C++
Để biến chuỗi "3/4" thành 2 số nguyên 3 và 4, chúng ta sẽ sử dụng 2 công cụ siêu mạnh của thư viện <string>:
input.find('/'): Tìm vị trí của dấu gạch chéo.stoi() (String TO Integer): Ép kiểu từ một chuỗi con thành số nguyên.
3. Code Demo: Nâng cấp hệ thống Phân số
Chúng ta sẽ đắp thêm Constructor xử lý chuỗi vào bộ khung Class PhanSo đã xây dựng ở bài trước:
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
class PhanSo {
private:
int tuSo;
int mauSo;
// Helper: Tìm Ước chung lớn nhất
int timUCLN(int a, int b) {
a = abs(a); b = abs(b);
while (b != 0) { int temp = b; b = a % b; a = temp; }
return (a == 0) ? 1 : a;
}
// Helper: Tự động rút gọn
void rutGon() {
if (mauSo == 0) {
cout << "[LỖI] Mẫu số không được bằng 0!\n";
exit(1);
}
if (mauSo < 0) { tuSo = -tuSo; mauSo = -mauSo; }
int ucln = timUCLN(tuSo, mauSo);
tuSo /= ucln; mauSo /= ucln;
}
public:
// 1. Constructor mặc định
PhanSo() : tuSo(0), mauSo(1) {}
// 2. Constructor nhận 2 số nguyên (Gốc)
PhanSo(int tu, int mau) : tuSo(tu), mauSo(mau) {
rutGon();
}
// 3. CONSTRUCTOR MỚI: Nhận vào chuỗi String từ API
PhanSo(string input) {
// Tìm vị trí của dấu '/'
size_t pos = input.find('/');
if (pos != string::npos) {
// Nếu có dấu '/', cắt chuỗi làm 2 phần và ép kiểu
tuSo = stoi(input.substr(0, pos)); // Từ đầu đến trước dấu '/'
mauSo = stoi(input.substr(pos + 1)); // Từ sau dấu '/' đến hết
} else {
// Nếu không có dấu '/' (VD: input là "5"), coi như mẫu bằng 1
tuSo = stoi(input);
mauSo = 1;
}
// Cực kỳ quan trọng: Parse xong thì vẫn phải gọi rút gọn!
rutGon();
}
void inPhanSo() const {
if (mauSo == 1) cout << tuSo << "\n";
else if (tuSo == 0) cout << "0\n";
else cout << tuSo << "/" << mauSo << "\n";
}
};
int main() {
cout << "--- HE THONG PARSE DU LIEU PHAN SO ---\n";
// Client gửi lên dạng chuẩn "3/4"
PhanSo ps1("3/4");
cout << "Parse '3/4' -> "; ps1.inPhanSo();
// Client gửi lên phân số chưa tối giản "10/25"
PhanSo ps2("10/25");
cout << "Parse '10/25' -> "; ps2.inPhanSo();
// Client gửi lên số âm ở mẫu "7/-14"
PhanSo ps3("7/-14");
cout << "Parse '7/-14' -> "; ps3.inPhanSo();
// Client gửi lên số nguyên "9"
PhanSo ps4("9");
cout << "Parse '9' -> "; ps4.inPhanSo();
return 0;
}
Nhận xét: Với Constructor thứ 3 này, Class PhanSo của bạn đã trở thành một "cỗ máy" cực kỳ linh hoạt. Dù hệ thống bên ngoài có ném vào số nguyên hay chuỗi text lộn xộn, nó đều tự động bóc tách, ép kiểu và chuẩn hóa dữ liệu một cách an toàn.
Tạm kết & Gợi mở
Đến đây, anh em đã hoàn thiện trọn vẹn kỹ năng kiểm soát đầu vào (Input) cho mọi Object bằng mọi cách thức (Overloading, Delegating, String Parsing). Dữ liệu bên trong Class của bạn giờ đây đã được bảo vệ tuyệt đối bằng lớp áo giáp private.
Nhưng sự bảo mật tuyệt đối đôi khi lại mang đến sự bất tiện vô cùng lớn!
Hãy tưởng tượng bạn đang viết một Class ToanHoc chuyên làm nhiệm vụ kiểm tra logic. Nó muốn lấy tuSo và mauSo của Class PhanSo ra để tính toán. Vì 2 thuộc tính đó là private, Class ToanHoc khóc ròng vì bị hệ thống chặn cửa, không cho phép truy cập.
Làm sao để một Class "mở cửa hậu" cho một người bạn thân thiết vào nhà xem đồ private mà không cần phơi bày dữ liệu ra public cho toàn thế giới dòm ngó?
Hẹn gặp lại anh em ở Bài 22: Hàm bạn - Lớp bạn (Phần 1).
Trong C++, ta không thể truy cập thuộc tính của một class từ bên ngoài. Việc sử dụng hàm bạn giúp chúng ta làm điều này. Cùng tìm hiểu về hàm bạn qua bài viết tiếp theo nhé!
Đừng quên Upvote để tiếp lửa cho series nhé!
All rights reserved