Classes abstratas
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 60 de 104.
Uma classe abstrata é qualquer classe que contenha pelo menos uma função virtual pura. Como ela tem uma interface incompleta, você não pode criar objetos de uma classe abstrata diretamente — ela existe exclusivamente para ser herdada.
class Shape {
public:
virtual double area() = 0; // Virtual pura - torna Shape abstrata
virtual double perimeter() = 0; // Outra função virtual pura
void printInfo() { // Método regular - tem implementação
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Erro: não é possível instanciar classe abstrataClasses abstratas podem conter uma combinação de funções virtuais puras (sem implementação), funções virtuais regulares (com implementação padrão) e funções não virtuais. Isso permite definir um comportamento comum e, ao mesmo tempo, exigir que as classes derivadas implementem funcionalidades específicas.
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 implementa todas as funções virtuais puras
c.printInfo(); // Usa o printInfo() herdado, chama o area() de CircleUma classe derivada deve implementar todas as funções virtuais puras para se tornar concreta (instanciável). Se deixar alguma sem implementação, ela própria permanecerá abstrata. Esse mecanismo garante que toda classe concreta na sua hierarquia forneça a funcionalidade necessária.
Desafio
FácilVamos criar um sistema de reprodutor de mídia que demonstra como classes abstratas definem contratos para diferentes tipos de mídia. Você criará uma classe base abstrata que estabelece o que todo reprodutor de mídia deve fazer e, em seguida, implementará reprodutores concretos para conteúdo de áudio e vídeo.
Você organizará seu código em três arquivos:
MediaPlayer.h: Defina uma classe abstrataMediaPlayerque sirva como modelo para todos os tipos de mídia. Sua classe abstrata deve ter:- Um membro
std::string titleprotegido - Um membro
int durationprotegido (em segundos) - Um construtor que inicialize ambos os valores
- Um método virtual puro
play(): cada tipo de mídia será reproduzido de maneira diferente - Um método virtual puro
getMediaType()que retorne umastd::string - Um método comum (não virtual)
showInfo()que exiba:<title> (<duration>s) - Type: <mediaType>. Esse método deve chamargetMediaType()para obter o tipo - Um destrutor virtual
- Um membro
Players.h: Implemente dois reprodutores de mídia concretos que herdem deMediaPlayer:AudioPlayer:- Um membro
std::string artistprivado - Um construtor que receba título, duração e artista
- Implemente
play()para exibir:Playing audio: <title> by <artist> - Implemente
getMediaType()para retornar"Audio"
VideoPlayer:- Um membro
std::string resolutionprivado - Um construtor que receba título, duração e resolução
- Implemente
play()para exibir:Playing video: <title> in <resolution> - Implemente
getMediaType()para retornar"Video"
- Um membro
main.cpp: Leia quatro entradas (cada uma em uma linha separada):- Título da faixa de áudio
- Duração do áudio (inteiro)
- Título do vídeo
- Duração do vídeo (inteiro)
Crie um
AudioPlayercom o artista"Unknown Artist"e umVideoPlayercom a resolução"1080p". Armazene ambos em um array de ponteirosMediaPlayer*.Percorra o array e, para cada reprodutor, chame primeiro
showInfo()e depoisplay(). Exiba uma linha em branco entre cada item de mídia. Libere os objetos alocados dinamicamente ao terminar.
Por exemplo, com as entradas Summer Vibes, 180, Nature Documentary e 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pObserve como o método showInfo() é definido uma vez na classe base abstrata, mas chama o método virtual puro getMediaType(). Isso demonstra como as classes abstratas podem combinar implementações concretas com métodos que as classes derivadas devem fornecer. Lembre-se de usar a palavra-chave override em todos os métodos sobrescritos.
Experimente você mesmo
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Ler entradas
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: Crie um AudioPlayer com o artista "Unknown Artist"
// TODO: Crie um VideoPlayer com a resolução "1080p"
// TODO: Armazene ambos em um array de ponteiros MediaPlayer*
// TODO: Percorra o array e para cada player:
// 1. Chame showInfo()
// 2. Chame play()
// 3. Imprima uma linha em branco entre cada item de mídia
// TODO: Limpe os objetos alocados dinamicamente
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online