Menu
CoddyTech

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ượng

Lớ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 Circle

Lớ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.

challenge icon

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ượng MediaPlayer, đó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 title có phạm vi truy cập protected
    • Một thành viên int duration có 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ọi getMediaType() để lấy loại phương tiện
    • Một hàm hủy ảo
  • 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 artist có 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 resolution có 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"
  • main.cpp: Đọc bốn đầu vào, mỗi đầu vào trên một dòng riêng:
    1. Tiêu đề bản nhạc
    2. Thời lượng âm thanh (số nguyên)
    3. Tiêu đề video
    4. Thời lượng video (số nguyên)

    Tạo một AudioPlayer với nghệ sĩ là "Unknown Artist" và một VideoPlayer vớ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ọi play(). 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 1080p

Lư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;
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch C++ online