/*
================================================================================
BÀI TOÁN: QUẢN LÝ DANH SÁCH SẢN PHẨM
================================================================================
Mô tả:
Chương trình quản lý danh sách sản phẩm trong một cửa hàng bán lẻ.
Hệ thống hỗ trợ hai loại sản phẩm:
- SanPhamVatLy : sản phẩm có hình thức vật lý (điện thoại, laptop, ...)
- SanPhamKyThuat: sản phẩm kỹ thuật số / phần mềm (ứng dụng, game, ...)
Chức năng chính:
1. Nhập danh sách sản phẩm từ bàn phím.
2. In toàn bộ danh sách sản phẩm.
3. Tìm kiếm sản phẩm theo tên.
4. Sắp xếp danh sách theo giá (tăng dần).
5. Tăng / giảm số lượng tồn kho bằng toán tử ++ / --.
6. Ghép hai danh sách sản phẩm bằng toán tử +.
Yêu cầu OOP đã triển khai:
02. Tính đóng gói – dữ liệu private, truy cập qua getter/setter
03. Tính kế thừa – SanPhamVatLy & SanPhamKyThuat kế thừa SanPham
04. Tính đa hình – virtual hienThi(), tinhThue()
05. Tính trừu tượng – lớp trừu tượng SanPham (pure virtual)
06. Template – hàm timMax<T>() và lớp DanhSach<T>
07. Toán tử nhập/xuất – operator>> , operator<<
08. Toán tử ++ / -- – tăng / giảm soLuong
09. Toán tử + / - – ghép / lấy hiệu hai DanhSach
10. Hàm cấu tử rỗng – SanPham(), SanPhamVatLy(), SanPhamKyThuat()
11. Hàm cấu tử đầy đủ – SanPham(...), SanPhamVatLy(...), SanPhamKyThuat(...)
12. Main – nhập danh sách
13. Main – in danh sách
14. Main – sắp xếp, tìm kiếm
================================================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <stdexcept>
#include <functional>
using namespace std;
class SanPham {
protected:
string maSP;
string tenSP;
double gia;
int soLuong;
public:
// Cấu tử rỗng (Default Constructor)
SanPham() : maSP(""), tenSP(""), gia(0.0), soLuong(0) {}
// Cấu tử đầy đủ tham số (Parameterized Constructor)
SanPham(const string& ma, const string& ten, double g, int sl)
: maSP(ma), tenSP(ten), gia(g), soLuong(sl) {}
// Hàm hủy ảo (Virtual Destructor) để tránh rò rỉ bộ nhớ khi giải phóng con trỏ đa hình
virtual ~SanPham() {}
// Các hàm Getter và Setter giúp đảm bảo tính đóng gói
string getMa() const { return maSP; }
string getTen() const { return tenSP; }
double getGia() const { return gia; }
int getSoLuong() const { return soLuong; }
void setGia(double g) { if (g >= 0) gia = g; }
void setSoLuong(int sl){ if (sl >= 0) soLuong = sl; }
// Các hàm thuần ảo (Pure Virtual Functions) bắt buộc các lớp con phải override
virtual void hienThi() const = 0;
virtual double tinhThue() const = 0;
virtual string loaiSP() const = 0;
// Nạp chồng toán tử tăng/giảm (++ / -- prefix) để thay đổi số lượng tồn kho
SanPham& operator++() { ++soLuong; return *this; }
SanPham& operator--() { if (soLuong > 0) --soLuong; return *this; }
// Nạp chồng toán tử xuất (operator<<) sử dụng tính đa hình để gọi hàm hienThi()
friend ostream& operator<<(ostream& os, const SanPham& sp) {
sp.hienThi();
return os;
}
// Nạp chồng toán tử nhập (operator>>) để nhập các thông tin cơ bản của sản phẩm
friend istream& operator>>(istream& is, SanPham& sp) {
cout << " Ma SP : "; getline(is, sp.maSP);
cout << " Ten SP : "; getline(is, sp.tenSP);
cout << " Gia (VND) : "; is >> sp.gia; is.ignore();
cout << " So luong : "; is >> sp.soLuong; is.ignore();
return is;
}
};
// ============================================================================
// GIẢI THÍCH: LỚP SAN PHAM VAT LY KẾ THỪA (INHERITANCE) TỪ SAN PHAM
// - Mở rộng thêm 2 thuộc tính: trongLuong và xuatXu.
// - Đa hình (Polymorphism): Ghi đè (override) hàm tính thuế và hiển thị riêng cho đồ vật lý.
// ============================================================================
class SanPhamVatLy : public SanPham {
private:
double trongLuong;
string xuatXu;
public:
// Cấu tử rỗng gọi cấu tử rỗng của lớp cha
SanPhamVatLy() : SanPham(), trongLuong(0.0), xuatXu("") {}
// Cấu tử đầy đủ sử dụng Member Initializer List để truyền dữ liệu lên lớp cha
SanPhamVatLy(const string& ma, const string& ten, double g, int sl, double tl, const string& xx)
: SanPham(ma, ten, g, sl), trongLuong(tl), xuatXu(xx) {}
string loaiSP() const override { return "Vat Ly"; }
// Triển khai quy tắc tính thuế riêng: Xuất xứ nước ngoài chịu thuế 10%, Việt Nam 5%
double tinhThue() const override {
return (xuatXu != "Viet Nam") ? gia * 0.10 : gia * 0.05;
}
// Định dạng bảng hiển thị thông tin sản phẩm vật lý ra màn hình
void hienThi() const override {
cout << fixed << setprecision(0);
cout << " [VAT LY] " << maSP
<< " | " << left << setw(24) << tenSP
<< " | Gia: " << right << setw(12) << gia
<< " | SL: " << setw(4) << soLuong
<< " | " << trongLuong << "kg | " << xuatXu
<< " | Thue: " << tinhThue() << "\n";
}
// Nạp chồng toán tử nhập riêng, tận dụng toán tử nhập của lớp cha (ép kiểu static_cast)
friend istream& operator>>(istream& is, SanPhamVatLy& sp) {
is >> static_cast<SanPham&>(sp);
cout << " Trong luong(kg): "; is >> sp.trongLuong; is.ignore();
cout << " Xuat xu : "; getline(is, sp.xuatXu);
return is;
}
};
// ============================================================================
// GIẢI THÍCH: LỚP SAN PHAM KY THUAT KẾ THỪA TỪ SAN PHAM
// - Quản lý các mặt hàng số, phần mềm với thuộc tính riêng: phienBan, nhaPhat, coGiayPhep.
// - Triển khai quy tắc thuế VAT cố định 8% cho phần mềm.
// ============================================================================
class SanPhamKyThuat : public SanPham {
private:
string phienBan;
string nhaPhat;
bool coGiayPhep;
public:
// Cấu tử rỗng
SanPhamKyThuat() : SanPham(), phienBan("1.0"), nhaPhat(""), coGiayPhep(false) {}
// Cấu tử đầy đủ tham số
SanPhamKyThuat(const string& ma, const string& ten, double g, int sl, const string& pv, const string& np, bool cgp)
: SanPham(ma, ten, g, sl), phienBan(pv), nhaPhat(np), coGiayPhep(cgp) {}
string loaiSP() const override { return "Ky Thuat So"; }
// Thuế sản phẩm số mặc định là 8% giá bán
double tinhThue() const override {
return gia * 0.08;
}
// Định dạng hiển thị thông tin sản phẩm kỹ thuật số
void hienThi() const override {
cout << fixed << setprecision(0);
cout << " [KY THUAT] " << maSP
<< " | " << left << setw(24) << tenSP
<< " | Gia: " << right << setw(12) << gia
<< " | SL: " << setw(4) << soLuong
<< " | v" << phienBan
<< " | " << nhaPhat
<< " | GiayPhep: " << (coGiayPhep ? "Co" : "Khong")
<< " | Thue: " << tinhThue() << "\n";
}
// Nạp chồng toán tử nhập cho các thuộc tính đặc thù của sản phẩm số
friend istream& operator>>(istream& is, SanPhamKyThuat& sp) {
is >> static_cast<SanPham&>(sp);
cout << " Phien ban : "; getline(is, sp.phienBan);
cout << " Nha phat : "; getline(is, sp.nhaPhat);
cout << " Co giay phep (1/0): "; is >> sp.coGiayPhep; is.ignore();
return is;
}
};
// ============================================================================
// GIẢI THÍCH: HÀM MẪU (FUNCTION TEMPLATE) TIMMAX
// - Hàm tổng quát cho phép tìm kiếm phần tử lớn nhất trong một vector bất kỳ.
// - Sử dụng con trỏ hàm/biểu thức Lambda (`soSanh`) để tăng tính linh hoạt khi so sánh.
// ============================================================================
template <typename T>
T timMax(const vector<T>& ds, function<bool(const T&, const T&)> soSanh) {
if (ds.empty()) throw runtime_error("Danh sach rong!");
T maxVal = ds[0];
for (const auto& item : ds)
if (soSanh(maxVal, item)) maxVal = item;
return maxVal;
}
// ============================================================================
// GIẢI THÍCH: LỚP MẪU (CLASS TEMPLATE) DANH SACH
// - Lưu trữ mảng các con trỏ kiểu `T*`. Giúp quản lý danh sách đa hình (SanPham*).
// - Chứa toàn bộ các hàm chức năng: Thêm, In, Sắp xếp, Tìm kiếm, Thống kê dữ liệu.
// ============================================================================
template <typename T>
class DanhSach {
private:
vector<T*> ds; // Vector lưu trữ danh sách các con trỏ đối tượng
public:
DanhSach() {}
~DanhSach() {}
// Thêm một con trỏ sản phẩm mới vào danh sách
void them(T* sp) { ds.push_back(sp); }
// Lấy số lượng sản phẩm hiện tại trong danh sách
int kichThuoc() const { return (int)ds.size(); }
// Truy xuất đến phần tử thứ i (có kiểm tra an toàn chỉ số mảng)
T* layPhanTu(int i) const {
if (i < 0 || i >= (int)ds.size()) throw out_of_range("Chi so ngoai pham vi!");
return ds[i];
}
// Duyệt danh sách và xuất thông tin từng sản phẩm dựa trên toán tử << đã nạp chồng
void inDanhSach() const {
if (ds.empty()) { cout << " (Danh sach trong)\n"; return; }
for (const auto& p : ds) cout << *p;
}
// Thuật toán Bubble Sort dùng để sắp xếp các con trỏ sản phẩm theo giá tăng dần
void sapXepTheoGia() {
int n = (int)ds.size();
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
if (ds[j]->getGia() > ds[j+1]->getGia())
swap(ds[j], ds[j+1]);
}
// Tìm kiếm sản phẩm theo tên (đã chuẩn hóa về chữ thường để không phân biệt Hoa/Thường)
vector<T*> timKiemTheoTen(const string& ten) const {
vector<T*> ketQua;
string tenLower = ten;
transform(tenLower.begin(), tenLower.end(), tenLower.begin(), ::tolower);
for (auto p : ds) {
string t = p->getTen();
transform(t.begin(), t.end(), t.begin(), ::tolower);
if (t.find(tenLower) != string::npos)
ketQua.push_back(p);
}
return ketQua;
}
// Hàm tính toán tổng tiền hàng tồn kho (Giá sản phẩm * Số lượng tương ứng)
double tongGiaTriTonKho() const {
double tong = 0;
for (auto p : ds) tong += p->getGia() * p->getSoLuong();
return tong;
}
// Toán tử +: Ghép nối (gộp) hai danh sách sản phẩm lại với nhau
DanhSach<T> operator+(const DanhSach<T>& other) const {
DanhSach<T> result;
for (auto p : ds) result.ds.push_back(p);
for (auto p : other.ds) result.ds.push_back(p);
return result;
}
// Toán tử -: Loại bỏ các sản phẩm ở danh sách bên trái nếu bị trùng mã SP với danh sách bên phải
DanhSach<T> operator-(const DanhSach<T>& other) const {
DanhSach<T> result;
for (auto p : ds) {
bool thuocOther = false;
for (auto q : other.ds)
if (p->getMa() == q->getMa()) { thuocOther = true; break; }
if (!thuocOther) result.ds.push_back(p);
}
return result;
}
// Nạp chồng toán tử xuất cho cả một Danh Sách dữ liệu giúp rút ngắn câu lệnh in ở main
friend ostream& operator<<(ostream& os, const DanhSach<T>& dl) {
for (const auto& p : dl.ds) os << *p;
return os;
}
};
// ============================================================================
// GIẢI THÍCH: HÀM TRỢ GIÚP IN TIÊU ĐỀ
// Tách biệt các phân đoạn chức năng trên màn hình console để giao diện trực quan hơn.
// ============================================================================
void inTieuDe(const string& tieu) {
cout << "\n" << string(70, '=') << "\n";
cout << " " << tieu << "\n";
cout << string(70, '=') << "\n";
}
// ============================================================================
// GIẢI THÍCH: HÀM MAIN - ĐIỀU KHIỂN CHƯƠNG TRÌNH CHÍNH
// Thực hiện gọi tuần tự các chức năng: Nhập, Xuất danh sách, Demo toán tử ++/--,
// Sắp xếp tăng dần, Tìm kiếm theo từ khóa, Thống kê tổng tiền, Sử dụng Hàm mẫu timMax,
// và thực hiện gộp hai danh sách bằng toán tử +.
// ============================================================================
int main() {
system("chcp 65001 > nul"); // Thiết lập bảng mã UTF-8 để console hiển thị tiếng Việt mượt mà
cout << "================================================\n";
cout << " QUAN LY DANH SACH SAN PHAM – OOP C++\n";
cout << "================================================\n";
DanhSach<SanPham> danhSach;
// --- CHỨC NĂNG 1: NHẬP DANH SÁCH SẢN PHẨM PHÂN LOẠI ---
inTieuDe("NHAP DANH SACH SAN PHAM");
int n;
cout << "So luong san pham can nhap: ";
cin >> n; cin.ignore();
for (int i = 0; i < n; i++) {
cout << "\n--- San pham " << i+1 << " ---\n";
cout << " Loai (1 = Vat Ly / 2 = Ky Thuat So): ";
int loai; cin >> loai; cin.ignore();
if (loai == 1) {
SanPhamVatLy* sp = new SanPhamVatLy();
cin >> *sp;
danhSach.them(sp);
} else {
SanPhamKyThuat* sp = new SanPhamKyThuat();
cin >> *sp;
danhSach.them(sp);
}
}
// --- CHỨC NĂNG 2: IN DANH SÁCH GỐC VỪA NHẬP ---
inTieuDe("DANH SACH SAN PHAM DA NHAP");
danhSach.inDanhSach();
// --- CHỨC NĂNG 3: MINH HỌA TOÁN TỬ TĂNG GIẢM (++ / --) TỒN KHO ---
inTieuDe("DEMO TOAN TU ++ VA --");
if (danhSach.kichThuoc() > 0) {
SanPham* sp0 = danhSach.layPhanTu(0);
cout << " Truoc ++: SL = " << sp0->getSoLuong() << "\n";
++(*sp0);
cout << " Sau ++: SL = " << sp0->getSoLuong() << "\n";
--(*sp0);
cout << " Sau --: SL = " << sp0->getSoLuong() << "\n";
}
// --- CHỨC NĂNG 4: SẮP XẾP SẢN PHẨM THEO GIÁ TĂNG DẦN ---
inTieuDe("DANH SACH SAU KHI SAP XEP THEO GIA TANG DAN");
danhSach.sapXepTheoGia();
danhSach.inDanhSach();
// --- CHỨC NĂNG 5: TÌM KIẾM THEO TÊN SẢN PHẨM ---
inTieuDe("TIM KIEM SAN PHAM THEO TEN");
cout << " Nhap tu khoa tim kiem: ";
string tuKhoa; getline(cin, tuKhoa);
auto ketQua = danhSach.timKiemTheoTen(tuKhoa);
if (ketQua.empty()) {
cout << " Khong tim thay san pham nao!\n";
} else {
cout << " Tim thay " << ketQua.size() << " san pham:\n";
for (auto p : ketQua) cout << *p;
}
// --- CHỨC NĂNG 6: THỐNG KÊ TỔNG GIÁ TRỊ TỒN KHO ---
inTieuDe("THONG KE");
cout << fixed << setprecision(0);
cout << " Tong gia tri ton kho: " << danhSach.tongGiaTriTonKho() << " VND\n";
// --- CHỨC NĂNG 7: SỬ DỤNG HÀM MẪU TEMPLATE ĐỂ TÌM GIÁ CAO NHẤT ---
inTieuDe("SAN PHAM CO GIA CAO NHAT (template timMax)");
vector<double> dsGia;
for (int i = 0; i < danhSach.kichThuoc(); i++)
dsGia.push_back(danhSach.layPhanTu(i)->getGia());
if (!dsGia.empty()) {
double maxGia = timMax<double>(dsGia, [](const double& a, const double& b){ return a < b; });
cout << " Gia cao nhat trong danh sach: " << maxGia << " VND\n";
}
// --- CHỨC NĂNG 8: MINH HỌA TOÁN TỬ + ĐỂ GỘP HAI DANH SÁCH ---
inTieuDe("DEMO TOAN TU + (ghep danh sach)");
DanhSach<SanPham> ds1, ds2;
ds1.them(new SanPhamVatLy("SP100","Ban phim co", 850000, 10, 0.5,"Viet Nam"));
ds2.them(new SanPhamKyThuat("SP200","Phan mem diet virus", 299000, 50,"3.1","Kaspersky",true));
cout << " Danh sach 1:\n"; ds1.inDanhSach();
cout << " Danh sach 2:\n"; ds2.inDanhSach();
cout << " Ket qua ghep (ds1 + ds2):\n";
cout << ds1 + ds2;
cout << "\n" << string(70, '=') << "\n";
cout << " Chuong trinh ket thuc. Cam on!\n";
cout << string(70, '=') << "\n";
return 0;
}