Giao diện
Chương 8
Giải trọn một đề thi thật trong 60 phút
Bảy chương trước dạy từng mảnh. Chương này ghép lại, dưới đúng điều kiện phòng thi. Đề dùng ở đây là đề thi thật của học kỳ 2 năm học 2024–2025, không sửa một chữ.
Cách dùng chương này
Đừng đọc hết rồi mới làm. Đọc mục 8.1, đóng sách, bấm giờ 60 phút, tự làm. Xong rồi mới mở phần còn lại để đối chiếu.
Làm lần đầu mà không xong là chuyện bình thường — trung bình lớp của đề này là 4.8. Điều quan trọng là bạn biết mình hụt ở đâu trước ngày thi, chứ không phải sau.
8.1 Đề bài
Mục tiêu 60 phút60:00Chưa bắt đầu
Năm chặng: đọc đề và vẽ UML → dựng khung → cài đặt → chạy và so từng dòng → nộp
cpp
DE 1 - HE THONG PHUONG TIEN GIAO THONG Thoi gian: 60 phut
1. Lop truu tuong Vehicle
- Thuoc tinh: string id, string brand, int year
- Constructor day du
- virtual void displayInfo() const = 0 (thuan ao)
- Virtual destructor
2. Lop LandVehicle (truu tuong) ke thua Vehicle: them int wheels
3. Lop AirVehicle (truu tuong) ke thua Vehicle: them int maxAltitude
4. Lop Car ke thua LandVehicle: them int seats, string fuelType
5. Lop Motorbike ke thua LandVehicle: them bool hasGear
6. Lop Helicopter ke thua AirVehicle: them int rotors
Trong main():
- Tao vector chua Vehicle*
- Them 1 Car, 1 Motorbike, 1 Helicopter
- Goi displayInfo() bang con tro Vehicle* (da hinh)
Test case:
Car c("C01", "Toyota", 2020, 4, "Xang", 5);
Motorbike m("M01", "Honda", 2018, 2, true);
Helicopter h("H01", "Airbus", 2015, 3000, 4);
Ket qua mong doi:
[Car] Toyota (C01) - 2020 | Seats: 5 | Fuel: Xang | Wheels: 4
[Motorbike] Honda (M01) - 2018 | Gear: Co | Wheels: 2
[Helicopter] Airbus (H01) - 2015 | Altitude: 3000m | Rotors: 4
Thang diem: Tinh truu tuong 1 | Thiet ke lop truu tuong, ke thua day du 2
Da hinh qua con tro 1 | Tinh dong goi 1
Chuong trinh chay dung 3 | Chia file ro rang 2 | UML (bonus) 28.2 Phân bổ 60 phút
| Phút | Việc | Vì sao |
|---|---|---|
| 0 – 8 | Đọc đề, gạch danh từ, vẽ UML | Lấy 2 điểm thưởng và có bản kế hoạch |
| 8 – 22 | Dựng khung: tạo đủ file, viết hết phần khai báo .h | Lấy chắc 2 điểm chia file trước khi hết giờ |
| 22 – 45 | Cài đặt từng .cpp, biên dịch sau mỗi lớp | Phần dài nhất, nhưng lúc này chỉ còn là gõ |
| 45 – 55 | Chạy, so output từng dòng với đề | 3 điểm “chạy đúng” |
| 55 – 60 | Dọn, nén, đặt tên, nộp | Mất bài vì nộp sai là mất trắng |
Nguyên tắc quan trọng nhất của 60 phút
Lấy điểm chắc trước, điểm khó sau. Chia file 2 điểm và UML 2 điểm là 4 điểm — nhiều hơn cả mục “chạy đúng” — mà không đòi hỏi gì ngoài thao tác. Một bài có đủ file, có UML, chạy được một nửa vẫn hơn hẳn một bài làm dở dang phần logic.
Trung bình lớp là 4.8. Nghĩa là lấy trọn 4 điểm “dễ” cộng một phần “chạy đúng” đã đưa bạn lên trên trung bình.
8.3 Phút 0 – 8: đọc đề và vẽ UML
Đề đã liệt kê sẵn các lớp, nên bước gạch danh từ chỉ mất một phút. Vẽ ngay quan hệ:


Ba điều đọc ra được từ sơ đồ, trước khi gõ dòng nào
Ba tầng, không phải hai. Car gọi constructor của LandVehicle, LandVehicle gọi constructor của Vehicle.
Hai lớp giữa vẫn trừu tượng. Đề ghi rõ “(trừu tượng)”. Nghĩa là LandVehicle và AirVehicle không cài đặt displayInfo() — chúng chỉ thêm thuộc tính rồi truyền tiếp trách nhiệm xuống.
Sáu lớp = sáu cặp file, trừ hai lớp chỉ cần .h? Không — LandVehicle và AirVehicle vẫn có constructor cần cài đặt, nên vẫn đủ cặp .h / .cpp. Tổng cộng 13 file kể cả main.cpp.
Chép sơ đồ này vào đầu main.cpp dưới dạng comment. Xong 2 điểm thưởng, mất 3 phút.
8.4 Phút 8 – 22: dựng khung
Tạo đủ 13 file và viết hết phần khai báo trước, chưa cài đặt gì. Lý do: nếu hết giờ, bạn vẫn có đủ file và đủ thiết kế — đó là 2 điểm chia file cộng 2 điểm thiết kế lớp, lấy được ngay cả khi chương trình chưa chạy.
cpp
#ifndef VEHICLE_H
#define VEHICLE_H
#include <string>
class Vehicle {
protected: // protected vi lop con can dung
std::string id;
std::string brand;
int year;
public:
Vehicle(const std::string& id, const std::string& brand, int year);
std::string getId() const; // dong goi: 1 diem
std::string getBrand() const;
int getYear() const;
virtual void displayInfo() const = 0; // truu tuong: 1 diem
virtual ~Vehicle(); // bat buoc khi co ham ao
};
#endifTầng giữa chỉ thêm thuộc tính, không cài displayInfo():
cpp
class LandVehicle : public Vehicle {
protected:
int wheels;
public:
LandVehicle(const std::string& id, const std::string& brand,
int year, int wheels);
int getWheels() const;
};Tầng lá là nơi duy nhất có override:
cpp
class Car : public LandVehicle {
private:
int seats;
std::string fuelType;
public:
Car(const std::string& id, const std::string& brand, int year,
int wheels, const std::string& fuelType, int seats);
void displayInfo() const override;
};Thứ tự tham số — chỗ mất điểm rất oan
Đề cho test case Car c("C01", "Toyota", 2020, 4, "Xang", 5); — tức là id, brand, year, wheels, fuelType, seats.
Nếu bạn khai báo constructor theo thứ tự ..., seats, fuelType thì dòng test của đề sẽ không biên dịch, hoặc tệ hơn là biên dịch được nhưng gán nhầm. Luôn khai báo constructor đúng thứ tự trong test case của đề, không theo thứ tự bạn thấy hợp lý.
8.5 Phút 22 – 45: cài đặt
Từ đây chỉ còn là gõ theo khung. Chuỗi constructor ba tầng:
text
Vehicle::Vehicle(const string& id, const string& brand, int year)
: id(id), brand(brand), year(year) {}
LandVehicle::LandVehicle(const string& id, const string& brand,
int year, int wheels)
: Vehicle(id, brand, year), wheels(wheels) {}
Car::Car(const string& id, const string& brand, int year,
int wheels, const string& fuelType, int seats)
: LandVehicle(id, brand, year, wheels), seats(seats), fuelType(fuelType) {}Và phần in — chép định dạng từ đề, từng dấu cách:
cpp
void Car::displayInfo() const {
cout << "[Car] " << brand << " (" << id << ") - " << year
<< " | Seats: " << seats
<< " | Fuel: " << fuelType
<< " | Wheels: " << wheels << endl;
}main.cpp đúng như đề yêu cầu:
cpp
vector<Vehicle*> ds;
ds.push_back(new Car("C01", "Toyota", 2020, 4, "Xang", 5));
ds.push_back(new Motorbike("M01", "Honda", 2018, 2, true));
ds.push_back(new Helicopter("H01", "Airbus", 2015, 3000, 4));
for (Vehicle* v : ds) v->displayInfo(); // da hinh: 1 diem
for (Vehicle* v : ds) delete v; // nho tra bo nho8.6 Phút 45 – 55: chạy và so từng dòng
sh
g++ -std=c++17 -Wall *.cpp -o main.exe
main.exeKết quả phải khớp tuyệt đối với khối in trên đề:
text
[Car] Toyota (C01) - 2020 | Seats: 5 | Fuel: Xang | Wheels: 4
[Motorbike] Honda (M01) - 2018 | Gear: Co | Wheels: 2
[Helicopter] Airbus (H01) - 2015 | Altitude: 3000m | Rotors: 4So output là so từng ký tự
Đây là chỗ mất điểm nhiều nhất mà sinh viên không ngờ tới. Các lỗi thật đã gặp:
"Toyota(C01)" thiếu dấu cách trước ngoặc — đề in "Toyota (C01)".
"2020| Seats" thiếu dấu cách trước gạch đứng — đề in "2020 | Seats".
"Fuel Type:" đề chỉ in "Fuel:".
Cách chắc ăn: mở đề ra, đặt cạnh màn hình, đọc song song từng dòng. Hoặc chép khối kết quả của đề vào comment trong main.cpp ngay từ phút thứ 8.
8.7 Phút 55 – 60: nộp
- Xoá
main.exe, xoá thư mục.vscodenếu có. - Mở cửa sổ dòng lệnh mới, vào thư mục bài làm, chạy lại lệnh
g++một lần nữa để chắc chắn. - Nén thư mục thành
<MSSV>_<Ho ten khong dau>.zip. - Chép vào
C:\OOPđúng như đề ghi. Kiểm tra lại là file đã thật sự nằm ở đó.
8.8 Tự chấm
Chấm bài mình vừa làm bằng đúng thang điểm của đề. Trung thực, vì người duy nhất bị thiệt khi chấm rộng tay là chính bạn:
| Mục | Cách tự kiểm | Điểm của bạn |
|---|---|---|
| Tính trừu tượng 1 | Vehicle có hàm thuần ảo = 0 và không tạo đối tượng được | |
| Thiết kế lớp, kế thừa đầy đủ 2 | Đủ 6 lớp, đúng 3 tầng, hai lớp giữa vẫn trừu tượng | |
| Đa hình qua con trỏ 1 | vector<Vehicle*> và gọi v->displayInfo() | |
| Tính đóng gói 1 | protected/private + có getter | |
| Chạy đúng, test case hợp lý 3 | Output khớp từng ký tự với đề | |
| Chia file rõ ràng 2 | 13 file, đúng tên, có header guard, không include .cpp | |
| UML (bonus) +2 | Có sơ đồ, dù chỉ là comment trong main.cpp |
Tổng 0/10
8.9 Mười chỗ mất điểm dù code gần đúng
- Quên
;sau dấu}đóng lớp. - Quên
virtual ~Vehicle()— mất điểm thiết kế và rò rỉ bộ nhớ. - Cài
displayInfo()ởLandVehicletrong khi đề ghi lớp đó trừu tượng. - Constructor sai thứ tự tham số so với test case của đề.
- Dùng
vector<Vehicle>thay vìvector<Vehicle*>— object slicing, mất luôn điểm đa hình. - Thiếu một dấu cách trong chuỗi in.
- Gộp nhiều lớp vào một file
.h— mất điểm chia file. #include "Car.cpp"thay vì đưa file vào lệnh biên dịch.- Quên
override, gõ thiếuconstnên ghi đè hụt mà không biết. - Không vẽ UML — bỏ trắng 2 điểm thưởng.
Lời giải đầy đủ
Bản giải hoàn chỉnh của đề này nằm ở TH/2026/giaotrinh/vi_du/de_thi_vehicle/, 13 file, biên dịch sạch với -Wall -Wextra và cho ra đúng ba dòng output ở mục 8.6.
Chỉ mở nó sau khi đã tự làm hết 60 phút. Đọc lời giải trước khi tự vật lộn là cách nhanh nhất để tưởng mình biết làm — và mục 0.1 đã cho thấy cảm giác đó tốn kém thế nào.
Tự kiểm tra — đề thứ hai, tự lực hoàn toàn
Mục tiêu 60 phút60:00Chưa bắt đầu
Năm chặng: đọc đề và vẽ UML → dựng khung → cài đặt → chạy và so từng dòng → nộp
60 phút, bấm giờ, không mở sách.
text
QUAN LY KHOA HOC TRUC TUYEN (de thi HK1 2025-2026)
Cau 1 (6 diem)
1. Lop truu tuong Course:
- protected string title
- Ham thuan ao string getType()
2. Lop ke thua:
- OnlineCourse : them int videoCount
- OfflineCourse: them string location
3. Trong main(): tao 1 khoa online, 1 khoa offline, goi getType()
Ket qua mong doi:
[OnlineCourse] Lap trinh C++, So video: 25
Loai khoa hoc: Online
[OfflineCourse] OOP Nang cao, Dia diem: Phong B101
Loai khoa hoc: Offline
Cau 2 (4 diem)
1. Lop Enrollment: private int fee, getter getFee()
Constructor: neu fee <= 0 -> throw invalid_argument("Hoc phi khong hop le!")
2. Lop ScholarshipEnrollment ke thua Enrollment: override getFee() (giam 50%)
3. Trong main(): dung Enrollment*, tao 2 dang ky hop le, 1 dang ky loi, bat ngoai le
Ket qua mong doi:
Dang ky khoa hoc, hoc phi: 2000000
Dang ky hoc bong, hoc phi: 1000000
Loi: Hoc phi khong hop le!
Thang diem: Thiet ke dung lop 2 | Ham ao va override 2 | Ngoai le 1
Chay dung 3 | Chia file 2 | UML/comment (bonus) 2Đề này gộp chương 5, 6 và 7. Làm xong hãy tự chấm bằng bảng ở mục 8.8, và ghi lại ba chỗ mất điểm lớn nhất của mình. Ba chỗ đó chính là những chương bạn cần đọc lại.