Académique Documents
Professionnel Documents
Culture Documents
raquelmini@ufmg.br
DEE – UFMG – 2018
Unidade I – Gerenciando a Complexidade
I.1. A complexidade inerente dos sistemas de software
I.2. A estrutura de sistemas complexos
I.3. Trazendo ordem ao caos
2
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A complexidade de sistemas de software
Alguns sistemas de software não são complexos ...
Exemplos
Aplicativos que são especificados, construídos, criados,
mantidos e utilizados por uma mesma pessoa
Sistemas que têm um propósito limitado e um ciclo de vida curto
Neste caso, podemos simplesmente refazê-lo, se necessário ...
Jogar o sistema fora e construir outro
ao invés de tentar reutilizá-lo,
consertá-lo ou estender
sua funcionalidade
3
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A complexidade de sistemas de software (cont.)
Os sistemas de software que nos interessam são os
complexos ...
Software “industrial”
Possui um ciclo de vida longo para o produto
É extremamente difícil para um único indivíduo entender todos os
detalhes de seu projeto
A complexidade do sistema ultrapassa
a capacidade intelectual humana!
Podemos gerenciar sua complexidade,
mas não fazer com que ela desapareça!!
O gerenciamento deve ser feito por
“mortais” e não gênios ...
4
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Por que os sistemas de software são complexos?
O domínio do problema é complexo
Quem desenvolve não é quem conhece o domínio de aplicação
Dificuldade de capturar os requisitos do sistema
Os requisitos podem não ser estáveis
É necessário pensar na evolução do sistema
programar para o futuro!
5
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
6
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
É difícil gerenciar o processo de desenvolvimento
Tamanho não é virtude em software
Hoje é comum encontrar sistemas com milhões de linhas de
código, decompostas em centenas ou milhares de módulos
separados ...
É necessário gerenciar uma equipe de desenvolvimento,
composta por um número razoável de pessoas para
criar o sistema!
Muitas vezes, dispersa geograficamente
Problemas de comunicação em uma equipe
Mais comuns do que se imagina!
O desafio principal é manter a coesão e a integridade do sistema
7
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Tem-se uma flexibilidade muito grande quando se
desenvolve software
Isto pode ser uma grande desvantagem, pois há a tendência de se
“reinventar a roda”
Um empreiteiro da construção civil não planta árvores para obter a
madeira que vai usar na construção
Mas o desenvolvedor de software muitas vezes faz isto !!!
Nas diversas engenharias temos componentes padronizados que são
os nossos blocos primitivos
Mas falta padronização na indústria de software !!!
Um construtor de edifícios pensaria em adicionar um novo subsolo a
um edifício de 100 andares?
Usuários de software geralmente pedem que este tipo de modificação seja
efetuada em um sistema existente ...
“Isto é só uma questão de programação” !!!
8
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Quanto mais complexo um sistema, mais
provável que ele entre em colapso
Sem gerenciar a complexidade, sistemas de software
São entregues com atraso
Estouram o orçamento
Não atendem aos requisitos especificados
Podem apresentar desempenho insatisfatório
Podem ter vida útil curta
9
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A solução começa com ...
Entender como os sistemas complexos são
organizados
Trazer esta organização para os sistemas de software
10
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplos de sistemas complexos
Um computador pessoal ...
placa mãe, monitor, teclado, HD, etc ...
placa mãe: microprocessador, memória, barramento, etc.
microprocessador: unidade aritmética, unidade de controle, registradores
...
registradores: portas lógicas (ANDs, ORs, etc)
portas lógicas: transistores, resistores, etc ...
....
Estrutura hierárquica, com diferentes níveis de abstração, cada
um deles construído sobre o outro!
O computador pessoal somente funciona corretamente devido à
colaboração existente entre as suas diversas partes
Podemos descrever seu funcionamento porque conseguimos decompô-lo
em partes que podem ser estudadas separadamente
Em cada nível de abstração, os elementos cooperam entre si para
desempenhar sua funcionalidade, através de uma interface conhecida,
e oferecem serviços para os níveis mais altos
11
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A complexidade é
organizada na forma
de uma hierarquia
Um sistema complexo é
composto por subsistemas
interrelacionados
Por sua vez, estes possuem
seus próprios subsistemas,
e assim por diante, até
chegarmos aos componentes
elementares
12
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Os atributos de sistemas complexos
A escolha de quais componentes de um sistema são
elementares (primitivos) é relativamente arbitrária
Depende basicamente daquilo que o observador do sistema
pretende enxergar
A força dos relacionamentos também muda
Relacionamentos internos entre os subcomponentes de um
determinado nível da hierarquia são fortes
Relacionamentos entre componentes da hierarquia
em níveis distintos são fracos
13
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Entendendo melhor a força entre os módulos
Modularidade
É a qualidade de um módulo que garante a sua capacidade de
extensão e reuso
Boa modularidade baixo acoplamento e alta coesão
Acoplamento
Medida da força de associação (dependência) entre os módulos
Coesão
Medida da força de associação dentro de um módulo
O nível da abstração utilizado
é a chave para entender
o acoplamento e a coesão
existente entre as partes
14
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A forma canônica de sistemas complexos
A descoberta de abstrações e mecanismos comuns facilita o
entendimento de sistemas complexos
Ex: um motorista consegue “pilotar” um novo modelo de automóvel,
simplesmente identificando seus componentes
Volante, freio, acelerador, embreagem, marchas, ignição, ...
Estes são mecanismos comuns presentes em todos os carros
Um sistema complexo geralmente contém várias hierarquias
Hierarquia estrutural: “todo-parte”
Ex: um carro tem sistemas de propulsão, controle de direção, frenagem, ...
Hierarquia de generalização/ especialização: “é um tipo de”
Um animal vertebrado é um tipo de animal
Um mamífero é um tipo de animal vertebrado
Um primata é um tipo de mamífero
Um macaco é um tipo de primata
15 ...
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A forma canônica de sistemas complexos
Adiantando alguns conceitos do curso ...
A hierarquia “é um tipo de” será modelada, na análise
orientada a objetos, por meio do mecanismo de herança
A hierarquia “todo parte”, por meio do mecanismo de
composição
16
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Se sabemos o raciocínio a seguir no projeto de um sistema
complexo (ex: decomposição em hierarquias), por que o
desenvolvimento de software é problemático???
O modelo de desenvolvimento OO é relativamente novo
Descobrir o que há em comum em um sistema, abstrair seus
mecanismos, etc ... não é uma tarefa simples
É menos simples ainda para sistemas inexistentes para os quais
estaremos projetando a hierarquia
Inventando os mecanismos, agrupando as partes
Podemos lidar com um número bastante grande de possíveis escolhas
Experiência + aprender com as boas soluções obter padrões
Padrões de arquitetura
Padrões de projeto
17
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Decomposição
É uma técnica básica de tratamento da complexidade
Essência: decompor algo em partes menores
“Dividir para conquistar"
Em sistemas de software podemos ter:
Decomposição funcional
Análise, projeto e programação estruturados
Enfoca o que precisa ser feito funções e fluxo de execução
Um problema maior é decomposto sucessivamente em problemas menores,
até que sejam simples o suficiente para serem resolvidos
Decomposição orientada a objetos
Análise, projeto e programação orientados a objetos
Enfoca a construção de módulos que colaboram entre si para
18 solucionar um problema
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Decomposição Funcional
Decomposição
Orientada a Objetos
19
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Decomposição (cont.)
Análise e projeto estruturados
Realizam a decomposição modular
Um módulo é dividido em módulos menores até atingir um nível de
abstração no qual o problema é facilmente resolvido
Segue a abordagem “Top Down”
Princípio: a divisão do sistema em subsistemas permitirá distribuir o
trabalho entre diferentes pessoas ou grupos
Meta difícil, pois os subsistemas podem apresentar
uma série de dependências
Top Down
20
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Decomposição (cont.)
Programação estruturada
Decomposição funcional (algorítmica)
Utiliza um nível de abstração alto, baseado em funções
Módulo = agrupamento de funções correlatas
Possui funções que atuam sobre conjuntos relacionados de dados
Ex: read(), write(), open(), close(), ...
Existe uma separação clara entre os dados e as operações sobre eles
Dados organizados em estruturas de dados globais, disponíveis a
qualquer parte do programa
Funções específicas de manipulação são
empregadas para evitar o uso direto dos
dados, reduzindo o acoplamento
Dados são passados entre módulos por meio
de parâmetros de funções e sub-rotinas
21
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Análise, projeto e programação estruturados
22
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini Fonte: Gane e Sarson, Análise Estruturada de Sistemas, 1983
Exemplo: análise, projeto e programação estruturados
23
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini Fonte: Gane e Sarson: Análise Estruturada de Sistemas, 1983
Decomposição (cont.)
Programação estruturada (cont.)
Dificuldades com a programação estruturada
A evolução do sistema é mais difícil
Interdependência entre as funcionalidades e na forma
como elas são implementadas
Efeitos colaterais ocorrem entre as estruturas de dados,
podendo comprometer todo o software
Modificações sucessivas geram uma maior degradação do sistema
Pode significar menos tempo do sistema em produção
Necessário buscar mecanismos que permitam construir e
manter software de forma mais simples e robusta
26
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo: análise, projeto e programação OO
27
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Grau de abstração + próximo de A falta de domínio pode inverter
como o ser humano pensa todos os benefícios
Ajuda a organizar a complexidade Ineficiência e problema de
inerente dos sistemas de software desempenho
Melhora a interação entre o analista Softwares mal elaborados
e o especialista do domínio Não traduz conceitos em algo real
Explicita pontos comuns (herança)
OO requer treinamento e
Maior qualidade, favorecendo: experiência
Reúso de mecanismos comuns
Classes, abstrações, ...
Envolve custos de aprendizado
Desenvolvimento incremental Cuidado com o uso incorreto da
Capacidade de extensão tecnologia e seus preceitos
Robustez e correção Programar OO com mentalidade
Redução dos dados globais estruturada
Manutenção e extensão A definição incorreta de padrões
Mais fácil identificar a origem de pode levar a falhas estruturais e
problemas dificultar a programação
29
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na Unidade I vimos que ...
Um sistema de software é inerentemente complexo e
muitas vezes ultrapassa a capacidade intelectual humana
Sistemas complexos podem ser analisados a partir de sua
decomposição em coisas (objetos), que têm estado e
comportamentos próprios
A complexidade da decomposição pode ser organizada na
forma de uma hierarquia com duas relações básicas:
“é um tipo de” e “é parte de”
Os sistemas complexos geralmente evoluem
a partir de formas estáveis mais simples
31
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na Unidade II veremos ...
Como usar a metodologia OO para organizar a
complexidade de sistemas de software
Quais os principais mecanismos que poderão ser
utilizados para isto
32
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Unidade II - A Modelagem OO
II.1. Paradigmas de programação
II.2. A evolução dos modelos orientados a objetos
II.3. A abstração de objetos
Encapsulamento, interface e implementação
II.4. Reutilizando a implementação
II.5. Reutilizando a interface por meio de herança
II.6. Objetos intercambiáveis: polimorfismo
II.7. Introdução à análise e projeto orientados a
objetos
33
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Linguagem de Programação
Método padronizado para expressar instruções para um computador
Possui um conjunto de regras sintáticas e semânticas usadas para definir um
programa de computador
Especifica precisamente sobre quais dados um computador vai atuar, como esses
dados serão armazenados ou transmitidos e quais ações devem ser tomadas sob
várias circunstâncias
Objetivos
Aumentar a produtividade dos programadores
Usa linguagem simbólica ao invés de linguagem de máquina
Aumentar o entendimento por programadores humanos
Possui sintaxe de nível mais alto mais facilmente entendida
Gerar programas mais organizados e modulares
Possui estruturas mais definidas (loops, condições,...)
Aumentar a portabilidade entre computadores
Traduzida para o código de máquina do computador alvo
34
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Paradigmas das linguagens de programação:
Imperativo
Orientado à objetos
Concorrente
Funcional
Lógico
Scripting
35
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Descreve a computação como ações, enunciados ou
comandos que mudam o estado (variáveis) de um
programa
Baseada em comandos que atualizam variáveis
armazenadas
Em Latim, imperare significa “comando”
Na década de 1950, as primeiras linguagens de
programação eram imperativas
36
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Nestas linguagens, é necessário pensar em termos da
estrutura do computador, e não em termos da
estrutura do problema a ser resolvido
Linguagens imperativas constituem abstrações da linguagem
Assembly
O modelo do espaço da solução é o de uma “linha de
produção”
O programador deve estabelecer uma associação entre o
modelo da máquina (no espaço da solução) e o modelo do
problema que está sendo resolvido (no espaço do problema)
O esforço para fazer este mapeamento pode ser gigantesco
37
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Variáveis e comandos de atribuição são uma
abstração simples para o acesso e atualização do
conjunto de instruções do computador
Essas linguagens possuem uma relação próxima com a
arquitetura do computador
Implementação eficiente (no início)
Paradigma dominante até a década de 1990
A grande maioria dos softwares comerciais atualmente em uso
foram escritos em linguagem imperativa, assim como vários
softwares que estão correntemente sendo desenvolvidos
38
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Principais conceitos:
Variáveis
Comandos
Procedimentos
Abstração de dados (mais recentemente)
39
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Variáveis e comandos são conceitos chave
Muitos programas são escritos para modelar processos do
mundo real afetando entidades do mundo real
Uma entidade do mundo real frequentemente possui um estado
que varia com o tempo
Entidades do mundo real podem ser naturalmente modeladas
por variáveis
Processos do mundo real podem ser modelados por comandos
que inspecionam e atualizam essas variáveis
40
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Exemplo: programa para controle de voos
Uma aeronave possui um estado contendo sua posição,
altitude, velocidade, carga, quantidade de combustível, ...
O programa para controle de voos deve modelar os estados da
aeronave
Como o estado muda com o tempo, essas informações são
armazenadas em um grupo de variáveis
41
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação imperativa
Procedimentos
São abstrações de comandos
Abstração de dados
Abstração é a habilidade de concentrar nos aspectos
essenciais de um contexto qualquer, ignorando características
menos importantes
Não é essencial em linguagens imperativas e não é suportada
em algumas linguagens clássicas (C e Pascal)
42
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Arquitetura de um programa corresponde à forma na
qual ele é decomposto em unidades menores
juntamente com o relacionamento entre essas unidades
A qualidade da arquitetura de um programa afeta
diretamente o custo de implementação e de
manutenção
Acoplamento
Define o quanto uma unidade depende da outra para funcionar
Métrica para medir a qualidade de uma arquitetura
43
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Alto acoplamento
Modificações em uma unidade possuem alta
probabilidade de forçar modificações em outras
unidades
Baixo acoplamento
Modificações em uma unidade possuem alta
probabilidade de gerar poucas modificações em
outras unidades
Facilidade de manutenção
44
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Arquitetura de um
programa imperativo
com variáveis globais
45
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programa imperativo com variáveis globais
Manutenção cara e difícil
Necessário conhecer o papel de cada variável global
que é acessada e também o papel dos outros
procedimentos que acessam as mesmas variáveis
globais
Qualquer modificação em um procedimento pode
disparar modificações em cascata em outros
procedimentos
Abstração de dados se tornou um conceito chave
nas linguagens imperativas modernas!
46
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
As unidades de programas imperativos bem
projetados são procedimentos e pacotes (ou
tipos abstratos)
47
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Arquitetura de um
programa imperativo
com abstração de
dados
48
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programa imperativo com abstração de dados
Não existe variáveis globais, apenas variáveis locais e
parâmetros
Todas as unidades do programa são fracamente
acopladas
O programador que dará a manutenção será capaz de
entender cada unidade individualmente, podendo
modificá-la sem gerar modificações em cascata em
outras unidade do programa
Como a representação de cada tipo abstrato é
privada, a manutenção pode modificá-la com
segurança sem forçar modificações em outras
unidades
49
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programação orientada à objetos
Na programação orientada a objetos, implementa-se
um conjunto de classes que definem os objetos
presentes no sistema de software
Cada classe determina o comportamento (definido nos
métodos) e estados possíveis (atributos) de seus
objetos, assim como o relacionamento com outros
objetos
Possui 3 pilares:
Encapsulamento
Herança
Polimorfismo
50
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A análise e o projeto orientados a objetos
É uma metodologia que nos leva a uma decomposição
orientada a objetos de um sistema
Os modelos resultantes identificam “coisas”, “entidades”
São os chamados objetos que compõem o sistema
É totalmente distinta da análise estruturada, que busca
identificar procedimentos (funções)
Paradigma: conjunto de
teorias, padrões e métodos
que, juntos, representam um
modo de organizar o
conhecimento
51
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A evolução do processo de abstração
• Abstrair = retirar do domínio do problema os detalhes
relevantes e representá-los não mais na linguagem do
domínio, mas na linguagem da solução
52
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A evolução do processo de abstração (cont.)
A programação orientada a objetos tenta trazer o
espaço da solução para o espaço do problema
Ambos são representados como objetos !!
Permite uma adaptação ao problema
Adiciona novos tipos ao espaço da solução que
mapeiam os tipos existentes no espaço do problema
Assim, descreve-se o problema em termos do
problema e não em termos da solução!
Cada objeto funciona como uma
pequena parte do problema
Possui seu estado e suas operações, que podemos pedir que eles
executem - isso é semelhante ao comportamento dos objetos no
53
mundo real, que também possuem características e comportamentos!
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são objetos?
Um objeto é uma variável ... ele armazena dados
Então uma struct (registro do C) é um objeto?
Sim, uma struct é um objeto ... mas um objeto pode ser mais ...
Podemos pedir que algumas operações ocorram sobre os objetos
55
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que é um programa em linguagem OO? (cont.)
Conjunto de objetos dizendo uns para os outros o que
fazer por meio de envio de mensagens
Mensagens = chamadas a funções que pertencem a um objeto
em particular
Cada objeto tem a sua própria região de memória, que
pode ser composta por outros objetos, também
Ex: o objeto carro pode ser composto pelos objetos lataria,
rodas, motor, etc.
Cada objeto tem um tipo,
isto é, pertence a uma classe
56
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são objetos e classes?
Classe das aves e classe dos peixes
Os objetos, apesar de serem únicos, fazem parte de uma
classe de objetos que possuem características comuns
A criação de tipos de dados abstratos (classes) é um conceito
fundamental na programação OO
Tipos de dados abstratos (TADs) funcionam praticamente
da mesma maneira que os tipos fundamentais
Pode-se criar (instanciar) variáveis do tipo objetos ou instâncias
Pode-se manipular estas variáveis, enviando
mensagens ou requisições para elas
57
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são objetos e classes? (cont.)
Revendo alguns conceitos
Tipo: conjunto de elementos com comportamento e operações similares
Tipos primitivos (fundamentais)
Tipos indivisíveis providos pela linguagem de programação
Proporcionam acesso otimizado aos recursos do computador
int, char, short, long, float, double, boolean, ...
Tipos compostos
Tipos divisíveis, que possuem campos (primitivos ou compostos)
Tipos referência: possibilitam estender a linguagem e implementar a OO
Tipos abstratos de dados (TADs): define o tipo e uma lista de operações
que podem ser realizadas e seu comportamento externo
Descreve um tipo de forma independente de implementação
São modelados por meio de interfaces
58 Ex: pilha (cria, empilha, desempilha, esvazia, topo, número de elementos)
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são objetos e classes? (cont.)
Os objetos de cada classe compartilham algumas
características em comum
Toda conta possui um extrato
Todo caixa pode aceitar um depósito, etc.
Ao mesmo tempo, todo objeto tem seu próprio estado
Cada conta tem um extrato diferente, cada caixa tem um nome
Assim, ...
Caixas, clientes, contas, transações, podem, cada um, ser
representado por uma única entidade no programa
esta entidade é o objeto
Cada objeto pertence a uma classe que define
suas características e comportamento
59
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Classe
Conjunto de elementos com mesmas características
É um molde para a criação de objetos e define:
Uma estrutura de dados encapsulada
Um conjunto de operações para manipular esta estrutura
Uma interface de acesso bem definida
Implementa tipos abstratos de dados
Formaliza os princípios da OO
Herança, encapsulamento,
polimorfismo
Possibilitam alta coesão
e baixo acoplamento
Toda linguagem OO
possui classes
60
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Objeto
É uma instância (materialização) de uma classe
Possui seu próprio conjunto independente de atributos (dados)
Objetos de uma mesma classe podem compartilhar as mesmas
operações e em alguns casos, os mesmos atributos
Ao conjunto de atributos e operações é dado o nome de
membros
61
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Um objeto possui um estado, exibe um certo
comportamento e tem identidade única
Estado
Conjunto de atributos (valores armazenados em um objeto)
Transição de estado = alteração de valor (causada por operações)
Comportamento
Ações que o objeto pode realizar =
conjunto de métodos associados
Todo objeto é passivo
Espera por requisições (mensagens)
de outros objetos ou classes
Identidade
Todo objeto possui um identificador
implícito e único
62
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Objetos e classes
Após uma classe ser criada ...
Podemos criar quantos objetos da classe quanto for necessário
Podemos manipular estes objetos como se fossem elementos
que existissem no problema que está sendo solucionado
Objetos e interfaces
Como fazemos um objeto realizar trabalho útil?
Cada objeto possui métodos para desempenhar suas atividades
Método = função
Cada objeto só pode responder a determinadas requisições
Aquelas que ele reconhece como sendo suas
O conjunto de métodos de um objeto é conhecido como sendo a
63 sua interface
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Interfaces: exemplos
Lâmpada - representação UML
liga();
desliga()
Interface: aumenta_luminosidade();
Lampada luz;
diminui_luminosidade();
luz.liga();
luz.desliga();
luz.aumenta_luminosidade();
luz.diminui_luminosidade();
64
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Objetos e interfaces (cont.)
Quais métodos (funções) podemos chamar?
Isto é a interface
Onde estes métodos estão codificados, como são
implementados?
Isto é a implementação
Geralmente não interessa a quem usa o objeto conhecer os
detalhes da implementação ...
65
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Interface, Implementação e Encapsulamento
O cliente usa os serviços de um objeto por meio de
sua interface
O criador da classe expõe o mínimo da interface para
o cliente, escondendo o resto. Por quê?
Porque se está escondido, pode ser alterado de forma
controlada, sem quebrar o programa do cliente
Se fosse dado total acesso à implementação interna, o cliente
poderia quebrar a integridade do objeto
Objeto
X, Y, Z
66
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Encapsular
Separar o programa em partes, o mais isoladas
possível (baixo acoplamento)
Encapsulamento
Permite que você divida o programa em várias partes
menores e independentes
67
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Encapsulamento
Controle de acesso
A estratégia utilizada para garantir que determinadas partes de
uma classe não sejam acessíveis por seus clientes
• Uma interface não apresenta necessariamente todos
os métodos de um objeto
Objeto. mensagem (p)
• Métodos públicos: aqueles que podem
ser acessados pelo público em geral mensagem (p)
{
...
• Métodos privados: aqueles que // qualquer valor manipulado
aqui é x, y, z ou p
são internos aos objetos }
69
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Estratégia: Composição
• Utiliza um objeto de uma classe dentro de uma nova
classe
• Ex: classes roda, motor, lataria, ... e a classe carro
Notação UML:
70
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Estratégia: Composição (cont.)
• Implementa uma das técnicas básicas de
gerenciamento de complexidade
• Permite criar uma classe “todo” utilizando objetos de outras
classes (as partes)!
71
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Estratégia: Composição (cont.)
• Como se identifica a composição?
• Por meio dos seguintes verbos típicos: conter, possuir
• Ex: Um carro contém 4 rodas, 1 motor, 1 lataria, ..
Notação UML:
72
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Estratégia: Herança
• Permite modelar outro tipo de hierarquia entre classes
• Classes mais especializadas (filhas) herdam propriedades
da classe mais geral (classe pai)
• Pode-se compartilhar automaticamente métodos e
dados entre diferentes classes, subclasses e objetos
• Permite criar uma nova classe programando somente as
diferenças desta para a classe pai
• A classe filha herda a interface da classe pai, podendo
substituí-la quando se espera um objeto da classe pai
• Identifica-se a possibilidade de herança por meio
da seguinte expressão típica: “é um tipo de”
73
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Estratégia: Herança
O nome já indica
exatamente o
que ocorre ...
74
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Herança: Exemplos
Ave é um tipo de Animal
Ave come, dorme, voa e botaOvo
Mamífero é um tipo de Animal
Mamífero come, dorme e mama
Uma Ave (ou um Mamífero)
podem substituir Animal
São tipos de Animal
(possuem a mesma interface)
75
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Herança: Exemplos
• Triangle, Square e Circle são tipos de Shape
• Possuem a mesma interface;
• Podem substituir Shape ...
76
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Herança: Exemplos
• Cada tipo de Shape deve ter o seu
próprio método draw(), pois cada
uma se desenha de modo diferente!
• Os métodos podem ser sobrescritos
nas classes derivadas!
• Estamos dizendo:
“estou usando a mesma
interface, mas quero um
comportamento um
pouco diferente para
a classe derivada”
77
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Quando trabalhamos com hierarquias de herança,
muitas vezes queremos tratar um objeto, não por
seu tipo específico, mas por seu tipo base
Isto permite escrever código que não depende
do tipo específico
Ex: no caso das Shapes, podemos escrever código genérico que
as manipulasse (mandando-as se desenharem, por ex.), sem se
preocupar se são triângulos, círculos ou qualquer outra coisa ...
Este código não seria afetado pela adição de
novos tipos de Shapes ...
Adicionar novos tipos é uma das formas mais comuns de estender
um programa orientado a objetos para tratar novas situações
78
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Isto é o polimorfismo
• “o que possui várias formas”
• Propriedade de se usar o mesmo nome
para métodos diferentes, implementados
em diferentes níveis de uma hierarquia de classes
• Para cada classe, tem-se um comportamento
específico para o método
Não se preocupem demais com o conceito,
no momento: ficará claro depois!
79
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Análise: detalha “o que deve ser feito”
• Detalha requisitos do sistema
• Programação: “faz”
• Constrói o sistema Anális
e OO
Projet
o OO
Programaç
ão OO
80
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Análise orientada a objetos
• Método de análise que examina os requisitos a partir
da perspectiva das classes e objetos encontrados no
vocabulário do domínio do problema
• OBS: o produto da análise são modelos que servem
como entrada para o projeto, que por sua vez produz
os modelos que são utilizados para a programação
81
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Projeto orientado a objetos
• Método de projeto de software que abrange as tarefas
de decompor o sistema de maneira orientada a objetos
• Gera classes e objetos agrupados
• Uma notação própria deve ser utilizada para expressar
as idéias associadas
• Por exemplo a UML
O fundamental no projeto é responder as questões de
decomposição lógica do sistema
Quais são as classes e objetos?
Quais são as interfaces destes objetos?
Como os objetos interagem entre si?
82
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Programação orientada a objetos
• Método de implementação de software no qual:
• Os programas são organizados na forma de coleções
cooperativas de objetos
• Cada um dos objetos representa uma instância de uma classe
• As classes podem ser membros de hierarquias criadas por meio
de mecanismos como herança e composição
• Linguagem orientada a objetos
• Linguagem que suporta os mecanismos utilizados na
programação orientada a objetos
• Composição, herança, polimorfismo, encapsulamento, ...
83
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
3. Preencher a tabela abaixo buscando
responder cada item de forma objetiva e
concisa
Tópico Conceito Necessidade Aplicação
(O qué é?) (Para que serve? (Como usa? Qual o
Qual a formato de uso?)
vantagem?)
Decomposição de
sistemas complexos
Abstração
Encapsulamento
Herança
Composição
84
© Renato Mesquita,
Polimorfismo
Ana Liddy Magalhães e
Raquel Mini
4. O que é a interface de uma classe? Como o
conceito de interface de uma classe se
relaciona com o conceito de encapsulamento?
85
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na unidade II vimos ...
Como usar a metodologia OO para organizar a
complexidade de sistemas de software
Quais os principais mecanismos que poderiam ser
utilizados para isto
Uma introdução à análise e ao projeto orientado a
objetos
87
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na unidade III veremos ...
Como algumas destas idéias se transformam em
código, usando a linguagem C++.
Especificamente, em detalhes, como criar classes e
objetos em C++
Referência básica
Thinking in C++, Vol. 1
Parte 1: Capítulos 2 ao 5
Parte 2: Capítulos 6 ao 10
Parte 3: Capítulos 11 ao 13
88
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Unidade III - Classes e Objetos
III.1. Implementando classes e objetos em C++
Parte
e encapsulamento
III.3. Inicialização e destruição
III.4. Sobrecarga de funções e argumentos default
III.5. Constantes e controle de visibilidade
III.6. Ponteiros, referências, atributos dinâmicos,
gerenciamento de memória e o construtor de cópia
III.7. Sobrecarga de operadores e conversão de tipos
89
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Principais conceitos relacionados
Declaração
Forma de dizer ao compilador o nome de funções e variáveis
(externos) e suas características: não aloca espaço
“Esta função ou esta variável existe em algum lugar e é desta forma
que ela deve ser”
Não é possível usar nomes repetidos em um mesmo contexto
Definição
Explicita o conteúdo da função ou variável: aloca espaço
“Coloque esta função / variável aqui”
Variável: verifica tamanho (tipo) e solicita reserva de espaço
Função: gera código correspondente, ocupando espaço de memória
Declaração x definição
São conceitos complementares
Principal diferença é o momento de alocação de espaço
Uma definição pode ser também uma declaração, caso não tenha
90
ocorrido a declaração antes
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Declaração x definição
Exemplos //: C02:Declare.cpp
// Declaration & definition examples
extern int i; // Declaration without definition
extern float f(float); // Function declaration
float b; // Declaration & definition
float f(float a) { // Definition
return a + 1.0;
}
int i; // Definition
int h(int x) { // Declaration & definition
return x + 1;
}
int main() {
b = 1.0;
i = 2;
f(b);
h(i);
}
91
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de header files em C e C++
Arquivos de cabeçalho
Contém declarações externas de uma biblioteca fornecida
Usar a diretiva #include <nomearq> ou “nomearq.h”
Indica ao processador para abrir o arquivo e incluir ali seu conteúdo
<nomearq>: indica para procurar no caminho especificado no ambiente
ou linha de comando do compilador
“nomearq.h”: indica para procurar no caminho relativo à pasta atual
Se não achar, assume o caminho default (<>)
Extensões mais usadas: “.h”, “.hxx” e “.hpp”
Padrão atual de nome de arquivos
Pode ter mais de 8 caracteres e sem extensão
Ex: <iostream.h> <iostream>
As bibliotecas herdadas do C continuam com a extensão “.h” mas
podem ser usadas também com um “c” antes
Ex: <stdio.h> <cstdio>
92
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Principais conceitos relacionados
Namespace
Necessidade
Programas grandes são divididos em partes, cada uma mantida por
grupos / pessoas diferentes
Isso poderia levar à utilização de mesmo nome de variáveis
Aplicação
“Embrulha” espaços, possibilitando separar espaços distintos
Se houver nome idêntico, mas em outro namespace, não haverá colisão
de nomes
Formato
using namespace nome; (o padrão do C++ é o std)
Esta diretiva deixa o namespace disponível para todo o arquivo
Evite colocar no header, pois o expõe de forma inadequada
Ex: # include <iostream> #include <iostream.h>
using namespace std; (forma + antiga)
93
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Programa inicial
Programa 1: Hello World em C++
// C02:Hello.cpp - Saying Hello with C++
#include <iostream> // Stream declarations
Notas:
cout << (console using namespace std;
output): saída padrão int main() {
<< concatena saídas
int age = 8;
cin >> (console input):
entrada padrão cout << "Hello, World! I am "
>> concatena << age << " years old "
entradas
endl: “\n” na saída
<< endl;
padrão }
94
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
6. Analise os programas a seguir, presentes em
nosso livro texto, e que apresentam alguns
aspectos específicos da entrada e saída em
C++
Stream2.cpp - formatação da saída
Concat.cpp - concatenação de arranjos de caracteres
Numconv.cpp - leitura de entrada e formatação da saída
CallHello.cpp - uso da função system() para executar um
programa de dentro de outro
95
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Stream2.cpp - formatação da saída
// C02:Stream2.cpp
Notas: // More streams features
#include <iostream>
Manipuladores
using namespace std;
iostream: não int main() {
imprimem nada, mas // Specifying formats with manipulators:
cout << "a number in decimal: "
mudam o estado do
<< dec << 15 << endl;
stream de output (dec, cout << "in octal: " << oct << 15 << endl;
oct, hex) cout << "in hex: " << hex << 15 << endl;
Impressão de ponto cout << "a floating-point number: "
<< 3.14159 << endl;
flutuante definida pelo cout << "non-printing char (escape): "
compilador << char(27) << endl;
char(27): cast de }
char() com valor ASCII
(27) = “escape”
96
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Concat.cpp - concatenação de arranjos de caracteres
Notas: // C02:Concat.cpp
// Character array Concatenation
C++ é uma linguagem
#include <iostream>
de formato livre e using namespace std;
permite que a int main() {
cout << "This is far too long to put on a "
sentença continue na
"single line but it can be broken up with "
linha seguinte "no ill effects\nas long as there is no "
O “;” termina uma "punctuation separating adjacent character "
sentença "arrays.\n";
}
97
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Numconv.cpp - leitura de entrada e formatação da saída
// C02:Numconv.cpp
Notas: // Converts decimal to octal and hex
cin e cout pertencem #include <iostream>
às classes istream e using namespace std;
int main() {
ostream do C++ e
int number;
podem ser cout << "Enter a decimal number: ";
redirecionados para cin >> number;
nova entrada e saída cout << "value in octal = 0"
padrão << oct << number << endl;
Manipuladores cout << "value in hex = 0x"
iostream: não << hex << number << endl;
imprimem nada, mas }
mudam o estado do
stream de output (oct,
98
hex)
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
CallHello.cpp - uso da função system() para executar um programa
de dentro de outro
Notas: // C02:CallHello.cpp
<cstdlib> define system(); // Call another program
O argumento de system deve ser o #include <cstdlib> // Declare "system()"
using namespace std;
comando que seria colocado no int main() {
prompt do sistema operacional, system("Hello");
contendo seus próprios argumentos }
(pode ser montado em tempo
de execução)
O comando é executado e o controle
retorna para a instrução seguinte em
main()
Mostra também como é simples
utilizar as funções da biblioteca do C
no C++
99
(basta incluir o header e chamar a
© Renato Mesquita,
função)
Ana Liddy Magalhães e
Raquel Mini
7. Escreva um programa em C++ que leia um
conjunto de 4 valores i, a, b, c, onde i é um
valor inteiro e positivo e a, b, c, são quaisquer
valores reais e os escreva:
Se i=1 escrever os três valores a, b, c em ordem crescente.
Se i=2 escrever os três valores a, b, c em ordem decrescente.
Se i=3 escrever os três valores a, b, c de forma que o maior
entre a, b, c fique dentre os dois.
100
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
III.1.1. Utilizando objetos de classes existentes
na biblioteca padrão
Exemplo: funcionalidade básica das strings em
C++
101
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo: Lendo e escrevendo em arquivos
Nota:
102 getline () retorna true quando lê
© Renato Mesquita,
Ana Liddy Magalhães e com sucesso e false ao final do
Raquel Mini arquivo
Lendo de um arquivo e armazenando todas as
linhas numa string
103
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Criando vetores com a biblioteca vector<T> da biblioteca padrão
• Classe template, que permite a criação de vetores de tipos diferentes.
Especificamos o tipo no momento da declaração do vetor
• Podemos adicionar novos elementos ao vetor com push_back()
104
© Renato Mesquita,
Ana Liddy Magalhães e
Nota:
Raquel Mini push_back() acrescenta item ao final do
vetor
Pode-se criar um vetor de qualquer tipo
Exemplo: uso de um vector<int>
105
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Revisão do conteúdo relacionado ao capítulo 3
Funções recursivas
Tipos de dados pré-definidos na linguagem
Ponteiros
106
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções recursivas
Recursividade
Possibilidade de chamar novamente a função na qual
se está
É uma técnica de programação interessante e em
alguns casos útil
É necessário ter uma situação de parada para
“desempilhar” as chamadas realizadas e não “estourar”
a memória
107
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Precedência de operadores
Notas:
Autoincremento e autodecremento
++i: o incremento é realizado e o valor resultante é aplicado
i++: o valor atual é aplicado e depois é realizado o incremento
Processo semelhante para o decremento
Atribuição compacta (ex: A+=1)
Forma mais concisa e eficiente
108
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Tipos de dados pré-definidos em C e C++
char : para armazenamento de caracteres
Utiliza no mínimo 8 bits (um byte) de armazenamento
int : armazena números inteiros
Utiliza no mínimo 2 bytes de armazenamento
float e double : armazenam números de ponto flutuante
Formato padrão IEEE (sinal + expoente + mantissa)
float para precisão simples (4 bytes), double para precisão dupla (8 bytes)
Nota 1: a especificação do padrão C define apenas valores mínimo e
máximo que cada tipo primitivo deve manipular
Fica a cargo de cada compilador adequar valores, respeitando o padrão
Nota 2: o padrão C++ inclui ainda o tipo bool (valores Booleanos)
True: converte para valor inteiro 1
Valores inteiros não zero são convertidos implicitamente para true
False: converte para valor inteiro 0
109
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Especificadores adicionais de tipo
Nota: os tamanhos definidos são os menores do padrão
255
110
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
8. Escreva um programa que abra um arquivo e
conte o número de espaços em branco do
arquivo.
9. O que o seguinte programa faz?
111
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
10. Dado um vetor de números inteiros positivos
aleatórios entrados via teclado (número negativo
indica fim da entrada dos dados), faça um programa
utilizando a classe template vector para comprimir o
vetor suprimindo as repetições de números vizinhos
através da contagem do número de repetições de
cada um da seguinte forma:
Vetor de entrada: 1 1 1 4 1 1 4 4 25 67 67 67 67 2 2
Vetor de saída: 3 1 1 4 2 1 2 4 1 25 4 67 2 2
Saída
resultante:
a = 30 b = 21
114
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Ponteiros: declaração, atribuição, operador endereço e
operador de indireção ou de-referenciação (“conteúdo”)
Saída resultante:
115
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Ponteiros e vetores: aritmética de ponteiros
Saída
resultante:
10
2
33
42
51
20
30
40
116
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passagem de parâmetros por valor
O valor da variável passada como argumento não
altera após retorno da função que foi chamada
A passagem de parâmetros padrão em C é feita por cópia
Saída
resultante:
x = 47
a = 47
a = 5
x = 47
117
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passagem de parâmetros em C++ por referência
usando ponteiros
Possível saída
resultante:
x = 47
&x = 0x28feec
r = 47
&r = 0x28feec
r = 5
x = 5
118
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A passagem de parâmetros padrão em C é feita
por cópia
119
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passagem de parâmetros por referência, usando referências
Em C++ podemos declarar referências ...
int i;
int &j = i; // j é uma referência e precisa ser inicializada
// j referencia i ao alterar j, i também altera
j = 10; // faz i também igual a 10
121
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Ponteiros: aritmética de ponteiros
saída resultante:
*ip = 0
*++ip = 1
*(ip + 5) = 6
*ip2 = 6
*(ip2 – 4) = 2
*--ip2 = 5
ip2 – ip = 4
122
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
12. Seja p uma variável do tipo ponteiro, explique a diferença entre
p++; (*p)++; *(p++); *p++; *(p+10);
123
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
14. Após a execução do código abaixo, informe,
para cada letra, se a sequência de código está
correta e o que será impresso.
a) b) c)
d) e) f)
124
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outros pontos importantes
Regras de escopo
Criação de tipos: typedef, structs, ponteiros e
structs, enums
Arrays e Matrizes
Diretivas de compilação
Dicas para debug de programas
Uso de asserts
Ponteiros para função
Uso de Makefiles para estruturar projetos
125
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
// How variables are scoped
Regras de escopo int main() {
int scp1;
Definem onde uma // scp1 visible here
variável é válida, onde {
// scp1 still visible here
foi criada e como ela sai //.....
do escopo (é destruída) int scp2;
// scp2 visible here
O escopo de uma //.....
variável vai do par “{}” {
// scp1 & scp2 still visible here
mais próximo que a //..
contém int scp3;
// scp1, scp2 & scp3 visible here
Vai do ponto no qual ela // ...
foi definida até o “}” } // <-- scp3 destroyed here
referente ao bloco ao // scp3 not available here
// scp1 & scp2 still visible here
qual pertence // ...
} // <-- scp2 destroyed here
// scp3 & scp2 not available here
126 // scp1 still visible here
© Renato Mesquita, //..
Ana Liddy Magalhães e } // <-- scp1 destroyed here
Raquel Mini
Outros pontos importantes
Criação de tipos: typedef e structs
Typedef: possibilita uma descrição mais acurada de um tipo
Seu uso é importante junto a structs
Forma: typedef existing-type-description alias-name
Ex1: typedef unsigned long ulong;
Ex2: int *x, y; //x é ponteiro e y é int (* só vale para o 1°)
typedef int *IntPtr; //define o tipo ponteiro para inteiro
IntPtr x, y; //agora x e y são ponteiros
Struct: forma de organizar um grupo de variáveis
Possui um conjunto de dados, denominados membros
Torna a operação de um conjunto de dados muito simples
Uma vez criada, possibilita gerar várias instâncias
Sua declaração não aloca espaço (apenas ao instanciar variável do tipo)
Formas: struct rotulo {tipo d1; … tipo dn;}; // só declara, pode instanciar várias
127 struct {tipo d1; … tipo dn;} nome; // instancia 1 (nome) e não tem rótulo
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo: struct
128
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outros pontos
importantes
Criação de tipos:
ponteiros e structs
Às vezes é necessário
manipular um objeto do tipo
estrutura a partir de seu
endereço, utilizando um
ponteiro
Para obter seus elementos
específicos a partir de um
ponteiro, deve-se utilizar o
operador ‘->’
Um ponteiro pode ser
dinamicamente
redirecionado para apontar
para outro objeto de mesmo
129 tipo
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outros pontos importantes
Criação de tipos: enum
Tipo enumerado: é uma forma de atribuir nomes a números, propiciando
maior legibilidade ao código
Forma: enum rotulo {nome1=v1, nome2=v2, ... Nomen=vn};
Se não houver atribuição de valor, nome1=0, nome2=1, ...
Podem ser atribuídos valores de referência, basta fazer a atribuição
Ex: enum ShapeType { circle = 10, square = 20, rectangle = 50};
Se para algum valor não houve atribuição, pega o inteiro seguinte ao valor
anterior
Ex: enum exemplo { primeiro = 25, segundo };
neste caso, segundo recebe valor 26
Nota: em C pode-se fazer operações com enum (ex: exemplo++)
Em C++ já não pode (envolve 2 conversões de tipo enum int enum)
130
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outros pontos importantes
Vetores e Matrizes
Vetor é uma estrutura unidimensional contendo elementos de um mesmo tipo
Forma: tipo nome[tam]; possui elementos de [0] a [tam-1]
Ex: int a[10] = {10,20,30,40,50,60,70,80,90} (a[9] recebe 0)
Notas:
Ao passar um vetor como parâmetro para uma função utilizando apenas o seu
nome, na realidade está sendo passado seu endereço inicial de memória
Um vetor possui tamanho fixo, especificado na declaração
Em C++, é possível criar um vetor dinâmico utilizando a classe Vector
É possível ter um vetor de qualquer tipo, incluindo estruturas
Ao terminar de depurar, o código gerado pela macro pode ser removido com
#define NDEBUG
Deve ser colocado no programa antes da inclusão de <cassert>
Opcionalmente pode ser definindo NDEBUG na linha de comando do
compilador
NDEBUG é um flag usado em <cassert> para alterar a forma como o código é
gerado pela macro
136
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outros pontos importantes
Ponteiros para função
Da mesma forma que se faz com vetor, na qual o uso de seu nome
funciona como um ponteiro para ele (ex: a para a[tam]), o endereço de
uma função pode ser obtido a partir de seu nome
Pode também ser usada a sintaxe explícita &func()
Para chamar a função, basta usar uma das formas apresentadas abaixo
Nota
Após o ponteiro para a função
ser definido, ele é atribuído ao
endereço da função func()
usando fp = func (sem
argumentos)
O segundo caso apresenta
definição e inicialização
simultâneas
137
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
15. Defina uma função que receba um double e
retorne um int. Crie e inicialize um ponteiro
para essa função e chame essa função
através do ponteiro.
138
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O Capítulo 4 do nosso livro mostra como as bibliotecas
na linguagem C são estruturadas (de forma procedural)
Existem várias funções independentes que são agrupadas em
um arquivo, por sua “afinidade”
Muitas vezes se cria uma estrutura de dados (uma struct) que
agrupa os dados sobre os quais estas funções atuam
Esta é uma abordagem clássica, usada por várias
bibliotecas de sucesso e que usam a linguagem C
139
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
// Uma janela : Implementação clássica em C ...
typedef struct tag_Window {
int x, y; // Posição na tela
int cx, cy ; // Largura e altura
Canvas* my_canvas; // Estrutura que contém atributos
// a serem utilizados para desenho
} CWindow;
140
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
// outros includes não mostrados
#include "CWindow .h”
int main() {
CWindow janela1, janela2; // Duas janelas
initialize(&janela1, 1, 1, 100, 200);
initialize(&janela2, 110, 220, 120, 100);
...
show(&janela1);
show(&janela2);
...
COLORREF c1(255, 0 , 0 ); // RGB ==> vermelho
setTextColor(&janela1, c1);
textOut(&janela1, 3, 5, “Alo Mundo com Janelas”);
...
cleanup(&janela1);
cleanup(&janela2);
}
141
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Quais os problemas com esta abordagem?
Toda vez que trabalhamos com a estrutura, temos que
passar o endereço dela para a função
Apesar de as funções terem sido criadas para
manipular a estrutura, elas são separadas dela
Este é o velho problema da separação entre dados e
funções, que já havíamos discutido!
Outro problema que pode ocorrer é o de “briga de
nomes” entre bibliotecas: initialize e cleanup, por
exemplo, são muito comuns ...
142
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outra abordagem semelhante é a que utiliza
“handles” para estruturas de dados
Um handle é um tipo de identificador, um jeito de se
diferenciar um objeto de outro
Quando você cria uma janela a função retorna um handle, sempre
que você precisar manipular essa janela, passe esse hwnd
Exemplo - programação para MS-Windows:
HWND hjanela;
hjanela = CreateWindow(.... um monte de dados ...);
ShowWindow(hjanela, ....);
UpdateWindow(hjanela, ....);
...
SetWindowText(hjanela, ...);
MoveWindow(hjanela, ...);
etc ...
143
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A abordagem é bem parecida, mas esconde um
pouco mais a estrutura de dados
Várias APIs usam “handles” e manipulam os dados
escondidos atrás destes handles por meio de funções
Um "handle" é um identificador que o sistema operacional usa
para indexar uma tabela de objetos que ele possui
Ex: um handle de arquivo é um índice para a tabela de arquivos
abertos do processo
144
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A programação orientada a objetos nos permite uma
solução bem mais elegante: criar classes!
class CWindow {
int x, y; // Posição na tela
int cx, cy ; // Largura e altura
Canvas* my_canvas; // Estrutura que contém atributos
// a serem utilizados para desenho
public:
int initialize(int xp, int yp, int cx, cy);
// +- (ver construtor, III.3)
int show(); // mostra na tela
int cleanup(); // +- (ver destrutor, item III.3)
int move(int newx, int newy); //Move
int resize(int newcx, int newcy); // Redimensiona
int setTextColor(COLORREF cor); // Define cor do texto
int textOut(int x, int y, char* text); // Texto em x,y
};
145
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Veja que o livro cria esta classe usando a palavra
chave struct
Isto porque em C++ uma struct pode conter funções e
funcionar como uma classe
A diferença entre structs e classes em C++ está apenas na
visibilidade “default” de seus membros
Ambas “possuem” funções
Implementam o conceito de “comportamento”
Implementação das funções membro
int CWindow::move(int newx, int newy) {
x = newx;
y = newy;
show();
return 0;
}
146
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uma vez que a classe seja implementada, podemos
instanciar objetos e chamar seus métodos:
CWindow janela1;
janela1.initialize(1,1,100,200);
janela1.move(6,7);
O tamanho do objeto janela1 em memória é equivalente
ao da struct correspondente!
147
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
// Declaração da classe com includes necessários
#include "CWindow.h”
int main() {
CWindow janela1, janela2; // Duas janelas
janela1.initialize(1, 1, 100, 200);
janela2.initialize( 110, 220, 120, 100);
...
janela1.show();
janela2.show();
...
COLORREF c1(255, 0 , 0 ); // RGB ==> vermelho
janela1.setTextColor( c1);
janela1.textOut(3, 5, “Alo Mundo com Janelas Orientadas a
Objetos”);
...
janela1.cleanup();
janela2.cleanup();
}
148
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
OBS: como organizamos o código em C++?
Definição das classes, no “.h” e das funções no
“.cpp”
149
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Como tínhamos visto na unidade II ...
O usuário de uma classe segue “um contrato” de uso
A classe pode tornar disponível uma série de serviços,
através de sua interface pública
A interface pública é o conjunto de membros da classe
declarados como públicos
Em C++, se não for especificado o controle de
acesso, os atributos e métodos são ...
Em uma classe: privados por default
Em uma struct: públicos por default
150
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
C++ possui 3 especificadores de acesso:
public: pode ser acessadas fora do objeto, onde este
estiver definido
private: só podem ser acessados por membros da
mesma classe
protected: podem ser acessados por membros da
mesma classe e de classes derivadas
152
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Classes aninhadas: ainda a janela ...
#ifndef CWINDOW_H
#define CWINDOW_H
class CWindow {
class Canvas {
COLORREF textColor;
COLORREF lineColor;
// etc ...
public:
void setTextColor (COLORREF newColor);
};
int x, y; // Posição na tela
int cx, cy ; // Largura e altura
Canvas* my_canvas;
public:
...
int setTextColor(COLORREF cor); // Define cor do texto
int textOut(int x, int y, char* text); // Escreve texto em x,y
};
#endif // CWINDOW_H
153
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Classes e structs aninhadas: Implementação
#include ”CWindow.h”
154
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Classes ou funções Friend
São classes ou funções que podem acessar os
membros privados de uma outra classe!
Usado quando precisamos assegurar acesso a um
grupo de funções de determinada classe
A função friend tem acesso a dados privados ou protegidos de
uma classe, permitindo implementar operações mais flexíveis
do que as oferecidas pelas funções membro
155
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Friends: Exemplo
class X {
private:
int i;
public:
void initialize();
friend void g(X*, int); // Função friend global!
};
void X::initialize() { i = 0; }
void g(X *x, int i) {
x->i = i; // Atribui valor para atributo privado da classe
}
156
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
class X; // Declaração da classe X
class Y {
public:
void f(X*);
};
class X { // Definição de X
private:
int i;
public:
void initialize();
friend void Y::f(X*); // Função friend pertencente à classe Y
friend class Z; // A classe Z inteira é friend!!!
};
void Y::f(X *x) {
x->i = 47;
}
157
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Problemas com o uso de friends
• Este tipo de declaração diminui as características de
encapsulamento da POO
• Uma classe que usa friends em demasia muito
provavelmente é passível de uma análise mais
eficiente
• Na maior parte das vezes, a utilização de friends pode
ser evitada por meio de uma melhor especificação da
interface da classe que está se “expondo” com o uso
de friends
158
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
16. Crie uma classe que contém um int e um
ponteiro para outra instância da mesma classe.
Escreva uma função dentro desta classe que
receba um int indicando o comprimento da lista
que deve ser criada. Essa função deverá criar
uma lista encadeada começando pela célula
cabeça e inserir um contador de posição no
inteiro de cada célula (célula cabeça não tem
contador). O ponteiro da última célula deverá
apontar para NULL. Escreva também outra
função para imprimir o conteúdo da lista
encadeada.
159
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na Unidade III já vimos ...
III.1. Implementando classes e objetos em C++
III.2. Atributos e métodos: controle de acesso e
encapsulamento
161
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Agora veremos ...
III.3. Inicialização e destruição
III.4. Sobrecarga de funções e argumentos default
III.5. Constantes, funções inline e controle de
visibilidade
Referência básica
Thinking in C++, Vol. 1
Parte 2: Capítulos 6 ao 10
162
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Unidade III - Classes e Objetos
III.1. Implementando classes e objetos em C++
III.2. Atributos e métodos: controle de acesso
e encapsulamento
III.3. Inicialização e destruição
Parte2
165
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são os construtores?
São funções que têm o propósito explícito de
inicializar objetos
Têm o mesmo nome da classe, não retornam nada, têm
chamada automática na declaração dos objetos
class X {
int i;
public:
X(); // Construtor
};
void f() {
X a; // A função X() é executada automaticamente
}
166
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que são os destrutores?
Função complementar às funções construtoras de
uma classe
Sempre que o escopo de um objeto encerra-se,
esta função é chamada
Cada classe pode ter somente um destrutor, que jamais recebe
parâmetros
O destrutor também não tem nenhum tipo de retorno
class Y {
public:
~Y();
};
167
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Qual é a saída deste programa?
168
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Reimplementando CWindow com construtores e destrutores
#ifndef CWINDOW_H
#define CWINDOW_H
class CWindow {
int x, y; // Posição na tela
int cx, cy ; // Largura e altura
Canvas* my_canvas;
public:
CWindow (int xp, int yp, int cx, int cy); // Construtor
~CWindow() ; // Destrutor
int show(); // Mostra na tela
int move(int newx, int newy); // Move
int resize(int newcx, int newcy); // Redimensiona
int setTextColor(COLORREF cor); //
int textOut(int x, int y, char* text); // Texto em x,y
};
#endif // CWINDOW_H ///
169
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
#include ”CWindow.h”
CWindow::~CWindow() {
delete my_canvas; // desaloca memória – é complementar a new
}
170
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Em C, as variáveis só podem ser declaradas no início
de blocos
Em C++, elas podem ser declaradas em qualquer parte
Isto é feito para que se possa conseguir informação suficiente
para inicializar os objetos por meio de seus construtores
Um objeto não pode ser criado se ele não for também inicializado
Você pode esperar ter as informações necessárias para então definir
e inicializar um objeto ao mesmo tempo
Um construtor tem como obrigações
Inicializar todas as variáveis do objeto
Garantir, dentro do possível, a validade dos dados
171
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Boa prática: definir variáveis o mais perto possível do seu
local de uso e sempre inicializá-las quando são definidas
(é uma questão de segurança)
Ao reduzir a duração da disponibilidade da variável dentro do
escopo, reduz-se a chance dela ser mal utilizada em alguma outra
parte do escopo
Além disso, melhora a legibilidade pois o leitor não tem que desviar
sua atenção para outro local para obter o tipo da variável
172
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Inicialização agregada à definição
175
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O construtor default é tão importante que, se não há
nenhum construtor para uma estrutura (struct ou
classe), é criado automaticamente um
// Automatically-generated default constructor
class V {
int i;
}; // No constructor
int main() {
V v, v2[10];
}
struct Y {
float f;
int i;
Y(int a);
};
Y y2[2] = { Y(1) }; // erro: não se sabe como inicializar
// o 2º elemento
Y y3[7]; // erro: não se sabe como inicializar os elementos do vetor
Y y4; // erro: não se tem construtor default
177
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que será impresso pelo programa?
178
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
17. Modifique os programas abaixo para que os
mesmos utilizem construtores e destrutores.
179
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Em português, uma palavra pode ter diferentes
significados
Tudo depende do contexto em que ela foi empregada
180
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Em C teríamos que criar funções com nomes diferentes,
quando a única coisa que as diferencia conceitualmente
é o tipo de dados recebido
int escalar_int(int [], int [], int);
float escalar_float(float [], float [], int);
181
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Em C++, outro fator nos força a ter sobrecarga
de funções
A possibilidade de criarmos vários construtores
diferentes
Poder criar objetos de diferentes maneiras!
182
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
A idéia da sobrecarga de funções é muito simples
Usa-se um mesmo nome para a função, mas uma lista de
argumentos diferentes
O compilador utiliza o escopo, o nome da função e a lista de
argumentos para reconhecer cada função sobrecarregada
Apesar das funções terem o mesmo nome, o compilador
conseguirá distinguir que função chamar por meio dos
parâmetros utilizados
void print(char);
void print(float);
...
print(´a´);
print(3.1415);
183
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uma pergunta que muitas vezes se faz é:
pode-se fazer a sobrecarga pelo tipo de retorno?
void f();
int f();
184
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Os dois construtores
executam basicamente o
mesmo código
Um deles apenas inicializa x e y
com zero quando não recebe
argumentos
185
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Não poderíamos usar um único construtor?
Sim, desde que usássemos argumentos default!
Valor fornecido na declaração que o compilador insere
automaticamente se não for fornecido um valor na chamada
Ex: f(int a, int b=0) pode ser chamado por:
f(150, 2); f(200, 0); f(100);
O segundo argumento é automaticamente substituído pelo compilador
caso não seja fornecido e o primeiro seja int
186
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Argumentos default
Se os dois construtores
executassem ações completamente
diferentes, não faria sentido usar
argumentos default,
mas sim continuar a ter sobrecarga
das funções
187
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Argumentos default
As seguintes regras devem ser seguidas:
Somente os últimos argumentos da lista podem ter valores default
Não se pode ter argumento default seguido por um não default
Uma vez que começou a usar valor default, todos os demais devem ser
default
Não se pode omitir argumentos no meio da lista
Os valores default somente são colocados na declaração da função
O compilador precisa conhecer os valores default antes de usá-los
Algumas vezes pode-se colocar estes valores comentados na definição,
apenas para fins de documentação
void fn (int x /* = 0 */ ) { // ...
188
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Argumentos default
Exemplos:
f(int a=5, int b, float c); // NÃO PODE
g(int a, float b=0., float c=3.); // OK!
chamadas:
g(1), g(2, 7.), g(4, 5., 6.); // OK!
g(1, , 7); // NÃO PODE !
Argumentos default devem ser utilizados para tornar as
chamadas a funções mais simples, quando houver uma grande
lista de argumentos que podem receber valores típicos
O valor default deve ser o valor que mais provavelmente vai ocorrer
para aquele argumento, de forma que os programadores clientes
poderão muitas vezes ignorá-lo ou utilizá-lo somente quando for
necessário modificar este valor padrão
189
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Argumentos default
Cuidado para não confundir o compilador usando a
sobrecarga e argumentos default simultaneamente
onde não poderia ...
void impressao(int,float);
void impressao(int, float, char=’a’);
190
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
18. Modifique a classe abaixo para usar argumentos
default no construtor. Teste o construtor criando dois
objetos diferentes.
191
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
19. Uma definição da classe Time correta pode
incluir os dois construtores a seguir? Se não,
explique o por quê.
192
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Capítulos 8, 9 e 10 do livro
III.5.1. Constantes
III.5.2. Funções inline
III.5.3. Controle de visibilidade de nomes
193
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Objetivo na linguagem C++
Permitir que o programador especifique o que pode ser
modificado ou não em seu programa
Fornece maior controle e segurança a um programa em C++
Compilador acusa erro se você utilizar tipos incompatíveis em uma operação
envolvendo constante ou alterar uma constante de forma acidental
Especificação na linguagem C++
Utiliza a palavra reservada const, que pode ter diferentes
significados, dependendo de onde ela aparece
Para criar uma constante no programa, evitando “números mágicos”:
const int bufsize = 100; // substitui #define BUFSIZE 100
// macro na qual o pré-processador
// substitui valor, não ocupa memória
194
char buffer[bufsize];
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Vantagem do uso de constantes
Sobre o #define
Além de atuar de forma semelhante, uma constante tem um tipo, que
pode ser verificado pelo compilador: uma macro não tem
Outras vantagens adicionais
Constantes também podem ser usadas para garantir que uma
variável, depois de inicializada, não vai ter seu valor modificado ao
longo do programa
Em geral não ocupa memória (isso depende da complexidade do tipo de dado
e do compilador – sempre aloca com uso em ponteiros e com extern)
Possibilita realizar cálculos envolvendo os valores constantes em
tempo de compilação (importante na definição de arrays)
Pode ser usado com todos os tipos (predefinidos ou definidos pelo usuário)
Facilita a manutenção de programas
Boa prática: usar const ao invés de #define
195
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outras características
Uma constante tem que ser inicializada na declaração
Pois não pode haver uma atribuição para uma constante
É visível somente dentro do arquivo onde está definida
Se necessário, usar extern para obter valor externo e ser reconhecida
fora do arquivo
extern const int bufsize;
É possível inicializar constante com valor produzido em tempo de
execução
const char c = cin.get(); const char c2 = c + ‘a’;
Possibilita gerar código mais eficiente por eliminar (em vários
casos) a alocação de memória e a leitura de variáveis constantes
Pode ser empregado em vários contextos
Objetos, dados e função membros, argumentos de função, retornos de função,
196 …
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Constantes tratadas em
tempo de compilação
197
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de constantes com ponteiros
Ponteiro para constante
Formas: const tipo *var; ou tipo const *var;
Ex: const int *u; // u é um ponteiro que aponta para constante inteira
u pode apontar para qualquer coisa (não é constante), mas o que ele aponta não
pode ser alterado
Ponteiro constante
Forma: tipo var=valor; tipo* const ponteiro = &var;
Ex: int d=1; int *const p = &d;
p é um ponteiro constante que aponta para um inteiro
É necessário existir um endereço inicial, imutável para o ponteiro
O conteúdo pode alterar – ex: *p = 2 // legal
Ponteiro constante para objeto constante
Engloba as duas formas acima: neste caso, nem o ponteiro nem o conteúdo
do objeto podem ser alterados
Ex: int a=1; const int* const pa1 = &a; ou
int const* const pa2=&a
198
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Ponteiros e constantes: exemplo
const pode se aplicar ao endereço para o qual o ponteiro aponta ou se aplicar ao
conteúdo apontado
203
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de constantes na passagem de parâmetros e retorno
Todos os objetos temporários são constantes
Retorno de valor constante
Ao retornar valor, também ocorre cópia também não se faz
necessário para os tipos primitivos
Para outros tipos, não podem ser atribuídos a outro objeto ou
modificados
204
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de constantes na passagem de parâmetros e
retorno
Na passagem e retorno de endereços (ponteiro ou referências)
Sempre que possível, usar const ao passar um endereço para uma
função
O uso de const previne a ocorrência de alteração inadequada
Além disso, possibilita usá-la junto a outros objetos que forem const
A sintaxe por referência, além de mais simples, é mais eficiente
205
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passando referências constantes em funções
Esta é a forma mais adequada de passar parâmetros para funções,
onde se quer evitar a cópia e ainda garantir que não haja
modificação de valores internos
class X
{
// Um monte de atributos ==> é mais eficiente
// evitar a cópia!
};
void g (X x) {} // Passagem por cópia (ineficiente)
void g1(X& x) {} // Passagem por referência (OK, pode
// modificar)
void g2(const X& x) {} // Passagem por referência
// constante (OK, não pode
// modificar)
206
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Constantes em classes
Podemos ter atributos e funções membro constantes
class X {
const int i; // atributo constante
public:
X(int ii);
void f() const; // função membro constante
};
207
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de atributo constante dentro de uma classe
Aloca espaço específico em cada objeto
Representa um valor que é inicializado uma vez e não pode mais
ser alterado
“Esta constante é para toda a vida do objeto”
Cada objeto pode ter um valor diferente para esta constante
A inicialização da constante ocorre no construtor de cada objeto,
em um local específico
Lista de inicialização do construtor
Apresentada apenas na definição do construtor
Ocorre após a lista de argumentos, precedida do “:” e vem antes do “{“
Indica que deve ser executada antes de qualquer código do construtor
Criada para uso pela herança (a ser visto posteriormente)
Nota: a idéia desta lista foi estendida também para os tipos primitivos,
existindo construtores default para eles em substituição à atribuição
Ex: float pi (3.14159) é o mesmo que float pi = 3.14159
208
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo de atributo constante em uma classe inicializado com a
lista de inicialização do construtor
class X {
const int i; // atributo constante
public:
X(int ii);
// etc ...
};
X::X(int sz) { i = sz; } // Não pode inicializar assim,
// pois i é constante
X::X(int sz) : i(sz) {} // OK!
class Parte {
int t;
public:
Parte(int tt) ;
};
Parte::Parte(int tt) : t(tt) {}
Esta lista de
class Todo { inicialização pode ser
const int size; usada antes do corpo
Parte b; da função mesmo sem
public: ter atributo const.
Todo(int sz, int bt); No caso específico de
}; const, ela é obrigatória
Todo::Todo(int sz, int bt) : size(sz), b(bt) { }
210
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Objetos constantes e funções membro constantes
const int i = 1;
const X b(2); // objeto constante
Como o compilador pode garantir que um objeto do tipo
especificado vai permanecer constante?
Permitindo que somente funções membro constantes (funções
que o compilador garante que não modificam o estado do objeto)
sejam chamadas sobre os objetos constantes!
class X { void h(const X& rx) {
int i; rx.f(); // OK, f() é
public: constante
X(int ii); rx.g(); // ERRO!!!
int f() const; }
int g(); int main() {
}; X x1(10);
X::X(int ii) : i(ii) {} const X x2(20);
int X::f() const { return i; } x1.f(); //OK
211 int X::g() { i = 20; } x2.f(); //OK
© Renato Mesquita, x1.g(); //OK
Ana Liddy Magalhães e x2.g(); // ERRO!!! }
Raquel Mini
Objetos constantes e funções constante
Portanto, funções membro constante são aquelas funções
para as quais se tem a garantia de se manter constante o
objeto sobre o qual as funções são chamadas
Elas não modificam os atributos dos objetos!
São as únicas que podem ser chamadas por objetos constantes
São seguras de serem chamadas por todos os objetos
O const deve aparecer na declaração e na definição
Caso contrário, o compilador considera 2 funções distintas
Acusam erro caso a função tente alterar qualquer membro da função
ou chamar outra função membro não constante
Construtores e destrutores não podem ser funções const, pois
sempre realizam alteração no objeto
212
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Regra importante: faça com que todas as funções membro que
não precisem modificar atributos do objeto sejam constantes
Desta forma, elas poderão ser chamadas sobre objetos ou referências
constantes!
// contexto do exemplo anterior
void h(const X &refcons, X &ref) {
refcons.f(); // OK, f() é const
ref.f(); //OK, f() pode ser chamada por qualquer objeto
ref.g(); // OK, nem g() nem ref são const
refcons.g();// ERRO - g() não é const!
}
213
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
21. Localize os erros na seguinte classe e
explique como corrigi-los:
214
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
22. Faz sentido um construtor ser const? E um
destrutor? Por quê?
215
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: necessidade
Uso de macros
Uma das maneiras que a linguagem C (e C++) possui para
permitir uma maior eficiência do código
Vantagens do uso
Permite que ocorra uma simples substituição de código pelo pré-
processador, evitando:
Ter uma chamada a função, com todo o custo associado
Criar os argumentos e copiar seus valores
Fazer um CALL em Assembly, retornando o valor e efetuando um
RETURN em Assembly
Possui a conveniência e a legibilidade de uma chamada de função,
porém sem o custo adicional associado
216
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: necessidade
Macros: exemplos de uso que geram problemas
#define F(x) (x + 1)
Uso da macro Expandido pelo pré-processador
F(1) (1+1)
Problema 1
Desdobramento incorreto em função de erro ao defini-la
#define F (x) (x + 1) // note espaço ente F e (x)
F(1) (x) (x + 1)(1)
Problema 2
Precedência incorreta de operadores
#define CUBO(x) x*x*x
CUBO(a+b) a+b*a+b*a+b = a + 2.ba + b
218
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: necessidade
Macros: exemplos de uso que geram problemas
Problema 4: não se pode implementar uma função membro de
uma classe como macro!
class X {
int i;
public:
#define VAL(X::i) // Error
Por questões de eficiência, haveria a tendência de deixar todos os
atributos da classe como public!
Problemas adicionais: macros não permitem variáveis locais,
blocos, verificação de tipos, chamadas recursivas, etc.
Solução para todos os problemas das macros:
funções inline!
219
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: conceito
inline int par (int num) {return((num%2==0)? num : num+1);}
inline float cubo (float x) {return x*x*x;}
inline float dobro (float x) {return 2*x;}
Objetivo
O especificador inline dá uma dica para o compilador de que ele
deve tentar expandir o código para a função "inline” (como a macro
fazia), evitando o “overhead” associado à chamada à função
É apenas uma sugestão, não obriga o compilador a realizar desta forma
Todo o processo estará sob o controle do compilador
Não se pode garantir que toda chamada a uma função inline vai ser
realmente gerada "inline"
Para tornar a geração de código inline possível, você deve incluir o
corpo da função dentro da declaração
220
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: características
Toda função definida no corpo de uma classe é inline
class Circulo{
private:
int raio;
Ponto centro;
public:
Circulo(Ponto ce, int ra): centro(ce), raio(ra){ }
float area( ) const { return PI*raio*raio; }
};
Circulo::Circulo() e Circulo::area() são inline!
class Access {
int i;
public:
int get() const { return i; }
void set(int ii) { i = ii; }
};
223
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: aplicação
Muitas vezes, se usam funções sobrecarregadas para acessar /
modificar os atributos por meio de accessors e mutators
Accessors: funções que lêem informações do estado do objeto
Mutators: funções que alteram o estado do objeto
class Rectangle {
int wide, high;
public:
Rectangle(int w = 0, int h = 0) : wide(w), high(h) {}
int width() const { return wide; } // width for read / get
void width(int w) { wide = w; } // width for set
int height() const { return high; } // height for read / get
void height(int h) { high = h; } // height for set
};
int main() {
Rectangle r(19, 47);
// Change width & height
r.height(2 * r.width()); // height for set, width for read
r.width(2 * r.height()); // width for set, height for read
}
224
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções inline: aplicação
O uso de funções inline na definição da classe faz com
que sua interface fique “poluída”
Apresenta detalhes de implementação que deveriam estar
escondidos do usuário da classe
Para evitar isto e ainda poder utilizar funções inline, pode-se definir
funções membro inline fora da definição da classe
225
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
class Rectangle {
int wide, heigh;
public:
Rectangle(int w = 0, int h = 0);
int width() const;
void width(int w);
int height() const;
void height(int h);
};
228
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
25. No programa abaixo, troque todas as funções membro
para funções inline. Troque também a função
initialize() para o construtor.
229
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Manipular nomes é uma atividade fundamental na
programação
Quando o projeto fica grande, o volume de nomes a
manipular acaba ficando muito grande
Necessário ter estratégias para gerenciá-los
static: “algo que mantém sua posição” - visto sob 2 aspectos:
Localização física na memória (memória estática)
Alocado uma única vez em um endereço físico
Área especial para dados estáticos (não usa a pilha de execução)
Visibilidade proporcionada ao nome
Controlada pelo escopo (visibilidade local)
Vantagem sobre nomes globais: acesso + restrito, melhor controle
Facilita localizar erros, não alterada fora do escopo definido
e sem sobreposição de nomes
230
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Variáveis locais static
int count () {
static int num = 0; // inicializada uma única vez
int x = 0; // inicializada várias vezes
num++;
...
return 0;
}
A inicialização de num é feita somente na primeira vez
que a função é chamada
Na realidade, na primeira vez que o programa passa pela
declaração da variável
num mantém o seu valor de uma chamada para a outra
Isto significa que a variável static não é criada na pilha, mas sim na
área de variáveis estáticas do programa
231
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Variáveis locais static
232
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Objetos static
A construção do objeto static é feita no momento
que a thread de execução executa pela primeira
vez o código da função
Se a função não for executada nenhuma vez, não é
construído
O destrutor de um objeto static somente é
executado ao final do programa
Objeto static x global
O construtor de um objeto global é executado antes do main()
começar sua execução (é diferente do que ocorre com o static)
O destrutor de um objeto global é executado somente ao final
da execução do programa (é semelhante ao que ocorre com o
static, caso tenha sido construído)
233
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Obj a é global: construtor chamado
antes de main() e o destrutor chamado
ao final do programa
234
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Obj a é global: construtor chamado
antes de main() e o destrutor chamado
ao final do programa
235
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
26. Informe o que será impresso pelos códigos
abaixo:
a) b)
236
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Atributos de classe: atributos static
Atributos estáticos servem para implementar o
conceito de “Atributo de uma Classe”
Usado quando deseja-se que todos os objetos de uma
determinada classe compartilhem um certo dado
Único para toda a classe, independente do número de objetos
Todos os objetos compartilham e podem se comunicar por ele
class Ponto{
int x, y; Ponto_2
Ponto_1
static int cont;
// .... x x
y y
};
cont cont
Ponto Ponto_1, Ponto_2;
237
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Atributos de classe: atributos static
Pode ter escopo público, privado ou protegido
Pode ser acessado por funções membro ou por outras partes
do programa, quando público
Pode ser acessado antes da existência de qualquer objeto da
classe
Seu espaço de memória é reservado antes da criação de qualquer
objeto
Sua definição tem que ocorrer fora da classe (sem inline) e só uma
vez
class Ponto{
int x, y;
static int cont;
// ....
};
238 int Ponto::cont = 7;
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Atributos static constantes
Todo membro estático deve ser redeclarado fora da classe,
porém dentro do escopo do arquivo da classe
Neste ponto ele pode ser inicializado. Não se pode inicializar um
membro estático não constante dentro da definição da classe!
240
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções membro static: exemplo
241
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Funções membro static: exemplo
242
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
É possível inserir um objeto static da mesma
classe dentro da própria classe (padrão
singleton)
Objetivo: criar uma classe que pode possuir apenas uma
instância, disponível globalmente no código
Motivação
Há várias situações nas quais é útil criar uma classe que pode
possuir apenas uma instância – exemplos:
Deve existir apenas um sistema de arquivos, apesar de poderem
existir várias impressoras
Deve existir apenas um spool de impressão, apenas um conversor A/D
para um filtro digital, etc.
243
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Construção do singleton:
O construtor da classe é privado, de modo que a
criação da instância única é feita no método
Instance()
Instance é um método de classe (static) e,
portanto, pode ser chamado antes mesmo da
existência do objeto
Uma vez que a instância exista, Instance()
simplesmente retorna um ponteiro para ela
244
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Singleton: exemplo
245
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Singleton: consequências
Como só existe uma instância da classe, o programador
tem controle sobre como os clientes a acessam
Reduz a poluição no namespace global
Apenas o nome da classe entra nele
246
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: necessidade
Em projetos grandes, a falta de controle sobre o
espaço (escopo) de nomes pode ser um problema
Os nomes mais comuns já foram usados, sendo necessário
colocar nomes grandes e/ou complicados para garantir que
sejam diferentes
Podemos usar typedef para tentar simplificar, mas não seria
uma solução elegante e suportada em todos os ambientes
Solução em C++: usar namespaces
Subdividir o espaço de nomes globais em partes gerenciáveis,
Coloca o nome dos membros do namespace em um espaço
específico e separado (semelhante a um encapsulamento)
247
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: aplicação
Mecanismo do C++ para agrupar logicamente nomes
Se quaisquer declarações (variáveis, funções, classes, ...)
forem relacionadas entre si, elas poderão ser colocadas
em um mesmo espaço de nomes para expressar este fato
Existe um outro mecanismo de agrupamento físico
que é o uso de arquivos
Os namespaces vêm adicionar a este mecanismo o
agrupamento lógico
248
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: sintaxe
A sintaxe de criação de um namespace parece com a da classe
namespace MyLib {
// Declarations
}
int main() {}
250
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: características
Um namespace é um escopo com nome
Quanto maior o programa, mais úteis são os namespaces para
expressar a separação lógica de suas partes
Idealmente toda entidade em um programa deve estar em algum
namespace, para indicar o seu papel lógico no programa
A exceção é main(), que deve ser global
Namespaces: usos
Seu uso pode ocorrer basicamente de 3 formas:
Pela qualificação explícita (resolução de escopo)
Pela diretiva using
Pela declaração using
Estas formas serão detalhadas a seguir
251
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: uso pela
qualificação explícita
Explicita o escopo dos objetos
que estão em um namespace
252
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: uso com a diretiva using
Torna todos os nomes de um namespace disponíveis em um
determinado contexto (“importa” todo o namespace de uma vez)
254
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: cuidados
O objetivo dos namespaces é expressar uma estrutura lógica
A forma mais simples de estrutura para a qual ele pode ser usado é a
distinção entre o código escrito por uma pessoa e o escrito por outra
Quando usamos um único espaço de nomes global, torna-se
desnecessariamente difícil compor um programa composto por partes
separadas
O problema é que cada parte pode definir os mesmos nomes
Quando combinados em um mesmo programa, estes nomes colidem
... veja exemplo a seguir
255
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: cuidados
257
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces podem ser compostos, para gerar novos
Namespaces
258
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Namespaces: regras importantes
Não utilize diretivas using em arquivos .h
Utilize apenas nos .cpp, pois o seu alcance fica mais limitado
Uso livre de um namespace dentro de um arquivo, sem afetar outros
Cada .cpp pode ter o seu namespace, distinto dos demais
Se houver problemas de nomes duplicados quando estiver usando
mais de uma diretiva using nos .cpp, efetue a seleção deles
atribuindo declarações using
Nos .h, ou utilize as qualificações explícitas para acessar
determinados nomes encapsulados em namespaces, ou utilize
declarações using, para introduzir apenas os nomes
selecionados no escopo
259
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
27. Crie uma função que retorna o próximo valor
em um sequência de Fibonacci toda vez que
ela é chamada. Insira um parâmetro que é um
bool com o valor default igual a false tal que
quando você passa o argumento com valor
true ele começa do início da sequência de
Fibonacci. Crie a função main para utilizar
essa função.
260
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Na Unidade III já vimos ...
III.1. Implementando classes e objetos em C++
III.2. Atributos e métodos: controle de acesso e
encapsulamento
III.3. Inicialização e destruição
III.4. Sobrecarga de funções e argumentos default
III.5. Constantes, funções inline e controle de
visibilidade
262
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Agora veremos ...
III.6. Ponteiros, referências, atributos dinâmicos,
gerenciamento de memória e o construtor
de cópia
III.7. Sobrecarga de operadores e conversão de tipos
Referência básica
Thinking in C++, Vol. 1
Parte 3: Capítulos 11 ao 13
263
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Unidade III - Classes e Objetos
III.1. Implementando classes e objetos em C++
III.2. Atributos e métodos: controle de acesso e
encapsulamento
III.3. Inicialização e destruição
III.4. Sobrecarga de funções e argumentos default
III.5. Constantes e controle de visibilidade
III.6. Ponteiros, referências, atributos dinâmicos,
gerenciamento de memória e construtor de cópia
Parte
3
265
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que já vimos sobre o assunto? (cont.)
Como utilizar ponteiros e referências para passar
parâmetros por referência para funções
266
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O que já vimos sobre o assunto? (cont.)
Uso de referências constantes ao passar parâmetros
por referência para funções
Estes parâmetros não poderão ser modificados dentro da função
267
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Usando ponteiros, podemos fazer alocação dinâmica de
memória
268
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Destrutor e alocação dinâmica
Se a classe tem destrutor, ele é chamado ao
desalocar o objeto
269
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Referências: conceito
Referências são como ponteiros constantes, porém:
São automaticamente desreferenciados pelo compilador
Deixam a sintaxe mais simples e “enxuta” do que usando ponteiros
Ficam “amarradas” à memória à qual foram inicializadas
São adequadas para controlar a forma como os argumentos são
passados para dentro e para fora das funções
Muito usadas em lista de argumentos e retorno de valores em funções
São essenciais para dar suporte à sintaxe de sobrecarga de
operadores (a ser vista mais à frente)
270
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Referências: regras
Uma referência deve ser inicializada quando é definida
Um ponteiro pode ser inicializado a qualquer momento
Uma vez inicializada para um objeto, não pode mais ser
alterada para referenciar outro objeto
Um ponteiro pode apontar para outro objeto a qualquer momento
Não pode ter conteúdo nulo, ou seja, precisa estar
relacionada a uma região de memória legítima
Um ponteiro pode apontar para NULL
271
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo
272
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Referências: uso em funções
Como argumento
Qualquer alteração na referência dentro da função altera o
argumento também fora da função
Funciona igual ao ponteiro, porém possui uma sintaxe mais limpa
O ponteiro deixa explícito seu uso, a referência mantém implícito seu uso
274
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo de referências: uso em funções
275
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo de referências: uso em funções
276
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Referências constantes
Não permitem alterar o objeto referenciado
Se você sabe que a função irá respeitar o objeto (não alterará seu valor),
o uso de const permitirá que a função seja usada em todas as situações
Para tipos primitivos, a função não alterará os argumentos
Para tipos definidos pelo usuário, a função chamará apenas membros
constantes e não modificará dados públicos
Seu uso em argumentos de funções é especialmente importante,
porque a função pode receber um objeto temporário
Este pode ter sido criado como o retorno de uma outra função,
ou explicitamente, pelo usuário da função
Objetos temporários são sempre constantes,
portanto se não utilizarmos uma referência
constante o argumento não vai ser aceito
pelo compilador
277
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Orientações para passagem de argumentos
Ter por hábito, sempre que possível, passar
referências constantes
Garante maior eficiência
Evita ter que copiar valores, bastando empilhar seu endereço
Passagem por valor requer construtor de cópia e destrutor
Podem não estar sempre disponíveis
Se não for alterar, estes são desnecessários
278
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
28. Escreva uma função que receba um ponteiro
como parâmetro, modifique o conteúdo apontado
pelo ponteiro e retorne o conteúdo do ponteiro
como referência.
29. Crie uma classe com algumas funções membro e
passe o objeto desta classe como parâmetro para
a função do exercício anterior. Utilize const
<nomeclasse> * no argumento da função e
faça alguns membros da classe const.
a) Prove que você somente pode chamar funções membro
const dentro da função.
b) Troque o parâmetro para referência ao invés de ponteiro
e explique as diferenças.
279
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Construtor essencial para controlar a passagem de
parâmetros e o retorno de função por cópia
Devido à sua importância, o compilador sempre gera um construtor
automaticamente se este não for fornecido
Ao passar um objeto por valor, é criado um novo objeto a ser utilizado
pela função a partir de um fornecido (o original passado para a função)
Comportamento default: atributos do objeto são copiados, um a um
Pode ser reescrito pelo usuário sempre que for necessário
Para tipos primitivos, poderia ser realizada uma cópia por bits
Para tipos mais sofisticados, é necessário definir um construtor
específico
280
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
É executado toda vez que:
Um objeto da classe é passado e, em alguns casos, retornado,
por valor, para uma função
Não chama construtor de cópia no retorno de objeto local
Um objeto é declarado e inicializado com outro objeto da própria
classe
Forma geral: Objeto (Objeto &) (“X of Xref”)
O único argumento é o objeto a ser copiado
281
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo:
282
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo:
283
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo:
284
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo:
285
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
286
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passagem por valor:
Sempre chama o construtor de cópia
288
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• A necessidade de atributos dinâmicos
• Atributos dinâmicos são atributos que precisam ser
alocados e desalocados dinamicamente
• A alocação de atributos dinâmicos ocorre quando algum
membro da classe é um ponteiro
• Quando um objeto é criado, alocamos espaço para ele
• Isto é normalmente feito pelos construtores da classe
• Quando termina o escopo dos objetos, deve-se desalocar a
memória alocada para os atributos dinâmicos
• Para isso usamos o destrutor da classe
• É necessário criar o construtor de cópia para objetos com
atributos dinâmicos
• Caso contrário, teremos problemas graves na passagem de
289 parâmetros por cópia para funções
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Atributos dinâmicos
Solução:
1) ou passamos por referência;
290 2) ou criamos o construtor de
© Renato Mesquita,
Ana Liddy Magalhães e
cópia!
Raquel Mini
Atributos dinâmicos e o construtor de cópia
291
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
• Ponteiro this: necessidade
• Cada um dos objetos de uma determinada classe tem sua cópia
dos atributos de maneira automática
• As funções membro têm o código compartilhado por todos os
objetos da classe
• Como o programa sabe, dentro do código da função, qual é o
objeto que está sendo acessado?
• Para efetivar a ligação entre função membro e objeto, o C++ possui
um ponteiro implicitamente criado para cada função da classe,
denominado this
• Sua finalidade é apontar para o objeto que chamou a função
• Podemos usar este ponteiro quando queremos referenciar o
objeto como um todo (e não a seus atributos individuais)
292
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
293
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
30. Crie uma classe contendo um double *. O
construtor inicializa o double * chamando
new double e associando ao conteúdo
apontado pelo ponteiro o valor recebido como
parâmetro. O destrutor imprime o conteúdo
apontado pelo ponteiro e chama o delete
para o ponteiro. Agora crie uma função que
receba um objeto desta classe por valor e
chame esta função no main(). O que
acontece? Conserte o código criando o
294 construtor de cópia.
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
31. Crie uma classe com um construtor que
parece com um construtor de cópia, mas que
recebe um parâmetro extra com um valor
default. Mostre que esse construtor é utilizado
como construtor de cópia.
295
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Sobrecarga: é apenas uma outra forma de se fazer
uma chamada de função
Sobrecarga de funções: funções com o mesmo nome,
mas com parâmetros diferentes (e códigos diferentes)
Sobrecarga de operadores: operadores com o mesmo
nome, mas com operandos diferentes (e códigos
diferentes)
Diferenças:
A sintaxe é diferente: os argumentos para esta função não
aparecem dentro de parênteses, mas próximo aos
caracteres que aparecem em torno dos operadores
O compilador define como construir (que porção / tipo de
código chamar)
Tratar como função inline, realizar conversão entre tipos, aplicar
instrução assembly
296
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplo:
class Complex{
double re;
double im;
}
Complex A,B,C;
297
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Uso de operadores sobrecarregados em C++
Apenas aqueles já existentes na linguagem C
+ - * / % ^ &
| ~ ! = < > +=
-= *= /= %= ^= &= |=
<< >> >>= <<= == != <=
>= && || ++ -- ->* ,
-> [ ] ( ) new new[] delete delete[]
Os seguintes operadores não podem ser sobrecarregados
** Exponenciação em outras linguagens
:: Resolução de escopo
. Seleção de membro
.* Seleção de membro através de um ponteiro para função
298
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Outras regras
O operador sobrecarregado não pode alterar as
regras de precedência e de associatividade do C e
C++
Não alterar o número de argumentos requeridos pelo
operador
Ao menos um dos parâmetros do operador deverá ser
membro de uma classe
Somente expressões contendo tipos definidos pelo
usuário podem ter operador com sobrecarga
299
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Sintaxe para definição de operadores
sobrecarregados
Usar “operator” seguido do símbolo do operador
Se torna uma função como qualquer outra
300
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Sintaxe para definição de operadores sobrecarregados
Sobrecarga como função membro
class Nome_classe{
tipo_retorno operatorop (lista_de_parâmetros);
};
tipo_retorno Nome_classe::operatorop (lista_de_parâmetros) {
... // código da função
}
303
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Exemplos
304
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Passagem e retorno de valor: orientações gerais
Sempre utilizar passagem por referência
Deixa o código mais limpo e em geral é mais eficiente
Da mesma forma que com qualquer função, se não for necessário alterar um
argumento (ex: +, -), usar referência constante
Se a função for membro de uma classe usar uma função membro constante
Com operadores envolvendo atribuição (ex: +=, =), que alteram o valor à
esquerda, deve-se usar referência (sem const, pois o valor será alterado)
O tipo de valor de retorno a ser selecionado depende do significado
esperado do operador
Se é produzido um novo valor (ex: +), será necessário gerar um novo objeto
como valor de retorno
Todos os operadores de atribuição alteram o lvalue
Deve-se retornar o mesmo tipo do que está sendo modificado, de forma a permitir
atribuição múltipla (ex: a = b = c)
O valor de retorno de todos eles deve ser uma referência não constante a lvalue
Para operadores lógicos, retornar no mínimo um inteiro (o ideal é bool)
305
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Pontos importantes: Operadores ++ e –– (pré e pós incremento)
São criadas assinaturas diferentes para não gerar conflito (o
int não é usado)
Quando não é função membro
++a ou --a operator++ (a) ou operator-- (a)
a++ ou a-- operator++ (a,int) ou operator-- (a,int)
Quando é função membro
++b ou --b operator++ () ou operator-- ()
b++ ou b-- operator++ (int) ou operator– (int)
306
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Pré-fixados: retorna o valor incrementado, por
referência
O próprio item está sendo alterado e o operador pode
estar envolvido em outros contextos para os quais
pode ser necessário obter o valor de retorno
Para funções membro, retornar *this
Para funções não membro, retornar a referência para o
argumento incrementado
307
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Pós-fixados: retorna o valor antes de incrementar,
por valor (cópia)
Tem que criar objeto para armazenar o valor de
retorno (fazendo cópia), pois o valor inicialmente
recebido deverá ser incrementado
Desta forma, preserva-se o significado original destes operadores
308
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Pontos importantes: operadores + e -
Necessário retorno por valor, pois deve-se sempre pensar que um
operador poderá fazer parte de uma expressão complicada,
envolvendo vários operadores
Exemplo: a+b+c-d
Outros pontos importantes
Utilize passagem e retorno por referência sempre que for
possível!!!
Retorno de valor como constante também é uma boa prática
Todo objeto temporário é automaticamente constante
Busque otimizar o retorno
return Complexo (r, i); é mais eficiente que
Complexo tmp(r, i); return tmp;
O compilador entende que não tem outra finalidade para o objeto a não ser
retornar e já constrói o objeto direto no endereço de retorno
gera apenas uma chamada do construtor
309
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Operador friend de duas classes
310
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O operador = (atribuição)
Tem significado pré-definido (copia atributo por atributo)
Ele deve ser sobrecarregado sempre que seu comportamento
for diferente do operador de atribuição default criado pelo C++
311
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
O operador = (atribuição)
Solução para o problema a=a (auto-atribuição)
312
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Operadores de fluxo de entrada e saída
Podemos sobrecarregar os operadores >> e << para fazer a
leitura e impressão dos objetos das classes que definirmos
315
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
32. Escreva uma classe chamada Bird que
contém uma string, um static int e um int. No
construtor default, use o static int para gerar
automaticamente um identificador para a
string da forma Bird#1, Bird#2, etc. Insira um
operador << da classe ostream para imprimir
os objetos Bird. Insira o construtor de cópia e
os operadores +, -, * e / que operam com o
int.
316
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Conversão de tipos: necessidade
Se o compilador encontra uma expressão ou uma chamada a função
em que se utiliza um tipo de variável que não é aquela que se
esperaria naquela chamada...
Em C e em C++, o compilador pode muitas vezes efetuar uma conversão
automática de tipos, transformando-o no tipo que a função ou a expressão
esperam receber (conversão implícita)
Alternativamente, pode ocorrer a conversão explícita, com o uso de casts
Este mesmo efeito pode ser obtido em C++ para os tipos definidos
pelo programador
Funções para conversão de tipos podem ser definidas, o que pode ser
realizado de duas maneiras
Utilizando um tipo particular de construtor
Utilizando sobrecarga de um operador de conversão de tipos
317
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Conversão através de construtor: necessidade
(exemplo)
318
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Conversão utilizando o construtor: implementação
Por default, um construtor com um único argumento também define
uma conversão implícita
Para alguns tipos isto é o ideal, mas para outros pode causar sérios
erros
320
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
Conversão por sobrecarga de um operador de
conversão de tipos
Cria-se uma função da classe utilizando a palavra chave
operator seguida pelo tipo para o qual se quer converter o
objeto
321
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini
33. Considerando o código abaixo, explique o que
irá acontecer nas linhas (1) a (6).
322
© Renato Mesquita,
Ana Liddy Magalhães e
Raquel Mini