1 / 54

Tópicos em C++

Claudio Esperança Paulo Roma Cavalcanti. Tópicos em C++. Classes e Objetos. C++ é mais do que C com uns poucos sinos e apitos adicionais. O mecanismo básico para atingir programação orientada a objeto em C++ é o conceito de classe .

aric
Download Presentation

Tópicos em C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Claudio Esperança Paulo Roma Cavalcanti Tópicos em C++

  2. Classes e Objetos • C++ é mais do que C com uns poucos sinos e apitos adicionais. • O mecanismo básico para atingir programação orientada a objeto em C++ é o conceito de classe. • Fornece formas de encapsulamento e proteção de informação. • Permite reutilização de código.

  3. O que é OO? • Programação orientada a objeto foi o paradigma dominante dos anos 90. • Criou o conceito de objeto, que é um tipo de dado com uma estrutura e um estado. • Cada objeto define um conjunto de operações que podem acessar ou manipular esse estado. • Tipos definidos pelo usuário devem se comportar da mesma maneira de tipos pré-definidos (fornecidos pelo compilador).

  4. Tipos de Dados • Um tipo básico de dados de uma linguagem, como inteiro, real, ou caractere, fornece certas coisas: • Podem-se declarar novos objetos, com ou sem iniciação. • Pode-se copiar ou testar quanto à igualdade. • Pode-se executar a entrada e a saída de dados com esses objetos.

  5. Objetos • Um objeto é uma unidade atômica. • Não pode ser dissecado por um programador. • Proteção de informação torna os detalhes de implementação inacessíveis. • Encapsulamento é o agrupamento de dados, e operações que se aplicam a eles, para formar um agregado. • Mas escondendo a os detalhes de implementação. • Uma classe é o mesmo que uma estrutura, mas com os membros protegidos por default.

  6. Encapsulamento • O princípio de atomicidade é conhecido por encapsulamento. • O usuário não tem acesso direto às partes de um objeto ou a sua implementação. • O acesso é feito indiretamente, através de funções fornecidas com o objeto. • É uma forma de imitar a vida real. • Aparelhos eletrônicos possuem o seguinte aviso: “Não abra – não há partes consertáveis por um usuário”. • Pessoas sem treinamento que tentam consertar equipamentos com esses avisos acabam quebrando mais do que consertando.

  7. Reutilização de Código • A idéia é usar os mesmos componentes sempre que possível. • Quando o mesmo objeto é a solução fica fácil. • O difícil é quando se necessita de um objeto ligeiramente diferente. • C++ fornece diversos mecanismos para isso. • Se a implementação for idêntica exceto pelo tipo básico do objeto, pode-se usar uma template. • Herança é o mecanismo que permite estender a funcionalidade de um objeto.

  8. Classes • Classes são usadas para encapsular informação. • Já que as funções que manipulam o estado do objeto são membros da classe, elas são acessadas pelo operador “.”, como em uma estrutura de C. • Quando se chama uma função de uma classe, na realidade se está passando uma mensagem para o objeto. • A diferença básica entre OO de C++ e o velho C é puramente filosófica: em C++ o objeto tem o papel principal.

  9. Para Não Esquecer Jamais • Se você deseja usar OO, acostume-se a esconder todos os dados das classes. • C++ não é uma linguagem para preguiçosos. • Para isto existe uma seção de dados privados. • O compilador se esforçará ao máximo para mantê-los inacessíveis ao “mundo exterior”. • Toda classe possui um construtor e um destrutor, que dizem como uma instância do objeto deve ser iniciada e destruída. • Procure escrevê-los sempre. Evite defaults.

  10. class memoryCell { public: memoryCell ( ) { value = 0; } int read ( ) const { return value; } void write ( int x ) { value = x; } private: int value; }; Construtor value está protegido Exemplo Bobo

  11. main ( ) { memoryCell m; m.write ( 5 ); cout << “Conteúdo da célula é: “ #if 1 << m.read ( ) #else << m.value #endif << ‘\n’; return 0; } Erro!!! value é privado!!! Instancia a classe Armazena um valor Maneira correta Diretiva de pré-processamento Uso da Classe

  12. Interface • A interface descreve o que pode ser feito com o objeto. • Necessita de uma boa convenção de nomes. • Exemplo: primeira letra de nome de classe ou função minúscula, demais palavras do nome com primeira letra maiúscula (sem “_”). • memoryCell. • Dados: ponteiros com prefixo ptr e pode usar “_”. • Constantes e tipos enumeráveis em caixa alta: • GL_FLOAT, GLUT_DOUBLE; • Prefixo com o nome do pacote. • glLoadIdentity(), glVertex3f.

  13. Documentação • É fundamental uma documentação mínima em todo o arquivo de um sistema. • Existem ferramentas muito boas de domínio público, como o Doxygen. • Basta colocar diretivas na forma de comentários na própria interface. • É melhor acrescentar as diretivas durante a confecção da interface para não ter de voltar depois de “má vontade”.

  14. // memoryCell.h // evita incluir a interface de novo se ela já foi // incluída por outro arquivo. #ifndef __MEMORY_CELL__ #define __MEMORY_CELL__ /** * A very simple interface for a generic class. * Doxygen will generate the documentation, * in html, rtf and/or latex automatically. */ class memoryCell { public: /**empty construtor. * Sets the content of this cell to value. * * @param value new value for this cell. */ memoryCell ( int value = 0 ); /// destrutor. Não faz nada. ~memoryCell ( ) { } Interface memoryCell

  15. /**the correct way of returning the content of this cell. * @return the content of this cell. */ int read ( ) const; /** sets a new value for this cell. * @param x new value. */ void write ( int x ); private: ///holds the content of this cell. int cell_value; }; #endif Não altera dados desta classe Altera dados desta classe Interface memoryCell

  16. Implementação • A implementação contém os detalhes de como a interface foi codificada, de forma a atender as especificações do projeto. • As declarações dos nomes das funções ficam na declaração da classe e as implementações são definidas depois, normalmente num arquivo separado (.C, .cpp, ou .cc). • Usam a sintaxe de função mais o nome da classe e o operador de escopo “::”.

  17. // memoryCell.C #include memoryCell.h memoryCell::memoryCell ( int value ) { this->cell_value = value; } int memoryCell::read ( ) const { return this->cell_value; } void memoryCell::write ( int x ) { this->cell_value = x; } getter setter Construtor Membro desta classe Implementação memoryCell

  18. Construtores e Destrutores • O construtor diz como o objeto é declarado e iniciado. • Se a iniciação não casar com nenhum construtor, o compilador reclamará. • O destrutor diz como o objeto será destruído quando sair de escopo. • No mínimo deve liberar a memória que foi alocada por chamadas “new”no construtor. • Se nenhum destrutor for declarado será gerado um default, que aplicará o destrutor correspondente a cada dado da classe.

  19. Construtor de Cópia • O construtor de cópia é chamado sempre que o objeto for passado ou retornado por valor. • Se nenhum for declarado será criado um default, que aplicaráoconstrutor de cópia correspondente a cada dado da classe. • Se a declaração for privada, o construtor de cópia será desativado (não poderá ser invocado). • Ou escreva um construtor de cópia decente ou então desative-o.

  20. memoryCell ( const memoryCell& m ) { *this = m; } Operador de atribuição Construtor de Cópia Exemplo de Construtor de Cópia Uso: memoryCell m1 (4 ); memoryCell m2 ( m1);

  21. Operador de Atribuição • É usado para copiar objetos do mesmo tipo. • O operador de cópia cria um novo objeto, mas o operador “=“ age sobre um objeto já existente. • Operadores de atribuição retornam, em geral, referências constantes. Retorno por valor não é uma boa idéia ... • Retornar uma referência não constante torna (A = B) = C válido, mas sem sentido. • O resultado é atribuir C à referência A, sem nunca ter atribuído à B. • Se não for definido haverá uma cópia membro a membro dos dados da classe por default.

  22. const memoryCell& memoryCell::operator = ( const memoryCell& m ) { this->cell_value = m.cell_value; return *this; } Referência para este objeto Operador de atribuição Exemplo de Operador = Uso: memoryCell m1 (4 ); memoryCell m2 = m1;

  23. Iniciação X Construtor memoryCell ( int value = 0 ) :cell_value ( value ){ } • A seqüência depois de “:” é a lista de iniciação. • Os membros são iniciados na ordem em que são declarados e não na ordem da lista. • O ideal é iniciar cada membro da classe pelo seu próprio construtor. • É preferível iniciar os membros da classe usando listas de iniciação ao invés de atribuir no construtor. • Cada membro não especificado na lista é iniciado pelo seu construtor vazio. Só depois é que as atribuições são feitas (trabalho dobrado). • Se a iniciação não for simples, só aí usa-se o corpo do construtor.

  24. Sobreposição de Operadores • Em C++ todos os operadores podem ser sobrepostos, a exceção de: “.”, “.*”, “?” e “sizeof”. • Precedência e aridade não são alteradas. • Um operador binário retorna, em geral, um objeto por valor, porque o resultado é armazenado em um temporário. • Também pode ser implementado invocando o operador de atribuição.

  25. const memoryCell& memoryCell::operator += ( const memoryCell& m ) { this->cell_value += m.cell_value; return *this; } memoryCell memoryCell::operator + ( const memoryCell& m ) const { memoryCell temp ( *this ); temp += m; return temp; } Referência para este objeto Adiciona o segundo operando Retorna temp por valor Exemplo de Sobreposição

  26. Templates • Templates servem para escrever rotinas que funcionam para tipos arbitrários. • O mecanismo baseado em typedef permite criar rotinas genéricas. • Mas não é suficiente se queremos rotinas que funcionem com dois tipos diferentes. • Uma template não é uma função comum, mas sim um padrão para criar funções. • Quando uma template é instanciada com um tipo particular, uma nova função é criada. • A template é expandida (como uma macro) para prover uma função real. • O compilador gera código a partir da template para cada combinação diferente de parâmetros.

  27. Exemplo de Função Template template <classEtype> inline const Etype&Max ( const Etype& a, const Etype& b ) { return a > b ? a : b; } • Etype define o tipo do parâmetro da template. • O código da template pode ser substituído como uma macro, caso se use a opção inline. • Deve-se retornar uma referência, pois não se sabe de antemão o tamanho do dado retornado. • O operador “>”deve estar definido no tipo Etype.

  28. Classes Template • A sintaxe é similar ao de uma função template. • Não há sentido em gerar uma biblioteca de templates. • Lembre-se que o compilador não sabe que tipos serão necessários. Logo, não gera código algum. • Ou todo o código da template está junto com a interface, ou a template deve ser instanciada de antemão para cada tipo a ser usado na aplicação.

  29. // memoryCell.h #ifndef __MEMORY_CELL__ #define __MEMORY_CELL__ /** *A very simple interface for a template class. */ template <class Etype> class memoryCell { public: /** empty construtor. * Sets the content of this cell to value. */ memoryCell ( const Etype& value = Etype() ) { cell_value = value; } /** the correct way of returning the content of this cell. * @return the content of this cell. */ const Etype& read ( ) const { return cell_value; } /** sets a new value for this cell. * @param x new value. */ void write ( const Etype& x ) { cell_value = x; } private: /// holds the content of this cell. Etype cell_value; }; #endif Referência constante Especificação template antes da classe Pode assumir qualquer tipo TemplatememoryCell

  30. main ( ) { memoryCell<int> m; m.write ( 5 ); cout << “Conteúdo da célula é: “ #if 1 << m.read ( ) #else << m.cell_value #endif << ‘\n’; return 0; } Instancia a template com int Uso da TemplatememoryCell

  31. Herança • Talvez o principal objetivo de programação orientada a objeto seja a reutilização de código. • Templates são apropriadas quando a funcionalidade básica do código é independente de tipo. • Herança serve para estender a funcionalidade de um objeto. • Criam-se novos tipos com propriedades restritas ou estendidas do tipo original.

  32. Polimorfismo • Polimorfismo permite que um objeto possa armazenar vários tipos diferentes de objetos. • Quando uma operação for aplicada a um objeto polimorfo será selecionada automaticamente aquela adequada ao tipo armazenado. • Sobreposição (overload) de funções e operadores é um exemplo de polimorfismo. • Neste caso, a seleção da função é feita em tempo de compilação, o que limita o comportamento polimorfo.

  33. Reutilização • Em C++ é possível postergar a seleção da função até que o programa esteja sendo executado. • O mecanismo básico usa herança. • Freqüentemente, no projeto de uma nova classe, descobre-se que há uma classe similar escrita anteriormente. • Reutilização de código sugere que não se comece do zero, mas que se escreva uma nova classe baseada na classe existente. • Em C++ o mecanismo é criar uma classe abstrata polimorfa que possa armazenar os objetos das classes similares.

  34. Exemplos • Classe Vetor: Vetores limitados. • Classe Data: Calendários diversos (Gregoriano, Hebreu, Chinês). • Impostos: vários tipos de contribuintes (solteiro, casado, separado, cabeça de casal). • Formas: (círculos, quadrados, triângulos).

  35. Três Opções • Definir classes completamente independentes. • Implica em escrever vários pedaços de código idênticos. • Aumenta a chance de erro e cada mudança deve ser replicada. • Usar uma única classe e controlar as funções por if / else e switches. • Manutenção é difícil, pois cada alteração requer recompilar todo o código. • Extensão das classes por um programador não é possível a menos que todo o fonte esteja disponível. • If / else gasta tempo de execução, mesmo que o resultado seja conhecido em tempo de compilação. • Não há segurança de tipos, pois se toda forma está contida em uma única classe, pode-se atribuir um círculo a um triângulo.

  36. Derivação • Derivar classes a partir de uma classe base (herança). • Uma classe derivada herda todas as propriedades da classe base. • Cada classe derivada é uma nova classe. Logo, a classe base não é afetada por mudanças nas classes derivadas. • Uma classe derivada é compatível por tipo com a base, mas o contrário é falso. • Classes irmãs não são compatíveis por tipo.

  37. Análise • As primeiras duas opções são típicas de programação procedural. • A terceira opção é aquela que deve ser adotada em programação orientada a objeto. classDerivada: publicBase { // membros não listados são herdados. public: // construtores e destrutores (em geral // diferentes da base) // membros da base sobrepostos // novos membros públicos private: // dados adicionais (geralmente privados) // funções privadas adicionais // membros da base a serem desativados };

  38. Uso de Ponteiros com Herança • Ponteiro para classe base • classeBase* bPtr = NULL; • classeBase bVar;  Objeto do tipo base. • bPtr = &bVar;  Trivialmente válido. • bPtr = &dVar;  dVar é do tipo classeDerivada. • bPtr->print();  Chama print da classe base. • Ponteiro para classe derivada • classeDerivada* dPtr = NULL; • classeDerivada dVar;  Objeto do tipo derivado. • dPtr = &dVar;  Trivialmente válido. • dPtr = &bVar;  ERRO!! • dPtr->print();  Chama print da derivada.

  39. Apontar Ponteiro Derivado para Classe Base • Gera erro. • Classe derivada pode ter métodos e variáveis que não existem na classe base. • Objeto da classe base não é do mesmo tipo do objeto da classe derivada.

  40. Apontar ponteiro Base para Classe Derivada • Mecanismo válido. • Mas só podem ser chamados métodos da classe base. • Chamada a métodos da classe derivada gera erro. • Tipo de um ponteiro ou referência define os métodos que podem ser invocados.

  41. Funções Virtuais • Objetos (e não os ponteiros) definem os métodos que devem ser invocados. • Suponha círculo, triângulo e retângulo derivados de uma classe forma. • Cada um com seu próprio método de desenho (draw). • Para desenhar qualquer forma, chama-se draw a partir de um ponteiro para forma. • O programa determina, em tempo de execução, qual draw chamar (formas são tratadas de maneira genérica).

  42. Declaração Virtual • Draw deve ser declarada virtual na classe base. • Sobreponha-se draw em cada classe derivada. • As assinaturas devem ser idênticas. • Um vez declarada virtual, será virtual em todas as classes derivadas. • É boa prática manter a declaração virtual nas classes derivadas, embora não seja mandatário.

  43. Tipos de Herança • Pública: todos os membros públicos da classe básica permanecem públicos. • Membros privados permanecem privados. • Relacionamento É-Um (IS-A). • Privada: mesmo membros públicos são escondidos. • Relacionamento Tem-Um (HAS-A). • É preferível usar composição do que herança privada. • É a herança default.

  44. Herança de Construtores • O estado de público ou privado dos construtores, construtor de cópia, e operador de atribuição são herdados. • Se eles forem herdados, mas não definidos na classe derivada, os operadores correspondentes são aplicados a cada membro.

  45. Binding Estático e Dinâmico • No bindingestático a decisão de que função invocar para resolver uma sobreposição é tomada em tempo de compilação. • No bindingdinâmico a decisão é tomada em tempo de execução. • C++ usa binding estático por default, porque se achava no passado que o overhead seria significativo. • Para forçar o binding dinâmico o programador deve especificar a função como virtual.

  46. Porque binding dinâmico? • Quando se declara um ponteiro para uma classe como argumento de uma função, normalmente o tipo do ponteiro é o da classe base. • Assim, pode ser passada qualquer uma das classes derivadas e o tipo do objeto apontado é determinado em tempo de execução. • Virtualidade é herdada. • Funções redefinidas em classes derivadas devem ser declaradas virtuais. • Uma decisão em tempo de execução só é necessária quando o objeto for acessado através de ponteiros.

  47. Construtores e Destrutores Virtuais? • Construtores nunca são virtuais. • Destrutores devem ser virtuais na classe base apenas. • O tipo de um construtor sempre pode ser determinado em tempo de compilação. • O destrutor deve ser virtual para garantir que o destrutor do objeto real é chamado. • A classe derivada pode ter membros adicionais que foram alocados dinamicamente, que só podem ser desalocados pelo destrutor da classe derivada.

  48. Custo • Polimorfismo tem um custo que impacta na performance. • STL não usa polimorfismo por questão de eficiência. • Cada classe com função virtual tem uma tabela vtable. • Para cada função virtual, vtable tem um ponteiro para função apropriada. • Se a classe derivada tiver a mesma função virtual da classe base, o ponteiro da vtable aponta para a função na base.

  49. Casting (compatibilização) • Downcasting • Operador dynamic_cast • Determina o tipo do objeto em tempo de execução. • Retorna 0 se não for o tipo certo (não pode sofrer cast) NewClass *ptr = dynamic_cast < NewClass *> objectPtr; • Pode compatibilizar endereço de objeto do tipo base para ponteiro para classe derivada.

  50. Informação de Tipo • Keyword typeid • Header <typeinfo> • Uso: typeid(object) • Retorna type_info do objeto • Tem informação sobre o tipo do operando, incluindo nome. • typeid(object).name()

More Related