Lớp trừu tượng
Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 60 trên 104.
Lớp trừu tượng (abstract class) là lớp có ít nhất một hàm ảo thuần túy (pure virtual function). Vì giao diện của lớp chưa hoàn chỉnh, bạn không thể trực tiếp tạo đối tượng từ lớp trừu tượng, lớp này chỉ tồn tại để được kế thừa.
class Shape {
public:
virtual double area() = 0; // Hàm ảo thuần túy, khiến Shape trở thành lớp trừu tượng
virtual double perimeter() = 0; // Một hàm (function) ảo thuần túy khác
void printInfo() { // Phương thức (method) thông thường, có phần triển khai
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Lỗi: không thể tạo instance của lớp trừu tượngLớp trừu tượng có thể chứa hàm ảo thuần túy (không có phần triển khai), hàm ảo thông thường (có phần triển khai mặc định) và hàm không ảo. Nhờ vậy, bạn có thể xác định hành vi chung, đồng thời yêu cầu các lớp dẫn xuất triển khai chức năng cụ thể.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override { return 3.14159 * radius * radius; }
double perimeter() override { return 2 * 3.14159 * radius; }
};
Circle c(5.0); // OK: Circle triển khai tất cả các hàm (function) thuần ảo
c.printInfo(); // Sử dụng printInfo() kế thừa và gọi area() của CircleLớp dẫn xuất phải triển khai tất cả các hàm ảo thuần túy để trở thành lớp cụ thể (có thể khởi tạo). Nếu lớp này không triển khai một hàm nào, bản thân nó vẫn là lớp trừu tượng. Cơ chế này đảm bảo mọi lớp cụ thể trong hệ phân cấp của bạn đều cung cấp chức năng cần thiết.
Thử thách
DễHãy xây dựng một hệ thống phát đa phương tiện để minh họa cách các lớp trừu tượng định nghĩa hợp đồng cho nhiều loại phương tiện khác nhau. Bạn sẽ tạo một lớp cơ sở trừu tượng quy định những việc mọi trình phát đa phương tiện phải làm, sau đó triển khai các trình phát cụ thể cho nội dung âm thanh và video.
Bạn sẽ sắp xếp code trong ba tệp:
MediaPlayer.h: Định nghĩa lớp trừu tượngMediaPlayer, đóng vai trò làm bản thiết kế cho mọi loại phương tiện. Lớp trừu tượng của bạn cần có:- Một thành viên
std::string titlecó phạm vi truy cập protected - Một thành viên
int durationcó phạm vi truy cập protected (tính bằng giây) - Một hàm khởi tạo khởi tạo cả hai giá trị
- Một phương thức thuần ảo
play(): mỗi loại phương tiện sẽ phát theo cách khác nhau - Một phương thức thuần ảo
getMediaType()trả vềstd::string - Một phương thức thông thường (không ảo)
showInfo()in ra:<title> (<duration>s) - Type: <mediaType>. Phương thức này cần gọigetMediaType()để lấy loại phương tiện - Một hàm hủy ảo
- Một thành viên
Players.h: Triển khai hai trình phát đa phương tiện cụ thể kế thừa từMediaPlayer:AudioPlayer:- Một thành viên
std::string artistcó phạm vi truy cập private - Một hàm khởi tạo nhận tiêu đề, thời lượng và nghệ sĩ
- Triển khai
play()để in ra:Playing audio: <title> by <artist> - Triển khai
getMediaType()để trả về"Audio"
VideoPlayer:- Một thành viên
std::string resolutioncó phạm vi truy cập private - Một hàm khởi tạo nhận tiêu đề, thời lượng và độ phân giải
- Triển khai
play()để in ra:Playing video: <title> in <resolution> - Triển khai
getMediaType()để trả về"Video"
- Một thành viên
main.cpp: Đọc bốn đầu vào, mỗi đầu vào trên một dòng riêng:- Tiêu đề bản nhạc
- Thời lượng âm thanh (số nguyên)
- Tiêu đề video
- Thời lượng video (số nguyên)
Tạo một
AudioPlayervới nghệ sĩ là"Unknown Artist"và mộtVideoPlayervới độ phân giải là"1080p". Lưu cả hai trong một mảng các con trỏMediaPlayer*.Duyệt qua mảng và với mỗi trình phát, trước tiên gọi
showInfo(), sau đó gọiplay(). In một dòng trống giữa mỗi mục phương tiện. Khi hoàn tất, hãy giải phóng các đối tượng đã cấp phát động.
Ví dụ, với các đầu vào Summer Vibes, 180, Nature Documentary và 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pLưu ý rằng phương thức showInfo() được định nghĩa một lần trong lớp cơ sở trừu tượng nhưng lại gọi phương thức thuần ảo getMediaType(). Điều này cho thấy các lớp trừu tượng có thể kết hợp phần triển khai cụ thể với những phương thức mà các lớp dẫn xuất phải cung cấp. Hãy nhớ dùng từ khóa override cho mọi phương thức được ghi đè.
Tự làm thử
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Đọc dữ liệu đầu vào
std::string audioTitle;
int audioDuration;
std::string videoTitle;
int videoDuration;
std::getline(std::cin, audioTitle);
std::cin >> audioDuration;
std::cin.ignore();
std::getline(std::cin, videoTitle);
std::cin >> videoDuration;
// TODO: Tạo AudioPlayer với nghệ sĩ "Unknown Artist"
// TODO: Tạo VideoPlayer với độ phân giải "1080p"
// TODO: Lưu cả hai vào mảng (array) các con trỏ MediaPlayer*
// TODO: Duyệt mảng và với mỗi trình phát:
// 1. Gọi showInfo()
// 2. Gọi play()
// 3. In ra một dòng trống giữa các mục nội dung đa phương tiện
// TODO: Giải phóng các đối tượng (object) được cấp phát động
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiBiên dịch và tạo bản dựng C++Tệp header và tệp nguồnNamespace và phạm viGiới thiệu về OOP trong C++Lớp và đối tượngCon trỏ thisPhương thức (hàm thành viên)Thuộc tính (dữ liệu thành viên)Cơ bản về hàm khởi tạo và hàm hủyÔn tập: máy tính đơn giản4Thuộc tính của lớp
Thành viên đối tượng và thành viên tĩnhHàm truy xuất và hàm thiết lậpHàm thành viên hằngTừ khóa mutablePhương thức và biến tĩnhHàm và lớp bạnÔn tập: Trình quản lý tài khoản ngân hàng7Kế thừa
Kế thừa cơ bảnMức truy cập trong kế thừaThứ tự gọi hàm khởi tạo và hàm hủyGhi đè phương thứcHàm ảo và VTableĐa kế thừaKế thừa ảoÔn tập: hệ phân cấp nhân viên2Quản lý bộ nhớ
Bộ nhớ ngăn xếp và heapCon trỏ và tham chiếuBộ nhớ động (new/delete)Con trỏ thông minh trong C++RAII trong C++Ôn tập: Trình quản lý mảng động5Tính đóng gói
Phạm vi truy cập trong C++Tìm hiểu sâu về phạm vi truy cậpẨn thông tinStruct và lớpLớp lồng nhau và lớp bên trongÔn tập: hệ thống hồ sơ sinh viên8Đa hình
Đa hình lúc biên dịch và khi chạyNạp chồng hàmÔn lại hàm ảoHàm ảo thuần túyLớp trừu tượngThiết kế interface trong C++Ép kiểu động và RTTIÔn tập: Máy tính hình học3Hàm khởi tạo và hàm hủy
Hàm khởi tạo mặc địnhHàm khởi tạo có tham sốHàm khởi tạo sao chépHàm khởi tạo di chuyểnDanh sách khởi tạoHàm khởi tạo ủy quyềnTìm hiểu sâu về hàm hủyQuy tắc 3 / 5 / 0Ôn tập: lớp chuỗi6Nạp chồng toán tử
Giới thiệu về nạp chồng toán tửNạp chồng toán tử số họcNạp chồng toán tử so sánhToán tử luồngNạp chồng toán tử gánNạp chồng toán tử [] và ()Toán tử chuyển đổi kiểuÔn tập: lớp ma trận9Mẫu
Mẫu hàmMẫu lớpChuyên biệt hóa mẫuMẫu biến tham sốCơ bản về SFINAE và đặc điểm kiểuÔn tập: vùng chứa tổng quátTự luyện tập: Trình biên dịch C++ online