Você está na página 1de 714

Machine Translated by Google

Machine Translated by Google

ACESSO ON-LINE
Obrigado por adquirir uma nova cópia de Introdução à Programação com C++, Terceira Edição.
Seu livro inclui seis meses de acesso pré-pago ao site complementar do livro. Esta assinatura pré-paga oferece
acesso total às seguintes áreas de suporte ao aluno:

Use uma moeda para raspar o revestimento e revelar o código de acesso do aluno.
Não use faca ou outro objeto pontiagudo, pois pode danificar o código.

Para acessar a Introdução à Programação com C++, Terceira Edição, Site Complementar para o

navegador. O processo leva apenas alguns minutos e só precisa ser concluído uma vez.

1. Acesse www.pearsoninternationaleditions.com/liang 2. Clique


em Site complementar.
3. Clique no botão Registrar .
4. Na página de inscrição, insira seu código de acesso de estudante* encontrado abaixo do painel de raspar.
Não digite os travessões. Você pode usar letras minúsculas ou maiúsculas.
5.
processo, basta clicar no botão Precisa de ajuda? ícone.
6.
Introdução à programação com site complementar C++!

Para fazer login depois de se registrar:


Você só precisa se registrar neste site complementar uma vez. Depois disso, você poderá fazer login a qualquer
momento em www.pearsoninternationaleditions.com/liang
solicitado.

não será mais válido.


Machine Translated by Google

Referência rápida de C++

se declarações instruções de troca Matriz/inicializador

se (condição) { switch (intExpressão) lista interna [10];


{ lista interna [] = {1, 2, 3, 4};
declarações; valor do caso 1:
} declarações; Matriz/inicializador multidimensional
quebrar;
... lista interna [10][15];
se (condição) { caso Valente: lista interna [2][2] = {{1, 2}, {3, 4}};
declarações;
declarações; quebrar;
} padrão:
Criação/exclusão de memória dinâmica
outro declarações;
{ } int* p1 = novo int;
declarações; int* p2 = novo int[10]; excluir p1;
}
excluir [] p2;

if (condição1) {

declarações; Funções usadas com frequência


}
senão se (condição2) time(0) retorna a hora atual
{ srand(seed) define uma nova semente para gerar números aleatórios
declarações; rand() retorna um número inteiro aleatório
} pow(a, b) retorna ab
outro
Funções de personagem
{
declarações;
isdigit(c) retorna verdadeiro se c for um dígito.
}
isalpha(c) retorna verdadeiro se c for uma letra.
isalnum(c) retorna verdadeiro se c for uma letra ou um dígito.
islower(c) retorna verdadeiro se c for uma letra minúscula.
isupper(c) retorna verdadeiro se c for uma letra maiúscula.
tolower(c) retorna letras minúsculas para c.
toupper(c) retorna uma letra maiúscula para c.
Declarações de Loop
Funções de string C
enquanto (condição) {
strlen retorna o comprimento da string
strcpy copia uma string
declarações;
strcat concatenar duas strings
}
strcmp compara duas strings
fazer
converte uma string em um valor longo
durante isso converte um número inteiro em uma string
{
declarações;
A string Funções de Membro da Classe
} while (condição);
anexar acrescenta novo conteúdo à string
para (inicialização; condição; inserir insere novo conteúdo na string
ajustamento) em recupera caractere da string
{ [] operador de subscrito de string
declarações; retorna o comprimento da string.
} comprimento substr retorna uma substring da string

Site complementar: www.cs.armstrong.edu/liang/cpp3e


Machine Translated by Google

ACESSO ON-LINE

Obrigado por adquirir uma nova cópia de Introdução à Programação com C++, Terceira Edição.
Seu livro inclui seis meses de acesso pré-pago ao site complementar do livro. Esta assinatura pré-paga oferece
acesso total às seguintes áreas de suporte ao aluno:

Use uma moeda para raspar o revestimento e revelar o código de acesso do aluno.
Não use faca ou outro objeto pontiagudo, pois pode danificar o código.

Para acessar a Introdução à Programação com C++, Terceira Edição


vez, você precisará se registrar on-line usando um computador com conexão à Internet e um navegador da
web. O processo leva apenas alguns minutos e só precisa ser concluído uma vez.

1. Acesse www.pearsoninternationaleditions.com/liang 2. Clique


em Site complementar.
3. Clique no botão Registrar .
4. Na página de inscrição, insira seu código de acesso de estudante* encontrado abaixo do painel de raspar.
Não digite os travessões. Você pode usar letras minúsculas ou maiúsculas.

5. Siga as instruções na tela. Se precisar de ajuda a qualquer momento durante o processo de registro on-line,
basta clicar no link Precisa de ajuda? ícone.
6.
Introdução à programação com site complementar C++!

Para fazer login depois de se registrar:


Você só precisa se registrar neste site complementar uma vez. Depois disso, você poderá fazer login a qualquer
momento em www.pearsoninternationaleditions.com/
liang solicitado.

*Importante: O código de acesso só pode ser utilizado uma vez. Esta assinatura é válida por seis meses após
a ativação e não é transferível. Se este código de acesso já tiver sido revelado, poderá já não ser válido.
Machine Translated by Google

Referência rápida de C++

se declarações instruções de troca Matriz/inicializador

se (condição) { switch (intExpressão) lista interna [10];


{ lista interna [] = {1, 2, 3, 4};
declarações; valor do caso 1:
} declarações; Matriz/inicializador multidimensional
quebrar;
... lista interna [10][15];
se (condição) { caso Valente: lista interna [2][2] = {{1, 2}, {3, 4}};
declarações;
declarações; quebrar;
} padrão:
Criação/exclusão de memória dinâmica
outro declarações;
{ } int* p1 = novo int;
declarações; int* p2 = novo int[10]; excluir p1;
}
excluir [] p2;

if (condição1) {

declarações; Funções usadas com frequência


}
senão se (condição2) time(0) retorna a hora atual
{ srand(seed) define uma nova semente para gerar números aleatórios
declarações; rand() retorna um número inteiro aleatório
} pow(a, b) retorna ab
outro
Funções de personagem
{
declarações;
isdigit(c) retorna verdadeiro se c for um dígito.
}
isalpha(c) retorna verdadeiro se c for uma letra.
isalnum(c) retorna verdadeiro se c for uma letra ou um dígito.
islower(c) retorna verdadeiro se c for uma letra minúscula.
isupper(c) retorna verdadeiro se c for uma letra maiúscula.
tolower(c) retorna letras minúsculas para c.
toupper(c) retorna uma letra maiúscula para c.
Declarações de Loop
Funções de string C
enquanto (condição) {
strlen retorna o comprimento da string
strcpy copia uma string
declarações;
strcat concatenar duas strings
}
strcmp compara duas strings
fazer
converte uma string em um valor longo
durante isso converte um número inteiro em uma string
{
declarações;
A string Funções de Membro da Classe
} while (condição);
anexar acrescenta novo conteúdo à string
para (inicialização; condição; inserir insere novo conteúdo na string
ajustamento) em recupera caractere da string
{ [] operador de subscrito de string
declarações; retorna o comprimento da string.
} comprimento substr retorna uma substring da string

Site complementar: www.cs.armstrong.edu/liang/cpp3e


Machine Translated by Google

acompanhe a programação
Através do poder da prática e da personalização imediata

feedback, MyProgrammingLab melhora seu desempenho.

Contribuições da Edição Internacional por


Anisha Sharma

MyProgrammingLab™
Saiba mais em www.myprogramminglab.com

SEMPRE APRENDENDO PEARSON


Diretora Editorial: Márcia Horton
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Introdução a

Programação
Com

C++ Terceira edição

Contribuições da Edição Internacional por

Mohit P. Tahiliani
NITK Surathkal

Y. Daniel Liang
Universidade Estadual do Atlântico Armstrong

Salão Prentice
Upper Saddle River Boston Columbus São Francisco Nova York
Indianápolis Londres Toronto Sydney Cingapura Tóquio Montreal
Dubai Madrid Hong Kong Cidade do México Munique Paris Amsterdã Cidade do Cabo
Machine Translated by Google

Diretora Editorial: Márcia Horton Editor de aquisições, edição internacional: Sandhya Ghoshal
Editor Executivo: Tracy Johnson Administrador de publicação, edição internacional: Hema Mehta
Editora Associada: Carole Snyder Editor do Projeto, Edição Internacional: Karthik Subramanian
Assistente Editorial: Jenah Blitz-Stoehr Supervisor de Operações: Nick Sklitsis
Diretora de Marketing: Christy Lesko Compradora de manufatura: Lisa McDowell
Gerente de Marketing: Yez Alayan Diretor de Arte: Anthony Gemmellaro
Coordenadora de Marketing: Kathryn Ferranti Designer de texto e capa: Anthony Gemmellaro
Diretor de Produção: Erin Gregg Gerente, Pesquisa Visual: Karen Sanatar
Editor-chefe: Scott Disanno Gerente, Direitos e Permissões: Michael Joyce
Gerente de Projeto de Produção: Kayla Smith-Tarbox Coordenador de permissão de texto: Brian Wysock
Editora, Edição Internacional: Angshuman Chakraborty Arte da capa: imagens Tetra/imagens brilhantes
Administrador Editorial e Analista de Negócios, Gerente Líder de Projetos de Mídia: Renata Buetera
Edição Internacional: Shokhi Shah Khandelwal Gerenciamento de projetos de serviço completo: Laserwords
Editor associado de impressão e mídia, edição internacional: Impressora de capa: Lehigh-Phoenix Color/Hagerstown
Anuprova Dey Chowdhury

Pearson Educação Limitada


Portão de Edimburgo
Harlow
Essex CM20 2JE
Inglaterra

e empresas associadas em todo o mundo

Visite-nos na World Wide Web em:


www.pearsoninternationaleditions.com

© Pearson Education Limited 2014

Os direitos de Y. Daniel Liang de ser identificado como autor deste trabalho foram reivindicados por ele de acordo com a Lei de Direitos Autorais, Designs e Patentes de 1988.

Adaptação autorizada da edição dos Estados Unidos, intitulada Introdução à Programação com C++, Terceira Edição, ISBN 978-0-13-325281-1, de Y. Daniel Liang, publicada pela Pearson
Education © 2014.

Todos os direitos reservados. Nenhuma parte desta publicação pode ser reproduzida, armazenada em um sistema de recuperação ou transmitida de qualquer forma ou por qualquer meio,
eletrônico, mecânico, fotocópia, gravação ou outro, sem a permissão prévia por escrito do editor ou uma licença que permita cópia restrita. no Reino Unido, emitido pela Copyright Licensing
Agency Ltd, Saffron House, 6–10 Kirby Street, Londres EC1N 8TS.

Todas as marcas registradas aqui utilizadas são de propriedade de seus respectivos proprietários. O uso de qualquer marca
registrada neste texto não confere ao autor ou editor quaisquer direitos de propriedade de marca registrada em tais marcas
registradas, nem o uso de tais marcas registradas implica qualquer afiliação ou endosso deste livro por tais proprietários.

A Microsoft e/ou seus respectivos fornecedores não fazem declarações sobre a adequação das informações contidas nos documentos e gráficos relacionados publicados como parte dos
serviços para qualquer finalidade. Todos esses documentos e gráficos relacionados são fornecidos “no estado em que se encontram”, sem qualquer tipo de garantia. A Microsoft e/ou seus
respectivos fornecedores se isentam de todas as garantias e condições relacionadas a essas informações, incluindo todas as garantias e condições de comercialização, sejam elas expressas,
implícitas ou estatutárias, adequação a uma finalidade específica, título e não violação. Em nenhum caso a Microsoft e/ou seus respectivos fornecedores serão responsáveis por quaisquer
danos especiais, indiretos ou consequenciais ou quaisquer danos resultantes de perda de uso, dados ou lucros, seja em uma ação contratual, negligência ou outra ação ilícita, decorrente de de
ou em conexão com o uso ou desempenho de informações disponíveis nos serviços.

Os documentos e gráficos relacionados aqui contidos podem incluir imprecisões técnicas ou erros tipográficos. Alterações são adicionadas periodicamente às informações aqui contidas. A
Microsoft e/ou seus respectivos fornecedores podem fazer melhorias e/ou alterações nos produtos e/ou programas aqui descritos a qualquer momento. Capturas de tela parciais podem ser
visualizadas na íntegra na versão de software especificada.

Microsoft® e Windows® são marcas registradas da Microsoft Corporation nos EUA e em outros países. Este livro não é patrocinado, endossado ou afiliado à Microsoft Corporation.

ISBN 10: 0-273-79324-1


ISBN 13: 978-0-273-79324-3

Dados de catalogação na publicação da Biblioteca Britânica


Um registro de catálogo para este livro está disponível na Biblioteca Britânica

10 9 8 7 6 5 4 3 2 1 14
13 12 11 10

Composto em tempos por Laserwords

Impresso e encadernado por Courier Kendalville nos Estados Unidos da América A política da
editora é usar papel fabricado a partir de florestas sustentáveis.
Machine Translated by Google

Este livro é dedicado aos meus atuais e ex-alunos


de C++. Você me inspirou e me ajudou a
continuar a melhorar este livro.

Para Samantha, Michael e Michelle


Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Caro leitor,
Muitos de vocês forneceram comentários sobre edições anteriores de Introdução à Programação com C++, e
seus comentários e sugestões melhoraram muito o livro. Esta edição foi substancialmente aprimorada em
termos de apresentação, organização, exemplos, exercícios e suplementos – incluindo o seguinte:

n Seções e capítulos reorganizados apresentam assuntos em uma ordem lógica

n Muitos novos exemplos e exercícios interessantes estimulam o interesse

n A introdução do tipo string no Capítulo 4 permite que os alunos escrevam programas usando
cordas cedo

n Os pontos-chave no início de cada seção destacam conceitos e materiais importantes

n Pontos de verificação no final de cada seção verificam a compreensão do aluno sobre o material
abordado

Visite www.cs.armstrong.edu/liang/cpp3e/correlation.html para obter uma lista completa de novos recursos, o que há de novo?

bem como correlações com a edição anterior.


Este livro ensina programação usando um método orientado a problemas que se concentra na resolução
de problemas em vez da sintaxe. Tornamos a programação introdutória interessante usando problemas
instigantes em um contexto amplo. O fio condutor dos primeiros capítulos é a resolução de problemas. Sintaxe
e bibliotecas apropriadas são introduzidas para permitir que os leitores escrevam programas para resolver
problemas. Para apoiar o ensino de programação orientado a problemas, o livro oferece uma ampla variedade
de problemas em vários níveis de dificuldade para motivar os alunos. Para atrair estudantes de todas as
especialidades, os problemas abrangem muitas áreas de aplicação, incluindo matemática, ciências, negócios,
finanças, jogos e animação.
O livro se concentra primeiro nos fundamentos, introduzindo conceitos e técnicas básicas de programação orientado pelo problema

antes de projetar classes personalizadas. Os conceitos e técnicas fundamentais de loops, funções e arrays são
a base da programação. Construir essa base sólida prepara os alunos para aprender programação orientada a
objetos e programação C++ avançada.
Este livro ensina C++. Os fundamentos da resolução de problemas e da programação são os mesmos, fundamentos primeiro

independentemente da linguagem de programação que você usa. Você pode aprender programação usando
qualquer linguagem de programação de alto nível, como Python, Java, C++ ou C#. Depois de saber programar
em uma linguagem, é fácil aprender outras linguagens, porque as técnicas básicas para escrever programas
são as mesmas.
A melhor forma de ensinar programação é através do exemplo, e a única forma de aprender programação exemplos e exercícios

é fazendo. Os conceitos básicos são explicados por meio de exemplos e muitos exercícios com vários níveis
de dificuldade são fornecidos para os alunos praticarem. Em nossos cursos de programação, atribuímos
exercícios de programação após cada aula.
Nosso objetivo é produzir um texto que ensine resolução de problemas e programação em um contexto
amplo, usando uma ampla variedade de exemplos interessantes. Se você tiver algum comentário ou sugestão
para melhorar este livro, envie-me um e-mail.

Sinceramente,

Y. Daniel Liang
y.daniel.liang@gmail.com
www.cs.armstrong.edu/liang
www.pearsoninternationaleditions.com/liang

7
Machine Translated by Google

Prefácio
O que há de novo nesta edição?
Esta terceira edição melhora substancialmente a Introdução à Programação com C++, Segunda Edição. As principais
melhorias são as seguintes:

revisão completa n Uma revisão completa para melhorar a clareza, apresentação, conteúdo, exemplos e exercícios

novos exemplos e exercícios n Novos exemplos e exercícios para motivar e estimular o interesse dos alunos pela programação

Pontos chave n Pontos-chave que destacam os conceitos importantes abordados em cada seção

Pontos de verificação n Pontos de verificação que fornecem perguntas de revisão para ajudar os alunos a acompanhar seu progresso de aprendizagem
e avaliar seu conhecimento sobre um conceito ou exemplo importante

Notas de vídeo n Novos VideoNotes que fornecem pequenos tutoriais em vídeo projetados para reforçar os principais conceitos

objetos de string mais cedo n Introdução de objetos string no Capítulo 4 para permitir que strings sejam usadas na parte inicial
do livro

IO simples cedo n Introdução de entrada e saída simples no Capítulo 4 para permitir que os alunos escrevam programas
usando arquivos antecipadamente

funções em um capítulo n Inclusão de funções no Capítulo 6, que agora cobre todas as questões relacionadas às funções

seções de erros comuns n Seções de capítulo sobre erros e armadilhas comuns para afastar os alunos de erros comuns de programação

n Substituição de exemplos complexos por outros mais simples (por exemplo, Resolvendo o problema do Sudoku no
exemplos simplificados Capítulo 8 é substituído por um problema de verificação se uma solução está correta. A solução completa para o
problema do Sudoku é movida para o Site Complementar.)

eficiência e técnicas do n Bônus expandido O Capítulo 18 apresenta técnicas algorítmicas: programação dinâmica, divisão e conquista,
algoritmo retrocesso e algoritmo ganancioso com novos exemplos para projetar algoritmos eficientes

C++11 n Introdução de novos recursos C++11 de loops foreach e inferência automática de tipo nos capítulos bônus e de
funções lambda nos suplementos no site complementar

Características Pedagógicas
O livro usa os seguintes elementos para ajudar os alunos a aproveitar ao máximo o material:

n O capítulo Objetivos lista o que os alunos devem aprender para que possam determinar se
eles atingiram esses objetivos após concluir o capítulo.

n O capítulo Introdução abre a discussão com problemas representativos para dar ao leitor uma visão geral do que
esperar.

n Os pontos principais destacam os conceitos importantes abordados em cada seção.

n Os Pontos de Verificação fornecem perguntas de revisão para ajudar os alunos a acompanhar seu progresso à
medida que leem o capítulo e avaliam seu aprendizado.

n Problemas e estudos de caso, cuidadosamente escolhidos e apresentados em um estilo fácil de acompanhar,


ensinam conceitos de programação e resolução de problemas. O livro usa muitos exemplos pequenos, simples e
estimulantes para apresentar ideias importantes.

n O Resumo do Capítulo analisa os assuntos importantes que os alunos devem compreender


e lembre-se. Isso os ajuda a reforçar os conceitos-chave do capítulo.

8
Machine Translated by Google

Prefácio 9

n Os questionários de autoteste estão disponíveis on-line no MyProgrammingLab (www.myprogramminglab


.com) para que os alunos façam autotestes sobre conceitos e técnicas de programação.

n Os exercícios de programação, agrupados por seções, oferecem aos alunos oportunidades de aplicar suas
habilidades recém-adquiridas. O nível de dificuldade é classificado como fácil (sem asterisco), moderado (*),
difícil (**) ou desafiador (***). O truque para aprender programação é praticar, praticar e praticar. Para esse
fim, o livro fornece vários exercícios.

n Notas, Dicas, Cuidados e Notas Pedagógicas são inseridas ao longo do texto para oferecer conselhos
valiosos e informações sobre aspectos importantes do desenvolvimento do programa.

Observação

Fornece informações adicionais sobre o assunto e reforça conceitos importantes.

Dica
Ensina bons estilos e práticas de programação.

Cuidado
Ajuda os alunos a evitar as armadilhas dos erros de programação.

Nota Pedagógica
Dá conselhos sobre como usar os materiais do livro de maneira eficaz.

Pedidos flexíveis de capítulos


O livro oferece ordenações flexíveis de capítulos, conforme mostrado no diagrama a seguir:

Capítulo 1 Introdução ao Capítulo 9 Objetos e Classes Capítulo 17 Recursão


Computadores, programas e C++

Capítulo 10 Orientado a Objetos Capítulo 18 Desenvolvendo Eficiência


Capítulo 2 Elementar Pensamento Algoritmos
Programação

Capítulo 11 Ponteiros e Dinâmicos Capítulo 19 Classificação


Capítulo 3 Seleções Gerenciamento de memória

Capítulo 4 Funções Matemáticas, Capítulo 12 Modelos, vetores e pilhas Capítulo 20 Listas vinculadas, filas e filas
Caracteres e Strings prioritárias

Capítulo 5 Loops Capítulo 13 Entrada e Saída de Arquivo Capítulo 21 Árvores de pesquisa binária

Os capítulos
Capítulo 6 Funções Capítulo 22 Contêineres STL 18–26 são
Capítulo 14 Sobrecarga do Operador
bônus

Capítulo 23 Algoritmos STL capítulos


Capítulo 7 Unidimensional
Capítulo 15 Herança e postados no
Matrizes e strings C livro
Polimorfismo
Capítulo 24 Gráficos e Companheiro
Formulários Local na rede Internet
Capítulo 8 Multidimensional
Matrizes Capítulo 16 Tratamento de Exceções
Capítulo 25 Gráficos Ponderados e
Formulários

Capítulo 26 Árvores AVL e


Espalhar árvores
Machine Translated by Google

10 Prefácio

Organização do Livro
Os capítulos podem ser agrupados em três partes, que juntas formam uma introdução sólida à resolução de
problemas e à programação usando C++.

Parte I: Fundamentos de Programação (Capítulos 1–8)

Esta parte é um trampolim que prepara você para embarcar na jornada de aprendizagem de programação com C+
+. Você começará a conhecer C++ (Capítulo 1) e aprenderá técnicas elementares de programação com tipos de
dados primitivos, expressões e operadores (Capítulo 2), instruções de seleção (Capítulo 3), funções matemáticas,
caracteres e strings (Capítulo 4), loops (Capítulo 5), funções (Capítulo 6) e matrizes (Capítulos 7–8).

Parte II: Programação Orientada a Objetos (Capítulos 9–16)

Esta parte apresenta a programação orientada a objetos. C++ é uma linguagem de programação orientada a
objetos que usa abstração, encapsulamento, herança e polimorfismo para fornecer grande flexibilidade,
modularidade e capacidade de reutilização no desenvolvimento de software. Você aprenderá a programar com
objetos e classes (Capítulo 9); aulas de design (Capítulo 10); explore ponteiros e gerenciamento dinâmico de
memória (Capítulo 11); desenvolver classes genéricas usando modelos (Capítulo 12); use classes IO para entrada
e saída de arquivos (Capítulo 13); usar operadores para simplificar funções (Capítulo 14); definir classes a partir
de classes base (Capítulo 15); e criar programas robustos usando tratamento de exceções (Capítulo 16).

Parte III: Algoritmos e Estruturas de Dados (Capítulo 17 e Capítulos Bônus 18–26)

Esta parte apresenta os principais assuntos de um curso típico de estruturas de dados. O Capítulo 17 introduz a
recursão para escrever funções para resolver problemas inerentemente recursivos. O Capítulo 18 apresenta como
medir a eficiência do algoritmo para escolher um algoritmo apropriado para aplicações. O Capítulo 19 apresenta
vários algoritmos de classificação e analisa suas complexidades. Você aprenderá como projetar e implementar
listas vinculadas, filas e filas prioritárias no Capítulo 20. O Capítulo 21 apresenta árvores de pesquisa binária. Os
capítulos 22 e 23 cobrem a biblioteca de modelos padrão em C++. Os capítulos 24 e 25 apresentam algoritmos e
aplicações de grafos.
O Capítulo 26 apresenta árvores de busca binária balanceadas.

Ferramentas de desenvolvimento C++


Você pode usar um editor de texto, como o Bloco de Notas do Windows ou o WordPad, para criar programas C++
e pode compilar e executar os programas a partir da janela de comando. Você também pode usar uma ferramenta
de desenvolvimento C++, como Visual C++ ou Dev-C++. Essas ferramentas oferecem suporte a um ambiente de
desenvolvimento integrado (IDE) para o rápido desenvolvimento de programas C++. Edição, compilação,
construção, execução e depuração de programas são integrados em uma interface gráfica de usuário.
O uso eficaz dessas ferramentas aumentará muito a produtividade da sua programação. A criação, compilação e
execução de programas usando Visual C++ e Dev-C++ são apresentadas nos suplementos do site complementar.
Os programas deste livro foram testados no Visual C++ 2012 e no compilador GNU C++.

Prática e avaliação on-line


com MyProgrammingLab™
MyProgrammingLab ajuda os alunos a compreender totalmente a lógica, a semântica e a sintaxe da programação.
Através de exercícios práticos e feedback imediato e personalizado, o MyProgrammingLab melhora a competência
de programação de alunos iniciantes que muitas vezes lutam com os conceitos básicos e paradigmas de linguagens
de programação populares de alto nível.
Uma ferramenta de auto-estudo e lição de casa, um curso MyProgrammingLab consiste em centenas de
pequenos problemas práticos organizados em torno da estrutura deste livro. Para os estudantes, o
Machine Translated by Google

Prefácio 11

O sistema detecta automaticamente erros na lógica e na sintaxe dos envios de código e oferece dicas
específicas que permitem aos alunos descobrir o que deu errado e por quê. Para os instrutores, um boletim
de notas abrangente rastreia as respostas corretas e incorretas e armazena o código inserido pelos alunos
para revisão.
MyProgrammingLab é oferecido aos usuários deste livro. Para uma demonstração completa, para ver
o feedback de instrutores e alunos ou para começar a usar o MyProgrammingLab em seu curso, visite
www.myprogramminglab.com.

Site de recursos para estudantes


O site de recursos para estudantes, acessível em www.pearsoninternationaleditions.com/liang, contém o
seguinte:

n Respostas para pontos de verificação

n Soluções para exercícios de programação pares

n Código fonte para os exemplos

n Animações de algoritmo

n Erro

Suplementos
O texto cobre os assuntos essenciais. Os suplementos ampliam o texto para introduzir tópicos adicionais
que possam ser de interesse dos leitores. Os suplementos estão disponíveis no Site Companion
(www.pearsoninternationaleditions.com/liang).

Site de recursos do instrutor


O site de recursos para instrutores, acessível em www.pearsoninternationaleditions.com/liang, contém o
seguinte:

n Slides do Microsoft PowerPoint com botões interativos para visualizar o código-fonte colorido e com
sintaxe destacada e para executar programas sem sair dos slides.

n Soluções para todos os exercícios de programação. Os alunos têm acesso às soluções de


exercícios de programação numerados.

n Exames exemplares. A maioria dos exames tem quatro partes:

n Perguntas de múltipla escolha ou de resposta curta

n Corrigir erros de programação

n Rastrear programas

n Escrever programas

n Projetos. Em geral, cada projeto dá uma descrição e pede aos alunos que analisem, desenhem e
implementem o projeto.

Alguns alunos solicitaram os materiais no site de recursos do instrutor. Por favor


entenda que estes são apenas para instrutores e tais solicitações não serão atendidas.

Notas de vídeo
Vinte por cento das VideoNotes desta edição são novas! As VideoNotes foram introduzidas na edição Nota de vídeo

anterior para fornecer ajuda adicional, apresentando exemplos de tópicos importantes e para mostrar como
resolver problemas completamente, desde o design até a codificação. VideoNotes pode
Machine Translated by Google

12 Prefácio

ser acessado no site complementar do livro usando o código de acesso do aluno impresso na capa interna
deste livro. Se você tiver um livro usado, poderá adquirir acesso às VideoNotes e outros conteúdos
premium através do link Comprar no site complementar (www.pearsoninternationaleditions.com/liang).

Agradecimentos
Gostaria de agradecer à Armstrong Atlantic State University por me permitir ensinar o que escrevo e por
me apoiar a escrever o que ensino. O ensino é a fonte de inspiração para continuar a melhorar o livro. Sou
grato aos instrutores e alunos que ofereceram comentários, sugestões, relatórios de bugs e elogios.

Este livro foi bastante aprimorado graças às excelentes críticas desta e das edições anteriores. Os
seguintes revisores contribuíram: Anthony James Allevato (Virginia Tech); Alton B. Coalter (Universidade
do Tennessee, Martin); Linda Cohen (Forsyth Tech); Frank David Ducrest (Universidade de Louisiana,
Lafayette); Waleed Farag (Universidade de Indiana da Pensilvânia); Max I. Fomitchev (Universidade
Estadual da Pensilvânia); Jon Hanrath (Instituto de Tecnologia de Illinois); Michael Hennessy (Universidade
de Oregon); Debbie Kaneko (Universidade Old Dominion); Henry Ledgard (Universidade de Toledo); Brian
Linard (Universidade da Califórnia, Riverside); Dan Lipsa (Universidade Estadual de Armstrong Atlantic);
Jayantha Herath (Universidade Estadual de St. Cloud); Daqing Hou (Universidade Clarkson); Hui Liu
(Universidade Estadual do Missouri); Ronald Marsh (Universidade de Dakota do Norte); Peter Maurer
(Universidade Baylor); Jay McCarthy (Universidade Brigham Young); Jay D. Morris (Universidade Old
Dominion); Charles Nelson (Rock Valley College); Ronald Del Porto (Universidade Estadual da Pensilvânia);
Mitch Pryor (Universidade do Texas); Martha Sanchez (Universidade do Texas em Dallas); William B.
Seales (Universidade de Kentucky); Kate Stewart (Faculdade Comunitária de Tallahassee); Ronald Taylor
(Universidade Estadual de Wright); Matthew Tennyson (Universidade Bradley); David Topham (Ohlone
College); Margaret Tseng (Montgomery College); e Barbara Tulley (Elizabethtown College).

É um grande prazer, uma honra e um privilégio trabalhar com a Pearson. Gostaria de agradecer a
Tracy Johnson e seus colegas Marcia Horton, Carole Snyder, Yez Alayan, Scott Disanno, Kayla Smith-
Tarbox, Gillian Hall e seus colegas por organizarem, produzirem e promoverem este projeto.

Como sempre, estou em dívida com minha esposa, Samantha, por seu amor, apoio e incentivo.
Os editores desejam agradecer a Moumita Mitra (Manna), do Bangabasi College, Calcutá, pela revisão
do conteúdo da Edição Internacional.
Machine Translated by Google

Breve Conteúdo
1 Introdução aos Computadores,
20 listas vinculadas, filas e
Programas e C++ 21
Filas prioritárias 20-1
2 Programação Elementar 49
21 árvores de pesquisa binária 21-1
3 seleções 91 22 contêineres STL 22-1
4 Funções matemáticas, caracteres e strings
23 Algoritmos STL 23-1
137
24 Gráficos e Aplicações 24-1
5 voltas 175
25 Gráficos e Aplicações Ponderadas 25-1
6 funções 227
26 Árvores AVL e Árvores Splay 26-1
7 matrizes unidimensionais e strings C 285
8 Matrizes Multidimensionais 329
9 Objetos e Classes 361 Apêndices
10 Pensamento Orientado a Objetos 391
Palavras-chave C ++ 673
11 dicas e dinâmicas B O conjunto de caracteres ASCII 675
Gerenciamento de memória 431
Gráfico de precedência do operador C 677
12 modelos, vetores e pilhas 475
Sistemas Numéricos D 679
13 Entrada e Saída de Arquivo 511
E Operações bit a bit 683
14 Sobrecarga do Operador 543
15 Herança e Polimorfismo 579
16 Tratamento de Exceções 617 Índice 685
17 Recursão 645 Créditos 709

Os seguintes capítulos bônus estão em www


.pearsoninternationaleditions.com/liang.
O acesso a este conteúdo premium requer um código de
acesso de estudante válido. Instruções sobre como obter um
código de acesso são fornecidas no site complementar.

18 Desenvolvendo Algoritmos Eficientes 18-1


19 Classificação 19-1

13
Machine Translated by Google

Conteúdo
Capítulo 1 Introdução aos Computadores,
Programas e C++ 21

1.1 Introdução 1.2 22


O que é um computador? 22
1.3 Linguagens de programação 29
1.4 Sistemas operacionais 32
1.5 História do C++ 1.6 33
Um programa C++ simples 1.7 34
Ciclo de desenvolvimento de programa C+ 38
+ 1.8 Estilo de programação e documentação 1.9 40
Erros de programação 41

Capítulo 2 Programação Elementar 49


2.1 Introdução 2.2 50
Escrevendo um programa simples 50
2.3 Lendo entradas do teclado 2.4 52
Identificadores 55
2.5 Variáveis 2.6 55
Instruções de atribuição e expressões de atribuição
2.7 57
Constantes nomeadas 2.8 59
Tipos de dados numéricos e operações 2.9 60
Avaliando expressões e precedência de operadores 2.10 65
Estudo de caso: exibindo a hora atual 2.11 Operadores 67
de atribuição aumentados 2.12 Operadores de 69
incremento e decremento 2.13 Conversões de 70
tipo numérico 2.14 Processo de 72
desenvolvimento de software 2.15 Estudo 75
de caso: contagem de unidades monetárias 2.16 79
Erros comuns 81

Capítulo 3 Seleções 91
3.1 Introdução 3.2 92
O tipo de dados bool 3.3 92
Instruções if 3.4 93
Instruções if-else bidirecionais 3.5 96
Instruções if-else aninhadas e multidirecionais 3.6 Erros 97
comuns e armadilhas 3.7 Estudo de 99
caso: Calculando o índice de massa corporal 3.8 104
Estudo de caso: Computando impostos 106
3.9 Gerando Números Aleatórios 3.10 109
Operadores Lógicos 3.11 111
Estudo de Caso: Determinando Ano Bissexto 114
3.12 Estudo de Caso: Loteria 115
3.13 Instruções switch 3.14 117
Expressões Condicionais 3.15 121
Precedência e Associatividade de Operadores 3.16 122
Depuração 124

14
Machine Translated by Google

Conteúdo 15

Capítulo 4 Funções Matemáticas,


Caracteres e Strings 137

4.1 Introdução 4.2 138


Funções matemáticas 4.3 Tipos 138
de dados de caracteres e operações 4.4 Estudo 142
de caso: Gerando caracteres aleatórios 4.5 Estudo de 146
caso: Adivinhando aniversários 4.6 Funções 148
de caracteres 4.7 Estudo de 151
caso: Convertendo um dígito hexadecimal em um valor
decimal 4.8 O tipo de 153
string 4.9 Estudo de caso: 154
revisando a loteria Programar Usando Strings 4.10 Formatando Saída 158
do Console 4.11 Entrada e Saída 160
Simples de Arquivo 164

Capítulo 5 Loops 175

5.1 Introdução 5.2 O 176


loop while 5.3 O loop do- 176
while 5.4 O loop for 5.5 Qual 188
loop usar? 191
194
5.6 Loops aninhados 196
5.7 Minimizando erros numéricos 5.8 198
Estudos de caso 5.9 199
Palavras-chave quebram e continuam 5.10 205
Estudo de caso: verificando palíndromos 5.11 208
Estudo de caso: exibindo números primos 210

Capítulo 6 Funções 227

6.1 Introdução 6.2 228


Definindo uma função 6.3 229
Chamando uma função 6.4 230
Funções void 6.5 232
Passando argumentos por valor 6.6 235
Modularizando código 6.7 236
Sobrecarregando funções 6.8 238
Protótipos de funções 6.9 241
Argumentos padrão 6.10 243
Funções inline Variáveis 244
locais, globais e estáticas 6.11 6.12 Passando 245
argumentos por referência 6.13 Referência 250
constante Parâmetros 6.14 Estudo de caso: 259
Convertendo Hexadecimais em Decimais 6.15 Abstração de 259
Funções e Refinamento Stepwise 262

Capítulo 7 Matrizes unidimensionais


e strings C 285

7.1 Introdução 7.2 286


Noções básicas de 287
array 7.3 Problema: números de loteria 293
Machine Translated by Google

16 Conteúdo

7.4 Problema: Baralho de cartas 296


7.5 Passando arrays para funções 298
7.6 Prevenindo mudanças de argumentos de array
em funções 300
7.7 Retornando arrays de funções 7.8 301
Problema: Contando as ocorrências de cada letra 7.9 304
Pesquisando arrays 7.10 306
Classificando arrays 310
7.11 C-Strings 312

Capítulo 8 Matrizes Multidimensionais 329


8.1 Introdução 8.2 330
Declarando matrizes bidimensionais 8.3 330
Processando matrizes bidimensionais 8.4 331
Passando matrizes bidimensionais para funções 8.5 334
Problema: Avaliando um teste de múltipla escolha 335
8.6 Problema: Encontrando um par mais 337
próximo 8.7 Problema: 339
Sudoku 8.8 Matrizes multidimensionais 342

Capítulo 9 Objetos e Classes 361

9.1 Introdução 9.2 362


Definindo Classes para Objetos 9.3 362
Exemplo: Definindo Classes e Criando Objetos 9.4 364
Construtores 9.5 367
Construindo e Usando Objetos 9.6 368
Separando Definição de Classe da Implementação 9.7 371
Prevenindo Múltiplas Inclusões 9.8 374
Funções Inline em Classes 9.9 375
Encapsulamento de Campo de 376
Dados 9.10 O Escopo das 379
Variáveis 9.11 Abstração de Classe e Encapsulamento 381

Capítulo 10 Pensamento Orientado a Objetos 391


10.1 Introdução 10.2 392
A classe string 10.3 Passando 392
objetos para funções 10.4 Array de 401
objetos 10.5 Instância e 404
membros estáticos 10.6 Funções de 406
membros constantes 10.7 Pensando 410
em objetos 10.8 Composição 412
de objetos 10.9 Estudo de 418
caso: a classe StackOfIntegers 10.10 Diretrizes de 420
design de classe 422

Capítulo 11 Ponteiros e Dinâmicos


Gerenciamento de memória 431
11.1 Introdução 11.2 432
Noções básicas sobre 432
ponteiros 11.3 Definindo tipos sinônimos usando
a palavra-chave typedef 437
11.4 Usando const com ponteiros 11.5 438
Matrizes e ponteiros 11.6 439
Passando argumentos de ponteiro em uma chamada de função 442
Machine Translated by Google

Conteúdo 17

11.7 Retornando um ponteiro de funções 446


11.8 Funções úteis de array 447
11.9 Alocação dinâmica de memória persistente 449
11.10 Criando e acessando objetos dinâmicos 11.11 453
O ponteiro this 11.12 455
Destruidores 11.13 456
Estudo de caso: a classe do curso 11.14 459
Construtores de cópia 11.15 462
Personalizando construtores de cópia 465

Capítulo 12 Modelos, vetores e pilhas 475


12.1 Introdução 12.2 476
Noções básicas de 476
modelos 12.3 Exemplo: uma 480
classificação genérica 482
12.4 Modelos de classe 12.5 489
Melhorando a pilha Classe 12.6 491
O vetor C++ Classe 12.7 Substituindo arrays usando 494
o vetor Classe 12.8 Estudo de caso: avaliando expressões 497

Capítulo 13 Entrada e Saída de Arquivo 511


13.1 Introdução 13.2 512
E/S de texto 512
13.3 Formatando saída 518
13.4 Funções: getline, get e put 13.5 Modos 519
fstream e abertura de arquivo 13.6 522
Testando estados de fluxo 524
13.7 E/S binária 526
13.8 Arquivo de acesso 533
aleatório 13.9 Atualizando arquivos 536

Capítulo 14 Sobrecarga do Operador 543

14.1 Introdução 14.2 544


A Classe Rational 14.3 544
Funções do Operador 14.4 550
Sobrecarregando o Operador Subscrito [] 552
14.5 Sobrecarregando operadores de atribuição aumentada 554
14.6 Sobrecarregando os operadores 555
unários 14.7 Sobrecarregando os operadores + 555
+ e –– 14.8 Funções amigas e classes amigas 557
14.9 Sobrecarregando os operadores << e >> 559
14.10 Conversões automáticas de tipo 561
14.11 Definindo funções não membros para
Sobrecarregando Operadores 562
14.12 A classe Rational com operadores de
função 563
sobrecarregados 14.13 Sobrecarregando os operadores = 571

Capítulo 15 Herança e Polimorfismo 579


15.1 Introdução 15.2 580
Classes Base e Classes Derivadas 15.3 580
Programação Genérica 15.4 588
Construtores e Destruidores 15.5 589
Redefinindo Funções 15.6 594
Polimorfismo 595
Machine Translated by Google

18 Conteúdo

15.7 Funções virtuais e ligação dinâmica 15.8 A 596


palavra-chave protegida 15.9 600
Classes abstratas e funções virtuais puras 15.10 601
Casting: static_cast versus dynamic_cast 609

Capítulo 16 Tratamento de Exceções 617


16.1 Introdução 16.2 618
Visão geral do tratamento de exceções 618
16.3 Vantagens do tratamento de 621
exceções 16.4 Classes de 623
exceção 16.5 Classes de exceção 627
personalizadas 16.6 632
Capturas múltiplas 16.7 637
Propagação de exceção 16.8 638
Relançamento de exceções 16.9 640
Especificação de exceção 16.10 Quando usar exceções 641

Capítulo 17 Recursão 645


17.1 Introdução 17.2 646
Exemplo: Fatoriais 17.3 646
Estudo de caso: Números de Fibonacci 650
17.4 Solução de problemas usando 653
recursão 17.5 Funções auxiliares 655
recursivas 17.6 Torres 658
de Hanói 17.7 Oito 662
Rainhas 17.8 Recursão versus 665
Iteração 17.9 Recursão de cauda 665

Os capítulos bônus a seguir estão no site complementar do livro em


www.pearsoninternationaleditions.com/liang.

Capítulo 18 Desenvolvendo Algoritmos Eficientes 18-1

Capítulo 19 Classificação 19-1

Capítulo 20 Listas vinculadas, filas e filas


prioritárias 20-1

Capítulo 21 Árvores de pesquisa binária 21-1

Capítulo 22 Contêineres STL 22-1

Capítulo 23 Algoritmos STL 23-1

Capítulo 24 Gráficos e Aplicações 24-1

Capítulo 25 Gráficos Ponderados e Aplicações 25-1

Capítulo 26 Árvores AVL e Árvores Splay 26-1


Machine Translated by Google

Conteúdo 19

Apêndices
Apêndice A Palavras-chave C++ 673
Apêndice B O conjunto de caracteres ASCII 675
Apêndice C Gráfico de Precedência do Operador 677
Apêndice D Sistemas Numéricos 679
Apêndice E Operações bit a bit 683

Índice 685

Crédito 709
Machine Translated by Google

Notas de vídeo
Nota de vídeo
VideoNotes estão disponíveis em www.pearsoninternationaleditions.com/liang, usando o código de acesso
do aluno impresso na capa interna deste livro.

Capítulo 1 Introdução aos Computadores, Capítulo 9 Objetos e Classes


Programas e C++ Usar aulas 364

Seu primeiro programa C++ 34 Definição de classe separada 371

Compilar e executar C++ 38 A classe de empréstimo 381

Tutorial de Visual C++ 39 A aula de fãs 387

Exibir cinco mensagens 46


Capítulo 10 Pensamento Orientado a Objetos
Capítulo 2 Programação Elementar A classe de string estática 392
versus instância 406
Obtenha informações 52
68 Pensando em objetos 412
Exibir hora atual
76 A classe MyInteger 427
Calcular pagamentos de empréstimos

Calcular IMC 87 Capítulo 11 Ponteiros e memória dinâmica


Seleções Gerenciamento
Capítulo 3
Noções básicas de ponteiro 432
Calcular IMC 104
Passar argumentos de ponteiro 443
Teste de subtração 109
Destruidor e construtor de cópia 456
Classifique três inteiros 128
Retornar um ponteiro 470

Capítulo 4 Funções Matemáticas,


Capítulo 12 Modelos, vetores e pilhas
Caracteres e Strings
Noções básicas de modelos 476
Adivinha aniversário 148
Classe de modelos 482
Formatar saída do console 160
A classe vetorial 491
Grande distância do círculo 169
Use vetor para substituir matrizes 503

Capítulo 5 Loops Capítulo 13 Entrada e Saída de Arquivo


Repita o teste de subtração 178 E/S de texto 514
Adivinhe um número 179 Estados de fluxo de teste 524

Redirecionar entrada e saída 186 E/S binária 526


Ler arquivo 186 Dividir um arquivo grande 540

Exibir cronograma de empréstimo 217


Capítulo 14 Sobrecarga do Operador
Capítulo 6 Funções O que é sobrecarga do operador? 544

A função máxima void 230 A classe Rational 544

vs. função de retorno de valor 232 Sobrecarrega o operador < A 550

Modularizar código 236 classe Complex 576

Passagem por 254


Capítulo 15 Herança e Polimorfismo
referência Refinamento 262
Definir classes derivadas 580
passo a passo 277
Polimorfismo e funções virtuais 595
Encontrar emirp prime Encontrar ponto de interseção 279
Aulas abstratas 601

Capítulo 7 Matrizes unidimensionais e strings A classe MyPoint 616

C Capítulo 16 Tratamento de Exceções


Números de loteria 295 622
Vantagens do tratamento de exceções
Matriz reversa 302 623
Classes de exceção C++
Ordenação por seleção 310 627
Crie classes de exceção personalizadas
Encontre o desvio padrão 320
A classe HexFormatException 643

Capítulo 8 Matrizes Multidimensionais Capítulo 17 Recursão

Processar matrizes 2-D 331 Pesquisa binária 657

Passar argumentos de array 2D 334 Torres de Hanói 658

Pontos mais próximos 337 O problema do GCD 667

Pontos mais próximos 3-D 350 Contar ocorrência 668

20
Machine Translated by Google

CAPÍTULO

1
Introdução
para computadores,
Programas e C++

Objetivos
n Compreender os conceitos básicos de informática, programas e sistemas operacionais
(§§1.2–1.4).
n Descrever a história do C++ (§1.5).

n Escrever um programa C++ simples para saída do console (§1.6).

n Compreender o ciclo de desenvolvimento de programas C++ (§1.7).

n Conhecer o estilo de programação e documentação (§1.8).

n Explicar as diferenças entre erros de sintaxe, erros de tempo de execução e erros


lógicos (§1.9).
Machine Translated by Google

22 Capítulo 1 Introdução a computadores, programas e C++

1.1 Introdução
O tema central deste livro é aprender como resolver problemas escrevendo um programa.
Chave
Apontar
Este livro é sobre programação. Então, o que é programação? O termo programação significa criar (ou desenvolver)
o que é programação? software, também chamado de programa. Em termos básicos, o software contém as instruções que dizem ao computador
programação – ou dispositivo computadorizado – o que fazer.
programa O software está ao seu redor, mesmo em dispositivos que você acha que não precisariam dele. É claro que você
espera encontrar e usar software em um computador pessoal, mas o software também desempenha um papel no
funcionamento de aviões, carros, telefones celulares e torradeiras. Em um computador pessoal, você usa processadores
de texto para escrever documentos, navegadores da Web para explorar a Internet e programas de e-mail para enviar
mensagens. Esses programas são exemplos de software. Os desenvolvedores de software criam software com a ajuda
de ferramentas poderosas chamadas linguagens de programação.
Este livro ensina como criar programas usando a linguagem de programação C++.
Existem muitas linguagens de programação, algumas das quais têm décadas. Cada linguagem foi inventada com um
propósito específico – para aproveitar os pontos fortes de uma linguagem anterior, por exemplo, ou para fornecer ao
programador um conjunto novo e exclusivo de ferramentas. Sabendo que existem muitas linguagens de programação
disponíveis, seria natural que você se perguntasse qual é a melhor.
Mas, na verdade, não existe uma “melhor” linguagem. Cada um tem seus pontos fortes e fracos. Programadores
experientes sabem que uma linguagem pode funcionar bem em algumas situações e outra linguagem pode ser mais
apropriada em outras. Portanto, programadores experientes tentam dominar diversas linguagens de programação, dando-
lhes acesso a um vasto arsenal de ferramentas de desenvolvimento de software.

Se você aprender a programar usando uma linguagem, será fácil aprender outras linguagens. A chave é aprender
como resolver problemas usando uma abordagem de programação. Esse é o tema principal deste livro.

Você está prestes a iniciar uma jornada emocionante: aprender a programar. No início, é útil revisar os princípios
básicos do computador, programas e sistemas operacionais. Se você estiver familiarizado com termos como CPU,
memória, discos, sistemas operacionais e linguagens de programação, poderá pular a revisão nas Seções 1.2–1.4.

1.2 O que é um computador?


Um computador é um dispositivo eletrônico que armazena e processa dados.
Chave
Apontar
Um computador inclui hardware e software. Em geral, o hardware compreende os elementos físicos visíveis de um
hardware computador, e o software fornece as instruções invisíveis que controlam o hardware e o fazem executar tarefas
Programas específicas. Conhecer o hardware do computador não é essencial para aprender uma linguagem de programação, mas
pode ajudá-lo a compreender os efeitos que as instruções de um programa têm sobre um computador e seus
componentes. Esta seção apresenta os componentes de hardware do computador e suas funções.

Um computador consiste nos seguintes componentes principais de hardware (veja a Figura 1.1):

n Unidade central de processamento (CPU)

n Memória (memória principal)

n Dispositivos de armazenamento (como discos e CDs)

n Dispositivos de entrada (como mouse e teclado)

n Dispositivos de saída (como monitores e impressoras)

n Dispositivos de comunicação (como modems e placas de interface de rede)

ônibus Os componentes de um computador são interligados por um subsistema denominado barramento. Pense em um
ônibus como um sistema de estradas que circula entre os componentes do computador; dados e energia viajam ao longo
do barramento de uma parte do computador para outra. Nos computadores pessoais, o barramento é integrado
Machine Translated by Google

1.2 O que é um computador? 23

CPU

Memória

Armazenar
Dispositivos

Ônibus

Entrada
Dispositivos

Saída
Dispositivos

Comunicação
Dispositivos

Figura 1.1 Um computador consiste em CPU, memória, dispositivos de armazenamento, dispositivos de entrada,
dispositivos de saída e dispositivos de comunicação.

a placa-mãe do computador , que é uma caixa de circuito que conecta as partes de um computador, conforme mostrado placa-mãe

na Figura 1.2.

1.2.1 Unidade Central de Processamento


A unidade central de processamento (CPU) é o cérebro do computador. Ele recupera instruções da memória e as Unidade de processamento central (CPU)

executa. A CPU geralmente possui dois componentes: uma unidade de controle e uma unidade aritmética/ lógica. A
unidade de controle controla e coordena as ações dos outros componentes. A unidade aritmética/lógica executa
operações numéricas (adição, subtração, multiplicação e divisão) e operações lógicas (comparações).

As CPUs atuais são construídas em pequenos chips semicondutores de silício que contêm milhões de minúsculos
interruptores elétricos, chamados transistores, para processar informações.
Todo computador possui um relógio interno, que emite pulsos eletrônicos a uma taxa constante.
Esses pulsos são usados para controlar e sincronizar o ritmo das operações. Uma velocidade de clock mais alta velocidade

permite que mais instruções sejam executadas em um determinado período. A unidade de medida da velocidade do
clock é o hertz (Hz), com 1 hertz equivalendo a 1 pulso por segundo. Na década de 1990, os computadores mediam a Hertz (Hz)
velocidade do clock em megahertz (MHz), mas a velocidade da CPU tem melhorado continuamente, e a velocidade megahertz (MHz)
do clock de um computador agora é normalmente expressa em gigahertz (GHz). Os mais novos processadores da Intel gigahertz (GHz)
funcionam a cerca de 3 GHz.
Machine Translated by Google

24 Capítulo 1 Introdução a computadores, programas e C++

CPU é colocada
sob o ventilador

Memória

Placa-mãe

Figura 1.2 A placa-mãe conecta as peças de um computador.

essencial As CPUs foram originalmente desenvolvidas com um núcleo. O núcleo é a parte do processador que realiza a leitura e
execução das instruções. Para aumentar o poder de processamento da CPU, os fabricantes de chips agora produzem CPUs
que contêm múltiplos núcleos. Uma CPU multicore é um componente único com dois ou mais núcleos independentes. Os
computadores de consumo atuais normalmente têm dois, três ou até quatro núcleos separados. Em breve, CPUs com
dezenas ou até centenas de núcleos estarão acessíveis.

1.2.2 Bits e Bytes


Antes de discutirmos a memória, vejamos como as informações (dados e programas) são armazenadas em um computador.

Um computador nada mais é do que uma série de interruptores. Cada switch existe em dois estados: ligado ou desligado.
Armazenar informações em um computador é simplesmente uma questão de ligar ou desligar uma sequência de interruptores.
Se a chave estiver ligada, seu valor será 1. Se a chave estiver desligada, seu valor será 0. Esses 0s e 1s são interpretados
pedaço
como dígitos no sistema numérico binário e são chamados de bits (dígitos binários).
byte A unidade mínima de armazenamento em um computador é um byte. Um byte é composto por oito bits. Um número
pequeno como 3 pode ser armazenado como um único byte. Para armazenar um número que não cabe em um único byte,
o computador usa vários bytes.
Dados de vários tipos, como números e caracteres, são codificados como uma série de bytes. Como programador, você
não precisa se preocupar com a codificação e decodificação de dados, que o sistema do computador executa
esquema de codificação automaticamente, com base no esquema de codificação. Um esquema de codificação
é um conjunto de regras que rege como um computador traduz caracteres, números e símbolos em dados com os quais o
computador pode realmente trabalhar. A maioria dos esquemas traduz cada caractere em uma sequência pré-determinada
de bits. No popular esquema de codificação ASCII, por exemplo, o caractere C é representado como 01000011 em um byte.

A capacidade de armazenamento de um computador é medida em bytes e múltiplos de bytes, como segue:

quilobytes (KB) n Um quilobyte (KB) equivale a cerca de 1.000 bytes.

megabyte (MB) n Um megabyte (MB) equivale a cerca de 1 milhão de bytes.

gigabytes (GB) n Um gigabyte (GB) equivale a cerca de 1 bilhão de bytes.

terabyte (TB) n Um terabyte (TB) equivale a cerca de 1 trilhão de bytes.


Machine Translated by Google

1.2 O que é um computador? 25

Um documento Word típico de uma página pode ocupar 20 KB. Portanto, 1 MB pode armazenar 50 páginas de documentos
e 1 GB pode armazenar 50.000 páginas de documentos. Um filme típico de alta resolução de duas horas pode ocupar 8 GB,
portanto seriam necessários 160 GB para armazenar 20 filmes.

1.2.3 Memória
A memória de um computador consiste em uma sequência ordenada de bytes para armazenar programas, bem como dados com memória

os quais o programa está trabalhando. Você pode pensar na memória como a área de trabalho do computador para executar um
programa. Um programa e seus dados devem ser movidos para a memória do computador antes de serem executados pela CPU.

Cada byte na memória possui um endereço único, conforme mostrado na Figura 1.3. O endereço é usado para localizar o endereço exclusivo

byte para armazenar e recuperar os dados. Como os bytes da memória podem ser acessados em qualquer ordem, a memória
também é chamada de memória de acesso aleatório (RAM). BATER

Endereço de memória Conteúdo da memória

2000 01000011 Codificação para o caractere 'C'


2001 01110010 Codificação para o caractere 'r'
2002 01100101 Codificação para o caractere 'e'
2003 01110111 Codificação para o caractere 'w'
2004 00000011 Codificação para o número 3

Figura 1.3 A memória armazena dados e instruções de programa em locais de memória com endereçamento exclusivo.

Os computadores pessoais atuais geralmente têm pelo menos 1 GB de RAM, mas é mais comum que tenham de 2 a 4 GB
instalados. De modo geral, quanto mais RAM um computador tiver, mais rápido ele poderá operar, mas há limites para essa
regra simples.
Um byte de memória nunca está vazio, mas seu conteúdo inicial pode não ter sentido para o seu programa.
O conteúdo atual de um byte de memória é perdido sempre que novas informações são colocadas nele.
Assim como a CPU, a memória é construída em chips semicondutores de silício que possuem milhões de transistores
embutidos em sua superfície. Comparados aos chips de CPU, os chips de memória são menos complicados, mais lentos e mais
baratos.

1.2.4 Dispositivos de armazenamento


A memória de um computador (RAM) é uma forma volátil de armazenamento de dados: as informações que foram armazenadas
na memória (ou seja, salvas) são perdidas quando o sistema é desligado. Programas e dados são armazenados permanentemente
em dispositivos de armazenamento e movidos, quando o computador realmente os utiliza, para a memória, que opera em dispositivo de armazenamento

velocidades muito mais rápidas do que os dispositivos de armazenamento permanente.


pode.

Existem três tipos principais de dispositivos de armazenamento:

n Unidades de disco magnético

n Unidades de disco óptico (CD e DVD)

n unidades flash USB


Machine Translated by Google

26 Capítulo 1 Introdução a computadores, programas e C++

dirigir Drives são dispositivos usados para operar um meio de armazenamento, como um disco ou CD. Um meio de
armazenamento armazena fisicamente dados e instruções de programa. A unidade lê dados da mídia e grava dados na
mídia.

Discos

disco rígido Um computador geralmente possui pelo menos uma unidade de disco rígido (veja a Figura 1.4). Os discos rígidos são
usados para armazenar dados e programas permanentemente. Os computadores mais novos possuem unidades de disco
rígido que podem armazenar de 200 a 800 GB de dados. As unidades de disco rígido geralmente ficam dentro do
computador, mas também estão disponíveis discos rígidos removíveis.

Figura 1.4 Uma unidade de disco rígido armazena programas e dados permanentemente.

CDs e DVDs

CD-R CD significa disco compacto. Existem dois tipos de unidades de CD: CD-R e CD-RW. Um CD-R
destina-se ao armazenamento permanente somente leitura; o usuário não pode modificar seu conteúdo depois de gravado.
CD-RW Um CD-RW pode ser usado como um disco rígido; isto é, você pode gravar dados no disco e, em seguida, sobrescrever
esses dados por novos dados. Um único CD pode conter até 700 MB. A maioria dos PCs novos está equipada com uma
unidade de CD-RW que pode funcionar com discos CD-R e CD-RW.
DVD DVD significa disco versátil digital ou disco de vídeo digital. DVDs e CDs são parecidos e você pode usá-los para
armazenar dados. Um DVD pode conter mais informações que um CD; a capacidade de armazenamento de um DVD padrão
é de 4,7 GB. Assim como os CDs, existem dois tipos de DVDs: DVD-R (somente leitura) e DVD-RW (regravável).

Unidades flash USB

Os conectores de barramento serial universal (USB) permitem ao usuário conectar vários dispositivos periféricos ao
computador. Você pode usar um USB para conectar uma impressora, câmera digital, mouse, unidade de disco rígido externa
e outros dispositivos ao computador.
Uma unidade flash USB é um dispositivo para armazenar e transportar dados. É um disco rígido portátil que pode ser
conectado à porta USB do seu computador. Um pen drive é pequeno – mais ou menos do tamanho de um pacote de
chicletes, como mostra a Figura 1.5. Atualmente, unidades flash USB estão disponíveis com capacidade de armazenamento
de até 256 GB.
Machine Translated by Google

1.2 O que é um computador? 27

Figura 1.5 As unidades flash USB são portáteis e podem armazenar muitos dados.

1.2.5 Dispositivos de Entrada e Saída


Os dispositivos de entrada e saída permitem que o usuário se comunique com o computador. Os dispositivos de entrada mais
comuns são teclados e mouses. Os dispositivos de saída mais comuns são monitores e impressoras.

Teclado
Um teclado é um dispositivo para inserir entradas. A Figura 1.6 mostra um teclado típico. Os teclados compactos estão disponíveis
sem teclado numérico.

Inserir

Função

Excluir

Subir página

Página para baixo


Modificador

Teclado numérico

Setas; flechas

Figura 1.6 Um teclado de computador possui teclas usadas para enviar entradas a um computador.

As teclas de função estão localizadas na parte superior do teclado e são precedidas pela letra F. tecla de função
Suas funções dependem do software utilizado.
Uma tecla modificadora é uma tecla especial (como a tecla Shift, Alt ou Ctrl ) que modifica a ação normal de outra tecla tecla modificadora
quando as duas são pressionadas simultaneamente.
O teclado numérico, localizado no lado direito da maioria dos teclados, é um conjunto separado de teclas teclado numérico
estilizado como uma calculadora e usado para inserir números rapidamente.
As teclas de seta, localizadas entre o teclado principal e o teclado numérico, são usadas para mover tecla de seta
o ponteiro do mouse para cima, para baixo, para a esquerda e para a direita na tela em muitos programas. Inserir chave
As teclas Inserir, Excluir, Page Up e Page Down são usadas em processamento de texto e outros programas para inserir texto Excluir chave
e objetos, excluir texto e objetos e mover-se para cima ou para baixo em um documento, uma tela por vez. Tecla de página para cima

Tecla de página para baixo


Machine Translated by Google

28 Capítulo 1 Introdução a computadores, programas e C++

Rato

Um mouse é um dispositivo apontador. É usado para mover um ponteiro gráfico (geralmente em forma de seta)
denominado cursor pela tela ou para clicar em objetos na tela (como um botão) para acioná-los para executar uma ação.

Monitor

O monitor exibe informações (texto e gráficos). A resolução da tela e a densidade dos pontos determinam a qualidade
da exibição.
resolução da tela A resolução da tela especifica o número de pixels nas dimensões horizontais e verticais do dispositivo de exibição.
pixel Pixels (abreviação de “elementos de imagem”) são pequenos pontos que formam uma imagem na tela.
Uma resolução comum para uma tela de 17 polegadas, por exemplo, é de 1.024 pixels de largura e 768 pixels de altura.
A resolução pode ser definida manualmente. Quanto maior a resolução, mais nítida e clara será a imagem.
Tamanho do ponto A distância entre pontos é a quantidade de espaço entre pixels, medida em milímetros. O menor
Quanto maior a densidade dos pontos, mais nítida será a exibição.

1.2.6 Dispositivos de Comunicação


Os computadores podem ser conectados em rede por meio de dispositivos de comunicação, como um modem dial-up
(modulador/ demodulador), um modem DSL ou a cabo, uma placa de interface de rede com fio ou um adaptador sem
fio.

modem discado n Um modem dial-up utiliza uma linha telefônica e pode transferir dados a velocidades de até 56.000 bps (bits
por segundo).

linha de assinante digital (DSL) n Uma conexão de linha de assinante digital (DSL) também usa uma linha telefônica padrão, mas pode
transfira dados 20 vezes mais rápido do que um modem dial-up padrão.

modem a cabo n Um modem a cabo utiliza a linha de TV a cabo mantida pela empresa de cabo e é gerado
geralmente mais rápido que o DSL.

placa de interface de rede (NIC) n Uma placa de interface de rede (NIC) é um dispositivo que conecta um computador a uma rede local (LAN),
rede local (LAN) conforme mostrado na Figura 1.7. LANs são comumente usadas em universidades, empresas e agências
governamentais. Uma NIC de alta velocidade chamada 1000BaseT pode transferir dados a 1.000 milhões
milhões de bits por segundo de bits por segundo (mbps).
(mbps)
n As redes sem fio são hoje extremamente populares em residências, empresas e escolas.
Cada laptop vendido hoje está equipado com um adaptador sem fio que permite que o computador se
conecte a uma rede local e à Internet.

Observação

As respostas aos pontos de verificação estão no site complementar.

1.1 Defina os termos hardware e software.


ÿVerificação de ponto
1.2 Liste os cinco principais componentes de hardware de um computador.

1.3 O que significa a sigla “CPU”?


1.4 Qual unidade é usada para medir a velocidade da CPU?

1.5 O que é um pouco? O que é um byte?


1.6 Para que serve a memória? O que significa RAM? Por que a memória é chamada de RAM?
1.7 Qual unidade é usada para medir o tamanho da memória?
1.8 Qual unidade é usada para medir o tamanho do disco?

1.9 Qual é a principal diferença entre memória e um dispositivo de armazenamento?


Machine Translated by Google

1.3 Linguagens de Programação 29

Placa de interface de rede

Figura 1.7 Uma rede local conecta computadores próximos uns dos outros.

1.3 Linguagens de Programação


Os programas de computador , conhecidos como software, são instruções que informam ao computador o que fazer.
Chave
Apontar
Os computadores não entendem as linguagens humanas; portanto, os programas devem ser escritos em
linguagens que os computadores possam entender. Existem centenas de linguagens de programação,
desenvolvidas para facilitar o processo de programação das pessoas. No entanto, todos os programas devem
ser convertidos em instruções que o computador possa executar.

1.3.1 Linguagem de Máquina


A linguagem nativa de um computador, que difere entre os diferentes tipos de computadores, é a sua
linguagem de máquina – um conjunto de instruções primitivas integradas. Essas instruções estão na forma linguagem de máquina

de código binário; portanto, se você quiser fornecer a um computador uma instrução em seu idioma nativo,
deverá inserir a instrução como código binário. Por exemplo, para somar dois números, talvez seja
necessário escrever uma instrução em código binário, como segue:

1101101010011010

1.3.2 Linguagem Assembly


Programar em linguagem de máquina é um processo tedioso. Além disso, os programas escritos
em linguagem de máquina são muito difíceis de ler e modificar. Por esta razão, a linguagem assembly linguagem assembly

foi criado nos primórdios da computação como uma alternativa às linguagens de máquina. Conjunto
Machine Translated by Google

30 Capítulo 1 Introdução a computadores, programas e C++

A linguagem usa uma palavra descritiva curta, conhecida como mnemônico, para representar cada uma das instruções
da linguagem de máquina. Por exemplo, o mnemônico add normalmente significa adicionar números e sub significa
subtrair números. Para adicionar os números 2 e 3 e obter o resultado, você pode escrever uma instrução em código
assembly como esta:

adicione 2, 3, resultado

As linguagens assembly foram desenvolvidas para facilitar a programação. Entretanto, como o computador não
montador pode executar linguagem assembly, outro programa – chamado assembler – é usado para traduzir programas em
linguagem assembly em código de máquina, como mostra a Figura 1.8.

Arquivo fonte de montagem Arquivo de código de máquina

... ...
Montador 1101101010011010
adicione 2, 3, resultado
... ...

Figura 1.8 Um montador traduz instruções da linguagem assembly em código de máquina.

Escrever código em linguagem assembly é mais fácil do que em linguagem de máquina. No entanto, ainda é tedioso
escrever código em linguagem assembly. Uma instrução em linguagem assembly corresponde essencialmente a uma
instrução em código de máquina. Escrever em assembly requer que você saiba como funciona a CPU. A linguagem
linguagem de baixo nível assembly é chamada de linguagem de baixo nível, porque a linguagem assembly é de natureza próxima à linguagem
de máquina e depende da máquina.

1.3.3 Linguagem de Alto Nível


linguagem de alto nível Na década de 1950, uma nova geração de linguagens de programação conhecidas como linguagens de alto nível
emergiu. Eles são independentes de plataforma, o que significa que você pode escrever um programa em uma
linguagem de alto nível e executá-lo em diferentes tipos de máquinas. Idiomas de alto nível são semelhantes ao inglês
e fáceis de aprender e usar. As instruções em uma linguagem de programação de alto nível são chamadas de instruções.
declaração Aqui, por exemplo, está uma instrução de linguagem de alto nível que calcula a área de um círculo com raio 5:

área = 5 * 5 * 3,1415

Existem muitas linguagens de programação de alto nível e cada uma foi projetada para uma finalidade específica.
propósito. A Tabela 1.1 lista alguns dos mais populares.
programa fonte Um programa escrito em uma linguagem de alto nível é chamado de programa fonte ou código fonte.
Código fonte Como um computador não pode executar um programa fonte, um programa fonte deve ser traduzido em código de
intérprete máquina para execução. A tradução pode ser feita usando outra ferramenta de programação chamada intérprete ou
compilador compilador.
Um intérprete lê uma instrução do código-fonte, traduz-a em código de máquina ou código de máquina virtual e, em
seguida, executa-a imediatamente, conforme mostrado na Figura 1.9a. Observe que uma instrução do código-fonte
pode ser traduzida em diversas instruções de máquina.
Um compilador traduz todo o código-fonte em um arquivo de código de máquina, e o arquivo de código de máquina
é então executado, conforme mostrado na Figura 1.9b.

1.10 Qual idioma a CPU entende?


ÿVerificação de ponto
1.11 O que é uma linguagem assembly?
1.12 O que é um montador?
Machine Translated by Google

1.3 Linguagens de Programação 31

1.13 O que é uma linguagem de programação de alto nível?


1.14 O que é um programa fonte?
1.15 O que é um intérprete?
1.16 O que é um compilador?
1.17 Qual é a diferença entre uma linguagem interpretada e uma linguagem compilada?

Tabela 1.1 Linguagens de programação populares de alto nível

Linguagem Descrição

Há Nomeado em homenagem a Ada Lovelace, que trabalhou em computadores mecânicos de uso geral. A linguagem Ada foi desenvolvida
para o Departamento de Defesa e é utilizada principalmente em projetos de defesa.

BÁSICO Código de instrução simbólica multifuncional para iniciantes. Ele foi projetado para ser aprendido e usado facilmente por iniciantes.

C Desenvolvido nos Laboratórios Bell. C combina o poder de uma linguagem assembly com a facilidade de uso e portabilidade de uma
linguagem de alto nível.

C++ C++ é uma linguagem orientada a objetos, baseada em C.

C# Pronunciado “C sustenido”. É um híbrido de Java e C++ e foi desenvolvido pela Microsoft.

COBOL Linguagem comum orientada para negócios. É usado para aplicativos de negócios.

FORTRAN FÓRMULA TRADUÇÃO. É popular para aplicações científicas e matemáticas.

Java Desenvolvido pela Sun Microsystems, agora parte da Oracle. É amplamente utilizado para desenvolver aplicativos de Internet
independentes de plataforma.

Pascal Nomeado em homenagem a Blaise Pascal, pioneiro das máquinas de calcular no século XVII. É uma linguagem simples, estruturada e
de uso geral, usada principalmente para ensinar programação.

Pitão Uma linguagem de script simples de uso geral, boa para escrever programas curtos.

Visual básico Visual Basic foi desenvolvido pela Microsoft e permite que os programadores desenvolvam rapidamente interfaces gráficas de
usuário.

Arquivo fonte de alto nível

... Saída
área = 5 * 5 * 3,1415; Intérprete
...

(a)

Arquivo fonte de alto nível Arquivo de código de máquina

...
... Saída
0101100011011100
área = 5 * 5 * 3,1415; Compilador Executor
1111100011000100
...
...

(b)

Figura 1.9 (a) Um intérprete traduz e executa um programa, uma instrução por vez. (b) Um compilador traduz todo o programa fonte
em um arquivo de linguagem de máquina para execução.
Machine Translated by Google

32 Capítulo 1 Introdução a computadores, programas e C++

1.4 Sistemas Operacionais


sistema O sistema operacional (SO) é o programa mais importante executado em um computador.
Chave
operacional (SO) O sistema operacional gerencia e controla as atividades de um computador.
Apontar

Os sistemas operacionais populares para computadores de uso geral são Microsoft Windows, Mac OS e Linux. Os programas
aplicativos, como um navegador da Web ou um processador de texto, não podem ser executados a menos que um sistema
operacional esteja instalado e em execução no computador. A Figura 1.10 mostra a inter-relação entre usuário, programas
aplicativos, sistema operacional e hardware.

Do utilizador

Programas aplicativos

Sistema operacional

Hardware

Figura 1.10 Usuários e aplicativos acessam o hardware do computador por meio do sistema operacional.

A seguir estão as principais tarefas de um sistema operacional:

n Controle e monitoramento das atividades do sistema

n Alocação e atribuição de recursos do sistema

n Agendamento de operações

1.4.1 Atividades do Sistema de Controle e Monitoramento


Os sistemas operacionais executam tarefas básicas, como reconhecer entradas do teclado, enviar saídas para o monitor, organizar
arquivos e pastas em dispositivos de armazenamento e controlar dispositivos periféricos, como unidades de disco e impressoras.
Os sistemas operacionais também garantem que diferentes programas e usuários trabalhando simultaneamente não interfiram uns
com os outros. Além disso, o SO é responsável pela segurança, garantindo que usuários e programas não autorizados não
acessem o sistema.

1.4.2 Alocando e Atribuindo Recursos do Sistema


O sistema operacional é responsável por determinar quais recursos de computador um programa precisa (como tempo de CPU,
espaço de memória, discos e dispositivos de entrada e saída) e por alocá-los e atribuí-los para executar o programa.

1.4.3 Agendamento de Operações


O sistema operacional é responsável por agendar as atividades dos programas para fazer uso eficiente dos recursos do sistema.
Para aumentar o desempenho do sistema, muitos dos sistemas operacionais atuais suportam técnicas como multiprogramação,
multithreading e multiprocessamento.

multiprogramação A multiprogramação permite que vários programas sejam executados simultaneamente, compartilhando a CPU.
A CPU é muito mais rápida que os outros componentes do computador. Como resultado, fica ocioso a maior parte do tempo
Machine Translated by Google

1.5 História do C++ 33

o tempo — por exemplo, enquanto aguarda a transferência de dados de um disco ou aguarda a resposta
de outros recursos do sistema. Um sistema operacional de multiprogramação tira vantagem dessa situação,
permitindo que vários programas usem a CPU quando ela estaria ociosa. Por exemplo, a multiprogramação
permite que você use um processador de texto para editar um arquivo ao mesmo tempo que o navegador
da Web baixa um arquivo.
Multithreading permite que um único programa execute várias tarefas ao mesmo tempo. Por exemplo, multithreading

um programa de processamento de texto permite aos usuários editar texto simultaneamente e salvá-lo em
um disco. Neste exemplo, editar e salvar são duas tarefas no mesmo aplicativo. Essas duas tarefas podem
ser executadas simultaneamente.
O multiprocessamento, ou processamento paralelo, utiliza dois ou mais processadores juntos para multiprocessamento

executar subtarefas simultaneamente e depois combinar soluções das subtarefas para obter uma solução
para toda a tarefa. É como uma operação cirúrgica em que vários médicos trabalham juntos em um paciente.

1.18 O que é um sistema operacional? Liste alguns sistemas operacionais populares.


ÿVerificação de ponto
1.19 Quais são as principais responsabilidades de um sistema operacional?
1.20 O que são multiprogramação, multithreading e multiprocessamento?

1.5 História do C++


C++ é uma linguagem de programação orientada a objetos de uso geral.
Chave
Apontar
C, C++, Java e C# estão relacionados. C++ evoluiu de C. Java foi modelado a partir de C++. C# é um
subconjunto de C++, com alguns recursos semelhantes ao Java. Se você conhece um desses idiomas, é
fácil aprender os outros.
C evoluiu da linguagem B, que evoluiu da BCPL (Basic Combined Program-ming Language). Martin BCPL

Richards desenvolveu o BCPL em meados da década de 1960 para escrever sistemas operacionais e
compiladores. Ken Thompson incorporou muitos recursos do BCPL em sua linguagem B e os usou para B

criar versões iniciais do sistema operacional UNIX nos Laboratórios Bell em 1970 em um computador DEC
PDP-7. Tanto BCPL quanto B não têm tipo – ou seja, cada item de dados ocupa uma “palavra” ou “célula”
de comprimento fixo na memória. A forma como um item de dados é tratado – por exemplo, como um
número ou uma string – é de responsabilidade do programador. Dennis Ritchie estendeu a linguagem B
adicionando tipos e outros recursos em 1971 para desenvolver o sistema operacional UNIX em um
computador DEC PDP-11. Hoje, C é portátil e independente de hardware. É amplamente utilizado para C

desenvolvimento de sistemas operacionais.


C++ é uma extensão de C, desenvolvida por Bjarne Stroustrup nos Bell Laboratories durante 1983–
1985. C++ adicionou vários recursos que melhoraram a linguagem C. Mais importante ainda, adicionou o
suporte ao uso de classes para programação orientada a objetos. A programação orientada a objetos
pode tornar os programas fáceis de reutilizar e manter. C++ pode ser considerado um superconjunto de C. C++

Os recursos de C são suportados por C++. Os programas C podem ser compilados usando compiladores
C++. Depois de aprender C++, você também será capaz de ler e compreender programas C.

Um padrão internacional para C++, conhecido como C++98, foi criado pela International Standard C++98

Organization (ISO) em 1998. O padrão ISO é uma tentativa de garantir que C++ seja portável – ou seja, Padrões ISO

seus programas compilados usando o compilador de um fornecedor podem ser compilado sem erros de
qualquer compilador de outro fornecedor em qualquer plataforma. Como o padrão já existe há algum tempo,
todos os principais fornecedores agora oferecem suporte ao padrão ISO. No entanto, os fornecedores de
compiladores C++ podem adicionar recursos proprietários ao compilador. Portanto, é possível que seu
programa seja compilado corretamente por um compilador, mas precise ser modificado para ser compilado
por um compilador diferente.
C++11
Um novo padrão, conhecido como C++11, foi aprovado pela ISO em 2011. O C++11 adicionou novos
recursos à linguagem principal e à biblioteca padrão. Esses novos recursos são muito úteis para
Machine Translated by Google

34 Capítulo 1 Introdução a computadores, programas e C++

programação C++ avançada. Apresentaremos alguns dos novos recursos nos capítulos de bônus e nos
suplementos do site complementar.
linguagem de
programação de uso geral C++ é uma linguagem de programação de uso geral, o que significa que você pode usar C++ para escrever
programação orientada a objetos código para qualquer tarefa de programação. C++ é uma linguagem de programação orientada a objetos (OOP).
(OOP) linguagem A programação orientada a objetos é uma ferramenta poderosa para o desenvolvimento de software reutilizável.
A programação orientada a objetos em C++ será abordada em detalhes a partir do Capítulo 9.

1.21 Qual é a relação entre C, C++, Java e C#?


ÿVerificação de ponto 1.22 Quem desenvolveu inicialmente o C++?

1.6 Um programa C++ simples


Um programa C++ é executado a partir da função principal .
Chave
Apontar
Vamos começar com um programa C++ simples que exibe a mensagem Bem-vindo ao C++! no console. (A
console palavra console é um termo antigo de computador. Refere-se ao dispositivo de entrada e exibição de texto de um
entrada do console computador. Entrada do console significa receber entradas do teclado e saída do console significa exibir a saída
saída do console no monitor.) O programa é mostrado na Listagem 1.1.

Listagem 1.1 Welcome.cpp


incluir biblioteca 1 #include <iostream>
usando espaço para nome 2 usando namespace std;

função principal 3 4 int principal()


{
Comente 56 // Exibe Bem-vindo ao C++ no console
saída cout << "Bem-vindo ao C++!" << fim;

retorno bem-sucedido retornar 0;


7 8 9 10}

Bem-vindo ao C++!

números de linha Os números de linha não fazem parte do programa, mas são exibidos para fins de referência. Então,
não digite números de linha em seu programa.
A primeira linha do programa
Nota de vídeo
Seu primeiro programa C++ #include <iostream>

Directiva do pré-processador é uma diretiva de pré-processador do compilador que informa ao compilador para incluir a biblioteca iostream
biblioteca neste programa, que é necessária para suportar entrada e saída do console. A biblioteca C++ contém código
arquivo de cabeçalho predefinido para o desenvolvimento de programas C++. A biblioteca como iostream é chamada de arquivo de cabeçalho
em C++, porque geralmente é incluído no início de um programa.
A declaração na linha 2

usando namespace std;

espaço para nome diz ao compilador para usar o namespace padrão. std é uma abreviatura de padrão. Name-space é um
mecanismo para evitar conflitos de nomenclatura em um programa grande. Os nomes cout e endl na linha 7 são
definidos na biblioteca iostream no namespace padrão. Para que o compilador encontre esses nomes, a
instrução da linha 2 deve ser usada. Namespace é um avançado
Machine Translated by Google

1.6 Um Programa C++ Simples 35

assunto abordado no Suplemento IV.B. Por enquanto, tudo que você precisa fazer é escrever a linha 2 em
seu programa para realizar quaisquer operações de entrada e saída.
Todo programa C++ é executado a partir de uma função principal. Uma função é uma construção que
contém instruções. A função principal definida nas linhas 4–10 contém duas instruções. Eles são colocados função principal

em um bloco que começa com uma chave esquerda, {, (linha 5) e termina com uma chave direita, } (linha bloquear

10). Cada instrução em C++ deve terminar com um ponto e vírgula (;), conhecido como terminador de terminador de instrução
instrução.
A instrução na linha 7 exibe uma mensagem para o console. cout (pronuncia-se see-out) significa saída
do console. O operador << , conhecido como operador de inserção de fluxo, envia uma string para o console. saída do console

Uma string deve ser colocada entre aspas. A instrução na linha 7 produz primeiro a string “Bem-vindo ao C+ operador de inserção de fluxo

+!” para o console e, em seguida, gera endl. Observe que endl significa linha final. Enviar endl para o
console encerra uma linha e libera o buffer de saída para garantir que a saída seja exibida imediatamente. fim da linha

A declaração (linha 9)

retornar 0;

é colocado no final de cada função principal para sair do programa. O valor 0 indica que o programa foi
encerrado com sucesso. Alguns compiladores funcionarão bem se esta instrução for omitida; entretanto, saída bem sucedida

outros compiladores não o farão. É uma boa prática sempre incluir esta instrução para que seu programa
funcione com todos os compiladores C++.
A linha 6 é um comentário que documenta o que é o programa e como ele é construído. Os comentários Comente

ajudam os programadores a comunicar e compreender o programa. Elas não são instruções de programação
e, portanto, são ignoradas pelo compilador. Em C++, um comentário é precedido por duas barras (//) em uma
linha, chamado de comentário de linha, ou colocado entre /* e */ em uma ou mais linhas, chamado de comentário de linha

comentário em bloco ou comentário de parágrafo. Quando o compilador vê //, ele ignora todo o texto após // bloquear comentário

na mesma linha. Quando vê /*, ele procura o próximo */ e ignora qualquer texto entre /* e */. comentário de parágrafo

Aqui estão exemplos dos dois tipos de comentários:

// Este programa aplicativo imprime Bem-vindo ao C++!


/* Este programa aplicativo imprime Bem-vindo ao C++! */
/* Este programa aplicativo imprime Bem-vindo ao C+
+! */

Palavras-chave, ou palavras reservadas, têm um significado específico para o compilador e não podem palavra-chave (ou palavra reservada)

ser usadas no programa para outros fins. Existem quatro palavras-chave neste programa: using, name-
space, int e return.

Cuidado
As diretivas do pré-processador não são instruções C++. Portanto, não coloque ponto e vírgula no final das diretivas não são declarações

diretivas do pré-processador. Fazer isso pode causar erros sutis.

Cuidado
Alguns compiladores não compilarão se você colocar espaços extras entre < e iostream ou entre iostream
e >. O espaço extra se tornará parte do nome do arquivo de cabeçalho.
Para garantir que seu programa funcionará com todos os compiladores, não coloque espaços extras nestes sem espaço extra
casos.

Cuidado
Os programas de origem C++ diferenciam maiúsculas de minúsculas. Seria errado, por exemplo, substituir maiúsculas e minúsculas

main no programa por Main.


Machine Translated by Google

36 Capítulo 1 Introdução a computadores, programas e C++

Observação

Você provavelmente está se perguntando sobre por que a função principal é declarada dessa
maneira e por que cout << "Bem-vindo ao C++!" << endl é usado para exibir uma mensagem
no console. Suas perguntas ainda não podem ser totalmente respondidas. Por enquanto, você
terá que aceitar que é assim que as coisas são feitas. Você encontrará as respostas nos
capítulos subsequentes.

caracteres especiais Você viu vários caracteres especiais (por exemplo, #, //, <<) no programa. Eles são usados
quase em todos os programas. A Tabela 1.2 resume seus usos.

Tabela 1.2 Caracteres Especiais

Personagem Nome Descrição

# Sinal de libra Usado em #include para denotar uma


diretiva de pré-processador.
<> Abrindo e fechando colchetes angulares Inclui um nome de biblioteca quando usado com
#include.

() Abrindo e fechando parênteses Usado com funções como main().

{} // Chaves de abertura e fechamento Denota um bloco para incluir instruções.

Barras duplas Precede uma linha de comentário.

<< Operador de inserção de fluxo Saídas para o console.


""
Aspas de abertura e fechamento Quebra uma string (ou seja, uma sequência
de caracteres).

; Ponto e vírgula Marca o final de uma declaração.

erros comuns Os erros mais comuns que os alunos encontrarão neste capítulo são erros de sintaxe. Como
qualquer outra linguagem de programação, C++ tem suas próprias regras gramaticais, chamadas
regras de sintaxe sintaxe, e você precisa escrever código que obedeça às regras de sintaxe. Se o seu programa violar
essas regras, o compilador C++ reportará erros de sintaxe. Preste muita atenção à pontuação. O
símbolo de redirecionamento << são dois <'s consecutivos. Cada instrução na função termina com ponto e vírgula (;).
O programa da Listagem 1.1 exibe uma mensagem. Depois de entender o programa, é fácil estendê-
lo para exibir mais mensagens. Por exemplo, você pode reescrever o programa para exibir três
mensagens, conforme mostrado na Listagem 1.2.

Listagem 1.2 WelcomeWithThreeMessages.cpp


incluir biblioteca 1 #include <iostream>
2 usando namespace std;

função principal 3 4 int principal()


5{
saída 6 cout << "Programar é divertido!" << fim;
7 cout << "Fundamentos Primeiro" << endl;
cout << "Orivado pelo problema" << endl;

retorno bem-sucedido 8 retornar 0;


9 10 11}

Programar é divertido!
Fundamentos orientados
para o primeiro problema
Machine Translated by Google

1.6 Um Programa C++ Simples 37

Além disso, você pode realizar cálculos matemáticos e exibir o resultado no console.
A Listagem 1.3 dá um exemplo desse tipo.

Listagem 1.3 ComputeExpression.cpp


1 #include <iostream> incluir biblioteca
2 usando namespace std;

3 4 int principal() função principal


5{
6 cout << "(10,5 + 2 * 3) / (45 - 3,5) = "; saída
7 cout << (10,5 + 2 * 3) / (45 - 3,5) << endl;
8

retornar 0; retorno bem-sucedido


9 10}

(10,5 + 2 * 3) / (45 – 3,5) = 0,39759036144578314

O operador de multiplicação em C++ é *. Como você pode ver, é um processo simples traduzir uma
expressão aritmética em uma expressão C++. Discutiremos as expressões C++ mais detalhadamente no Capítulo
2.
Você pode combinar várias saídas em uma única instrução. Por exemplo, a declaração a seguir desempenha
a mesma função das linhas 6–7.

cout << "(10,5 + 2 * 3) / (45 – 3,5) = "


<< (10,5 + 2 * 3) / (45 – 3,5) << final;

1.23 Explique as palavras-chave C++. Liste algumas palavras-chave C++ que você aprendeu neste capítulo.
ÿVerificação de ponto
1.24 C++ diferencia maiúsculas de minúsculas? Qual é o caso das palavras-chave C++?

1.25 Qual é a extensão do nome do arquivo de origem C++ e qual é o arquivo executável C++
extensão de nome no Windows?

1.26 O que é um comentário? Qual é a sintaxe de um comentário em C++? O comentário é ignorado pelo
compilador?

1.27 Qual é a instrução para exibir uma string no console?

1.28 O que significa o namespace std ?

1.29 Qual das seguintes diretivas de pré-processador está correta?


a. importar iostream
b. #include <iostream>

c. incluir <iostream>

d. #incluir iostream

1.30 Qual das seguintes diretivas de pré-processador funcionará com todos os compiladores C++?
a. #incluir <iostream>

b. #include <iostream>

c. incluir <iostream>

d. #include <iostream>
Machine Translated by Google

38 Capítulo 1 Introdução a computadores, programas e C++

1.31 Mostre a saída do seguinte código:


#include <iostream>
usando namespace std;

int principal()
{
"
contagem << "3,5 * 4/2 – 2,5 = << ( 3,5 * 4/2 – 2,5 ) << fim;

retornar 0;
}

1.32 Mostre a saída do seguinte código:


#include <iostream>
usando namespace std;

int principal()
{
cout << "C++" << "Java" << endl;
cout << "C++" << endl << "Java" << endl;
cout << "C++, " << "Java, " << "e C#" << endl;

retornar 0;
}

1.7 Ciclo de Desenvolvimento de Programa C++


O processo de desenvolvimento de programas C++ consiste em criar/ modificar código-fonte, compilar,
Chave
Apontar vincular e executar programas.

Você tem que criar seu programa e compilá-lo antes que ele possa ser executado. Este processo é repetitivo,
conforme mostrado na Figura 1.11. Se o seu programa apresentar erros de compilação, você deverá modificá-lo
Nota de vídeo
para corrigi-los e depois recompilá-lo. Se o seu programa apresentar erros de execução ou não produzir o resultado
Compilar e executar C++
correto, você deverá modificá-lo, recompilá-lo e executá-lo novamente.
O comando do compilador C++ executa três tarefas em sequência: pré-processamento, compilação e vinculação.
Precisamente, um compilador C++ contém três programas separados: pré-processador, compilador e vinculador.
Para simplificar, nos referimos a todos os três programas juntos como um compilador C++.

pré-processador n Um pré-processador é um programa que processa um arquivo fonte antes de ele ser transmitido ao
compilador. O pré-processador processa as diretivas. As diretivas começam com o sinal # . Por exemplo,
#include na linha 1 da Listagem 1.1 é uma diretiva que diz ao compilador para incluir uma biblioteca. O
pré-processador produz um arquivo intermediário.

n O compilador então traduz o arquivo intermediário em um arquivo de código de máquina. O arquivo de


arquivo objeto código de máquina também é conhecido como arquivo objeto. Para evitar confusão com objetos C++,
não usaremos esta terminologia no texto.

vinculador n O vinculador vincula o arquivo de código de máquina aos arquivos da biblioteca de suporte para formar
um arquivo executável. No Windows, o arquivo de código de máquina é armazenado em disco com
extensão .obj e os arquivos executáveis são armazenados com extensão .exe . No UNIX, o arquivo de
código de máquina possui uma extensão .o e os arquivos executáveis não possuem extensões de arquivo.

Observação

Arquivo de origem .cpp Um arquivo de origem C++ normalmente termina com a extensão .cpp. Alguns compiladores podem
aceitar outras extensões de nome de arquivo (por exemplo, .c, .cp ou .c), mas você deve usar a
extensão .cpp para ser compatível com todos os compiladores C++.
Machine Translated by Google

1.7 Ciclo de Desenvolvimento de Programa C++ 39

Criar/modificar código-fonte
Código fonte (desenvolvido pelo programador)

#include <iostream>
usando namespace std;
Salvo no disco
int principal()
{
Código fonte
// Exibe Bem-vindo ao C++ no console cout << "Bem-vindo ao C++!"
<< fim;

retornar 0;
} Pré-processador

Armazenado no disco

Fonte Modificada
Código

Compilador

Se erros de compilação
Armazenado no disco

Um arquivo objeto (por exemplo, Welcome.obj) é criado Código da máquina Código da Biblioteca

Vinculador

Armazenado no disco

Um arquivo executável (por exemplo, Welcome.exe) é criado Código executável

Execute o código executável, por

exemplo, Bem-vindo

Resultado

Se houver erros de tempo de execução ou resultados incorretos

Figura 1.11 O processo de desenvolvimento de programas em C++ consiste em criar/modificar código-fonte, compilar, vincular e
executar programas.

Você pode desenvolver um programa C++ a partir de uma janela de comando ou de um IDE. Um IDE
é um software que fornece um ambiente de desenvolvimento integrado (IDE) para o desenvolvimento ambiente de
rápido de programas C++. Edição, compilação, construção, depuração e ajuda on-line são integradas em desenvolvimento integrado (IDE)

uma interface gráfica do usuário. Basta inserir o código-fonte ou abrir um arquivo existente em uma janela
e clicar em um botão, item de menu ou tecla de função para compilar e executar o programa. Exemplos
de IDEs populares são Microsoft Visual C++, Dev-C++, Eclipse e NetBeans. Todos esses IDEs podem ser
baixados gratuitamente.
O Suplemento II.B apresenta como desenvolver programas C++ usando Visual C++. O Suplemento
II.D apresenta como desenvolver programas C++ usando Dev-C++. O Suplemento II.E apresenta como
Nota de vídeo
desenvolver programas C++ a partir do NetBeans IDE. O Suplemento IF apresenta como desenvolver
Tutorial de Visual C++
programas C++ a partir de uma janela de comando do Windows. O Suplemento IG apresenta como
desenvolver C++ em UNIX.
Machine Translated by Google

40 Capítulo 1 Introdução a computadores, programas e C++

1.33 O C++ pode ser executado em qualquer máquina? O que é necessário para compilar e executar programas C++?
ÿVerificação de ponto
1.34 Quais são as entradas e saídas de um compilador C++?

1.8 Estilo de Programação e Documentação


Um bom estilo de programação e documentação adequada tornam o programa fácil de ler e ajudam os
Chave
Apontar programadores a evitar erros.

estilo de programação O estilo de programação trata da aparência dos programas. Um programa poderia ser compilado e executado corretamente
mesmo se você o escrevesse em apenas uma linha, mas escrevê-lo dessa maneira seria um estilo de programação ruim
documentação porque seria difícil de ler. Documentação é o conjunto de observações explicativas e comentários relativos a um programa.
O estilo de programação e a documentação são tão importantes quanto a codificação. Um bom estilo de programação e
documentação apropriada reduzem a chance de erros e facilitam a leitura dos programas. Até agora você aprendeu alguns
bons estilos de programação. Esta seção os resume e fornece diversas orientações sobre como usá-los. Diretrizes mais
detalhadas sobre estilo de programação e documentação podem ser encontradas no Suplemento IE no site complementar.

1.8.1 Comentários e estilos de comentários apropriados


Inclua um resumo no início do programa para explicar o que ele faz, seus principais recursos e quaisquer técnicas
exclusivas que utiliza. Em um programa longo, você também deve incluir comentários que apresentem cada etapa principal
e expliquem qualquer coisa que seja difícil de ler. É importante fazer comentários concisos para que não sobrecarreguem
o programa ou dificultem a leitura.

1.8.2 Indentação e espaçamento adequados


Um estilo de recuo consistente torna os programas claros e fáceis de ler, depurar e manter.
recuo O recuo é usado para ilustrar os relacionamentos estruturais entre os componentes ou instruções de um programa. O
compilador C++ pode ler o programa mesmo que todas as instruções estejam em linha reta, mas o código alinhado
corretamente é mais fácil de ler e manter. Recue cada subcomponente ou instrução dois espaços a mais do que a
construção na qual está aninhado.
Um único espaço deve ser adicionado em ambos os lados de um operador binário, conforme mostrado aqui:

contagem << 3+4*4; Estilo ruim

contagem << 3 + 4 * 4; Bom estilo

Um espaço de linha única deve ser usado para separar segmentos do código para facilitar a leitura do programa.

1.35 Identifique e corrija os erros no código a seguir:


ÿVerificação de ponto
1 inclui <iostream>;
2 usando namespace std;

3 4 int principal
5{
6 // Exibe Bem-vindo ao C++ no console
7 cout << Bem-vindo ao C++! << fim;
8

retornar 0;
9 10}
Machine Translated by Google

1.9 Erros de Programação 41

1.36 Como você denota uma linha de comentário e um parágrafo de comentário?


1.37 Reformate o seguinte programa de acordo com o estilo de programação e documentação
diretrizes de tação.

#include <iostream>
usando namespace std;

int principal()
{
cout << "2 + 3 = "<<2+3;
retornar 0;
}

1.9 Erros de programação


Os erros de programação podem ser categorizados em três tipos: erros de sintaxe, erros de tempo
Chave
de execução e erros lógicos. Apontar

Erros de programação são inevitáveis, mesmo para programadores experientes. Os erros podem ser categorizados
em três tipos: erros de sintaxe, erros de tempo de execução e erros lógicos.

1.9.1 Erros de sintaxe


Os erros detectados pelo compilador são chamados de erros de sintaxe ou erros de compilação. Erros de sintaxe erro de sintaxe
resultam de erros na construção do código, como digitação incorreta de uma palavra-chave, omissão da erro de compilação
pontuação necessária ou uso de uma chave de abertura sem uma chave de fechamento correspondente. Esses
erros geralmente são fáceis de detectar porque o compilador informa onde eles estão e o que os causou. Por
exemplo, o programa a seguir na Listagem 1.4 possui um erro de sintaxe.

Listagem 1.4 ShowSyntaxErrors.cpp


1 #include <iostream>
2 usando namespace std

3 4 int principal()
5 {
6 cout << "Programar é divertido << endl;
7

retornar 0;
8 9}

Ao compilar este programa usando Visual C++, ele exibe os seguintes erros:

Compilar

1>Test.cpp(4): erro C2144: erro de sintaxe: 'int' deve ser precedido por ';'
1>Test.cpp(6): erro C2001: nova linha em constante
1>Test.cpp(8): erro C2143: erro de sintaxe: faltando ';' antes do 'retorno'

Três erros são relatados, mas na verdade o programa apresenta dois erros. Primeiro, o ponto e vírgula (;) está
faltando no final da linha 2. Segundo, a string Programming is fun deve ser fechada com aspas de fechamento
na linha 6.
Como um único erro geralmente exibe muitas linhas de erros de compilação, é uma boa prática corrigir os
erros da linha superior e trabalhar para baixo. A correção de erros que ocorrem anteriormente no programa
também pode corrigir erros adicionais que ocorrem posteriormente.
Machine Translated by Google

42 Capítulo 1 Introdução a computadores, programas e C++

Dica
Se você não sabe como corrigi-lo, compare seu programa de perto, caractere por caractere,
com exemplos semelhantes no texto. Nas primeiras semanas deste curso, você provavelmente
corrigir erro de sintaxe gastará muito tempo corrigindo erros de sintaxe. Em breve você estará familiarizado com a
sintaxe e poderá corrigir erros de sintaxe rapidamente.

1.9.2 Erros de tempo de execução

erro de tempo de execução Erros de tempo de execução fazem com que um programa seja encerrado de forma anormal. Eles ocorrem enquanto
um aplicativo está em execução se o ambiente detectar uma operação impossível de realizar. Erros de entrada
normalmente causam erros de tempo de execução. Um erro de entrada ocorre quando o programa está aguardando
que o usuário insira um valor, mas o usuário insere um valor que o programa não consegue manipular. Por exemplo,
se o programa espera ler um número, mas em vez disso o usuário insere uma string, isso causa a ocorrência de
erros de tipo de dados.
Outra fonte comum de erros de tempo de execução é a divisão por zero. Isso acontece quando o divisor é zero
para divisões inteiras. Por exemplo, o programa a seguir na Listagem 1.5 causaria um erro de tempo de execução.

Listagem 1.5 ShowRuntimeErrors.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 int eu = 4;
7 int j = 0;
erro de tempo de execução 8 cout << i/j << endl;
9

10 retornar 0;
11}

Aqui, i e j são chamados de variáveis. Apresentaremos variáveis no Capítulo 2. i tem um valor


de 4 e j tem valor 0. i / j na linha 8 causa um erro de divisão por zero em tempo de execução.

1.9.3 Erros Lógicos


erro lógico Erros lógicos ocorrem quando um programa não funciona da maneira pretendida. Erros desse tipo ocorrem por
diversos motivos. Por exemplo, suponha que você escreveu o seguinte programa na Listagem 1.6 para converter
35 graus Celsius em graus Fahrenheit:

Listagem 1.6 ShowLogicErrors.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5 {
6 cout << "Celsius 35 é grau Fahrenheit " cout << (9/5 ) * 35 << fim;
+ 32 << endl;

retornar 0;
7 8 9 10}

Celsius 35 é grau Fahrenheit 67


Machine Translated by Google

1.9 Erros de Programação 43

Você obterá Fahrenheit 67 graus, o que está errado. Deveria ser 95. Em C++, a divisão para números inteiros é o
quociente. A parte fracionária está truncada. Portanto, 9/5 é 1. Para obter o resultado correto, você precisa usar 9,0/
5, que resulta em 1,8.
Em geral, os erros de sintaxe são fáceis de encontrar e corrigir, porque o compilador indica onde os erros foram
introduzidos e por que estão errados. Erros de tempo de execução também não são difíceis de encontrar, porque os
motivos e locais dos erros são exibidos no console quando o programa é interrompido. Encontrar erros lógicos, por
outro lado, pode ser muito desafiador. Nos próximos capítulos, você aprenderá as técnicas de rastreamento de
programas e localização de erros lógicos.

1.9.4 Erros Comuns


Falta de chave de fechamento, falta de ponto e vírgula, falta de aspas para strings e nomes com erros ortográficos
são erros comuns cometidos por novos programadores.

Erro comum 1: chaves ausentes


Chaves são usadas para denotar um bloco no programa. Cada chave de abertura deve ser acompanhada por uma
chave de fechamento. Um erro comum é a falta da chave de fechamento. Para evitar esse erro, digite uma chave de
fechamento sempre que uma chave de abertura for digitada, conforme mostrado no exemplo a seguir.

int principal()
{

} Digite esta chave de fechamento imediatamente para corresponder à chave de abertura

Erro comum 2: falta de ponto e vírgula


Cada instrução termina com um terminador de instrução (;). Freqüentemente, um novo programador se esquece de
colocar um terminador de instrução para a última instrução de um bloco, conforme mostrado no exemplo a seguir:

int principal()
{
cout << "Programar é divertido!" << fim;
cout << "Fundamentos Primeiro" << endl;
cout << "Orientado pelo problema" << endl
}
Falta um ponto e vírgula

Erro comum 3: aspas ausentes


Uma string deve ser colocada entre aspas. Freqüentemente, um novo programador se esquece de colocar aspas no
final de uma string, conforme mostrado no exemplo a seguir:

cout << "Orignado pelo problema;

Faltando uma aspa

Erro comum 4: nomes com erros ortográficos


C++ diferencia maiúsculas de minúsculas. Nomes incorretos são um erro comum cometido por novos programadores.
Por exemplo, a palavra main está escrita incorretamente como Main no código a seguir:

1 int Principal() 2
{3
cout << (10,5 + 2 * 3) / (45 – 3,5) << endl;
4 retornar 0;
5 }

1.38 O que são erros de sintaxe (erros de compilação), erros de tempo de execução e erros lógicos?

1.39 Se você esquecer de colocar aspas de fechamento em uma string, que tipo de erro ocorrerá? ÿVerificação de ponto
Machine Translated by Google

44 Capítulo 1 Introdução a computadores, programas e C++

1.40 Se o seu programa precisar ler dados de um arquivo, mas o arquivo não existir, um erro
ocorreria ao executar este programa. Que tipo de erro é esse?
1.41 Suponha que você escreva um programa para calcular o perímetro de um retângulo e
erroneamente escreva seu programa de modo que ele calcule a área de um retângulo.
Que tipo de erro é esse?

1.42 Identifique e corrija os erros no código a seguir:

1 int Principal() 2
{3
cout << 'Bem vindo ao C++!;
retornar 0;
45)

Termos chave

assembler 30 vinculador
linguagem assembly 29 38 erro lógico 42
bits 24 linguagem de baixo nível
bloco 35 30 linguagem de máquina
bloco comentário 35 29 função principal
barramento 35 memória 25
22 byte 24 modem 28
modem a cabo 28 placa-mãe 23
unidade central de processamento namespace 34
(CPU) 23 placa de interface de rede (NIC) 28
comentário 35 erro arquivo de objeto
de compilação 38 sistema operacional (SO)
41 compilador 32 comentário de parágrafo
30 console 34 35 pixel 28
entrada do console 34 pré-processador 38
saída do programa 22
console 35 densidade de pontos 28 programação 22 erro
linha de assinante digital de tempo de
(DSL) 28 execução 42 resolução
esquema de de tela 28
codificação 24 hardware 22 software 22 código
arquivo de cabeçalho 34 linguagem de alto nível 30 ambiente
fonte 30
deprograma
desenvolvimento integrado
(IDE) 39 fonte 30 instrução
intérprete 30 30 terminador de instrução
palavra-chave (ou palavra reservada) 35 dispositivo de
35 biblioteca armazenamento 25 operador de
34 comentário de linha 35 inserção de fluxo 35 erro de sintaxe 41

Nota
Suplemento IA Os termos-chave acima são definidos neste capítulo. O Suplemento IA, Glossário, lista todos
os termos-chave e descrições utilizadas no livro, organizados por capítulos.
Machine Translated by Google

Resumo do Capítulo 45

Resumo do capítulo

1. Um computador é um dispositivo eletrônico que armazena e processa dados.

2. Um computador inclui hardware e software.

3. Hardware é o aspecto físico do computador que pode ser tocado.

4. Os programas de computador , conhecidos como software, são as instruções invisíveis que controlam o hardware
e o fazem executar tarefas.

5. Programação de computadores é a escrita de instruções (ou seja, código) para computadores


executar.

6. A unidade central de processamento (CPU) é o cérebro de um computador. Ele recupera instruções de


memória e os executa.

7. Os computadores usam zeros e uns porque os dispositivos digitais têm dois estados estáveis, denominados
por convenção como zero e um.

8. Um bit é um dígito binário 0 ou 1.

9. Um byte é uma sequência de 8 bits.

10. Um kilobyte tem cerca de 1.000 bytes, um megabyte tem cerca de 1 milhão de bytes, um gigabyte tem cerca de
1 bilhão de bytes e um terabyte tem cerca de 1.000 gigabytes.

11. A memória armazena dados e instruções de programa para a CPU executar.

12. Uma unidade de memória é uma sequência ordenada de bytes.

13. A memória é volátil porque a informação é perdida quando a energia é desligada.

14. Programas e dados são armazenados permanentemente em dispositivos de armazenamento e movidos para a
memória quando o computador realmente os utiliza.

15. A linguagem de máquina é um conjunto de instruções primitivas incorporadas em cada computador.

16. A linguagem assembly é uma linguagem de programação de baixo nível na qual um mnemônico é usado
para representar cada instrução de linguagem de máquina.

17. Idiomas de alto nível são semelhantes ao inglês e fáceis de aprender e programar.

18. Um programa escrito em uma linguagem de alto nível é chamado de programa fonte.

19. Um compilador é um programa de software que traduz o programa fonte em um programa de máquina.
programa de idiomas.

20. O sistema operacional (SO) é um programa que gerencia e controla o computador


Atividades.
Machine Translated by Google

46 Capítulo 1 Introdução a computadores, programas e C++

21. C++ é uma extensão de C. C++ adicionou vários recursos que melhoraram a linguagem C.
Mais importante ainda, adicionou o suporte ao uso de classes para programação orientada a objetos.

22. Os arquivos de origem C++ terminam com a extensão .cpp.

23. #include é uma diretiva de pré-processador. Todas as diretivas do pré-processador começam com o símbolo
ele era #.

24. O objeto cout junto com o operador de inserção de fluxo (<<) pode ser usado para exibir uma string no
console.

25. Todo programa C++ é executado a partir de uma função principal. Uma função é uma construção que
contém declarações.

26. Toda instrução em C++ deve terminar com ponto e vírgula (;), conhecida como instrução
Exterminador do Futuro.

27. Em C++, um comentário é precedido por duas barras (//) em uma linha, chamado de comentário de
linha, ou colocado entre /* e */ em uma ou mais linhas, chamado de comentário de bloco ou
comentário de parágrafo.

28. Palavras-chave, ou palavras reservadas, têm um significado específico para o compilador e não podem ser utilizadas
no programa para outros fins. Exemplos de palavras-chave são using, namespace, int e return.

29. Os programas de origem C++ diferenciam maiúsculas de minúsculas.

30. Você pode desenvolver aplicativos C++ a partir da janela de comando ou usando um IDE como
como Visual C++ ou Dev-C++.

31. Os erros de programação podem ser categorizados em três tipos: erros de sintaxe, erros de tempo de execução
e erros lógicos. Os erros relatados por um compilador são chamados de erros de sintaxe ou erros de
compilação. Erros de tempo de execução são erros que fazem com que um programa seja encerrado de
forma anormal. Erros lógicos ocorrem quando um programa não funciona da maneira pretendida.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Observação

Soluções para exercícios pares são fornecidas no site complementar. Soluções para todos os
nível de dificuldade exercícios são fornecidas no site de recursos do instrutor. O nível de dificuldade é classificado
como fácil (sem estrela), moderado (*), difícil (**) ou desafiador (***).

Seções 1.6–1.9
1.1 (Exibir duas mensagens) Escreva um programa que exiba Introdução aos Computadores
e Bem-vindo à Programação Orientada a Objetos.
Nota de vídeo
1.2 (Exibir cinco mensagens) Escreva um programa que exiba Welcome to C++ cinco
Exibir cinco mensagens
vezes.
Machine Translated by Google

Exercícios de Programação 47

*1.3 (Exibir um padrão) Escreva um programa que exiba o seguinte padrão:

* *********
*** *******
***** *****
******* ***
********* *
1.4 (Imprimir uma tabela) Escreva um programa que exiba a seguinte tabela:

p*5 p*10
25 50
50 100
125 250
página 5 10 25 50 250 500

1,2*0,1+3,3*0,3
1.5 (Compute Expressões) Escreva um programa que exiba o resultado de .
0,09 + 0,001

1.6 (Soma dos números ímpares) Escreva um programa que exiba a soma dos dez primeiros
números ímpares

1.7 ( p aproximado) p pode ser calculado usando a seguinte fórmula:

1 1 1 1
+ + + + c2 _
p = A6 * 11 + 4 9 16 25
1 1 1 1
+ + +
Escreva um programa que exiba o resultado de A6 * 11 + 4 9 16 25 2 e
1 1 1 1 1
+ + + +
A6* 11+ 4 9 16 25 36 2. Use 1.0 em vez de 1 em seu programa.

1.8 (Área e perímetro de um triângulo equilátero) Escreva um programa que exiba a área e o
perímetro de um triângulo equilátero que tem seus três lados iguais a 9,2, usando a
seguinte fórmula:

2/ 4
área = 1,732 * (lado1)

perímetro = 3 * lado1

1.9 (Área e perímetro de um quadrado) Escreva um programa que exiba a área e o perímetro
de um quadrado com lado 5,2 usando a seguinte fórmula:

2
área = (lado) e perímetro = 4 * lado

1.10 (Vendas médias em gramas) Suponha que um vendedor venda 6 quilos de mantimentos em 15
minutos, 30 minutos e 30 segundos. Escreva um programa que exiba a venda média em gramas
por hora (observe que 1 quilograma equivale a 1.000 gramas).

* 1.11 (Projeção populacional) O US Census Bureau projeta a população com base na


seguintes suposições:

n Um nascimento a cada 7 segundos


n Uma morte a cada 13 segundos
n Um novo imigrante a cada 45 segundos
Escreva um programa que exiba a população para cada um dos próximos cinco anos.
Suponha que a população atual seja 312.032.486 e que um ano tenha 365 dias. Dica: Em
C++, se dois inteiros realizam divisão, o resultado é o quociente. A parte fracionária está truncada.
Machine Translated by Google

48 Capítulo 1 Introdução a computadores, programas e C++

Por exemplo, 5/4 é 1 ( não 1,25 ) e 10/4 é 2 ( não 2,5 ). Para obter um resultado preciso com a parte
fracionária, um dos valores envolvidos na divisão deve ser um número com vírgula decimal. Por
exemplo, 5,0 / 4 é 1,25 e 10 / 4,0 é 2,5.

1.12 (Vendas médias em quilogramas) Suponha que um vendedor venda 5.553 gramas de mantimentos em 2
horas, 9 minutos e 30 segundos. Escreva um programa que exiba a venda média em quilogramas por
hora (observe que 1 quilograma equivale a 1.000 gramas).
Machine Translated by Google

CAPÍTULO

2
Elementar
Programação

Objetivos
n Escrever programas C++ que realizam cálculos simples (§2.2).

n Para ler a entrada do teclado (§2.3).

n Para usar identificadores para nomear elementos como variáveis e funções


(§2.4).

n Usar variáveis para armazenar dados (§2.5).

n Para programar usando instruções de atribuição e expressões de atribuição


(§2.6).

n Para nomear constantes usando a palavra-chave const (§2.7).

n Para declarar variáveis usando tipos de dados numéricos (§2.8.1).

n Para escrever literais inteiros, literais de ponto flutuante e literais em linguagem científica
notação (§2.8.2).

n Para realizar operações usando os operadores +, -, *, / e % (§2.8.3).

n Para realizar operações de expoente usando a função pow(a, b)


(§2.8.4).

n Escrever e avaliar expressões (§2.9).

n Para obter a hora atual do sistema usando time(0) (§2.10).

n Usar operadores de atribuição aumentados (+=, -=, *=, /=, %=) (§2.11).
n Distinguir entre pós-incremento e pré-incremento e entre
pós-decremento e pré-decremento (§2.12).

n Para converter números para um tipo diferente usando conversão (§2.13).

n Descrever o processo de desenvolvimento de software e aplicá-lo para desenvolver o


programa de pagamento de empréstimos (§2.14).

n Escrever um programa que converta uma grande quantia de dinheiro em pequenas


unidades (§2.15).

n Para evitar erros comuns na programação elementar (§2.16).


Machine Translated by Google

50 Capítulo 2 Programação Elementar

2.1 Introdução
Este capítulo se concentra no aprendizado de técnicas elementares de programação para resolver problemas.
Chave
Apontar
No Capítulo 1, você aprendeu como criar, compilar e executar programas básicos. Agora você aprenderá
como resolver problemas escrevendo programas. Através desses problemas, você aprenderá programação
elementar usando tipos de dados primitivos, variáveis, constantes, operadores, expressões e entradas e
saídas.
Suponha, por exemplo, que você queira solicitar um empréstimo estudantil. Dado o valor do empréstimo,
o prazo do empréstimo e a taxa de juros anual, como você escreve um programa para calcular seu pagamento
mensal e seu pagamento total? Este capítulo mostra como escrever tais programas. Ao longo do caminho,
você aprenderá as etapas básicas envolvidas na análise de um problema, no projeto de uma solução e na
implementação da solução criando um programa.

2.2 Escrevendo um Programa Simples


Escrever um programa envolve projetar uma estratégia para resolver um problema e então usar uma
Chave
Apontar linguagem de programação para implementar essa estratégia.

problema Primeiro, consideremos o problema simples de calcular a área de um círculo. Como escrevemos um programa
para resolver isso?
Escrever um programa envolve projetar algoritmos e traduzi-los em instruções de programação, ou
algoritmo código. Um algoritmo descreve como um problema é resolvido listando as ações que devem ser tomadas e a
ordem de sua execução. Algoritmos podem ajudar o programador a planejar um programa antes de escrevê-
lo em uma linguagem de programação. Os algoritmos podem ser descritos em linguagens naturais ou em
pseudo-código pseudocódigo (linguagem natural misturada com algum código de programação). O algoritmo para calcular a
área de um círculo pode ser descrito da seguinte forma:

1. Leia o raio do círculo.

2. Calcule a área usando a seguinte fórmula:

área = raio * raio * p

3. Exiba o resultado.

Dica
É uma boa prática delinear seu programa (ou seu problema subjacente) na forma de um
algoritmo antes de começar a codificar.

Quando você codifica – isto é, quando escreve um programa – você traduz um algoritmo em um
programa. Você já sabe que todo programa C++ inicia sua execução a partir da função principal.
Aqui, o esboço da função principal ficaria assim:

int principal() {

// Etapa 1: leitura no raio

// Passo 2: Área de computação

// Passo 3: Exibir a área


}

O programa precisa ler o raio inserido pelo usuário no teclado. Isso aumenta
duas questões importantes:

n Lendo o raio

n Armazenando o raio no programa


Machine Translated by Google

2.2 Escrevendo um Programa Simples 51

Vamos abordar a segunda questão primeiro. Para armazenar o raio, o programa precisa declarar
um símbolo chamado variável. Uma variável representa um valor armazenado na memória do computador. variável
Em vez de usar x e y como nomes de variáveis, escolha nomes descritivos: neste caso, raio por raio e área nomes descritivos
por área. Para que o compilador saiba qual raio e área
são, especifique seus tipos de dados. Esse é o tipo de dado armazenado em uma variável, seja um número tipo de dados

inteiro, um número de ponto flutuante ou qualquer outra coisa. Isso é conhecido como declaração de variáveis. número de ponto flutuante
C++ fornece tipos de dados simples para representar números inteiros, números de ponto flutuante (ou seja, declarar variáveis
números com ponto decimal), caracteres e tipos booleanos. Esses tipos são conhecidos como tipos de dados tipo de dados primitivo
primitivos ou tipos fundamentais.
Declare o raio e a área como números de ponto flutuante de precisão dupla. O programa pode ser expandido
da seguinte forma:

int principal() {

raio duplo ;
área dupla ;

// Etapa 1: leitura no raio

// Passo 2: Área de computação

// Passo 3: Exibir a área


}

O programa declara raio e área como variáveis. A palavra reservada double indica
esse raio e área são valores de ponto flutuante de precisão dupla armazenados no computador.
A primeira etapa é solicitar ao usuário que designe o raio do círculo. Você aprenderá como solicitar
informações ao usuário em breve. Por enquanto, para aprender como as variáveis funcionam, você pode atribuir
um valor fixo ao raio no programa enquanto escreve o código; posteriormente, você modificará o programa
para solicitar esse valor ao usuário.
O segundo passo é calcular a área atribuindo o resultado da expressão raio *
raio * 3,14159 para a área.
Na etapa final, o programa exibirá o valor da área no console usando cout
<<área.
O programa completo é mostrado na Listagem 2.1.

Listagem 2.1 ComputeArea.cpp


1 #include <iostream> incluir biblioteca
2 usando namespace std;

3 4 int principal()
5{
raio duplo ; declarar variável
6 7 área dupla ;

// Etapa 1: leitura no raio


8 raio = 20; atribuir valor
9
10 11 12 // Passo 2: Área de computação
13 área = raio * raio * 3,14159;
14
15 // Passo 3: Exibir a área
16 cout << "A área é " << area << endl;
17
18 retornar 0;
19}
Machine Translated by Google

52 Capítulo 2 Programação Elementar

A área é 1256,64

Variáveis como raio e área correspondem a localizações de memória. Cada variável tem um nome, tipo,
declarar uma variável tamanho e valor. A linha 6 declara que o raio pode armazenar um valor duplo . O valor não é definido até que
atribuir um valor você atribua um valor. A linha 10 atribui 20 ao raio. Da mesma forma, a linha 7 declara a variável area e a
linha 13 atribui um valor à area. Se você comentar a linha 10, o programa será compilado e executado, mas o
resultado será imprevisível, porque o raio não recebeu um valor adequado. No Visual C++, fazer referência a
uma variável não inicializada causará um erro de tempo de execução.
A tabela abaixo mostra o valor na memória de área e raio quando o programa é executado. Cada linha da
tabela mostra os novos valores das variáveis após a execução da instrução na linha correspondente do
programa. Este método de revisar como um programa funciona é chamado de rastreamento de um programa.
rastrear um programa O rastreamento de programas pode ajudá-lo a entender como os programas funcionam e a encontrar erros de
programa.

Linha# raio área

6 valor indefinido
7 valor indefinido
10 20
13 1256,64

A linha 16 envia uma string “A área é” para o console. Também envia o valor da área variável para o
console. Observe que as aspas não são colocadas ao redor da área. Se fossem, a string “area” seria enviada
ao console.

2.1 Mostre a saída do seguinte código:


ÿVerificação de ponto

área dupla = 5,2;


cout << "área"; cout
<<área;

2.3 Lendo a entrada do teclado


Ler a entrada do teclado permite que o programa aceite a entrada do usuário.
Chave
Apontar
Na Listagem 2.1, o raio é fixo no código-fonte. Para usar um raio diferente, é necessário modificar o código-
fonte e recompilá-lo. Obviamente, isso não é conveniente. Você pode usar o objeto cin para ler a entrada do
teclado, conforme mostrado na Listagem 2.2.

Listagem 2.2 ComputeAreaWithConsoleInput.cpp


Nota de vídeo 1 #include <iostream>
Obtenha informações
2 usando namespace std;

3 4 int principal()
{
56 // Etapa 1: leitura no raio
7 raio duplo ;
8 cout << "Insira o raio: ";
Machine Translated by Google

2.3 Lendo a entrada do teclado 53


cin >> raio; entrada
9
10 // Passo 2: Área de computação
11 área dupla = raio * raio * 3,14159;
12
13 // Passo 3: Exibir a área
14 cout << "A área é " << area << endl;
15 16
17 retornar 0;
18}

Insira um raio: 2,5 A área é


19,6349

Insira um raio: 23 A área é


1661,9

A linha 8 exibe uma string "Enter a radius:" no console. Isso é conhecido como prompt incitar
porque direciona o usuário a inserir algo. Seu programa deve sempre informar ao usuário o que
inserir ao esperar uma entrada do teclado.
A linha 9 usa o objeto cin para ler um valor do teclado.

Operador
Entrada do de extração de Variável
console fluxo

cin >> raio;

Observe que cin (pronuncia-se see-in) significa entrada do console. O símbolo >> , conhecido entrada do console
como operador de extração de fluxo, atribui uma entrada a uma variável. Conforme mostrado na operador de extração de fluxo
execução de exemplo, o programa exibe a mensagem "Insira um raio: "; o usuário então insere
o número 2, que é atribuído ao raio variável. O objeto cin faz com que um programa espere até
que os dados sejam inseridos no teclado e a tecla Enter seja pressionada. C++ converte
automaticamente os dados lidos do teclado para o tipo de dados da variável.

Observação

O operador >> é o oposto do operador << . O >> indica que os dados fluem de cin para
uma variável. O << mostra que os dados fluem de uma variável ou string para cout. Você
pode pensar no operador de extração de fluxo >> como uma seta que aponta para a
variável e no operador de inserção de fluxo << como uma seta que aponta para o cout,
conforme mostrado aqui:

cin >> variável; // variável cin ;


cout << "Bem-vindo "; // cout "Bem-vindo";
Machine Translated by Google

54 Capítulo 2 Programação Elementar

entrada múltipla Você pode usar uma única instrução para ler várias entradas. Por exemplo, a instrução a seguir lê três valores nas
variáveis x1, x2 e x3:

Variáveis

demônio >> x1 >> x2 >> x3;

A Listagem 2.3 dá um exemplo de leitura de múltiplas entradas do teclado. O exemplo lê três números e
exibe sua média.

Listagem 2.3 ComputeAverage.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 // Solicita ao usuário que insira três números
7 duplo número1, número2, número3;
cout << "Digite três números: ";
lendo três números cin >> número1 >> número2 >> número3;
8
9 10 11 //Calcula a média
12 média dupla = (número1 + número2 + número3)/ 3;
13
14 //Exibir resultado
""
15 cout << "A média de " << número3 << << número1 << << número2
""
16 << "é" << média << final;
17
18 retornar 0;
19 }

Digite três números: 1 2 3


A média de 1 2 3 é 2
insira a entrada em uma linha

Insira três números: 10,5 11 11,5 A

insira a entrada em várias linhas média


de 10,5 11 11,5 é 11

A linha 8 solicita que o usuário insira três números. Os números são lidos na linha 9. Você pode inserir
três números separados por espaços e, em seguida, pressionar a tecla Enter ou inserir cada número
seguido pela tecla Enter , conforme mostrado no exemplo de execução deste programa.

Observação

A maioria dos programas dos primeiros capítulos deste livro executa três etapas: entrada, processo
DOENÇA e saída, chamadas IPO. Entrada está recebendo entrada do usuário; o processo está produzindo
resultados usando a entrada; e a saída está exibindo os resultados.
Machine Translated by Google

2.5 Variáveis 55

2.2 Como você escreve as instruções para permitir que o usuário insira um número inteiro e um
valor duplo no teclado? ÿVerificação de ponto

2.3 Qual será a impressão se você inserir 2 2.5 ao executar o código a seguir?

largura dupla ;
altura dupla ;
cin >> largura >> altura;
cout << largura * altura;

2.4 Identificadores
Identificadores são os nomes que identificam elementos como variáveis e funções em um programa.
Chave
Apontar

Como você pode ver na Listagem 2.3, main, number1, number2, number3 e assim por diante são os nomes das coisas que
aparecem no programa. Na terminologia de programação, esses nomes são chamados de identificadores. Todos os identificador

identificadores devem obedecer às seguintes regras: regras de nomenclatura de identificador

n Um identificador é uma sequência de caracteres que compreende letras, dígitos e sublinhados


(_).

n Um identificador deve começar com uma letra ou sublinhado; não pode começar com um dígito.

n Um identificador não pode ser uma palavra reservada. (Consulte o Apêndice A, “Palavras-chave C++”, para obter uma
lista de palavras reservadas.)

n Um identificador pode ter qualquer comprimento, mas seu compilador C++ pode impor restrições.
Use identificadores de 31 caracteres ou menos para garantir a portabilidade.

Por exemplo, área e raio são identificadores legais, enquanto 2A e d+4 são identificadores ilegais porque não seguem as
regras. O compilador detecta identificadores ilegais e relata erros de sintaxe.

Observação

Como C++ diferencia maiúsculas de minúsculas, area, Area e AREA são identificadores diferentes. maiúsculas e minúsculas

Dica
Identificadores são usados para nomear variáveis, funções e outras coisas em um programa.
Identificadores descritivos tornam os programas fáceis de ler. Evite usar abreviações para identificadores—
usar palavras completas é mais descritivo. Por exemplo, numberOfStudents é melhor que
numStuds, numOfStuds ou numOfStudents. Usamos nomes descritivos para programas nomes descritivos
completos no texto. No entanto, por questões de brevidade, ocasionalmente usamos nomes
de variáveis como i, j, k, x e y nos trechos de código. Esses nomes também fornecem um tom
genérico aos trechos de código.

2.4 Quais dos seguintes identificadores são válidos? Quais são palavras-chave C++?
ÿVerificação de ponto
milhas, teste, a++, ––a, 4#R, $4, #44, aplicativos
principal, duplo, int, x, y, raio

2.5 Variáveis
Variáveis são usadas para representar valores que podem ser alterados no programa.
Chave
Apontar
Como você pode ver nos programas das seções anteriores, variáveis são usadas para armazenar valores a serem usados
posteriormente em um programa. Elas são chamadas de variáveis porque seus valores podem ser alterados. por que chamadas de variáveis?
Machine Translated by Google

56 Capítulo 2 Programação Elementar

No programa da Listagem 2.2, raio e área são variáveis do tipo ponto flutuante de precisão dupla. Você pode atribuir
qualquer valor numérico ao raio e à área, e os valores do raio e da área podem ser reatribuídos. Por exemplo, no
código a seguir, o raio é inicialmente 1,0 (linha 2) e depois alterado para 2,0 (linha 7), e a área é definida como
3,14159 (linha 3) e depois redefinida para 12,56636 (linha 8).

1 // Calcula a primeira área


2 raio = 1,0; 3 área = raio: 1,0
raio * raio * 3,14159; 4 cout << "A área é " 5 6 // Calcula área: 3.14159
a segunda área << área << "para raio" << raio;

7 raio = 2,0; 8 área = raio: 2,0


raio * raio * 3,14159; 9 cout << "A área é" área: 12.56636
<< área << "para raio" << raio;

Variáveis são usadas para representar dados de um determinado tipo. Para usar uma variável, você a declara
informando ao compilador seu nome, bem como o tipo de dados que ela pode armazenar. A declaração da variável
diz ao compilador para alocar espaço de memória apropriado para a variável com base em seu tipo de dados.
A sintaxe para declarar uma variável é

tipo de dados nomevariável;

declarar variável Aqui estão alguns exemplos de declarações de variáveis:

contagem interna ; // Declara count como uma variável inteira


raio duplo ; // Declara radius como uma variável dupla
taxa de juros dupla ; //Declara interestRate como uma variável dupla

tipo interno Esses exemplos usam os tipos de dados int e double. Posteriormente, você conhecerá tipos de dados adicionais,

tipo longo como short, long, float, char e bool.


Se as variáveis forem do mesmo tipo, elas poderão ser declaradas juntas, como segue:

tipo de dados variável1, variável2,..., variáveis;

As variáveis são separadas por vírgulas. Por exemplo,

int eu, j, k; // Declara i, j e k como variáveis int

Observação

declarar vs. definir Dizemos “declarar uma variável”, mas não “definir uma variável”. Estamos fazendo uma
distinção sutil aqui. Uma definição define o que é o item definido, mas uma declaração geralmente
envolve a alocação de memória para armazenar dados do item declarado.

Observação

convenção de nomenclatura de variáveis


Por convenção, os nomes das variáveis estão em letras minúsculas. Se um nome consistir em várias
palavras, concatene todas elas e coloque a primeira letra de cada palavra em maiúscula, exceto a primeira.
Exemplos de variáveis são raio e taxa de juros.

inicializar variáveis As variáveis geralmente têm valores iniciais. Você pode declarar uma variável e inicializá-la em uma única etapa.
Considere, por exemplo, o seguinte código:

contagem interna = 1;
Machine Translated by Google

2.6 Instruções de Atribuição e Expressões de Atribuição 57

Isso é equivalente às próximas duas afirmações:

contagem interna ;

contagem = 1;

Você também pode usar abreviações para declarar e inicializar variáveis do mesmo tipo juntas.
Por exemplo,

int i = 1, j = 2;

Observação

C++ permite uma sintaxe alternativa para declarar e inicializar variáveis, conforme mostrado
no exemplo a seguir:

int i(1), j(2);

o que equivale a

int i = 1, j = 2;

Dica
Uma variável deve ser declarada antes que um valor possa ser atribuído a ela. Uma variável
declarada em uma função deve receber um valor. Caso contrário, a variável é chamada de variável não inicializada
não inicializada e seu valor é imprevisível. Sempre que possível, declare uma variável e
atribua seu valor inicial em uma única etapa. Isto tornará o programa fácil de ler e evitará a programação
erros.

Cada variável tem um escopo. O escopo de uma variável é a parte do programa onde a variável pode escopo de uma variável
ser referenciada. As regras que definem o escopo de uma variável serão introduzidas gradualmente mais
adiante neste livro. Por enquanto, é suficiente saber que uma variável deve ser declarada e inicializada
antes de poder ser usada.

2.5 Identifique e corrija os erros no código a seguir:


ÿVerificação de ponto
1 #include<iostream>
2 usando namespace std;

3 4 int Principal()
5 {
6 int eu = k + 1;
7 cout << I << endl;
8
int eu = 1;
9 cout << i << endl;
10 11
12 retornar 0;
13}

2.6 Declarações de Atribuição e Atribuição


Expressões
Uma instrução de atribuição designa um valor para uma variável. Uma instrução de atribuição Chave
pode ser usada como uma expressão em C++. Apontar
Machine Translated by Google

58 Capítulo 2 Programação Elementar

declaração de atribuição Depois que uma variável é declarada, você pode atribuir um valor a ela usando uma instrução de
operador de atribuição atribuição. Em C++, o sinal de igual (=) é usado como operador de atribuição. A sintaxe para instruções
de atribuição é a seguinte:

variável = expressão;

expressão Uma expressão representa um cálculo envolvendo valores, variáveis e operadores que,
reunindo-os, avalia um valor. Por exemplo, considere o seguinte código:

int y = 1; raio //Atribuir 1 à variável y


duplo = 1,0; int x = 5 * (3/2 ) ; // Atribui 1,0 ao raio variável
x = y + 1; área = raio * raio * // Atribui o valor da expressão a x
3,14159; //Área //Atribuir a adição de y e 1 a x
de computação

Você pode usar uma variável em uma expressão. Uma variável também pode ser usada em ambos os lados do =
operador. Por exemplo,

x = x + 1;

Nesta declaração de atribuição, o resultado de x + 1 é atribuído a x. Se x for 1 antes do estado-


mento é executado, então se torna 2 após a execução da instrução.
Para atribuir um valor a uma variável, você deve colocar o nome da variável à esquerda da atribuição.
operador de gerenciamento. Assim, a seguinte afirmação está errada:

1 =x; // Errado

Observação

Em matemática, x = 2 * x + 1 denota uma equação. No entanto, em C++, x = 2 * + x


1 é uma instrução de atribuição que avalia a expressão 2 * o x + 1 e atribui
resultado como x.

Em C++, uma instrução de atribuição é essencialmente uma expressão que avalia o valor a ser
atribuído à variável no lado esquerdo do operador de atribuição. Por esse motivo, uma instrução de
expressão de atribuição atribuição também é conhecida como expressão de atribuição. Por exemplo, a seguinte afirmação está
correta:

contagem << x = 1;

o que equivale a

x = 1;
contagem << x;

Se um valor for atribuído a diversas variáveis, você poderá usar esta sintaxe:

eu = j = k = 1;

o que equivale a

k = 1;
j=k;
eu = j;

2.6 Identifique e corrija os erros no código a seguir:


ÿVerificação de ponto
1 #include <iostream>
2 usando namespace std;
Machine Translated by Google

2.7 Constantes Nomeadas 59

3 4 int principal()
5{
6 int i = j = k = 1;
7

retornar 0;
8 9}

2.7 Constantes Nomeadas


Uma constante nomeada é um identificador que representa um valor permanente.
Chave
Apontar
O valor de uma variável pode mudar durante a execução de um programa, mas uma constante nomeada, ou
simplesmente constante, representa dados permanentes que nunca mudam. Em nossa ComputeArea constante
programa, p é uma constante. Se você usa com frequência, não vai querer continuar digitando 3.14159; em
vez disso, você pode declarar uma constante para p. Aqui está a sintaxe para declarar uma constante:

tipo de dados const CONSTANTNAME = valor;

Uma constante deve ser declarada e inicializada na mesma instrução. A palavra const é uma palavra-
chave C++ para declarar uma constante. Por exemplo, você pode declarar p como uma constante e reescrever palavra-chave const
a Listagem 2.2 como mostrado na Listagem 2.4.

Listagem 2.4 ComputeAreaWithConstant.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5 {
6 const duplo PI = 3,14159; PI constante
7

// Etapa 1: leitura no raio


raio duplo ;
8 cout << "Insira o raio: ";
9 cin >> raio;
10 11 12
13 // Passo 2: Área de computação
14 área dupla = raio * raio * PI;
15
16 // Passo 3: Exibir a área
17 cout << "A área é ";
18 cout << área << endl;
19
20 retornar 0;
21}

Cuidado
Por convenção, as constantes são nomeadas em letras maiúsculas: PI, não pi ou Pi. convenção de nomenclatura constante

Observação

Há três benefícios em usar constantes: (1) você não precisa digitar repetidamente o mesmo valor; (2) se benefícios das constantes
você tiver que alterar o valor constante (por exemplo, de 3,14 para 3,14159
para PI), você precisa alterá-lo apenas em um único local no código-fonte; (3) nomes descritivos de
constantes tornam o programa fácil de ler.
Machine Translated by Google

60 Capítulo 2 Programação Elementar

2.7 Quais são os benefícios de usar constantes nomeadas? Declarar uma constante int SIZE
ÿVerificação de ponto com valor 20.

2.8 Traduza o seguinte algoritmo em código C++:


Etapa 1: declare uma variável dupla chamada milhas com valor inicial 100.
Etapa 2: declare uma constante dupla chamada KILOMETERS_PER_MILE com valor
1.609.

Passo 3: Declare uma variável dupla chamada quilômetros, multiplique milhas e


KILOMETERS_PER_MILE e atribua o resultado a quilômetros.
Etapa 4: exibir quilômetros no console.
O que são quilômetros após a Etapa 4?

2.8 Tipos de dados numéricos e operações


Chave
C++ possui nove tipos numéricos para inteiros e números de ponto flutuante com operadores +,
Apontar -, *, / e %.

2.8.1 Tipos Numéricos


Cada tipo de dados possui um intervalo de valores. O compilador aloca espaço de memória para cada
variável ou constante de acordo com seu tipo de dados. C++ fornece tipos de dados primitivos para valores
numéricos, caracteres e valores booleanos. Esta seção apresenta tipos de dados numéricos e operações.
A Tabela 2.1 lista os tipos de dados numéricos com seus intervalos e tamanhos de armazenamento típicos.

Tabela 2.1 Tipos de dados numéricos

Nome Sinonímia Faixa Tamanho de armazenamento

curto curto int


-215 to 215-1 (-32, 768 to 32,767) 0 to assinado de 16 bits

curto não assinado int curto não assinado 216-1 (65535) -231 to 16 bits não assinado

assinado 231-1 (-2147483648 to 2147483647) 0 to 232-1 32 bits

não assinado int não assinado (4294967295) -231 32 bits não assinado

longo longo interno (-2147483648) to 231 -1 (2147483647) 0 a 232-1 assinado de 32 bits

não assinado longo não assinado longo int (4294967295) 32 bits não assinado

flutuador
Faixa negativa: IEEE 754 de 32 bits

-3,4028235E+38 a -1,4E-45

Faixa positiva:

1.4E-45 a 3.4028235E+38

dobro Faixa negativa: IEEE 754 de 64 bits

-1,7976931348623157E+308 a -4,9E-324

Faixa positiva:

4.9E-324 a 1.7976931348623157E+308

longo duplo Faixa negativa: 80 bits

-1,18E+4932 a -3,37E-4932

Faixa positiva:

3,37E-4932 a 1,18E+4932

Dígitos decimais significativos: 19


Machine Translated by Google

2.8 Tipos de Dados Numéricos e Operações 61

C++ usa três tipos de inteiros: short, int e long. Cada tipo inteiro vem em dois sabores: assinado e
não assinado. Metade dos números representados por um int assinado são negativos e a outra metade assinado versus não assinado
não é negativa. Todos os números representados por um unsigned int não são negativos. Como você
tem o mesmo tamanho de armazenamento para ambos, o maior número que você pode armazenar em
um unsigned int é duas vezes maior que o maior número positivo que você pode armazenar em um
unsigned int. Se você sabe que o valor armazenado em uma variável é sempre não negativo, declare-o como sem sinal.

Observação

short int é sinônimo de curto. unsigned short int é sinônimo de unsigned tipos sinônimos
short. não assinado é sinônimo de int não assinado. longo interno
é sinônimo de longo. unsigned long int é sinônimo de unsigned long. Por exemplo,

int curto i = 2;

é o mesmo que

curto eu = 2;

C++ usa três tipos de números de ponto flutuante: float, double e long double. O tipo duplo tipos de ponto flutuante
geralmente é duas vezes maior que o flutuante. Portanto, double é conhecido como precisão dupla,
enquanto float é precisão simples. O duplo longo é ainda maior que o duplo. Para a maioria das
aplicações, é desejável usar o tipo duplo .
Por conveniência, C++ define as constantes INT_MIN, INT_MAX, LONG_MIN, LONG_MAX, FLT_MIN, FLT_MAX,
DBL_MIN e DBL_MAX no arquivo de cabeçalho <limits> . Essas constantes são úteis na programação. Execute o código
a seguir na Listagem 2.5 e veja quais valores constantes são definidos pelo seu compilador:

Listagem 2.5 LimitsDemo.cpp


1 #include <iostream>
2 #include <limites> cabeçalho de limites

3 usando namespace std;

4 5 int principal()
6{
7 cout << "INT_MIN é" << INT_MIN << endl;
8 cout << "INT_MAX é" << INT_MAX << endl;
9 cout << "LONG_MIN é" << LONG_MIN << endl;
10 cout << "LONG_MAX é" << LONG_MAX << endl;
11 cout << "FLT_MIN é" << FLT_MIN << endl;
12 cout << "FLT_MIN é" << FLT_MAX << endl;
13 cout << "DBL_MIN é" << DBL_MIN << endl;
14 cout << "DBL_MIN é" << DBL_MAX << endl;
15
16 retornar 0;
17}

INT_MIN é -2147483648
INT_MAX é 2147483647
LONG_MIN é -2147483648
LONG_MAX é 2147483647
FLT_MIN é 1.17549e-038
FLT_MAX é 3.40282e+038
DBL_MIN é 2.22507e-308
DBL_MAX é 1,79769e+308
Machine Translated by Google

62 Capítulo 2 Programação Elementar

Observe que essas constantes podem não ser definidas em alguns compiladores antigos.
o tamanho pode variar O tamanho dos tipos de dados pode variar dependendo do compilador e do computador que você está usando.
Normalmente, int e long têm o mesmo tamanho. Em alguns sistemas, long requer 8 bytes.
função sizeof Você pode usar a função sizeof para encontrar o tamanho de um tipo ou variável em sua máquina.
A Listagem 2.6 dá um exemplo que exibe o tamanho de int, long e double, e as variáveis age e area em sua
máquina.

Listagem 2.6 SizeDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
tamanho de (int) 6 cout << "O tamanho de int: " << sizeof(int) << " bytes" << endl;
7 cout << "O tamanho do comprimento: " << sizeof(long) << " bytes" << endl;
cout << "O tamanho do double: " << sizeof(double)
8 << "bytes" << endl;
9 10
11 área dupla = 5,4;
"
tamanho de (área) 12 cout << "O tamanho da área variável: " bytes" << tamanho de (área)
13 << << endl;
14
15 idade interna = 31;
"
16 cout << "O tamanho da variável idade: " << tamanhode(idade)
17 << bytes" << endl;
18
19 retornar 0;
20}

O tamanho do int: 4 bytes


O tamanho do longo: 4 bytes
O tamanho do duplo: 8 bytes
O tamanho da área variável: 8 bytes
O tamanho da idade variável: 4 bytes

Invocar sizeof(int), sizeof(long) e sizeof(double) (linhas 6–8) retorna o número de


bytes alocados para os tipos int, long e double , respectivamente. Invocar sizeof(area)
e sizeof(age) retorna o número de bytes alocados para as variáveis area e age,
respectivamente.

2.8.2 Literais Numéricos


literal Um literal é um valor constante que aparece diretamente em um programa. Nas instruções a seguir, por exemplo,
34 e 0,305 são literais:

int i = 34;
pé duploToMeters = 0,305;

literais octais e hexadecimais Por padrão, um literal inteiro é um número inteiro decimal. Para denotar um literal inteiro octal, use um 0 (zero)
à esquerda, e para denotar um literal inteiro hexadecimal, use um 0x ou 0X à esquerda
(zerox). Por exemplo, o código a seguir exibe o valor decimal 65535 para o número hexadecimal FFFF e o valor
decimal 8 para o número octal 10.
Machine Translated by Google

2.8 Tipos de Dados Numéricos e Operações 63


""
cout << 0xFFFF << << 010;

Números hexadecimais, números binários e números octais são apresentados no Apêndice D, “Sistemas Numéricos”.

Literais de ponto flutuante podem ser escritos em notação científica na forma de * 10b . por exemplo, a notação Para literais de ponto flutuante
científica para 123,456 é 1,23456 * 102 e para 0,0123456 é 1,23456 * 10-2 . notação científica

Uma sintaxe especial é usada para escrever números em notação científica. Por exemplo,
1,23456 * 102 é escrito como 1,23456E2 ou 1,23456E+2 e 1,23456 * 10-2 como 1,23456E-2. E (ou e) representa um
expoente e pode estar em letras minúsculas ou maiúsculas.

Observação

Os tipos float e double são usados para representar números com ponto decimal. tipo flutuante
Por que eles são chamados de números de ponto flutuante? Esses números são armazenados tipo duplo
internamente em notação científica. Quando um número como 50,534 é convertido em notação por que chamado de ponto flutuante?
científica, como 5,0534E+1, seu ponto decimal é movido (ou seja, flutua) para uma nova posição.

2.8.3 Operadores Numéricos


Os operadores para tipos de dados numéricos incluem os operadores aritméticos padrão: adição (+), subtração (–), operador
multiplicação (*), divisão (/) e resto (%), conforme mostrado na Tabela 2.2. Os operandos são os valores operados por
um operador. operandos

Tabela 2.2 Operadores Numéricos

Nome do operador Exemplo Resultado

+ Adição 34 + 1 35
- Subtração 34,0 - 0,1 33,9
* 300*30 9.000
Multiplicação

/ Divisão 1,0 / 2,0 0,5

% Módulo 20% 3 2

Quando ambos os operandos de uma divisão são inteiros, o resultado da divisão é o quociente e a parte fracionária divisão inteira
é truncada. Por exemplo, 5/2 produz 2, não 2,5, e –5/2 produz -2, não –2,5. Para realizar uma divisão matemática regular,
um dos operandos deve ser um número de ponto flutuante. Por exemplo, 5,0/2 resulta em 2,5.

O operador % , conhecido como módulo ou operador de resto , funciona apenas com operandos inteiros e produz o módulo
resto após a divisão. O operando da esquerda é o dividendo e o operando da direita é o divisor. Portanto, 7% 3 produz restante
1, 3% 7 produz 3, 12% 4 produz 0, 26% 8 produz 2 e 20% 13 produz 7.

2 0 3 3 1 Quociente

37 73 4 12 8 26 Divisor 13 20 Dividendo
6 0 12 24 13

1 3 0 2 7 Restante

O operador % é frequentemente usado com números inteiros positivos, mas também pode ser usado com números
inteiros negativos. O comportamento do operador % envolvendo números inteiros negativos depende do compilador. Em
C++, o operador % é apenas para números inteiros.
Módulo é muito útil em programação. Por exemplo, um número par % 2 é sempre 0 e um número ímpar % 2 é sempre módulo
1. Portanto, você pode usar esta propriedade para determinar se um número é par ou ímpar. Se hoje for sábado, será
sábado novamente em 7 dias. Suponha que você e seu
Machine Translated by Google

64 Capítulo 2 Programação Elementar

amigos vão se encontrar em 10 dias. Que dia é daqui a 10 dias? Você pode descobrir que o dia é terça-feira
usando a seguinte expressão:

Sábado é o 6º dia da semana


Uma semana tem 7 dias

(6 + 10)% 7 é 2
O segundo dia da semana é terça-feira
Depois de 10 dias

O programa na Listagem 2.7 obtém minutos e segundos restantes de um período de tempo


em segundos. Por exemplo, 500 segundos contém 8 minutos e 20 segundos.

Listagem 2.7 DisplayTime.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
// Solicita entrada do usuário
6 7 segundos inteiros ;
8 cout << "Digite um número inteiro para segundos: ";
9 cin >> segundos;
10 int minutos = segundos/ 60;
11 int segundos restantes = segundos% 60;
12 cout << segundos << "segundos é" << minutos <<
13 " minutos e " << segundos restantes << " segundos " << fim;
14
15 retornar 0;
16}

Insira um número inteiro para segundos: 500


500 segundos são 8 minutos e 20 segundos

Linha# segundos minutos segundos restantes


9 500

10 8

11 20

A linha 9 lê um número inteiro por segundos. A linha 10 obtém os minutos usando segundos/60.
A linha 11 (segundos% 60) obtém os segundos restantes após subtrair os minutos.
operador unário Os operadores + e - podem ser unários e binários. Um operador unário possui apenas um operando; um
operador binário operador binário tem dois. Por exemplo, o operador - em -5 é um operador unário para negar o número 5,
enquanto o operador - em 4 - 5 é um operador binário para subtrair 5 de 4.

2.8.4 Operações com Expoentes


função pow (a, b) A função pow(a, b) pode ser usada para calcular ab . pow é uma função definida no cmath
biblioteca. A função é invocada usando a sintaxe pow(a, b) (ou seja, pow(2.0, 3)) que retorna o resultado de
ab (23 ). Aqui, aeb são parâmetros para a função pow e os números 2.0 e 3 são valores reais usados para
invocar a função. Por exemplo,

cout << pow(2.0, 3) << endl; //Exibição 8.0


cout << pow(4,0, 0,5) << endl; //Exibição 2.0
Machine Translated by Google

2.9 Avaliando Expressões e Precedência de Operadores 65

cout << pow(2.5, 2) << endl; // Exibe 6,25 cout << pow(2.5,
-2) << endl; // Exibição 0,16

Observe que alguns compiladores C++ exigem que a ou b em pow(a, b) seja um valor decimal.
Aqui usamos 2.0 em vez de 2.
Mais detalhes sobre funções serão apresentados no Capítulo 6. Por enquanto, é suficiente saber
como invocar a função pow para realizar a operação de expoente.

2.9 Encontre o maior e o menor short, int, long, float e double em sua máquina.
Qual desses tipos de dados requer a menor quantidade de memória? ÿVerificação de ponto

2.10 Quais dos seguintes são literais corretos para números de ponto flutuante?
12,3, 12,3e+2, 23,4e-2, –334,4, 20,5, 39, 40

2.11 Quais dos seguintes são iguais a 52.534?


5,2534e+1, 0,52534e+2, 525,34e-1, 5,2534e+0

2.12 Mostre o resultado dos seguintes restos:

56 % 6
78 % 4
34 % 5
34 % 15
5%1
1%5

2.13 Se hoje for terça-feira, que dia será daqui a 100 dias?
2.14 Qual é o resultado de 25/4 ? Como você reescreveria a expressão se quisesse
o resultado seja um número de ponto flutuante?

2.15 Mostre o resultado do seguinte código:

cout << 2 * (5/2 + 5/2 ) << endl ; cout << 2 * 5/2


+ 2 * 5/2 << endl ; _ cout << 2 * (5/2 ) << endl; cout
<< 2 * 5/2 << endl ;

2.16 As afirmações a seguir estão corretas? Se sim, mostre a saída.

cout << " 25/4 é " << 25/4 << endl ; cout << "25/4,0
é " << 25 / 4,0 << endl; cout << " 3 * 2/4 é" << 3 * 2/4 <<
endl ; cout << "3,0 * 2/4 é"
<< 3,0 * 2/4 << final ;

2.17 Escreva uma instrução para exibir o resultado de 23,5 .


2
2.18 Suponha que m e r sejam inteiros. Escreva uma expressão C++ para o para obter uma flutuação
resultado do ponto mr.

2.9 Avaliando Expressões e Precedência de Operadores


As expressões C++ são avaliadas da mesma maneira que as expressões aritméticas.
Chave
Apontar
Escrever expressões numéricas em C++ envolve uma tradução direta de uma expressão aritmética
usando operadores C++. Por exemplo, a expressão aritmética

3 + 4x - 10( y - 5)(a + b + c) 9+x


+
5 x +9¢4 x eÿ
Machine Translated by Google

66 Capítulo 2 Programação Elementar

pode ser traduzido em uma expressão C++ como

(3 + 4 * x) / 5 - 10 * (y - 5) * (a + b + c) / x + 9 * (4 / x + (9 + x) / y)

avaliar uma expressão Embora C++ tenha sua própria maneira de avaliar uma expressão nos bastidores, o resultado de uma
expressão C++ e sua expressão aritmética correspondente são os mesmos. Portanto, você pode aplicar com
segurança a regra aritmética para avaliar uma expressão C++. Os operadores contidos entre pares de
parênteses são avaliados primeiro. Os parênteses podem ser aninhados; nesse caso, a expressão entre
parênteses internos é avaliada primeiro. Quando mais de um operador é usado em uma expressão, a
regra de precedência do operador seguinte regra de precedência de operador é usada para determinar a ordem de avaliação.

n Os operadores de multiplicação, divisão e resto são aplicados a seguir. Se uma expressão contiver
vários operadores de multiplicação, divisão e resto, eles serão aplicados da esquerda para a direita.

n Os operadores de adição e subtração são aplicados por último. Se uma expressão contiver vários
operadores de adição e subtração, eles serão aplicados da esquerda para a direita.

Aqui está um exemplo de como uma expressão é avaliada:

3 + 4 * 4 + 5 * (4 + 3) - 1

(1) entre parênteses primeiro


3+4*4+5*7–1
(2) multiplicação
3 + 16 + 5 * 7 – 1
(3) multiplicação
3 + 16 + 35 – 1

(4) adição
19 + 35 – 1

(5) adição
54-1 _ _

(6) subtração
53

A Listagem 2.8 fornece um programa que converte um grau Fahrenheit em Celsius usando a fórmula
celsius = (5 9 )( fahrenheit -32).

Listagem 2.8 FahrenheitToCelsius.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 //Insira um diploma em Fahrenheit
7 duplo Fahrenheit;
cout << "Insira um diploma em Fahrenheit: ";
entrada Fahrenheit cin >> Fahrenheit;
8
9 10 11 //Obter um grau Celsius
calcular Celsius 12 duplo Celsius = (5,0 / 9) * (Fahrenheit - 32);
13
Machine Translated by Google

2.10 Estudo de caso: Exibindo a hora atual 67


14 //Exibir resultado
15 cout << "Fahrenheit " << Fahrenheit << "em Celsius" " é " << saída de exibição
16 Celsius<< << endl;
17
18 retornar 0;
19}

Insira um grau em Fahrenheit: 100 Fahrenheit


100 é 37,7778 em Celsius

Linha# Fahrenheit Celsius

7 indefinido

9 100

12 37.7778

5
Tenha cuidado ao aplicar a divisão. A divisão de dois inteiros produz um inteiro em C++. 9é divisão inteira versus
decimal
traduzido para 5.0/9 em vez de 5/9 na linha 12, porque 5/9 produz 0 em C++.

2.19 Como você escreveria a seguinte expressão aritmética em C++?


ÿVerificação de ponto

4 3 + d(2 + uma)
a. - 9 (uma + aC) +
3(r + 34) a + bd

2,5+t
b. 5,5 * (r + 2,5)

2.10 Estudo de caso: Exibindo a hora atual


Você pode invocar a função time(0) para retornar a hora atual.
Chave
Apontar
O problema é desenvolver um programa que exiba a hora atual em GMT (Greenwich Mean Time) no formato
hora:minuto:segundo, como 13:19:8.
A função time(0) , no arquivo de cabeçalho ctime , retorna o tempo atual em segundos decorridos desde função de tempo (0)
00:00:00 de 1º de janeiro de 1970 GMT, conforme mostrado na Figura 2.1. Desta vez é conhecida como a
época UNIX. A época é o ponto em que o tempo começa. 1970 foi o ano em que o sistema operacional UNIX Época UNIX
foi formalmente introduzido.

Decorrido
tempo
Tempo

Época UNIX Hora atual


01-01-1970 tempo (0)
00:00:00 GMT

Figura 2.1 Invocar time(0) retorna o número de segundos desde a época Unix.
Machine Translated by Google

68 Capítulo 2 Programação Elementar

Você pode usar esta função para obter a hora atual e então calcular o segundo atual,
minuto e hora da seguinte forma:

1. Obtenha o total de segundos desde a meia-noite de 1º de janeiro de 1970, em totalSeconds , invocando


time(0) (por exemplo, 1203183086 segundos).

2. Calcule o segundo atual a partir de totalSeconds % 60 (por exemplo, 1203183086 segundos % 60 = 26, que
é o segundo atual).

3. Obtenha o total de minutos totalMinutes dividindo totalSeconds por 60 (por exemplo,


1203183086 segundos/ 60 = 20053051 minutos).

4. Calcule o minuto atual a partir de totalMinutes % 60 (por exemplo, 20053051 minutos % 60 = 31, que é o
minuto atual).

5. Obtenha o total de horas totalHoras dividindo totalMinutos por 60 (por exemplo, 20053051
minutos / 60 = 334217 horas).

6. Calcule a hora atual a partir do total de horas % 24 (por exemplo, 334217 horas % 24 = 17, que é a hora
atual).

A Listagem 2.9 mostra o programa completo seguido de uma execução de amostra.

Nota de vídeo
Listagem 2.9 ShowCurrentTime.cpp
Exibir hora atual
1 #include <iostream>
incluir ctime 2 #incluir <ctime>
3 usando namespace std;

4 5 int principal()
6{
7 // Obtenha o total de segundos desde meia-noite de 1º de janeiro de 1970
total de segundos int totalSegundos = tempo(0);
8
9 10 //Calcula o segundo atual no minuto na hora
segundos atuais 11 int currentSecond = totalSeconds% 60;
12
13 //Obtém o total de minutos
minutos atuais 14 int totalMinutos = totalSegundos/ 60;
15
16 //Calcula o minuto atual na hora
minuto atual 17 int CurrentMinute = totalMinutos% 60;
18
19 //Obtém o total de horas
total de horas 20 int totalHoras = totalMinutos/ 60;
21
22 //Calcula a hora atual
hora atual 23 int hora atual = totalhoras% 24;
24
25 //Exibir resultados
saída de exibição 26 cout << "O horário atual é " << <<hora atual << ":"
27 currentMinute << ":" << currentSecond << "GMT" << endl;
28
29 retornar 0;
30}

A hora atual é 17:31:26 GMT


Machine Translated by Google

2.11 Operadores de Atribuição Aumentada 69

Linha# 8 11 14 17 20 23
Variáveis

total de segundos 1203183086


atualSegundo 26
totalMinutos 20053051
minuto atual 31
total de horas 334217
hora atual 17

Quando time(0) (linha 8) é invocado, ele retorna a diferença, medida em segundos, entre
o GMT atual e meia-noite de 1º de janeiro de 1970 GMT.

2.20 Como você obtém os segundos, minutos e horas atuais?


ÿVerificação de ponto

2.11 Operadores de Atribuição Aumentada


Os operadores +, -, *, / e % podem ser combinados com o operador de atribuição para formar operadores
Chave
aumentados. Apontar

Freqüentemente, o valor atual de uma variável é usado, modificado e depois reatribuído à mesma variável. Por
exemplo, a instrução a seguir aumenta a contagem da variável em 1:

contagem = contagem + 1;

C++ permite combinar operadores de atribuição e adição usando uma atribuição aumentada.
operador de gerenciamento. Por exemplo, a afirmação anterior pode ser escrita da seguinte forma:

contar += 8;

O += é chamado de operador de atribuição de adição. Outros operadores aumentados são mostrados na Tabela operador de atribuição de adição
2.3.

Tabela 2.3 Operadores de Atribuição Aumentada

Nome do operador Exemplo Equivalente

+= Atribuição de adição eu += 8 eu = eu + 8
-= Atribuição de subtração eu -= 8 eu = eu – 8
*= Atribuição de multiplicação eu *= 8 eu = eu * 8

/= Atribuição de divisão eu /= 8 eu = eu / 8
%= Atribuição de módulo eu %= 8 eu = eu% 8

O operador de atribuição aumentada é executado por último depois dos outros operadores no
expressão são avaliadas. Por exemplo,

x /= 4 + 5,5 * 1,5;

é o mesmo que

x = x / (4 + 5,5 * 1,5);
Machine Translated by Google

70 Capítulo 2 Programação Elementar

Cuidado
Não há espaços nos operadores de atribuição aumentada. Por exemplo, + = deveria
ser +=.

Observação

Assim como o operador de atribuição (=), os operadores (+=, -=, *=, /=, %=) podem ser
usados para formar uma instrução de atribuição, bem como uma expressão. Por exemplo, no
código a seguir, x += 2 é uma instrução na primeira linha e uma expressão na segunda linha.

x+= 2; // Declaração
cout << (x += 2); // Expressão

2.21 Mostre a impressão do seguinte código:


ÿVerificação de ponto

intuma = 6;
uma -= uma + 1;
cout << a << endl;
uma *= 6;
cout << a << endl;
uma /= 2;
cout << a << endl;

2.12 Operadores de Incremento e Decremento


Os operadores de incremento (++) e decremento (--) servem para incrementar e
Chave
Apontar decrementar uma variável em 1.

operador de incremento (++) Os ++ e -- são dois operadores abreviados para aumentar e diminuir uma variável em 1. Eles são úteis
operador de decremento (--) porque geralmente é o quanto o valor precisa ser alterado em muitas tarefas de programação. Por
exemplo, o código a seguir aumenta i em 1 e decrementa j
por 1.

int i = 3, j = 3;
eu++; // eu me torno 4
j--; // j se torna 2

i++ é pronunciado como i plus plus e i-- como i menos menos. Esses operadores são conhecidos
pós-incremento como incremento pós-fixado (pós-incremento) e decremento pós-fixado (pós-decremento), porque os
pós-decremento operadores ++ e -- são colocados após a variável. Esses operadores também podem ser colocados
antes da variável. Por exemplo,

int i = 3, j = 3;
++eu; // eu me torno 4
--j; // j se torna 2

++i incrementa i em 1 e --j decrementa j em 1. Esses operadores são conhecidos como incremento
pré-incremento de prefixo (pré-incremento) e decremento de prefixo (pré-decremento).
pré-decremento Como você pode ver, o efeito de i++ e ++i ou i-- e --i são os mesmos nos exemplos anteriores. No
entanto, seus efeitos são diferentes quando usados em expressões. A Tabela 2.4 descreve suas
diferenças e dá exemplos.
Machine Translated by Google

2.12 Operadores de Incremento e Decremento 71

Tabela 2.4 Operadores de Incremento e Decremento


Nome do operador Descrição Exemplo (suponha que i = 1)

++era pré-incremento Aumente var em 1 e use o novo valor int j = ++i;


var na instrução
// j é 2, eu é 2
era++ pós-incremento Aumente var em 1, mas use o valor int j = i++;
var original na instrução
// j é 1, i é 2
--era pré-decremento Diminua var em 1 e use o novo valor int j = --i;
var na instrução
// j é 0, i é 0
era-- postdecrement Diminua var em 1 e use o int j = eu--;
valor var original na instrução
// j é 1, i é 0

Aqui estão exemplos adicionais para ilustrar as diferenças entre a forma de prefixo de ++
(ou --) e a forma postfix de ++ (ou --). Considere o seguinte código:

int eu = 10;
Mesmo efeito que
int novoNum = 10 * i++; int novoNum = 10 * i;
cout << "i é " << i << ", eu = eu + 1;
newNum é " << newNum;

eu tenho 11 anos, newNum é 100

Neste caso, i é incrementado em 1, então o valor antigo de i é usado na multiplicação. Então


newNum torna-se 100. Se i++ for substituído por ++i como segue,

int eu = 10;
Mesmo efeito que eu = eu + 1;
int novoNum = 10 * (++i);
cout << "i é " << i << ", int novoNum = 10 * i;
newNum é " << newNum;

eu tenho 11, novoNum é 110

i é incrementado em 1 e o novo valor de i é usado na multiplicação. Assim novoNum


torna-se 110.
Aqui está outro exemplo:

duplo x = 1,1;
duplo y = 5,4;
duplo z = x–– + (++y);

Depois que todas as três linhas forem executadas, x se tornará 0,1, y se tornará 6,4 e z se tornará 7,5.

Cuidado
Para a maioria dos operadores binários, C++ não especifica a ordem de avaliação dos operandos.
Normalmente, você assume que o operando esquerdo é avaliado antes do operando direito. Isso
não é garantido em C++. Por exemplo, suponha que i seja 1; então a expressão

++eu + eu
Machine Translated by Google

72 Capítulo 2 Programação Elementar


avalia como 4 (2 + 2) se o operando esquerdo (++i) for avaliado primeiro e avalia como 3 (2 + 1) se
o operando direito (i) for avaliado primeiro.

ordem de avaliação de operandos Como o C++ não pode garantir a ordem de avaliação dos operandos, você não deve escrever código
que dependa da ordem de avaliação dos operandos.

2.22 Quais das seguintes afirmações são verdadeiras?


ÿVerificação de ponto
a. Qualquer expressão pode ser usada como uma instrução em C++.

b. A expressão x++ pode ser usada como uma declaração.

c. A afirmação x = x + 5 também é uma expressão.

d. A afirmação x = y = x = 0 é ilegal.

2.23 Mostre a impressão do seguinte código:

intuma = 6; int
b = a++; cout << a
<< endl; cout << b << endl; uma
= 6; b = ++a; cout << a << endl;
cout << b
<< endl;

2.24 Mostre a impressão do seguinte código:

intuma = 6; int
b = uma--; cout << a
<< endl; cout << b << endl; uma
= 6; b = --uma; cout << a <<
endl; cout
<< b << endl;

2.13 Conversões de tipo numérico


Números de ponto flutuante podem ser convertidos em inteiros usando conversão explícita.
Chave
Apontar
Você pode atribuir um valor inteiro a uma variável de ponto flutuante? Sim. Você pode atribuir um valor de ponto
flutuante a uma variável inteira? Sim. Ao atribuir um valor de ponto flutuante a uma variável inteira, a parte fracionária do
valor de ponto flutuante é truncada (não arredondada). Por exemplo:

int i = 34,7; // i se torna 34 double f = i; // f agora é 34


double g = 34.3; //g se torna 34,3 int j = g; // j tem agora
34 anos

Você pode realizar operações binárias com dois operandos de tipos diferentes? Sim. Se um inteiro e um número de ponto
flutuante estiverem envolvidos em uma operação binária, o C++ converte automaticamente o inteiro em um valor de ponto
flutuante. Portanto, 3 * 4,5 é igual a 3,0 * 4,5.
C++ também permite converter um valor de um tipo para outro explicitamente usando um
operador de elenco operador de fundição. A sintaxe é

static_cast<tipo>(valor)
Machine Translated by Google

2.13 Conversões de Tipo Numérico 73

onde valor é uma variável, um literal ou uma expressão e tipo é o tipo para o qual você deseja converter o
valor .
Por exemplo, a seguinte afirmação

cout << static_cast<int>(1.7);

exibe 1. Quando um valor duplo é convertido em um valor int , a parte fracionária é truncada.
A seguinte declaração

cout << static_cast<double>(1) / 2;

exibe 0,5, porque 1 é convertido em 1,0 primeiro e depois 1,0 é dividido por 2. No entanto, a instrução

contagem << 1/2 ;

exibe 0, porque 1 e 2 são números inteiros e o valor resultante também deve ser um número inteiro.

Observação

A conversão estática também pode ser feita usando a sintaxe (tipo) , ou seja, fornecendo o tipo de
destino entre parênteses, seguido por uma variável, um literal ou uma expressão. Isso é chamado
de elenco estilo C. Por exemplo, Elenco estilo C

int i = (int)5,4;

Isto é o mesmo que

int i = static_cast<int>(5.4);

O operador C++ static_cast é recomendado pelo padrão ISO. É preferível ao elenco estilo C.

A conversão de uma variável de um tipo com um intervalo pequeno para uma variável de um tipo com um
intervalo maior é conhecida como ampliação de um tipo. A conversão de uma variável de um tipo com um ampliando um tipo
intervalo grande para uma variável de um tipo com um intervalo menor é conhecida como estreitamento de um estreitando um tipo
tipo. Limitar um tipo, como atribuir um valor duplo a uma variável int , pode causar perda de precisão. perda de precisão
Informações perdidas podem levar a resultados imprecisos. O compilador emite um aviso quando você restringe
um tipo, a menos que você use static_cast para tornar a conversão explícita.

Observação

A conversão não altera a variável que está sendo convertida. Por exemplo, d não é alterado após a
conversão no código a seguir:

duplo d = 4,5;
int i = static_cast<int>(d); //i torna-se 4, mas d permanece inalterado

A Listagem 2.10 fornece um programa que exibe o imposto sobre vendas com dois dígitos após a vírgula
decimal.

Listagem 2.10 SalesTax.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 //Insira o valor da compra
Machine Translated by Google

74 Capítulo 2 Programação Elementar

valor de compra duplo ;


7 cout << "Insira o valor da compra: ";
8 cin >> valor da compra;
9
10 imposto duplo = valor da compra * 0,06;
fundição 11 cout << "O imposto sobre vendas é" << static_cast<int>(imposto * 100) / 100.0;
12 13
14 retornar 0;
15}

Insira o valor da compra: 197,55 O


imposto sobre vendas é 11,85

Linha# imposto sobre o valor da compra Saída


7 Indefinido
9 197,55
11 11.853
12 O imposto sobre vendas é 11,85

formatação de números A variável PurchaseAmount armazena o valor da compra inserido pelo usuário (linhas 7 a 9).
Suponha que o usuário digitou 197,55. O imposto sobre vendas é de 6% da compra, então o
imposto é avaliado como 11.853 (linha 11). O extrato na linha 12 exibe o imposto 11,85 com dois
dígitos após a vírgula. Observe que

imposto * 100 é 1185,3


static_cast<int>(imposto * 100) é 1185
static_cast<int> (imposto * 100)/100,0 é 11,85

Assim, o extrato da linha 12 exibe o imposto 11,85 com dois dígitos após a vírgula.

2.25 Podem diferentes tipos de valores numéricos ser usados juntos em um cálculo?
ÿVerificação de ponto
2.26 O que uma conversão explícita de double para int faz com a parte fracionária
do valor duplo ? A conversão altera a variável que está sendo lançada?
2.27 Mostre a seguinte saída:

duplo f = 12,5;
int eu = f;
cout << "f é" << f << endl;
cout << "eu sou" << eu << endl;

2.28 Se você alterar static_cast<int>(tax * 100) / 100.0 para static_


cast<int>(tax * 100) / 100 na linha 12 da Listagem 2.10, qual será o
resultado para o valor de compra de entrada de 197,556?
2.29 Mostre a impressão do seguinte código:

valor duplo = 5;
cout << quantidade / 2 << endl;
cout << 5/2 << endl ;
Machine Translated by Google

2.14 Processo de Desenvolvimento de Software 75

2.14 Processo de Desenvolvimento de Software


O ciclo de vida de desenvolvimento de software é um processo de vários estágios que inclui
Chave
especificação de requisitos, análise, design, implementação, teste, implantação e Apontar

manutenção.

Desenvolver um produto de software é um processo de engenharia. Os produtos de software, não importa


quão grandes ou pequenos sejam, têm o mesmo ciclo de vida: especificação de requisitos, análise, design,
implementação, teste, implantação e manutenção, conforme mostrado na Figura 2.2.

Requisitos
Especificação

Entrada, Processo, Saída


DOENÇA
Análise de sistema

Sistema
Projeto

Implementação

Teste

Implantação

Manutenção

Figura 2.2 Em qualquer estágio do ciclo de vida de desenvolvimento de software, pode ser necessário
voltar a um estágio anterior para corrigir erros ou lidar com outros problemas que possam impedir o
funcionamento do software conforme esperado.

A especificação de requisitos é um processo formal que busca compreender o problema que o software especificação de requisitos

irá resolver e documentar detalhadamente o que o sistema de software deve fazer. Esta fase envolve
interação próxima entre usuários e desenvolvedores. A maioria dos exemplos neste livro são simples e
seus requisitos são claramente definidos. No mundo real, porém, os problemas nem sempre são bem
definidos. Os desenvolvedores precisam trabalhar em estreita colaboração com seus clientes (indivíduos
ou organizações que usarão o software) e estudar o problema cuidadosamente para identificar o que o
software deve fazer.
A análise do sistema busca analisar o fluxo de dados e identificar as entradas e saídas do sistema. A análise de sistema

análise ajuda a identificar primeiro qual é a saída e, em seguida, descobrir quais dados de entrada você
precisa para produzir a saída.
O projeto do sistema é o processo para obter a saída da entrada. Esta fase envolve o uso de vários projeto de sistema

níveis de abstração para dividir o problema em componentes gerenciáveis e projetar estratégias para
implementar cada componente. Você pode visualizar cada componente como um subsistema que executa
uma função específica do sistema. A essência da análise e design do sistema é entrada, processo e saída
(IPO). DOENÇA

A implementação envolve traduzir o design do sistema em programas. Programas separados são implementação

escritos para cada componente e depois integrados para funcionarem juntos. Esta fase requer o uso de
uma linguagem de programação como C++. A implementação envolve codificação, autoteste e depuração
(ou seja, encontrar erros, chamados bugs, no código).
Machine Translated by Google

76 Capítulo 2 Programação Elementar

testando O teste garante que o código atenda às especificações de requisitos e elimine bugs. Uma equipe
independente de engenheiros de software não envolvidos no projeto e implementação do produto geralmente
conduz esses testes.
Implantação A implantação disponibiliza o software para uso. Dependendo do tipo de software, ele poderá ser instalado
na máquina de cada usuário ou instalado em um servidor acessível pela Internet.
manutenção A manutenção se preocupa em atualizar e melhorar o produto. Um produto de software deve continuar a
funcionar e melhorar em um ambiente em constante evolução. Isso requer atualizações periódicas do produto
para corrigir bugs recém-descobertos e incorporar alterações.
Para ver o processo de desenvolvimento de software em ação, criaremos agora um programa que calcula pagamentos de empréstimos.
O empréstimo pode ser um empréstimo para um carro, um empréstimo para estudantes ou um empréstimo para hipoteca de uma casa.

Nota de vídeo Para um curso introdutório à programação, nos concentramos na especificação, análise, design, implementação
Calcular pagamentos de empréstimos e teste de requisitos.

Etapa 1: Especificação de Requisitos

O programa deve satisfazer os seguintes requisitos:

n Permitir que o usuário insira a taxa de juros anual, o valor do empréstimo e o número de anos para
quais pagamentos serão feitos

n Calcular e exibir o pagamento mensal e os valores totais do pagamento

Etapa 2: Análise do Sistema

A saída é o pagamento mensal e o pagamento total, que podem ser obtidos por meio das seguintes fórmulas:

valor do empréstimo * taxa de juros mensal


pagamento mensal =
1
1-
(1 + taxa de juros mensal) número de anos * 12

pagamento total = pagamento mensal * número de anos * 12

Portanto, os dados necessários para o programa são a taxa de juros mensal, a duração do empréstimo em
anos e o valor do empréstimo.

Observação

A especificação dos requisitos diz que o usuário deve inserir a taxa de juros anual, o valor do
empréstimo e o número de anos durante os quais os pagamentos serão feitos. Durante a análise,
entretanto, é possível que você descubra que a entrada é insuficiente ou que alguns valores são
desnecessários para a saída. Se isso acontecer, você poderá modificar a especificação de requisitos.

Observação

No mundo real, você trabalhará com clientes de todas as profissões. Você pode desenvolver software
para químicos, físicos, engenheiros, economistas e psicólogos. É claro que você não terá (ou precisará)
conhecimento completo de todas essas áreas. Portanto, você não precisa saber como as fórmulas são
derivadas, mas dada a taxa de juros anual, o valor do empréstimo e o número de anos durante os
quais os pagamentos serão feitos, você pode calcular o pagamento mensal neste programa. No
entanto, você precisará se comunicar com os clientes e entender como funciona um modelo matemático
para o sistema.

Etapa 3: Projeto do Sistema

Durante o design do sistema, você identifica as etapas do programa.


Machine Translated by Google

2.14 Processo de Desenvolvimento de Software 77

Passo 1. Solicite ao usuário que insira a taxa de juros anual, o valor do empréstimo e o número de
anos. (A taxa de juros é comumente expressa como uma porcentagem do principal por um
período de um ano. Isso é conhecido como taxa de juros anual.)

Passo 2. A entrada para a taxa de juros anual é um número em formato percentual, como 4,5%. O
programa precisa convertê-lo em decimal dividindo-o por 100.
Para obter a taxa de juros mensal a partir da taxa de juros anual, divida por 12, pois um
ano tem 12 meses. Para obter a taxa de juro mensal em formato decimal, deve dividir a
taxa de juro anual em percentagem por 1200. Por exemplo, se a taxa de juro anual for
4,5%, então a taxa de juro mensal será 4,5/1200 = 0,00375.

Passo 3. Calcule o pagamento mensal usando a fórmula anterior.

Passo 4. Calcule o pagamento total, que é o pagamento mensal multiplicado por 12 e multiplicado
pelo número de anos.

Passo 5. Exiba o pagamento mensal e o pagamento total.

Etapa 4: Implementação

A implementação também é conhecida como codificação (escrever o código). Na fórmula, você deve
numberOfYears*12
calcular (1 + taxa de juros mensal) , que pode ser obtido usando pow(1 +
mensalInterestRate, numberOfYears * 12).
A Listagem 2.11 fornece o programa completo.

Listagem 2.11 ComputeLoan.cpp


1 #include <iostream>
2 #incluir <cmath> incluir biblioteca cmath
3 usando namespace std;

4 5 int principal()
6{
7 //Insira a taxa de juros anual
cout << "Insira a taxa de juros anual, por exemplo 8,25: ";
taxa de juros anual dupla ;
8 cin >> taxa de juros anual; insira a taxa de juros
9
10 11 12 //Obtém a taxa de juros mensal
13 double taxa de juros mensal = taxa de juros anual / 1200;
14
15 //Insira o número de anos
16 cout << "Insira o número de anos como um número inteiro, por exemplo 5: ";
17 número interno de anos;
18 cin >> númeroDeAnos;
19
20 //Insira o valor do empréstimo
21 cout << "Insira o valor do empréstimo, por exemplo 120.000,95: ";
22 valor do empréstimo duplo ;
23 cin >> valor do empréstimo;
24
25 //Calcula o pagamento
26 duplo pagamento mensal = empréstimoAmount * mensalInterestRate / pagamento mensal
27 (1 - 1 / pow(1 + taxa de juros mensal, número de anos * 12));
28 double totalPayment = mensalPayment * numberOfYears * 12; pagamento total
29
Machine Translated by Google

78 Capítulo 2 Programação Elementar


30 pagamentomensal = static_cast<int>(pagamentomensal * 100) / 100,0;
31 totalPayment = static_cast<int>(totalPayment * 100) / 100.0;
32
33 //Exibir resultados
34 cout << "O pagamento mensal é " << mensalPayment << endl <<
exibir resultado 35 "O pagamento total é " << totalPayment << endl;
36
37 retornar 0;
38}

Insira a taxa de juros anual, por exemplo 7,25: 3 Insira o número de


anos como um número inteiro, por exemplo 5: 5 Insira o valor do empréstimo,
por exemplo 120000,95: 1000 O pagamento mensal é 17,96

O pagamento total é 1078,12

Linha# 10 13 18 23 26 28 30 31
Variáveis

taxa de juros anual 3

taxa de juros mensal 0,0025

número de anos 5

montante do empréstimo 1000

mensalPagamento 17.9687

totalPagamento 1078.12

mensalPagamento 17,96

totalPagamento 1078.12

função pow (a, b) Para usar a função pow(a, b) , você deve incluir a biblioteca cmath no programa
(linha 2) da mesma forma que você inclui a biblioteca iostream (linha 1).
O programa solicita que o usuário insira AnnualInterestRate, numberOfYears e lendAmount nas linhas
7–23. Se você inserisse uma entrada diferente de um valor numérico, ocorreria um erro de tempo de execução.

Escolha o tipo de dados mais apropriado para a variável. Por exemplo, numberOfYears
é melhor declarado como int (linha 17), embora possa ser declarado como long, float ou double.
Observe que unsigned short pode ser o mais apropriado para numberOfYears. Para simplificar, entretanto,
os exemplos neste livro usarão int para inteiro e double para valores de ponto flutuante.

A fórmula para calcular o pagamento mensal é traduzida em código C++ em linhas


26–27. A linha 28 obtém o pagamento total.
A conversão é usada nas linhas 30 a 31 para obter um novo mensalPayment e totalPayment
com dois dígitos após as casas decimais.

Etapa 5: Teste

Depois que o programa for implementado, teste-o com alguns dados de entrada de amostra e verifique se a
saída está correta. Alguns dos problemas podem envolver muitos casos, como você verá em capítulos
posteriores. Para esses tipos de problemas, é necessário projetar dados de teste que cubram todos os casos.
Machine Translated by Google

2.15 Estudo de Caso: Contando Unidades Monetárias 79

Dica
A fase de design do sistema neste exemplo identificou várias etapas. É uma boa abordagem
codificar e testar essas etapas de forma incremental , adicionando-as uma de cada vez. Essa código incremental e teste

abordagem torna muito mais fácil identificar problemas e depurar o programa.

2.30 Como você escreveria a seguinte expressão aritmética?


ÿVerificação de ponto

-b + 2b2 - 4ac
2a

2.15 Estudo de Caso: Contagem de Unidades Monetárias


Esta seção apresenta um programa que divide uma grande quantia de dinheiro em
Chave
unidades menores. Apontar

Suponha que você queira desenvolver um programa que transforme uma determinada quantia de dinheiro em
unidades monetárias menores. O programa permite que o usuário insira um valor duplo representando um total
em dólares e centavos, e gera um relatório listando o equivalente monetário no número máximo de dólares,
quartos, moedas de dez centavos, centavos e centavos, nesta ordem, para resultar no número mínimo de número mínimo de moedas

moedas, conforme mostrado na amostra executada.


Aqui estão as etapas no desenvolvimento do programa:

1. Solicite ao usuário que insira o valor como um número decimal, como 11,56.

2. Converta o valor (por exemplo, 11,56) em centavos (1156).

3. Divida os centavos por 100 para encontrar o número de dólares. Obtenha os centavos restantes
usando os centavos restantes 100.

4. Divida os centavos restantes por 25 para encontrar o número de quartos. Obtenha o restante
centavos usando os centavos restantes, resto 25.

5. Divida os centavos restantes por 10 para encontrar o número de moedas. Obtenha o restante
centavos usando os centavos restantes, resto 10.

6. Divida os centavos restantes por 5 para encontrar o número de centavos. Obtenha o restante
centavos usando os centavos restantes resto 5.

7. Os centavos restantes são os centavos.

8. Exiba o resultado.

O programa completo é dado na Listagem 2.12.

Listagem 2.12 ComputeChange.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 //Recebe o valor
7 cout << "Insira um valor em dobro, por exemplo 11,56: ";
8 valor duplo ;
9 cin >> quantidade;
10

11 int valor restante = static_cast<int>(valor * 100);


Machine Translated by Google

80 Capítulo 2 Programação Elementar

12
13 // Encontre o número de um dólar
dólares
14 int númeroOfOneDollars = valor restante / 100;
15 valor restante = valor restante% 100;
16
17 // Encontre o número de trimestres no valor restante
trimestres 18 int númeroOfQuarters = valor restante / 25;
19 valor restante = valor restante% 25;
20
21 // Encontre o número de moedas no valor restante
moedas
22 int númeroDeDimes = valor restante / 10;
23 valor restante = valor restante% 10;
24
25 // Encontre o número de centavos no valor restante
moedas
26 int númeroOfNickels = quantidade restante / 5;
27 valor restante = valor restante% 5;
28
29 // Encontre o número de centavos no valor restante
centavos 30 int númeroDePennies = valor restante;
31
32 //Exibir resultados
saída cout << "Sua quantia << " << quantidade << " consiste em " " << fim <<
33
" " dólares" << endl <<
34 numberOfOneDollars << <<
" "
35 numberOfQuarters << << "quartos" << endl <<
" " "moedas" << endl <<
36 numberOfDimes << <<
" "
37 numberOfNickels << << "níqueis" << endl <<
" "
38 numberOfPennies << "centavos" << endl;
39
40 retornar 0;
41}

Insira um valor em dobro, por exemplo 11,56: 11,56 Seu valor 11,56
consiste em 11 dólares

2 trimestres
0 centavos
1 centavo
1 centavo

Linha# 9 11 14 15 18 19 22 23 26 27 30
Variáveis

quantia 11.56
Montante restante 1156 56 6 6 1
número de um dólar 11
númeroDeQuartos 2
número de moedas 0
númeroOfNickels 1
número de centavos 1

A variável amount armazena o valor inserido no teclado (linhas 7–9). Esta variável não deve ser alterada,
pois o valor deve ser utilizado no final do programa para visualização dos resultados. O programa introduz a
variável restanteAmount (linha 11) para armazenar a alteração de restanteAmount.
Machine Translated by Google

2.16 Erros Comuns 81

O valor variável é uma casa decimal dupla que representa dólares e centavos. É convertido em uma
variável int restantesAmount, que representa todos os centavos. Por exemplo, se o montante
é 11,56, então o restanteAmount inicial é 1156. O operador de divisão produz a parte inteira da
divisão. Portanto, 1156/100 é 11. O operador resto obtém o resto da divisão. Portanto, 1156% 100 é 56.

O programa extrai o número máximo de dólares do valor total e obtém o valor restante na variável
restantesAmount (linhas 14–15). Em seguida, extrai o número máximo de trimestres de
restantesAmount e obtém um novo restanteAmount
(linhas 18–19). Continuando o mesmo processo, o programa encontra o número máximo de moedas,
moedas e centavos no valor restante.
Um problema sério com este exemplo é a possível perda de precisão ao converter um valor duplo perda de precisão
para um valor restante int. Isto pode levar a um resultado impreciso. Se você tentar inserir o valor
10,03, 10,03 * 100 se tornará 1002,9999999999999. Você descobrirá que o programa exibe 10 dólares
e 2 centavos. Para resolver o problema, insira o valor como um valor inteiro representando centavos
(veja o Exercício de Programação 2.24).

2.16 Erros Comuns


Erros comuns de programação elementar geralmente envolvem variáveis não declaradas, variáveis
Chave
não inicializadas, estouro de número inteiro, divisão de número inteiro não intencional e erros Apontar
de arredondamento.

Erro comum 1: variáveis não declaradas/não inicializadas e variáveis não utilizadas


Uma variável deve ser declarada com um tipo e atribuído um valor antes de ser usada. Um erro comum
é não declarar uma variável ou inicializar uma variável. Considere o seguinte código:

taxa de juros dupla = 0,05;


juros duplos = taxa de juros * 45;

Este código está errado, porque interestRate recebeu um valor 0,05, mas interestrate não foi
declarado e inicializado. C++ diferencia maiúsculas de minúsculas, portanto considera interestRate
e taxa de juros como duas variáveis diferentes.
Se uma variável for declarada, mas não for usada no programa, pode ser um potencial erro de
programação. Portanto, você deve remover a variável não utilizada do seu programa. Por exemplo, no
código a seguir, taxRate nunca é usado. Portanto, deve ser removido do código.

taxa de juros dupla = 0,05;


taxa de imposto dupla = 0,05;
juros duplos = taxa de juros * 45;
cout << "O interesse é" << juros << endl;

Erro comum 2: estouro de número inteiro


Os números são armazenados com um número limitado de dígitos. Quando é atribuído a uma variável
um valor muito grande (em tamanho) para ser armazenado, isso causa um estouro. Por exemplo, a transbordar

execução da instrução a seguir causa estouro, porque o maior valor que pode ser armazenado em uma
variável do tipo short é 32767. 32768 é muito grande.

valor curto = 32767 + 1; // o valor na verdade se tornará -32768

Da mesma forma, a execução da instrução a seguir causa estouro porque o menor valor que pode
ser armazenado em uma variável do tipo short é –32768. O valor –32769 é muito grande para ser
armazenado em uma variável curta .

valor curto = -32768 - 1; // o valor na verdade se tornará 32767


Machine Translated by Google

82 Capítulo 2 Programação Elementar

C++ não relata erros de estouro. Tenha cuidado ao trabalhar com números próximos ao intervalo máximo ou
mínimo de um determinado tipo.
Quando um número de ponto flutuante é muito pequeno (ou seja, muito próximo de zero) para ser armazenado,
estouro negativo ocorre um underflow. C++ aproxima-o de zero. Então, normalmente você não precisa se preocupar com o underflow.

Erro comum 3: erros de arredondamento


Um erro de arredondamento, também chamado de erro de arredondamento, é a diferença entre a aproximação
calculada de um número e seu valor matemático exato. Por exemplo, 1/3 é aproximadamente 0,333 se você
mantiver três casas decimais e é 0,3333333 se você mantiver sete casas decimais. Como o número de dígitos que
podem ser armazenados em uma variável é limitado, erros de arredondamento são inevitáveis. Os cálculos
aproximação de ponto flutuante envolvendo números de ponto flutuante são aproximados porque esses números não são armazenados com total
precisão. Por exemplo,

flutuar a = 1000,43;
flutuar b = 1000,0;
cout << a - b << endl;

exibe 0,429993, não 0,43. Os inteiros são armazenados com precisão. Portanto, cálculos com números inteiros
produzem um resultado inteiro preciso.

Erro comum 4: Divisão inteira não intencional


C++ usa o mesmo operador de divisão, ou seja, /, para realizar divisão de número inteiro e de ponto flutuante.
Quando dois operandos são inteiros, o operador / realiza uma divisão inteira. O resultado da operação é o quociente.
A parte fracionária está truncada. Para forçar dois inteiros a realizar uma divisão de ponto flutuante, transforme um
dos inteiros em um número de ponto flutuante. Por exemplo, o código em (a) mostra que a média é 1 e o código em
(b) mostra que a média é 1,5.

int número1 = 1; int número1 = 1;


int número2 = 2; int número2 = 2;
média dupla = (número1 + número2)/ 2; média dupla = (número1 + número2) / 2,0;
cout << média << endl; cout << média << endl;
(a) (b)

Erro comum 5: esquecimento dos arquivos de cabeçalho


Esquecer de incluir os arquivos de cabeçalho apropriados é um erro comum de compilação. A função pow é definida
no arquivo de cabeçalho cmath e a função time é definida no arquivo de cabeçalho ctime .
Para usar a função pow em seu programa, ele precisa incluir o cabeçalho cmath .
Para usar a função time em seu programa, ele precisa incluir o cabeçalho ctime .
Para cada programa que usa a entrada e saída do console, você precisa incluir o iostream
cabeçalho.

Termos chave
algoritmo 50 constante 59
operador de atribuição (=) 58 tipo de dados 51
declaração de atribuição 58 declarar variáveis 51
Elenco estilo C 73 operador de decremento (--) 70
operador de fundição 72 tipo duplo 63
palavra-chave const 59 expressão 58
Machine Translated by Google

Resumo do Capítulo 83

tipo float 63 pós-incremento 70 pré-


número de ponto flutuante 51 decremento 70 pré-
identificador 55 incremento 70 tipo de
operador de incremento (++) 70 dados primitivo 51 pseudocódigo
código incremental e teste 70 tipo 50 especificação de
int 56 requisitos 75 escopo de uma variável 57
IPO 54 análise de sistema 75 design
literal 62 tipo de sistema 75 underflow
longo 56 82
estreitamento (de tipos) 73
operandos 63 Época UNIX 67
operador 63 variável 51
overflow 81 pós- ampliação (de tipos) 73
decremento 70

Resumo do capítulo
1. O objeto cin junto com o operador de extração de fluxo (>>) pode ser usado para ler uma entrada do console.

2. Identificadores são nomes para nomear elementos de um programa. Um identificador é uma sequência de caracteres
que consiste em letras, dígitos e sublinhados (_). Um identificador deve começar com uma letra ou sublinhado.
Não pode começar com um dígito. Um identificador não pode ser uma palavra reservada.

3. A escolha de identificadores descritivos pode facilitar a leitura dos programas.

4. A declaração de uma variável informa ao compilador que tipo de dados uma variável pode conter.

5. Em C++, o sinal de igual (=) é usado como operador de atribuição.

6. Uma variável declarada em uma função deve receber um valor. Caso contrário, a variável é
chamado não inicializado e seu valor é imprevisível.

7. Uma constante nomeada ou simplesmente constante representa dados permanentes que nunca mudam.

8. Uma constante nomeada é declarada usando a palavra-chave const.

9. Por convenção, as constantes são nomeadas em letras maiúsculas.

10. C++ fornece tipos inteiros (short, int, long, unsigned short, unsigned int e unsigned long) que
representam inteiros assinados e não assinados de vários tamanhos.

11. Inteiros sem sinal são inteiros não negativos.

12. C++ fornece tipos de ponto flutuante (float, double e long double) que representam
números de ponto flutuante de várias precisões.
Machine Translated by Google

84 Capítulo 2 Programação Elementar

13. C++ fornece operadores que realizam operações numéricas: + (adição), – (subtração),
* (multiplicação), / (divisão) e % (módulo).

14. Aritmética inteira (/) produz um resultado inteiro.

15. Em C++, o operador % é apenas para números inteiros.

16. Os operadores numéricos em uma expressão C++ são aplicados da mesma maneira que em uma expressão aritmética.
expressão.

17. O operador de incremento (++) e o operador de decremento (––) incrementam ou decrementam um


variável por 1.

18. C++ fornece operadores aumentados += (atribuição de adição), –= (atribuição de subtração), *= (atribuição de
multiplicação), /= (atribuição de divisão) e %= (atribuição de módulo).

19. Ao avaliar uma expressão com valores de tipos mistos, C++ lança automaticamente o
operandos para tipos apropriados.

20. Você pode converter explicitamente um valor de um tipo para outro usando o <static_
notação cast>(type) ou a notação legada estilo c (type) .

21. Na ciência da computação, a meia-noite de 1º de janeiro de 1970 é conhecida como a época UNIX.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Observação

O compilador geralmente fornece um motivo para um erro de sintaxe. Se você não sabe como corrigi-lo,
aprenda com exemplos compare seu programa de perto, caractere por caractere, com exemplos semelhantes no
texto.

Observação

análise e design de documentos Os instrutores podem pedir que você documente sua análise e design para exercícios selecionados.
Use suas próprias palavras para analisar o problema, incluindo a entrada, a saída e o que precisa ser
computado, e descreva como resolver o problema em pseudocódigo.

Seções 2.2–2.12

2.1 (Converter Celsius em Fahrenheit) Escreva um programa que leia um grau Celsius em um valor duplo no
console, depois o converta para Fahrenheit e exiba o resultado. A fórmula para a conversão é a seguinte:

Fahrenheit = (9/5) * Celsius + 32

Dica: em C++, 9/5 é 1, mas 9,0/5 é 1,8.


Machine Translated by Google

Exercícios de Programação 85

Aqui está um exemplo de execução:

Insira um grau em Celsius: 43 43 Celsius é


109,4 Fahrenheit

2.2 (Calcule o volume de um cilindro) Escreva um programa que leia o raio e o comprimento de um cilindro e
calcule a área e o volume usando as seguintes fórmulas:

área = raio * raio * volume = área * Pi


comprimento

Aqui está um exemplo de execução:

Insira o raio e o comprimento de um cilindro: 5,5 12 A área é 95,0331

O volume é 1140,4

2.3 (Converter pés em metros) Escreva um programa que leia um número em pés, converta-o em metros e
exiba o resultado. Um pé equivale a 0,305 metros. Aqui está um exemplo de execução:

Insira um valor para pés: 16,5 16,5 pés


equivalem a 5,0325 metros

2.4 (Converter libras em quilogramas) Escreva um programa que converta libras em quilogramas. O programa
solicita que o usuário insira um número em libras, converte-o em quilogramas e exibe o resultado. Uma
libra equivale a 0,454 quilogramas. Aqui está uma amostra
correr:

Insira um número em libras: 55,5 55,5 libras


equivalem a 25,197 quilogramas

* 2.5 (Aplicação financeira: calcular gorjetas) Escreva um programa que leia o subtotal e a taxa de gorjeta e
depois calcule a gorjeta e o total. Por exemplo, se o usuário inserir 10 para subtotal e 15% para taxa
de gorjeta, o programa exibirá US$ 1,5 como gorjeta e US$ 11,5 como total. Aqui está um exemplo de
execução:

Insira o subtotal e uma taxa de gorjeta: 10 15 A gorjeta é de US$ 1,5 e


o total é de US$ 11,5

** 2.6 (Soma dos dígitos de um inteiro) Escreva um programa que leia um inteiro entre 0 e 1000 e some todos os
dígitos do inteiro. Por exemplo, se um número inteiro for 932, a soma de todos os seus dígitos será 14.

Dica: Use o operador % para extrair dígitos e use o operador / para remover o dígito extraído. Por
exemplo, 932% 10 = 2 e 932/10 = 93.

Aqui está um exemplo de execução:

Insira um número entre 0 e 1000: 999


A soma dos algarismos é 27
Machine Translated by Google

86 Capítulo 2 Programação Elementar

* 2.7 (Encontre o número de anos) Escreva um programa que solicite ao usuário que insira os minutos
(por exemplo, 1 bilhão) e exiba o número de anos e dias dos minutos. Para simplificar, suponha
que um ano tenha 365 dias. Aqui está um exemplo de execução:

Insira o número de minutos: 1000000000 1000000000


minutos são aproximadamente 1902 anos e 214 dias

* 2.8 (Hora atual) A Listagem 2.9, ShowCurrentTime.cpp, fornece um programa que exibe a hora atual
em GMT. Revise o programa para que ele solicite ao usuário que insira o deslocamento do
fuso horário para GMT e exiba a hora no fuso horário especificado. Aqui está uma amostra
correr:

Insira a diferença de fuso horário para GMT: -5


A hora atual é 4:50:34

2.9 (Física: aceleração) A aceleração média é definida como a mudança de velocidade dividida pelo
tempo necessário para fazer a mudança, conforme mostrado na seguinte fórmula:

uma =
v1 - v0
t

Escreva um programa que solicite ao usuário que insira a velocidade inicial v0 em metros/
segundo, a velocidade final v1 em metros/segundo e o intervalo de tempo t em segundos e
exibe a aceleração média. Aqui está um exemplo de execução:

Insira v0, v1 e t: 5,5 50,9 4,5 A aceleração


média é 10,0889

2.10 (Ciência: cálculo de energia) Escreva um programa que calcule a energia necessária para aquecer
água desde uma temperatura inicial até uma temperatura final. Seu programa deverá solicitar
ao usuário que insira a quantidade de água em quilogramas e as temperaturas inicial e final da
água. A fórmula para calcular a energia é

Q = M * (temperatura final – temperatura inicial) * 4184

onde M é o peso da água em quilogramas, as temperaturas estão em graus Celsius e a energia


Q é medida em joules. Aqui está um exemplo de execução:

Insira a quantidade de água em quilogramas: 55,5 Insira a


temperatura inicial: 3,5 Insira a temperatura
final: 10,5 A energia necessária é 1625484,0

2.11 (Projeção da população) Reescreva o exercício de programação 1.11 para solicitar que o usuário
insira o número de anos e exiba a população após o número de anos. Use a dica do Exercício
de Programação 1.11 para este programa. Aqui está um exemplo de execução do programa:

Insira o número de anos: 5


A população em 5 anos é 325932970
Machine Translated by Google

Exercícios de Programação 87

2.12 (Física: calculando o comprimento da pista) Dada a aceleração a de um avião e a velocidade de decolagem v,
você pode calcular o comprimento mínimo da pista necessário para um avião decolar usando a seguinte
fórmula:

v2
comprimento =
2a

Escreva um programa que solicite ao usuário que insira v em metros/segundo (m/s) e a aceleração e
2
geração a em metros/segundo ao quadrado (m/s exiba o comprimento mínimo da pista.
Aqui está um exemplo de execução:

Insira velocidade e aceleração: 60 3,5 O


comprimento mínimo da pista para este avião é 514,286

** 2.13 (Aplicação financeira: valor composto) Suponha que você economize $100 por mês em uma conta poupança
com taxa de juros anual de 5%. Assim, a taxa de juros mensal é 0,05/12 = 0,00417. Após o primeiro
mês, o valor na conta passa a ser

100 * (1 + 0,00417) = 100,417

Após o segundo mês, o valor na conta passa a ser

(100 + 100,417) * (1 + 0,00417) = 201,252

Após o terceiro mês, o valor da conta passa a ser

(100 + 201,252) * (1 + 0,00417) = 302,507

e assim por diante.

Escreva um programa que solicite ao usuário que insira um valor de poupança mensal e exiba o valor
da conta após o sexto mês. (No Exercício de Programação 5.32, você usará um loop para simplificar o
código e exibir o valor da conta para qualquer mês.)

Insira o valor da poupança mensal: 100 Após o


sexto mês, o valor da conta é de $ 608,81

* 2.14 (Aplicação de saúde: IMC) O Índice de Massa Corporal (IMC) é uma medida de saúde baseada no peso. Pode
ser calculado pegando o seu peso em quilogramas e dividindo pelo quadrado da sua altura em metros.
Nota de vídeo
Escreva um programa que solicite ao usuário que insira o peso em libras e a altura em polegadas e
Calcular IMC
exiba o IMC. Observe que uma libra equivale a 0,45359237 quilogramas e uma polegada equivale a
0,0254 metros. Aqui está um exemplo de execução:

Insira o peso em libras: 95,5 Insira a


altura em polegadas: 50 O IMC é
26,8573

2.15 (Geometria: distância de dois pontos) Escreva um programa que solicite ao usuário que insira dois pontos (x1,
y1) e (x2, y2) e exiba a distância entre eles.
Machine Translated by Google

88 Capítulo 2 Programação Elementar

2 2
A fórmula para calcular a distância é 2(x2 - x1) + (y2 - y1) . Observe que você
pode usar pow(a, 0.5) para calcular 2a. Aqui está um exemplo de execução:

Insira x1 e y1: 1,5 -3,4 Insira x2 e y2:


4 5 A distância entre os dois
pontos é 8,764131445842194

2.16 (Geometria: área de um hexágono) Escreva um programa que solicite ao usuário que insira o lado de um
hexágono e exiba sua área. A fórmula para calcular a área de um hexágono é

323 2
Área = é ,
2

onde s é o comprimento de um lado. Aqui está um exemplo de execução:

Entre no lado: 5,5


A área do hexágono é 78,5895

* 2.17 (Ciência: temperatura térmica) Quão frio está lá fora? A temperatura por si só não é suficiente para fornecer
a resposta. Outros factores, incluindo a velocidade do vento, a humidade relativa e a luz solar,
desempenham papéis importantes na determinação do frio exterior. Em 2001, o Serviço Meteorológico
Nacional (NWS) implementou a nova temperatura térmica para medir o frio usando a temperatura e a
velocidade do vento. A fórmula é:

twc = 35,74 + 0,6215ta - 35,75v0,16 + 0,4275tav0,16

onde ta é a temperatura externa medida em graus Fahrenheit e v é a velocidade medida em milhas por
hora. twc é a temperatura de sensação térmica. A fórmula não pode ser usada para velocidades de
vento abaixo de 2 mph ou temperaturas abaixo de –58°F ou acima de 41°F.

Escreva um programa que solicite ao usuário que insira uma temperatura entre –58°F e 41°F e uma
velocidade do vento maior ou igual a 2 e exiba a temperatura da sensação térmica. Use pow(a, b)
para calcular v0.16 . Aqui está um exemplo de execução:

Insira a temperatura em Fahrenheit: 5,3 Insira a velocidade


do vento em milhas por hora: 6 O índice de sensação térmica
é -5,56707

2.18 (Imprimir uma tabela) Escreva um programa que exiba a seguinte tabela:

pow (x, y)
x e 1,2 3.00281
2,5 2,4 47.5913
5,0 1,2 3,6 1.92776
2,4 5,0 79.6262
3,6 2,5 24.5899

* 2.19 (Geometria: área de um triângulo) Escreva um programa que solicite ao usuário que insira três pontos (x1,
y1), (x2, y2), (x3, y3) de um triângulo e exiba sua área. A fórmula para calcular a área de um triângulo
é

s = (lado1 + lado2 + lado3)/2;

área = 2s(s - lado1)(s - lado2)(s - lado3)


Machine Translated by Google

Exercícios de programação 89

Aqui está um exemplo de execução:

Insira três pontos para um triângulo: 1,5 -3,4 4,6 5 9,5 -3,4 A área do triângulo é
33,6

* 2.20 (Inclinação de uma reta) Escreva um programa que solicite ao usuário que insira as coordenadas de dois
pontos (x1, y1) e (x2, y2) e exiba a inclinação da reta que conecta os dois pontos. A fórmula da
inclinação é (y2 - y1)/(x2 - x1). Aqui está um exemplo de execução:

Insira as coordenadas para dois pontos: 4,5 -5,5 6,6 -6,5 A inclinação da
linha que conecta dois pontos (4,5, -5,5) e (6,6, -6,5) é -0,47619

* 2.21 (Custo de dirigir) Escreva um programa que solicite ao usuário que insira a distância a percorrer, a
eficiência de combustível do carro em milhas por galão e o preço por galão, e exiba o custo da
viagem. Aqui está um exemplo de execução:

Insira a distância de condução: 900,5 Insira


milhas por galão: 25,5 Insira o preço
por galão: 3,55 O custo de condução
é $ 125,36

Seções 2.13–2.16

* 2.22 (Aplicação financeira: calcular juros) Se você conhece o saldo e a taxa de juros percentual anual, pode
calcular os juros do próximo pagamento mensal usando a seguinte fórmula:

juros = saldo x (taxa de juros anual/1200)

Escreva um programa que leia o saldo e a taxa de juros percentual anual e exiba os juros do mês
seguinte. Aqui está um exemplo de execução:

Insira o saldo e a taxa de juros (por exemplo, 3 por 3%): 1000 3,5 Os juros são
2,91667

* 2.23 (Aplicação financeira: valor do investimento futuro) Escreva um programa que leia o valor do investimento,
a taxa de juros anual e o número de anos, e exiba o valor do investimento futuro usando a seguinte
fórmula:

ValorInvestimentofuturo =
valor do investimento x (1 + taxa de juros mensal) número de anos * 12

Por exemplo, se você inserir o valor 1.000, a taxa de juros anual de 3,25% e o número de anos 1, o
valor do investimento futuro será 1.032,98. Aqui está um exemplo de execução:

Insira o valor do investimento: 1.000


Insira a taxa de juros anual em porcentagem: 4,25 Insira o
número de anos: 1 O valor
acumulado é $ 1.043,34

* 2.24 (Aplicação financeira: unidades monetárias) Reescreva a Listagem 2.12, ComputeChange.cpp, para
corrigir a possível perda de precisão ao converter um valor float em um valor int .
Insira a entrada como um número inteiro cujos dois últimos dígitos representam os centavos. Por
exemplo, a entrada 1156 representa 11 dólares e 56 centavos.
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

CAPÍTULO

3
Seleções

Objetivos
n Para declarar variáveis bool e escrever expressões booleanas usando relações
operadores nacionais (§3.2).

n Implementar controle de seleção usando instruções if unidirecionais (§3.3).

n Implementar controle de seleção usando instruções if bidirecionais (§3.4).

n Para implementar o controle de seleção usando instruções if aninhadas e if-else


multidirecionais (§3.5).

n Para evitar erros e armadilhas comuns em declarações if (§3.6).

n Programar usando declarações de seleção para uma variedade de exemplos (IMC,


ComputeTax, SubtractionQuiz) (§§3.7–3.9).
n Para gerar números aleatórios usando a função rand e definir uma semente usando a
função srand (§3.9).

n Para combinar condições usando operadores lógicos (&&, || e !) (§3.10).

n Para programar usando instruções de seleção com condições combinadas


(Ano bissexto, loteria) (§§3.11–3.12).
n Para implementar o controle de seleção usando instruções switch (§3.13).

n Escrever expressões usando expressões condicionais (§3.14).

n Examinar as regras que regem a precedência e a associatividade dos operadores


(§3.15).

n Para depurar erros (§3.16).


Machine Translated by Google

92 Capítulo 3 Seleções

3.1 Introdução
O programa pode decidir quais instruções executar com base em uma condição.
Chave
Apontar
Se você inserir um valor negativo para raio na Listagem 2.2, ComputeAreaWithConsoleInput.cpp, o
programa exibirá um resultado inválido. Se o raio for negativo, você não deseja que o programa calcule a
problema área. Como você pode lidar com essa situação?
declarações de seleção Como todas as linguagens de programação de alto nível, C++ fornece instruções de seleção: instruções
que permitem escolher ações com cursos alternativos. Você pode usar a seguinte instrução de seleção
para substituir as linhas 12–15 na Listagem 2.2:

se (raio < 0)
{
cout << "Entrada incorreta" << endl;
}
outro
{
área = raio * raio * PI;
cout << "A área do círculo de raio " " é " << raio
<< << área << endl;
}

Expressão booleana As instruções de seleção usam condições que são expressões booleanas. Uma expressão booleana
Valor booleano é uma expressão avaliada como um valor booleano: verdadeiro ou falso. Apresentamos agora tipos
booleanos e operadores relacionais.

3.2 O tipo de dados bool


O tipo de dados bool declara uma variável com o valor verdadeiro ou falso.
Chave
Apontar
tipo de dados bool Como você compara dois valores, como se um raio é maior que 0, igual a 0 ou menor que 0? C++ fornece
operador relacional seis operadores relacionais, mostrados na Tabela 3.1, que podem ser usados para comparar dois valores
(suponha que o raio seja 5 na tabela).

Tabela 3.1 Operadores Relacionais

Operador Nome do símbolo matemático Exemplo (o raio é 5) Resultado

< < menor que raio < 0 falso


<= <– menos que ou igual a raio <= 0 falso
> > maior que raio > 0 verdadeiro

>= >– maior ou igual ao raio >= 0 verdadeiro

== = igual a raio == 0 falso


!= =/ não é igual a raio! = 0 verdadeiro

Cuidado
== versus = O operador de teste de igualdade consiste em dois sinais de igual (==), e não em um único sinal de igual
(=). O último símbolo é usado para atribuição.

O resultado da comparação é um valor booleano: verdadeiro ou falso. Uma variável que contém um
Variável booleana valor booleano é conhecida como variável booleana. O tipo de dados bool é usado para declarar variáveis
booleanas. Por exemplo, a instrução a seguir atribui true à variável LightsOn:

bool luzesOn = verdadeiro;


Machine Translated by Google

3.3 se Declarações 93

verdadeiro e falso são literais booleanos, assim como um número como 10. Eles são palavras-chave e não podem ser Literais booleanos
usados como identificadores em seu programa.
Internamente, C++ usa 1 para representar verdadeiro e 0 para falso. Se você exibir um valor bool para
no console, 1 será exibido se o valor for verdadeiro e 0 se for falso.
Por exemplo,

cout << (4 < 5);

exibe 1, porque 4 <5 é verdadeiro.

cout << (4 > 5);

exibe 0, porque 4 > 5 é falso.

Observação

Em C++, você pode atribuir um valor numérico a uma variável bool . Qualquer valor diferente converter números em bool
de zero é avaliado como verdadeiro e valor zero é avaliado como falso. Por exemplo, após valor
as seguintes declarações de atribuição, b1 e b3 tornam-se verdadeiros e b2 torna-se falso.

bool b1 = -1,5; // O mesmo que bool b1 = verdadeiro


boole b2 = 0; // O mesmo que bool b2 = falso
boole b3 = 1,5; // O mesmo que bool b3 = verdadeiro

3.1 Liste seis operadores relacionais.

3.2 Supondo que x é 1, mostre o resultado das seguintes expressões booleanas: ÿVerificação de ponto

(x > 0)
(x < 0)
(x! = 0)
(x >= 0)
(x! = 1)

3.3 Mostre a impressão do seguinte código:

bool b = verdadeiro;
int eu = b;
cout << b << endl;
cout << i << endl;

3.3 Declarações if
Uma instrução if é uma construção que permite a um programa especificar um caminho alternativo
Chave
de execução. Apontar

Os programas que você escreveu até agora são executados em sequência. No entanto, muitas vezes há
situações em que é necessário fornecer caminhos alternativos. C++ fornece vários tipos de instruções de
seleção: instruções if unidirecionais, instruções if-else bidirecionais , instruções if aninhadas, instruções
switch e expressões condicionais.
Uma instrução if unilateral executa uma ação se e somente se a condição for verdadeira. A sintaxe declaração if

para uma instrução if unilateral é mostrada aqui:

if (expressão booleana) {

declarações);
}
Machine Translated by Google

94 Capítulo 3 Seleções

O fluxograma da Figura 3.1a ilustra como C++ executa a sintaxe de uma instrução if .
fluxograma Um fluxograma é um diagrama que descreve um algoritmo ou processo, mostrando as etapas como
caixas de vários tipos e sua ordem conectando-as com setas. As operações do processo são
representadas nessas caixas e as setas que as conectam representam o fluxo de controle. Uma
caixa de diamante é usada para denotar uma condição booleana e uma caixa retangular é usada
para representar instruções.

falso falso
expressão booleana (raio >= 0)

verdadeiro verdadeiro

Declarações) área = raio * raio * PI;


cout << "A área do círculo de " <<
" raio " << raio << " é " << área;

(a) (b)

Figura 3.1 Uma instrução if executa instruções se a expressão booleana for avaliada
para verdadeiro.

Se a expressão booleana for avaliada como verdadeira, as instruções no bloco serão executadas.
Como exemplo, veja o seguinte código:

if (raio >= 0) {

área = raio * raio * PI;


cout << "A área do círculo de" <<
" raio " << raio << "é" <<área;
}

O fluxograma da afirmação anterior é mostrado na Figura 3.1b. Se o valor do raio


for maior ou igual a 0, então a área é calculada e o resultado é exibido; caso contrário, as duas
instruções do bloco não serão executadas.
A expressão booleana está entre parênteses. Por exemplo, o código em (a) abaixo está errado.
A versão corrigida é mostrada em (b).

se eu> 0 se (eu > 0)


{ {
cout << "i é positivo" << endl; cout << "i é positivo" << endl;
} }

(a) Errado (b) Correto


Machine Translated by Google

3.3 se Declarações 95

As chaves podem ser omitidas se incluírem uma única instrução. Por exemplo, as seguintes afirmações
são equivalentes.

se (eu > 0) { if (i > 0) cout


Equivalente << "i é positivo" << endl;
cout << "i é positivo" << endl;
}

(a) (b)

A Listagem 3.1 fornece um programa que solicita ao usuário que insira um número inteiro. Se o número
for múltiplo de 5, exiba HiFive. Se o número for par, exiba HiEven.

Listagem 3.1 SimpleIfDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 // Solicita ao usuário que insira um número inteiro
7 número interno ;
cout << "Digite um número inteiro: ";
cin >> número; insira a entrada
8
9 se (número% 5 == 0) verifique 5

10 cout << "HiFive" << endl;


11 12 13
14 se (número% 2 == 0) verifique mesmo

15 cout << "OiEven" << endl;


16
17 retornar 0;
18 }

Insira um número inteiro: 4


Olá, mesmo

Insira um número inteiro: 30


Olá, cinco
Olá, mesmo

O programa solicita que o usuário insira um número inteiro (linha 9) e exibe HiFive se for múltiplo de
5 (linhas 11–12) e HiEven se for par (linhas 14–15).

3.4 Escreva uma instrução if que atribua 1 a x se y for maior que 0.


3.5 Escreva uma declaração if que aumente o salário em 3% se a pontuação for superior a 90. ÿVerificação de ponto
Machine Translated by Google

96 Capítulo 3 Seleções

3.6 O que há de errado no código a seguir?

se raio >= 0
{
área = raio * raio * PI;
cout << "A área do círculo de " " raio " << raio <<
<< "é" <<área;
}

3.4 Declarações if-else bidirecionais


Uma instrução if-else decide quais instruções serão executadas com base no fato de a
Chave
Apontar condição ser verdadeira ou falsa.

Uma instrução if unilateral executa uma ação se a condição especificada for verdadeira. Se a condição
for falsa, nada será feito. Mas e se você quiser realizar uma ação alternativa quando a condição for
falsa? Você pode usar uma instrução if bidirecional . Uma instrução if-else bidirecional especifica ações
diferentes, dependendo se a condição é verdadeira ou falsa.
Aqui está a sintaxe para uma instrução if-else bidirecional:

if (expressão booleana) {

declaração(ões)-para-o-caso-verdadeiro;
}
outro
{
declaração(ões)-para-o-caso-falso;
}

O fluxograma do demonstrativo é mostrado na Figura 3.2.

verdadeiro falso
expressão booleana

Declaração(ões) para o caso verdadeiro Declaração(ões) para o caso falso

Figura 3.2 Uma instrução if-else executa instruções para o caso verdadeiro se a
expressão booleana for avaliada como verdadeira; caso contrário, as instruções para o
caso falso serão executadas.

Se a expressão booleana for avaliada como verdadeira, as instruções para o caso verdadeiro serão
executadas; caso contrário, as instruções para o caso falso serão executadas. Por exemplo, considere o
seguinte código:

instrução if-else bidirecional if (raio >= 0) {

área = raio * raio * PI;


Machine Translated by Google

3.5 Instruções if - else aninhadas e multidirecionais 97


cout << "A área do círculo de raio " radius << <<
"é" <<área;
}
outro
{
cout << "Raio negativo";
}

Se raio >= 0 for verdadeiro, a área é calculada e exibida; se for falso, será exibida a mensagem “Raio
negativo” .
Como de costume, as chaves podem ser omitidas se incluírem apenas uma instrução. Portanto, no exemplo
anterior, as chaves que delimitam a instrução cout << "Raio negativo" podem ser omitidas.

Aqui está outro exemplo da instrução if-else . O exemplo verifica se um número é par ou ímpar, da seguinte
forma:

se (número% 2 == 0)
cout << número << "é par.";
outro
cout << número << " é estranho.";

3.7 Escreva uma declaração if que aumente o salário em 3% se a pontuação for superior a 90, caso contrário
sábio aumenta o salário em 1%. ÿVerificação de ponto

3.8 Qual é a impressão do código em (a) e (b) se o número for 30? E se o número
é 35?

if (número% 2 == 0) cout if (número% 2 == 0) cout


<< número << "é par." << fim; << número << "é par." << fim;
outro
cout << número << " é estranho." << fim; cout << número << " é estranho." << fim;

(a) (b)

3.5 Instruções if -else aninhadas e multivias


Uma instrução if pode estar dentro de outra instrução if para formar uma instrução if aninhada .
Chave
Apontar
A instrução em uma instrução if ou if-else pode ser qualquer instrução C++ legal, incluindo outra instrução if ou
if-else . Diz-se que a instrução if interna está aninhada dentro da instrução if externa . A instrução if interna pode
conter outra instrução if ; na verdade, não há limite para a profundidade do aninhamento. Por exemplo, o seguinte
é uma instrução if aninhada:

se (eu > k) {

se (j > k) instrução if aninhada

cout << "i e j são maiores que k" << endl;


}
outro
cout << "i é menor ou igual a k" << endl;

A instrução if (j > k) está aninhada dentro da instrução if (i > k) .


A instrução if aninhada pode ser usada para implementar múltiplas alternativas. Por exemplo, a afirmação
apresentada na Figura 3.3a atribui uma nota em letras à variável nota de acordo com a pontuação, com múltiplas
alternativas.
Machine Translated by Google

98 Capítulo 3 Seleções

if (pontuação >= 90,0) if (pontuação >= 90,0)


cout << "A nota é A"; senão cout << "A nota é A"; senão
if if (pontuação >= 80,0) cout <<
(pontuação >= 80,0) cout Equivalente "A nota é B"; senão if
<< "A nota é B"; senão if (pontuação >= 70,0) cout << "A
nota é C"; senão if
(pontuação >= 70,0) cout (pontuação >= 60,0) cout << "A
<< "A nota é C"; senão if nota é D"; senão cout <<
"A
(pontuação >= 60,0) cout nota é F";
Isto é melhor
<< "A nota é D"; senão
cout
<< "A nota é F";

(a) (b)

Figura 3.3 Um formato preferido para múltiplas alternativas é mostrado em (b) usando uma instrução
if-else multidirecional .

A execução desta instrução if prossegue conforme mostrado na Figura 3.4. A primeira condição
(pontuação >= 90,0) é testada. Se for verdade, a nota é A. Se for falso, a segunda condição
(pontuação >= 80,0) é testada. Se a segunda condição for verdadeira, a nota é B. Se essa condição
for falsa, a terceira condição e o resto das condições (se necessário) são testadas até que uma condição
seja satisfeita ou todas as condições sejam falsas. Neste último caso, a nota é F. Observe que uma
condição é testada somente quando todas as condições anteriores são falsas.

falso
pontuação >= 90

falso
verdadeiro
pontuação >= 80

A nota é A
falso
verdadeiro
pontuação >= 70

A nota é B
falso
verdadeiro
pontuação >= 60

A nota é C
verdadeiro

A nota é D

A nota é F

Figura 3.4 Você pode usar uma instrução if-else multidirecional para atribuir uma nota.
Machine Translated by Google

3.6 Erros Comuns e Armadilhas 99

A instrução if na Figura 3.3a é equivalente à instrução if na Figura 3.3b. Na verdade, a Figura 3.3b
é o estilo de codificação preferido para múltiplas instruções if alternativas . Esse estilo, chamado de
instruções if-else multidirecionais , evita recuos profundos e torna o programa fácil de ler. instrução if multidirecional

3.9 Suponha que x = 3 e y = 2; mostre a saída, se houver, do código a seguir. Qual é a saída se x =
3 e y = 4? Qual é a saída se x = 2 e y = 2? Desenhe um fluxograma do código. ÿVerificação de ponto

se (x > 2) {

se (y > 2)
{
int z = x + y;
cout << "z é" << z << endl;
}
}
outro
cout << "x é" << x << endl;

3.10 Suponha x = 2 e y = 3. Mostre a saída, se houver, do código a seguir. Qual é a saída se x = 3 e y


= 2? Qual é a saída se x = 3 e y = 3?

se (x > 2)
se (y > 2) {

int z = x + y;
cout << "z é" << z << endl;
}
outro
cout << "x é" << x << endl;

3.11 O que há de errado no código a seguir?

se (pontuação >= 60,0)


cout << "A nota é D";
senão se (pontuação >= 70,0)
cout << A nota é C";
senão se (pontuação >= 80,0)
cout << A nota é B";
senão se (pontuação >= 90,0)
cout << "A nota é A";
outro
cout << "A nota é F";

3.6 Erros e armadilhas comuns


Esquecer os colchetes necessários, colocar ponto-e-vírgula em uma instrução if , confundir Chave
== com = e cláusulas else pendentes são erros comuns em instruções de seleção. Apontar

Declarações duplicadas em declarações if-else e testes de igualdade de valores duplos são


armadilhas comuns.

Erro comum 1: esquecer o aparelho necessário


As chaves podem ser omitidas se o bloco contiver uma única instrução. No entanto, esquecer as chaves
quando elas são necessárias para agrupar múltiplas instruções é uma prática comum de programação.
Machine Translated by Google

100 Capítulo 3 Seleções

erro. Se você modificar o código adicionando novas instruções em uma instrução if sem colchetes, você terá
que inserir os colchetes. Por exemplo, o código a seguir em (a) está errado. Deve ser escrito entre colchetes
para agrupar múltiplas afirmações, conforme mostrado em (b).

se (raio >= 0) se (raio >= 0)


área = raio * raio * PI; {
cout << "A área" é " "
área = raio * raio * PI;
<< cout << "A área" é " "
<<área;
<< <<área;
}

(a) Errado (b) Correto

Em (a), a instrução de saída do console não faz parte da instrução if . É igual ao seguinte código:

se (raio >= 0)
área = raio * raio * PI;

cout << "A área "


<< " é " <<área;

Independentemente da condição na instrução if , a instrução de saída do console é sempre executada.

Erro comum 2: ponto e vírgula errado na linha if


Adicionar um ponto e vírgula no final de uma linha if , conforme mostrado em (a) abaixo, é um erro comum.

Erro lógico Corpo vazio

if (raio >= 0); if (raio >= 0) { };


{ Equivalente {
área = raio * raio * PI; área = raio * raio * PI;
cout << "A área " cout << "A área" é " "
<< " é " <<área; << <<área;
} }

(a) (b)

Esse erro é difícil de encontrar, porque não é um erro de compilação nem de tempo de execução; é um
erro lógico. O código em (a) é equivalente ao de (b) com corpo vazio.

Erro comum 3: usar erroneamente = para ==


O operador de teste de igualdade são dois sinais de igual (==). Em C++, se você usar = para == por engano,
isso levará a um erro lógico. Considere o seguinte código:

if (contagem = 3)
cout << "contagem é zero" << endl;
outro
cout << "a contagem não é zero" << endl;

Ele sempre exibe "contagem é zero", porque contagem = 3 atribui 3 à contagem e a expressão de
atribuição é avaliada como 3. Como 3 é um valor diferente de zero, ele é interpretado como uma condição
verdadeira pela instrução if . Lembre-se de que qualquer valor diferente de zero é avaliado como verdadeiro
e valor zero é avaliado como falso.
Machine Translated by Google

3.6 Erros Comuns e Armadilhas 101

Erro comum 4: teste redundante de valores booleanos


Para testar se uma variável bool é verdadeira ou falsa em uma condição de teste, é redundante usar o
operador de teste de igualdade como o código em (a):

se (par == verdadeiro) Equivalente se (mesmo)


cout << "É par."; cout << "É par.";

(a) Isto é melhor (b)

Em vez disso, é melhor testar a variável bool diretamente, como em (b). Outra boa razão para fazer isso
é evitar erros difíceis de detectar. Usando o operador = em vez de ==
operador para comparar a igualdade de dois itens em uma condição de teste é um erro comum. Isso pode
levar à seguinte afirmação errônea:

se (par = verdadeiro)
cout << "É par.";

Esta afirmação atribui verdadeiro a par, de modo que par é sempre verdadeiro. Portanto, a condição
para a instrução if é sempre verdadeira.

Erro comum 5: Ambiguidade pendente


O código em (a) abaixo possui duas cláusulas if e uma cláusula else . Qual cláusula if corresponde à cláusula
else ? O recuo indica que a cláusula else corresponde à primeira cláusula if .
No entanto, a cláusula else na verdade corresponde à segunda cláusula if . Esta situação é conhecida como
ambigüidade do else pendente . A cláusula else sempre corresponde ao if sem correspondência mais recente pendurado outra ambiguidade
cláusula no mesmo bloco. Portanto, a instrução em (a) é equivalente ao código em (b).

int i = 1, j = 2, k = 3; int i = 1, j = 2, k = 3;
Equivalente
se (i > j) se se (i > j) se
(i > k) (i > k)
cout << "A"; cout << "A";
Isso é melhor outro
outro
com recuo cout << "B";
cout << "B";
correto
(a) (b)

Como (i > j) é falso, nada é exibido nas afirmações em (a) e (b). Forçar
a cláusula else para corresponder à primeira cláusula if , você deve adicionar um par de chaves:

int i = 1, j = 2, k = 3;

se (eu > j) {

se (eu > k)
cout << "A";
}
outro
cout << "B";

Esta declaração exibe B.

Erro comum 6: teste de igualdade de dois valores de ponto flutuante


Conforme discutido no Erro Comum 3 na Seção 2.16, os números de ponto flutuante têm precisão limitada e
os cálculos que envolvem números de ponto flutuante podem introduzir erros de arredondamento.
Portanto, um teste de igualdade de dois valores de ponto flutuante não é confiável. Por exemplo, você espera
que o código a seguir exiba x é 0,5, mas surpreendentemente ele exibe x não é 0,5.
Machine Translated by Google

102 Capítulo 3 Seleções


duplo x = 1,0 - 0,1 - 0,1 - 0,1 - 0,1 - 0,1;
if (x == 0,5) cout
<< "x é 0,5" << endl;
outro
cout << "x não é 0,5" << endl;

Aqui, x não é exatamente 0,5, mas está muito próximo de 0,5. Você não pode testar com segurança a igualdade de
dois valores de ponto flutuante. No entanto, você pode comparar se eles estão próximos o suficiente testando se a
diferença dos dois números é menor que algum limite. Ou seja, dois números xey são muito próximos se x - y 6 e,
para um valor muito pequeno, e. e, uma letra grega pronunciada como épsilon, é comumente usada para denotar um
valor muito pequeno. Normalmente, você define e como 10-14 para comparar dois valores do tipo double e como
10-7 para comparar dois valores do tipo float . Por exemplo, o seguinte código

const duplo EPSILON = 1E-14;


duplo x = 1,0 - 0,1 - 0,1 - 0,1 - 0,1 - 0,1;
se (abs(x - 0,5) <épsilon)
cout << "x é aproximadamente 0,5" << endl;

exibirá que x é
aproximadamente 0,5
A função abs(a) no arquivo da biblioteca cmath pode ser usada para retornar o valor absoluto de a.

Armadilha Comum 1: Simplificando a Atribuição de Variável Booleana


Freqüentemente, novos programadores escrevem o código que atribui uma condição de teste a uma variável bool
como o código em (a):

se (número% 2 == 0) bool par =


Equivalente
par = verdadeiro; número% 2 == 0;
outro
par = falso; Isto é melhor

(a) (b)

Isto não é um erro, mas deveria ser melhor escrito como mostrado em (b).

Armadilha Comum 2: Evitando Código Duplicado em Casos Diferentes


Freqüentemente, novos programadores escrevem o código duplicado em diferentes casos que devem ser combinados
em um só lugar. Por exemplo, o código destacado na instrução a seguir está duplicado:

if (no estado) {

mensalidade = 5.000;
cout << "A mensalidade é " << mensalidade << endl;
}
outro
{
mensalidade = 15.000;
cout << "A mensalidade é " << mensalidade << endl;
}

Isso não é um erro, mas é melhor escrevê-lo da seguinte forma:

if (no estado) {

mensalidade = 5.000;
}
outro
Machine Translated by Google

3.6 Erros Comuns e Armadilhas 103

{
mensalidade = 15.000;
}
cout << "A mensalidade é " << mensalidade << endl;

O novo código elimina a duplicação e facilita a manutenção do código, pois se a instrução print for
modificada, você só precisará alterar em um local.

Armadilha comum 3: valores inteiros podem ser usados como valores booleanos
Em C++, um booleano verdadeiro é tratado como 1 e falso como 0. Um valor numérico pode ser usado
como um valor booleano. Em particular, C++ converte um valor diferente de zero em verdadeiro e 0 em falso.
Um valor booleano pode ser usado como um número inteiro. Isso pode levar a possíveis erros lógicos.
Por exemplo, o código a seguir em (a) contém um erro lógico. Suponha que amount seja 40, o código
exibirá Amount is more than 50, porque !amount é avaliado como 0 e 0 <= 50 é verdadeiro. O código
correto deve ser conforme mostrado em (b).

se (! quantidade <= 50) se (!(quantidade <= 50))


cout << "O valor é maior que 50"; cout << "O valor é maior que 50";

(a) (b)

3.12 Mostre a saída do seguinte código:


ÿVerificação de ponto

valor interno = 5; valor interno = 5;

se (quantidade >= 100) se (quantidade >= 100)


{ cout << "O valor é " cout << "O << quantidade << " ";
cout << "O valor é " cout << "O << quantidade << " "; imposto é " << quantidade * 0,03;
imposto é " << quantidade * 0,03;
}

(a) (b)

valor interno = 5; valor interno = 0;

se (valor >= 100); se (quantidade = 0)


cout << "O valor é " cout << "O << quantidade << " "; cout << "Valor é zero";
imposto é " << quantidade * 0,03; outro
cout << "O valor não é zero";

(c) (d)

3.13 Quais das seguintes afirmações são equivalentes? Quais estão corretos
recuado?

se (eu > 0) se se (eu > 0) { se (eu > 0) se (eu > 0)


(j > 0) x = se (j > 0) se (j > 0) se (j > 0)
0; outro x = 0; x = 0; x = 0;
se (k > 0) y = 0; senão z = senão se (k > 0) senão se (k > 0) senão se (k > 0)
0; y = 0; y = 0; y = 0;
} outro outro
outro z = 0; z = 0;
z = 0;

(a) (b) (c) (d)


Machine Translated by Google

104 Capítulo 3 Seleções

3.14 Reescreva a seguinte instrução usando uma expressão booleana:

se (contagem% 10 == 0)
novaLinha = verdadeiro;
outro
novaLinha = falso;

3.15 As afirmações a seguir estão corretas? Qual é o melhor?

se (idade < 16) se (idade < 16)


conta << conta <<
“Não é possível tirar carteira de motorista”; “Não é possível tirar carteira de motorista”;
if (idade >= 16) outro
cout << conta <<
“Pode tirar carteira de motorista”; “Pode tirar carteira de motorista”;

(a) (b)

3.16 Qual é a saída do código a seguir se o número for 14, 15 e 30?

if (número% 2 == 0) cout if (número% 2 == 0) cout


<< número << "é par"; se (número% 5 << número << "é par"; senão se
== 0) (número% 5 == 0)
número << "é múltiplo de 5"; cout << número << "é múltiplo de 5"; cout <<

(a) (b)

3.7 Estudo de caso: Calculando o Índice de Massa Corporal


Você pode usar instruções if aninhadas para escrever um programa que interprete o Índice de Massa Corporal.
Chave
Apontar
O Índice de Massa Corporal (IMC) é uma medida de saúde baseada na altura e no peso. Você pode calcular
seu IMC pegando seu peso em quilogramas e dividindo-o pelo quadrado de sua altura em metros. A
interpretação do IMC para pessoas com 20 anos ou mais é a seguinte:

IMC Interpretação
IMC 6 18,5 Abaixo do peso
18,5… IMC 6 25,0 Normal

25,0… IMC 6 30,0 Sobrepeso


30,0… IMC Obeso

Escreva um programa que solicite ao usuário que insira o peso em libras e a altura em polegadas e exiba
o IMC. Observe que uma libra equivale a 0,45359237 quilogramas e uma polegada equivale a 0,0254
metros. A Listagem 3.2 fornece o programa.

Listagem 3.2 ComputeAndInterpreteBMI.cpp


Nota de vídeo 1 #include <iostream>
Calcular IMC 2 usando namespace std;

3 4 int principal()
5{
6 // Solicita ao usuário que insira o peso em libras
Machine Translated by Google

3.7 Estudo de caso: Calculando o Índice de Massa Corporal 105

cout << "Insira o peso em libras: ";


peso duplo ;
7 cin >> peso; peso de entrada
8
9 // Solicita ao usuário que insira a altura em polegadas
10 cout << "Insira a altura em polegadas: ";
11 altura dupla ;
12 cin >> altura; altura de entrada
13 14 15
16 const duplo QUILOGRAMAS_PER_POUND = 0,45359237; // Constante
17 const duplo METERS_PER_INCH = 0,0254; // Constante
18
19 // Calcula o IMC
20 peso duploInKilograms = peso * KILOGRAMS_PER_POUND; double
21 heightInMeters = altura * METERS_PER_INCH; IMC duplo = peso em
22 quilogramas / calcular IMC
23 (alturaInMeters * alturaInMeters);
24
25 //Exibir resultado
26 cout << "IMC é " << imc << endl; saída de exibição
27 se (IMC < 18,5)
28 cout << "Abaixo do peso" << endl;
29 senão se (IMC < 25)
30 cout << "Normal" << endl;
31 senão se (IMC < 30)
32 cout << "Excesso de peso" << endl;
33 outro
34 cout << "Obeso" << endl;
35
36 retornar 0;
37}

Insira o peso em libras: 146


Insira a altura em polegadas: 70
O IMC é 20,9486
Normal

Linha# peso altura pesoInKilograms alturaInMeters bmi Saída


9 146
14 70
20 66.22448602
21 1.778
22 20.9486
26 O IMC é
20,9486
32 Normal

Duas constantes KILOGRAMS_PER_POUND e METERS_PER_INCH são definidas em linhas


16–17. Usar constantes aqui torna o programa fácil de ler.
Você deve testar o programa inserindo a entrada que cobre todos os casos possíveis para o IMC
garantir que o programa funcione para todos os casos. testar todos os casos
Machine Translated by Google

106 Capítulo 3 Seleções

3.8 Estudo de Caso: Computação de Impostos


Você pode usar instruções if aninhadas para escrever um programa para calcular impostos.
Chave
Apontar
O imposto de renda pessoal federal dos Estados Unidos é calculado com base no status do depósito e na renda tributável.
Existem quatro status de depósito: arquivadores solteiros, arquivamento de casado em conjunto ou viúvo(a) qualificado,
arquivamento de casado separadamente e chefe de família. As taxas de imposto variam a cada ano.
A Tabela 3.2 mostra as taxas para 2009. Se você é, digamos, solteiro e tem uma renda tributável de US$ 10.000, os primeiros
US$ 8.350 são tributados a 10% e os outros US$ 1.650 são tributados a 15%, portanto, seu imposto total é de US$ 1.082,50.

Tabela 3.2 Alíquotas de Imposto Pessoal Federal dos EUA de 2009

Marginal Casado arquivando em conjunto


Taxa de imposto Solteiro ou viúva(s) qualificada(s) Casado arquivando separadamente Chefe de família

10% US$ 0 – US$ 8.350 US$ 0 – US$ 16.700 US$ 0 – US$ 8.350 US$ 0 – US$ 11.950

15% US$ 8.351 – US$ 33.950 US$ 16.701 – US$ 67.900 US$ 8.351 – US$ 33.950 US$ 11.951 – US$ 45.500

25% US$ 33.951 – US$ 82.250 US$ 67.901 – US$ 137.050 US$ 33.951 – US$ 68.525 US$ 45.501 – US$ 117.450

28% US$ 82.251 – US$ 171.550 US$ 137.051 – US$ 208.850 US$ 68.526 – US$ 104.425 US$ 117.451 – US$ 190.200

33% US$ 171.551 – US$ 372.950 US$ 208.851 – US$ 372.950 US$ 104.426 – US$ 186.475 US$ 190.201 – US$ 372.950

35% $ 372.951 + $ 372.951 + $ 186.476 + $ 372.951 +

Você deve escrever um programa para calcular o imposto de renda pessoal. Seu programa deve solicitar que o usuário
insira o status do pedido e a renda tributável e calcule o imposto. Insira 0 para declarantes solteiros, 1 para arquivamento de
casado em conjunto ou viúvo(a) qualificado, 2 para arquivamento de casado separadamente e 3 para chefe de família.

Seu programa calcula o imposto sobre o lucro tributável com base no status do pedido. O status do arquivamento pode ser
determinado usando declarações if descritas a seguir:

se (estado == 0) {

// Calcula o imposto para arquivadores únicos


}
senão if (status == 1) {

// Calcular imposto para casamento declarado em conjunto ou viúvo(a) qualificado(a)


}
senão if (status == 2) {

// Calcula o imposto para declaração de casamento separadamente


}
senão if (status == 3) {

//Calcula o imposto para o chefe de família


}
outro {
//Exibe status errado
}

Para cada status de depósito existem seis alíquotas de imposto. Cada taxa é aplicada a um determinado valor do lucro
tributável. Por exemplo, de um rendimento tributável de $ 400.000 para declarantes únicos, $ 8.350 são tributados a 10%,
(33.950 – 8.350) a 15%, (82.250 – 33.950) a 25%, (171.550 – 82.250) a 28%, (372.950 – 171.550) a 33% e (400.000 – 372.950)
a 35%.
Machine Translated by Google

3.8 Estudo de Caso: Computação de Impostos 107

A Listagem 3.3 fornece a solução para calcular impostos para arquivadores únicos. A solução completa é
deixado como exercício.

Listagem 3.3 ComputeTax.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
// Solicita ao usuário para inserir o status do arquivamento
7 cout << "(0-arquivador solteiro, 1-casado em conjunto, "
6
8 << "ou viúvo(a) qualificado(a), " << "2- << fim
9 casados separadamente, 3-chefes de família)" << endl
10 << "Insira o status do arquivamento: ";
11
12 status interno ;
13 cin >> status; status de entrada
14
15 // Solicita ao usuário que insira o lucro tributável
16 cout << "Insira o lucro tributável: ";
17 renda dupla ;
18 cin >> renda; renda de entrada
19
20 // Calcular imposto
21 duplo imposto = 0; calcular imposto
22
23 if (status == 0) // Calcula o imposto para arquivadores únicos
24 {
25 se (renda <= 8350)
26 imposto = renda * 0,10;
27 senão se (renda <= 33950)
28 imposto = 8.350 * 0,10 + (renda - 8.350) * 0,15;
29 senão se (renda <= 82250)
30 imposto = 8350 * 0,10 + (33950 - 8350) * 0,15 +
31 (renda - 33.950) * 0,25;
32 senão se (renda <= 171550)
33 imposto = 8350 * 0,10 + (33950 - 8350) * 0,15 +
34 (82.250 - 33.950) * 0,25 + (renda - 82.250) * 0,28;
35 senão se (renda <= 372950)
36 imposto = 8350 * 0,10 + (33950 - 8350) * 0,15 +
37 (82250 - 33950) * 0,25 + (171550 - 82250) * 0,28 +
38 (renda - 171.550) * 0,33;
39 outro
40 imposto = 8350 * 0,10 + (33950 - 8350) * 0,15 +
41 (82250 - 33950) * 0,25 + (171550 - 82250) * 0,28 +
42 (372.950 - 171.550) * 0,33 + (renda - 372.950) * 0,35;
43 }
44 else if (status == 1) // Calcula o imposto para arquivo de casamento em conjunto
45 {
46 // Deixado como exercício
47 }
48 else if (status == 2) // Calcula o imposto para casados separadamente
49 {
50 // Deixado como exercício
51 }
Machine Translated by Google

108 Capítulo 3 Seleções


52 else if (status == 3) // Calcula o imposto para o chefe da família
53 {
54 // Deixado como exercício
55 }
56 outro
57 {
58 cout << "Erro: status inválido";
programa de saída 59 retornar 0;
60 }
61
62 //Mostra o resultado
saída de exibição 63 cout << "O imposto é" << static_cast<int>(imposto * 100) / 100,0 << endl;
64
retornar 0;
65 66}

(0-declarante solteiro, 1-casado em conjunto ou viúvo(a) qualificado(a),


2-casados separadamente, 3-chefes de família)
Insira o status do arquivamento: 0
Insira o lucro tributável: 400.000
O imposto é 117684

Linha# status renda imposto Saída

13 0

18 400.000

21 0

40 130599

63 O imposto é 130599

O programa recebe o status do pedido e o lucro tributável. As declarações if-else multidirecionais (linhas
23, 44, 48, 52, 56) verificam o status do depósito e calculam o imposto no qual ele se baseia.

testar todos os casos Para testar um programa, você deve fornecer a entrada que abrange todos os casos. Para este programa,
sua entrada deve abranger todos os status (0, 1, 2, 3). Para cada status, teste o imposto para cada uma das
seis faixas. Portanto, há um total de 24 casos.

Dica
Para todos os programas, você deve escrever uma pequena quantidade de código e testá-la antes de adicionar
desenvolvimento mais código. Isso é chamado de desenvolvimento e teste incrementais. Essa abordagem facilita a identificação
incremental e testes de erros, porque os erros provavelmente estão no novo código que você acabou de adicionar.

3.17 As afirmações a seguir são equivalentes?


ÿVerificação de ponto

if (renda <= 10.000) imposto if (renda <= 10.000) imposto


= renda * 0,1; senão se = renda * 0,1; senão se
(renda <= 20.000) (renda> 10.000 &&
imposto = 1000 + renda <= 20.000)
(renda – 10.000) * 0,15; imposto = 1000 +
(renda – 10.000) * 0,15;
Machine Translated by Google

3.9 Gerando Números Aleatórios 109

3.9 Gerando Números Aleatórios


Você pode usar a função Rand() para obter um número inteiro aleatório.
Chave
Apontar
Suponha que você queira desenvolver um programa para um aluno da primeira série praticar subtração. O
programa gera aleatoriamente dois números inteiros de um único dígito, número1 e número2, com número1
>= número2, e exibe ao aluno uma pergunta como "Quanto é 9 – 2?" Após o aluno inserir a resposta, o
Nota de vídeo
programa exibe uma mensagem indicando se a resposta está correta.
Teste de subtração
Para gerar um número aleatório, use a função Rand() no arquivo de cabeçalho cstdlib . Esta função retorna um número
inteiro aleatório entre 0 e RAND_MAX. RAND_MAX é uma constante dependente da plataforma. No Visual C++, RAND_MAX é função rand()
32767.
Os números que rand() produz são pseudoaleatórios. Ou seja, toda vez que é executado no mesmo sistema, Rand() produz pseudoaleatório
a mesma sequência de números. Na máquina do autor, por exemplo, a execução dessas três instruções sempre produzirá os
números 130, 10982 e 1090.

cout << rand() << endl << rand() << endl << rand() << endl;

Por que? O algoritmo da função Rand() usa um valor chamado semente para controlar como gerar os função rsand(semente)
números. Por padrão, o valor inicial é 1. Se você alterar o valor inicial para um valor diferente, a sequência
de números aleatórios será diferente. Para alterar a semente, use o comando srand(seed)
função no arquivo de cabeçalho cstdlib . Para garantir que o valor inicial seja diferente cada vez que você
executar o programa, use time(0). Conforme discutido na Seção 2.10, “Estudo de caso: exibindo a hora
atual”, invocar time(0) retorna a hora atual em segundos decorridos desde 00:00:00 de 1º de janeiro de 1970
GMT. Portanto, o código a seguir exibirá um número inteiro aleatório com uma semente aleatória.

srand(tempo(0));
cout << rand() << endl;

Para obter um número inteiro aleatório entre 0 e 9, use

rand()% 10

O programa pode ser configurado para funcionar da seguinte forma:

Etapa 1: gere dois números inteiros de um dígito em número1 e número2.


Etapa 2: se número1 < número2, troque número1 por número2.
Etapa 3: peça ao aluno que responda “O que é número1 – número2?”
Passo 4: Verifique a resposta do aluno e verifique se está correta.

O programa completo é mostrado na Listagem 3.4.

Listagem 3.4 SubtractionQuiz.cpp


1 #include <iostream>
2 #include <ctime> // para função de tempo incluir ctime
3 #include <cstdlib> // para funções Rand e srand incluir cstdlib
4 usando namespace std;

5 6 int principal()
7{
// 1. Gere dois números inteiros aleatórios de um dígito
srand(tempo(0)); definir uma semente

8 int número1 = rand()% 10; número aleatório1


9 int número2 = rand()% 10; número aleatório2
10
11 // 2. Se número1 < número2, troque número1 por número2
12 13 14 se (número1 <número2)
Machine Translated by Google

110 Capítulo 3 Seleções


15 {
trocar números 16 temperatura interna = número1;
17 número1 = número2;
18 número2 = temperatura;
19 }
20
21 // 3. Peça ao aluno para responder "o que é número1 – número2?"
" - "
22 cout << "O que é " << número1 << int << número2 << "? ";
23 resposta;
digite a resposta 24 cin >> resposta;
25
26 // 4. Classifique a resposta e exiba o resultado
exibir resultado 27 if (número1 - número2 == resposta)
28 cout << "Você está correto!";
29 outro
" " - "
30 cout << "Sua resposta está errada. << número2<< número1 <<
31 << "deve ser" << (número1 - número2) << endl;
32
retornar 0;
33 34}

Quanto é 5 – 2? 3
Você está certo!

O que é 4 – 2? 1
Sua resposta está errada.
4 – 2 deveria ser 2

Linha# número 1 número 2 temperatura responder Saída

10 2
11 4
16 2
17 4
18 2
24 1
30 Sua resposta está errada
4 – 2 deveria ser 2

Para trocar duas variáveis número1 e número2, uma variável temporária temp (linha 16) é usada primeiro
para manter o valor em número1. O valor em number2 é atribuído a number1 (linha 17) e o valor em temp é
atribuído a number2 (linha 18).

3.18 Qual das alternativas a seguir é uma saída possível da invocação de rand()?
ÿVerificação de ponto
323,4, 5, 34, 1, 0,5, 0,234
3.19a . Como você gera um número inteiro aleatório i tal que 0… i 6 20?
b. Como você gera um número inteiro aleatório i tal que 10… i 6 20?
c. Como você gera um número inteiro aleatório i tal que 10… i … 50?
Machine Translated by Google

3.10 Operadores Lógicos 111

d. Escreva uma expressão que retorne -1 ou 1 aleatoriamente.


e. Descubra o que é RAND_MAX na sua máquina.

3.20 Escreva uma expressão que obtenha um número inteiro aleatório entre 34 e 55. Escreva um
expressão que obtém um número inteiro aleatório entre 0 e 999.

3.10 Operadores Lógicos


Os operadores lógicos !, && e || pode ser usado para criar uma expressão booleana composta.
Chave
Apontar

Às vezes, uma combinação de diversas condições determina se uma instrução será executada.
Você pode usar operadores lógicos para combinar essas condições. Os operadores lógicos, também
conhecidos como operadores booleanos, operam em valores booleanos para criar um novo valor booleano. A
Tabela 3.3 fornece uma lista de operadores booleanos. A Tabela 3.4 define o operador not (!) . O operador
not (!) nega verdadeiro para falso e falso para verdadeiro. A Tabela 3.5 define o operador and (&&) . O e
(&&) de dois operandos booleanos é verdadeiro se e somente se ambos os operandos forem verdadeiros.
A Tabela 3.6 define o operador ou (||) . O ou (||) de dois operandos booleanos é verdadeiro se pelo menos
um dos operandos for verdadeiro.

Tabela 3.3 Operadores Booleanos

Operador Nome Descrição


! não negação lógica
&& e conjunção lógica
|| ou disjunção lógica

Tabela 3.4 Tabela Verdade para Operador !

p !p Exemplo (suponha idade = 24, peso = 140)


verdadeiro falso !(age > 18) é falso, porque (age > 18) é verdadeiro.
falso verdadeiro
!(peso == 150) é verdadeiro, porque (peso == 150)
é falso.

Tabela 3.5 Tabela Verdade para Operador &&

p1 p2 p1 && p2 Exemplo (suponha idade = 24, peso = 140)


falso falso falso (idade > 18) && (peso <= 140) é verdadeiro, porque (idade
falso verdadeiro falso > 18) e (peso <= 140) são ambos verdadeiros.
verdadeiro falso falso (idade > 18) && (peso > 140) é falso, porque (peso > 140)
verdadeiro verdadeiro verdadeiro é falso.

Tabela 3.6 Tabela Verdade para Operador ||

p1 p2 p1 || p2 Exemplo (suponha idade = 24, peso = 140)


falso falso falso (idade > 34) || (peso <= 140) é verdadeiro, porque (peso
falso verdadeiro verdadeiro <= 140) é verdadeiro.
verdadeiro falso verdadeiro
(idade > 34) || (peso >= 150) é falso, porque (idade > 34) e
verdadeiro verdadeiro verdadeiro (peso >= 150) são ambos falsos.
Machine Translated by Google

112 Capítulo 3 Seleções

A Listagem 3.5 fornece um programa que verifica se um número é divisível por 2 e 3, por 2 ou
3 e por 2 ou 3 , mas não ambos:

Listagem 3.5 TestBooleanOperators.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 número interno ;
7 cout << "Digite um número inteiro: ";
entrada 8 cin >> número;
9

e 10 if (número% 2 == 0 && número% 3 == 0)


11 cout << número << "é divisível por 2 e 3." << fim;
12
ou 13 if (número% 2 == 0 || número% 3 == 0)
14 cout << número << "é divisível por 2 ou 3." << fim;
15
16 if ((número % 2 == 0 || número % 3 == 0) &&
17 !(número % 2 == 0 && número % 3 == 0))
18 cout << número << "divisível por 2 ou 3, mas não por ambos." << fim;
19
20 retornar 0;
21}

Insira um número inteiro: 4


4 é divisível por 2 ou 3.
4 é divisível por 2 ou 3, mas não por ambos.

Insira um número inteiro: 18


18 é divisível por 2 e 3
18 é divisível por 2 ou 3.

(número % 2 == 0 && número % 3 == 0) (linha 10) verifica se o número é divisível por 2 e 3.


(número % 2 == 0 || número % 3 == 0) (linha 13) verifica se o número é divisível por 2 ou 3. Portanto,
a expressão booleana nas linhas 16–17

((número % 2 == 0 || número % 3 == 0) && !(número %


2 == 0 && número % 3 == 0))

verifica se o número é divisível por 2 ou 3 , mas não ambos.

Cuidado
Em matemática, a expressão

1 <= númeroDeDiasNoMês <= 31

está correto. No entanto, está incorreto em C++, porque 1 <= numberOfDaysInAMonth


é avaliado como um valor bool e, em seguida, um valor bool (1 para verdadeiro e 0 para
Machine Translated by Google

3.10 Operadores Lógicos 113

false) é comparado com 31, o que levaria a um erro lógico. A expressão correta é operandos incompatíveis

(1 <= número de dias em mês) && (número de dias em mês <= 31)

Observação

A lei de De Morgan, em homenagem ao matemático e lógico britânico nascido na Índia Augustus De Da lei de Morgan
Morgan (1806-1871), pode ser usada para simplificar expressões booleanas. A lei afirma:

!(condição1 && condição2) é o mesmo que !


condição1 || !condição2
!(condição1 || condição2) é o mesmo que !
condição1 && !condição2

Por exemplo,

!(número% 2 == 0 && número% 3 == 0)

pode ser simplificado usando uma expressão equivalente:

(número% 2 != 0 || número% 3 != 0)

Como outro exemplo,

!(número == 2 || número == 3)

é melhor escrito como

número!= 2 && número!= 3

Se um dos operandos de um operador && for falso, a expressão será falsa; se um dos operandos de um || operador é
verdadeiro, a expressão é verdadeira. C++ usa essas propriedades para melhorar o desempenho desses operadores. Ao avaliar p1
&& p2, C++ avalia p1 e, se for verdadeiro, avalia p2; caso contrário, não avalia p2. Ao avaliar p1 || p2, C++ avalia p1 e, se for falso,
avalia p2; caso contrário, não avalia p2.

Portanto, nos referimos a && como o operador AND condicional ou de curto-circuito e a || como o operador OR operador condicional
condicional ou de curto-circuito . C++ também fornece os operadores bit a bit AND (&) e OR (|) , que são operador de curto-circuito
abordados nos Suplementos IV.J e IV.K para leitores avançados.

3.21 Supondo que x seja 1, mostre o resultado das seguintes expressões booleanas:
ÿVerificação de ponto

(verdadeiro) && (3 > 4)


!(x > 0) && (x > 0)
(x > 0) || (x < 0)
(x! = 0) || (x == 0)
(x >= 0) || (x < 0)
(x!= 1) ==!(x == 1)

3.22 (a) Escreva uma expressão booleana que seja avaliada como verdadeira se um número armazenado na
variável num estiver entre 1 e 100. (b) Escreva uma expressão booleana que seja avaliada como
verdadeira se um número armazenado na variável num estiver entre 1 e 100 ou o número é negativo.
3.23 (a) Escreva uma expressão booleana para x - 5 6 4.5. (b) Escreva uma expressão booleana para x - 5 7
4.5.
Machine Translated by Google

114 Capítulo 3 Seleções

3.24 Para testar se x está entre 10 e 100, quais das seguintes expressões são
correto?

a. 100 > x > 10


b. (100 > x) && (x > 10)
c. (100 > x) || (x > 10)
d. (100 > x) e (x > 10)
e. (100 > x) ou (x > 10)

3.25 As duas expressões a seguir são iguais?


a. x% 2 == 0 && x% 3 == 0
b. x% 6 == 0

3.26 Qual é o valor da expressão x >= 50 && x <= 100 se x for 45, 67,
ou 101?

3.27 Suponha que, ao executar o programa, você insira a entrada 2 3 6 no console.


Qual é a saída?

#include <iostream>
usando namespace std;

int principal()
{
duplo x, y, z;
cin >> x >> y >> z;

cout << "(x < y && y < z) é " << (x < y && y < z) << endl;
cout << "(x < y || y < z) é " << (x < y || y < z) << endl;
cout << "!(x < y) é " << !(x < y) << endl;
cout << "(x + y < z) é " << (x + y < z) << endl;
cout << "(x + y > z) é " << (x + y > z) << endl;

retornar 0;
}

3.28 Escreva uma expressão booleana que seja avaliada como verdadeira se a idade for maior que 13 anos ou menos
do que 18.

3.29 Escreva uma expressão booleana que seja avaliada como verdadeira se o peso for maior que 50
libras ou altura é superior a 60 polegadas.
3.30 Escreva uma expressão booleana que seja avaliada como verdadeira se o peso for maior que 50
libras e altura é superior a 60 polegadas
3.31 Escreva uma expressão booleana que seja avaliada como verdadeira se o peso for maior que 50
libras ou a altura for maior que 60 polegadas, mas não ambos.

3.11 Estudo de caso: Determinando o ano bissexto


Um ano é bissexto se for divisível por 4 , mas não por 100, ou se for divisível por 400.
Chave
Apontar
Você pode usar as seguintes expressões booleanas para verificar se um ano é bissexto:

//Um ano bissexto é divisível por 4


bool isLeapYear = (ano % 4 == 0);
Machine Translated by Google

3.12 Estudo de Caso: Loteria 115

//Um ano bissexto é divisível por 4 mas não por 100


isLeapYear = isLeapYear && (ano% 100 ! = 0);

// Um ano bissexto é divisível por 4, mas não por 100 ou divisível por 400
isLeapYear = isLeapYear || (ano % 400 == 0);

ou você pode combinar todas essas expressões em uma:

isLeapYear = (ano % 4 == 0 && ano % 100 != 0) || (ano % 400 == 0);

A Listagem 3.6 fornece um programa que permite ao usuário inserir um ano e verificar se é um ano
bissexto.

Listagem 3.6 LeapYear.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5 {
6 cout << "Insira o ano: ";
ano interno ;
7 cin >> ano; entrada
8
9 // Verifica se o ano é bissexto
10 bool éAnoLeap = ano bissexto?
11 (ano % 4 == 0 && ano % 100 != 0) || (ano % 400 == 0);
12
13 //Mostra o resultado
14 15 if (é ano bissexto) declaração if
16 cout << ano << else "é um ano bissexto" << endl;
17
18 cout << ano << "não é um ano bissexto" << endl;
19
20 retornar 0;
21}

Digite um ano: 2008


2008 é um ano bissexto

Digite um ano: 1900 1900


não é um ano bissexto

Digite um ano: 2002


2002 não é um ano bissexto

3.12 Estudo de Caso: Loteria


O programa de loteria envolve a geração de números aleatórios, a comparação de dígitos e o
Chave
uso de operadores booleanos. Apontar
Machine Translated by Google

116 Capítulo 3 Seleções

Suponha que você desenvolva um programa para jogar na loteria. O programa gera aleatoriamente uma loteria de um número de dois

dígitos, solicita ao usuário que insira um número de dois dígitos e determina se o usuário ganha de acordo com a seguinte regra:

1. Se a entrada do usuário corresponder ao número da loteria na ordem exata, o prêmio será de US$ 10.000.

2. Se todos os dígitos inseridos pelo usuário corresponderem a todos os dígitos do número da loteria, o prêmio será
US$ 3.000.

3. Se um dígito na entrada do usuário corresponder a um dígito no número da loteria, o prêmio será de US$ 1.000.

Observe que os dígitos de um número de dois dígitos podem ser 0. Se um número for menor que 10, presumimos que o número é

precedido por 0 para formar um número de dois dígitos. Por exemplo, o número 8 é tratado como 08 e o número 0 é tratado como 00 no

programa. A Listagem 3.7 fornece o programa completo.

Listagem 3.7 Loteria.cpp


1 #include <iostream>
2 #include <ctime> // para função de tempo
3 #include <cstdlib> // para funções Rand e srand
4 usando namespace std;

5 6 int principal()
7{
//Gera uma loteria
9
8 srand(tempo(0));
gerar um número de loteria 10 int loteria = rand()% 100;
11
12 // Solicita ao usuário para inserir uma estimativa
13 cout << "Insira sua escolha de loteria (dois dígitos): ";
14 int adivinha;
digite um palpite 15 cin >> adivinha;
16
17 // Obtém os dígitos da loteria
18 int lotteryDigit1 = loteria / 10;
19 int lotteryDigit2 = loteria% 10;
20
21 // Obtém dígitos do palpite
22 int palpiteDigit1 = palpite / 10;
23 int palpiteDigit2 = palpite% 10;
24
25 cout << "O número da loteria é " << loteria << endl;
26
27 // Verifica o palpite
Combinação exata? 28 if (adivinha == loteria)
29 cout << "Correspondência exata: você ganha $ 10.000" << endl;
corresponde a todos os dígitos? 30 senão if (guessDigit2 == lotteryDigit1
31 && adivinhaDigit1 == lotteryDigit2)
32 cout << "Combine todos os dígitos: você ganha $ 3.000" << endl;
corresponde a um dígito? 33 senão if (palpiteDigit1 == lotteryDigit1
34 || adivinhaDigit1 == lotteryDigit2
35 || adivinhaDigit2 == loteriaDigit1
36 || adivinhaDigit2 == loteriaDigit2)
37 cout << "Combine um dígito: você ganha $ 1.000" << endl;
sem correspondência 38 outro
39 cout << "Desculpe, não corresponde" << endl;
40
41 retornar 0;
42}
Machine Translated by Google

3.13 Declarações switch 117

Digite sua escolha de loteria (dois dígitos): 00


O número da loteria é 0
Correspondência exata: você ganha US$ 10.000

Insira sua escolha de loteria (dois dígitos): 45


O número da loteria é 54
Combine todos os dígitos: você ganha $ 3.000

Insira sua escolha de loteria: 23


O número da loteria é 34
Acerte um dígito: você ganha US$ 1.000

Insira sua escolha de loteria: 23


O número da loteria é 14
Desculpe, não corresponde

Linha# 10 15 18 19 22 23 37
Variável

loteria 34

adivinhar 23

loteriaDigit1 3

loteriaDigit2 4

palpiteDigit1 2

palpiteDigit2 3

saída Acerte um dígito: você


ganha US$ 1.000

O programa gera uma loteria usando a função rand() (linha 10) e solicita que o usuário insira um palpite (linha
15). Observe que palpite% 10 obtém o último dígito de palpite e palpite / 10
obtém o primeiro dígito da estimativa, já que a estimativa é um número de dois dígitos (linhas 22–23).
O programa compara o palpite com o número da loteria nesta ordem:

1. Primeiro, verifique se o palpite corresponde exatamente ao da loteria (linha 28).

2. Caso contrário, verifique se a reversão do palpite corresponde à loteria (linhas 30–31).

3. Caso contrário, verifique se há um dígito na loteria (linhas 33–36).

4. Caso contrário, nada corresponde e exibe "Desculpe, não corresponde" (linhas 38–39).

3.13 Declarações switch


Uma instrução switch executa instruções com base no valor de uma variável ou expressão.
Chave
Apontar

A instrução if na Listagem 3.3, ComputeTax.cpp, faz seleções com base em um único valor verdadeiro.
ou condição falsa . Existem quatro casos para cálculo de impostos, que dependem do valor do status. Para dar
conta de todos os casos, foram usadas instruções if aninhadas . Uso excessivo aninhado
Machine Translated by Google

118 Capítulo 3 Seleções

instruções if tornam um programa difícil de ler. C++ fornece uma instrução switch para simplificar
a codificação para vários casos. Você pode escrever a seguinte instrução switch para substituir a
instrução if aninhada na Listagem 3.3:

mudar (status) {

caso 0: cálculo de imposto para declarantes únicos;


quebrar;
caso 1: calcular imposto para casados em conjunto ou viúvos qualificados;
quebrar;
caso 2: calcular imposto para declaração de casamento separadamente;
quebrar;
caso 3: cálculo do imposto para chefe de família; quebrar; padrão:
cout <<
"Erro: status inválido" << endl;
}

O fluxograma da instrução switch anterior é mostrado na Figura 3.5.

status é 0
Calcular imposto para arquivadores únicos quebrar

status é 1 Calcular imposto para casados em conjunto ou


quebrar
viúvos qualificados

estado é 2
Calcular imposto para declaração de casamento separadamente quebrar

status é 3
Calcular imposto para chefe de família quebrar

padrão
Ações padrão

Figura 3.5 A instrução switch verifica todos os casos e executa as instruções no caso correspondente.

Esta instrução verifica se o status corresponde ao valor 0, 1, 2 ou 3, nessa ordem. Havendo coincidência, é
computado o imposto correspondente; caso contrário, uma mensagem será exibida.
Aqui está a sintaxe completa da instrução switch :

declaração de mudança switch (expressão de switch) {

valor de caso1 : declaração(ões)1;

quebrar; valor de caso2 :


declaração(ões)2; quebrar;
...
valor de casoN : declaração(ões)N;
quebrar;
padrão: declaração(ões) por padrão;
}
Machine Translated by Google

3.13 Declarações switch 119

A instrução switch observa as seguintes regras: declaração de interrupção

n A expressão switch deve produzir um valor integral e sempre estar entre


parênteses.

n O valor1, . . . e valorN são expressões constantes integrais, o que significa que não podem conter
variáveis, como 1 + x. Esses valores são inteiros e não podem ser valores de ponto flutuante.

n Quando o valor em uma instrução case corresponde ao valor da expressão switch, as instruções
começando neste case são executadas até que uma instrução break ou o final da instrução
switch seja alcançado.

n O caso padrão , que é opcional, pode ser usado para executar ações quando nenhum dos casos
especificados corresponde à expressão switch.

n A palavra-chave break é opcional. A instrução break encerra imediatamente o switch


declaração.

Cuidado
Não se esqueça de usar uma instrução break quando necessário. Depois que um case é sem pausa
correspondido, as instruções começando no case correspondente são executadas até que uma
instrução break ou o final da instrução switch seja alcançado. Isso é chamado de comportamento de comportamento de queda
queda. Por exemplo, o código a seguir exibe Dias da semana para os dias 1 a 5 e Fins de semana
para os dias 0 e 6.

mudar (dia) {

case 1: // Passa para o próximo caso


case 2: // Passa para o próximo caso
case 3: // Passa para o próximo caso
case 4: // Passa para o próximo caso
caso 5: cout << "Dia da semana"; quebrar;
case 0: // Passa para o próximo caso
caso 6: cout << "Fim de semana";
}

Dica
Para evitar erros de programação e melhorar a manutenção do código, é uma boa ideia colocar um
comentário em uma cláusula case se break for omitido propositalmente.

Agora vamos escrever um programa para determinar o signo do Zodíaco Chinês para um determinado
ano. O Zodíaco Chinês é baseado em um ciclo de doze anos, sendo cada ano representado por um
animal: rato, boi, tigre, coelho, dragão, cobra, cavalo, ovelha, macaco, galo, cachorro e porco, neste ciclo,
como mostrado na Figura 3.6 (próxima página).
Observe que o ano % 12 determina o signo do Zodíaco. 1900 é o ano do rato, já que 1900% 12 é 4. A
Listagem 3.8 fornece um programa que solicita ao usuário que insira um ano e exibe o ano do animal.

Listagem 3.8 ChineseZodiac.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5 {6
cout << "Insira o ano: ";
Machine Translated by Google

120 Capítulo 3 Seleções

porco
rato

boi 0: macaco 1:
cachorro galo
2: cachorro

3: porco
galo tigre 4: rato
5: boi
ano % 12 =
6: tigre
coelho 7: coelho
macaco
8: dragão
9: cobra
ovelha Dragão 10: cavalo
11: ovelha
cobra de cavalo

Figura 3.6 O Zodíaco Chinês baseia-se num ciclo de doze anos.

ano interno ;
insira o ano cin >> ano;

determinar o signo do Zodíaco 7 mudança (ano % 12) {


8 9 10 11
12 caso 0: cout << "macaco" << endl; quebrar;
13 caso 1: cout << "galo" << endl; quebrar;
14 caso 2: cout << "cachorro" << endl; quebrar;
15 caso 3: cout << "porco" << endl; quebrar;
16 caso 4: cout << "rato" << endl; quebrar;
17 caso 5: cout << "boi" << endl; quebrar;
18 caso 6: cout << "tigre" << endl; quebrar;
19 caso 7: cout << "coelho" << endl; quebrar;
20 caso 8: cout << "dragão" << endl; quebrar;
21 caso 9: cout << "cobra" << endl; quebrar;
22 caso 10: cout << "cavalo" << endl; quebrar;
23 caso 11: cout << "ovelha" << endl; quebrar;
24 }
25
26 retornar 0;
27}

Digite um ano: coelho de


1963

Digite um ano: 1877


boi

3.32 Quais tipos de dados são necessários para uma variável switch ? Se a palavra-chave break não
ÿVerificação de ponto for usada após o processamento de um caso, qual será a próxima instrução a ser executada?
Você pode converter uma instrução switch em uma instrução if equivalente ou vice-versa?
Quais são as vantagens de usar uma instrução switch ?

3.33 O que é y depois que a seguinte instrução switch é executada? Reescreva o código
usando uma instrução if .
Machine Translated by Google

3.14 Expressões Condicionais 121

x = 3; y = 3;
mudar (x + 3) {

caso 6: y = 1;
padrão: y += 1;
}

3.34 O que é x depois que a seguinte instrução if-else é executada? Use uma instrução switch para reescrevê-
la e desenhe o fluxograma para a nova instrução switch .

int x = 1, a = 3;
se (uma == 1)
x+= 5;
senão se (a == 2)
x+= 10;
senão se (a == 3)
x+= 16;
senão se (a == 4)
x+= 34;

3.14 Expressões Condicionais


Uma expressão condicional avalia uma expressão com base em uma condição.
Chave
Apontar
Talvez você queira atribuir a uma variável um valor que seja restrito por determinadas condições. Por exemplo, a
instrução a seguir atribui 1 a y se x for maior que 0, e -1 a y se x for menor ou igual a 0. expressão condicional

se (x > 0) y
= 1;
outro
y = -1;

Alternativamente, como no próximo exemplo, você pode usar uma expressão condicional para obter o mesmo
resultado:

y = x > 0 ? onze ; _

As expressões condicionais têm uma estrutura completamente diferente e não incluem uma expressão explícita.
se. A sintaxe é mostrada aqui:

expressão booleana? expressão1: expressão2;

O resultado desta expressão condicional é expressão1 se a expressão booleana for


verdadeiro; caso contrário, o resultado será expressão2.
Suponha que você queira atribuir o número maior entre as variáveis num1 e num2 ao máximo. Você pode
simplesmente escrever uma declaração usando a expressão condicional:

máximo = num1 > num2? num1: num2;

Como outro exemplo, a instrução a seguir exibe a mensagem “num é par” se num for par e, caso contrário, exibe
“num é ímpar”.

cout << (num % 2 == 0 ? "num é par" : "num é ímpar") << endl;


Machine Translated by Google

122 Capítulo 3 Seleções


Observação

Os símbolos ? e : aparecem juntos em uma expressão condicional. Eles formam um


operador ternário operador condicional denominado operador ternário porque utiliza três operandos. É o
único operador ternário em C++.

3.35 Suponha que, ao executar o programa a seguir, você insira a entrada 2 3 6 no console. Qual é a saída?
ÿVerificação de ponto

#include <iostream>
usando namespace std;

int principal()
{
duplo x, y, z;
cin >> x >> y >> z;

cout << (x < y && y < z ? "classificado" : "não classificado") << endl;

retornar 0;
}

3.36 Reescreva as seguintes instruções if usando o operador condicional:

se (idade >= 16) se (contagem% 10 == 0)


preço do bilhete = 20; cout << contagem << endl;
outro outro
preço do bilhete = 10; contagem << contagem << " ";

3.37 Reescreva as seguintes expressões condicionais usando instruções if-else :


a. pontuação = x> 10 ? escala 3 *: escala 4 *;
b. imposto = renda> 10.000 ? renda * 0,2 : renda * 0,17 + 1000;
c. cout << (número % 3 == 0 ? i : j) << endl;

3.15 Precedência e Associatividade do Operador


A precedência e a associatividade dos operadores determinam a ordem em que os operadores são
Chave
Apontar avaliados.

operador precedente A Seção 2.9, “Avaliando Expressões e Precedência de Operadores”, introduziu a precedência de operadores
envolvendo operadores aritméticos. Esta seção discute a precedência do operador com mais detalhes. Suponha
que você tenha a seguinte expressão:

3 + 4 * 4 > 5 * (4 + 3) – 1 && (4 – 3 > 5)

Qual é o seu valor? Qual é a ordem de execução dos operadores?


A expressão entre parênteses é avaliada primeiro. (Os parênteses podem ser aninhados, caso em que a
expressão entre parênteses internos é executada primeiro.) Ao avaliar uma expressão sem parênteses, os
operadores são aplicados de acordo com a regra de precedência e a regra de associatividade.

A regra de precedência define a precedência para operadores, conforme mostrado na Tabela 3.7, que
contém os operadores que você aprendeu até agora. Os operadores são listados em ordem decrescente de
precedência, de cima para baixo. Os operadores lógicos têm precedência inferior aos operadores relacionais e
os operadores relacionais têm precedência inferior aos operadores aritméticos.
Operadores com a mesma precedência aparecem no mesmo grupo. (Consulte o Apêndice C, Gráfico de
Precedência de Operadores, para obter uma lista completa de operadores C++ e sua precedência.)
Machine Translated by Google

3.15 Precedência e Associatividade do Operador 123

Tabela 3.7 Gráfico de Precedência de Operadores

Precedência Operador

var++ e var-- (Postfix)


+, - (unário mais e menos), ++var e --var (prefixo)
static_cast<tipo>(v), (tipo) (Conversão)
! (Não)
*, /, % (multiplicação, divisão e resto)
+, - (adição e subtração binária)
<, <=, >, >= (Relacional)
==, != (Igualdade)
&& (E)
|| (OU)
=, +=, -=, *=, /=, %= (operador de atribuição)

Se os operadores com a mesma precedência estiverem próximos uns dos outros, a sua associatividade determina associatividade do operador
a ordem de avaliação. Todos os operadores binários, exceto os operadores de atribuição, são associativos à esquerda.
Por exemplo, como + e – têm a mesma precedência e são associativos à esquerda, a expressão

é equivalente a
a-b+c-d ((a – b) + c) - d

Os operadores de atribuição são associativos à direita. Portanto, a expressão

é equivalente a
uma = b += c = 5 uma = (b += (c = 5))

Suponha que a, b e c sejam 1 antes da atribuição; depois que toda a expressão é avaliada, a torna-se 6, b torna-
se 6 e c torna-se 5. Observe que a associatividade à esquerda para o operador de atribuição não faria sentido.

Dica
Você pode usar parênteses para forçar uma ordem de avaliação e também para facilitar
a leitura de um programa. O uso de parênteses redundantes não retarda a execução da
expressão.

3.38 Liste a ordem de precedência dos operadores booleanos. Avalie as seguintes expressões:
ÿVerificação de ponto

verdade || verdadeiro falso _

verdadeiro && verdadeiro || falso

3.39 Verdadeiro ou falso? Todos os operadores binários, exceto =, são associativos à esquerda.

3.40 Avalie as seguintes expressões:


2 * 2 - 3 > 2 && 4 – 2 > 5

2 * 2 - 3 > 2 || 4 – 2 > 5
Machine Translated by Google

124 Capítulo 3 Seleções

3.41 (x > 0 && x < 10) é o mesmo que ((x > 0) && (x < 10))? (x > 0 || x < 10) é o mesmo que ((x > 0) || (x < 10))? (x >
0 || x < 10 && y < 0) é o mesmo que (x > 0 || (x < 10 && y < 0))?

3.16 Depuração
A depuração é o processo de localização e correção de erros em um programa.
Chave
Apontar
Conforme discutido na Seção 1.9.1, os erros de sintaxe são fáceis de encontrar e corrigir porque o compilador indica de
onde vieram os erros e por que estão errados. Erros de tempo de execução também não são difíceis de encontrar, já
que o sistema operacional os exibe no console quando o programa é interrompido. Encontrar erros lógicos, por outro
lado, pode ser muito desafiador.
insetos Erros lógicos são chamados de bugs. O processo de localização e correção de erros é chamado de depuração.
depuração Uma abordagem comum para depuração é usar uma combinação de métodos para restringir a parte do programa onde
traços de mão o bug está localizado. Você pode rastrear manualmente o programa (ou seja, detectar erros lendo o programa) ou pode
inserir instruções de impressão para mostrar os valores das variáveis ou o fluxo de execução do programa. Esta
abordagem pode funcionar para um programa curto e simples. Entretanto, para um programa grande e complexo, a
abordagem mais eficaz para depuração é usar um utilitário de depurador.

depuração em IDE As ferramentas IDE C++, como Visual C++, incluem depuradores integrados. Os utilitários do depurador permitem
acompanhar a execução de um programa. Eles variam de um sistema para outro, mas todos suportam a maioria dos
seguintes recursos úteis:

n Executar uma única instrução por vez: O depurador permite executar uma
declaração por vez para que você possa ver o efeito de cada declaração.

n Rastreando ou passando por cima de uma função: Se uma função estiver sendo executada, você pode
pedir ao depurador para entrar na função e executar uma instrução por vez na função, ou você pode pedir
para ele passar por cima de toda a função. Você deve percorrer toda a função se souber que ela funciona.
Por exemplo, sempre passe por cima das funções fornecidas pelo sistema, como pow(a, b).

n Definir pontos de interrupção: Você também pode definir um ponto de interrupção em uma instrução específica.
Seu programa faz uma pausa quando atinge um ponto de interrupção e exibe a linha com o ponto de
interrupção. Você pode definir quantos pontos de interrupção desejar. Os pontos de interrupção são
particularmente úteis quando você sabe onde começa o erro de programação. Você pode definir um ponto
de interrupção nessa linha e executar o programa até atingir o ponto de interrupção.

n Exibindo variáveis: O depurador permite selecionar diversas variáveis e exibir seus valores. À medida que
você percorre um programa, o conteúdo de uma variável é continuamente atualizado.

n Exibindo pilhas de chamadas: O depurador permite rastrear todas as chamadas de função e lista todas as
funções pendentes. Este recurso é útil quando você precisa ter uma visão geral do fluxo de execução do
programa.

n Modificando variáveis: alguns depuradores permitem modificar o valor de uma variável durante a depuração.
Isso é conveniente quando você deseja testar um programa com amostras diferentes, mas não deseja sair
do depurador.

Dica
Se você usa o Microsoft Visual C++, consulte Aprendendo C++ Efetivamente com o Microsoft
Visual C++ no Suplemento II.C. O suplemento mostra como usar um depurador para rastrear
programas e como a depuração pode ajudá-lo a aprender C++ de maneira eficaz.
Machine Translated by Google

Resumo do Capítulo 125

Termos chave

Expressão booleana 92 comportamento de queda 119


tipo de dados bool 92 fluxograma 94
Valor booleano 92 associatividade do operador 123
instrução de interrupção 119 precedência do operador 122
operador condicional 113 declaração de seleção 92
pendurado mais ambigüidade 101 operador de curto-circuito 113
depuração 124 operador ternário 122

Resumo do capítulo

1. Uma variável do tipo bool pode armazenar um valor verdadeiro ou falso .

2. Internamente, C++ usa 1 para representar verdadeiro e 0 para falso.

3. Se você exibir um valor booleano no console, 1 será exibido se o valor for verdadeiro e 0 se o valor for falso.

4. Em C++, você pode atribuir um valor numérico a uma variável bool. Qualquer valor diferente de zero é avaliado
para verdadeiro e o valor zero é avaliado como falso.

5. Os operadores relacionais (<, <=, ==, !=, >, >=) produzem um valor booleano.

6. O operador de teste de igualdade consiste em dois sinais de igual (==), e não em um único sinal de igual (=). O
o último símbolo é para atribuição.

7. As declarações de seleção são usadas para programar cursos de ação alternativos.


Existem vários tipos de instruções de seleção: instruções if , instruções if-else bidirecionais, instruções if
aninhadas, instruções if-else multidirecionais , instruções switch e expressões condicionais.

8. Todas as diversas instruções if tomam decisões de controle com base em uma expressão booleana.
Com base na avaliação verdadeira ou falsa da expressão, estas afirmações seguem um de dois caminhos
possíveis.

9. Os operadores booleanos &&, || e ! operar com valores e variáveis booleanas.

10. Ao avaliar p1 && p2, C++ primeiro avalia p1 e depois avalia p2 se p1 for verdadeiro; se p1 for falso, não avalia
p2. Ao avaliar p1 || p2, C++ primeiro avalia p1 e depois avalia p2 se p1 for falso; se p1 for verdadeiro, não
avalia p2.
Portanto, && é referido como o operador AND condicional ou de curto-circuito, e || é referido como operador
OR condicional ou de curto-circuito.

11. A instrução switch toma decisões de controle com base em uma expressão switch.

12. A palavra-chave break é opcional em uma instrução switch , mas normalmente é usada no final de cada caso
para pular o restante da instrução switch . Se a pausa
instrução não estiver presente, a próxima instrução case será executada.

13. Os operadores nas expressões são avaliados na ordem determinada pelas regras de parênteses.
teses, precedência de operador e associatividade de operador.
Machine Translated by Google

126 Capítulo 3 Seleções

14. Os parênteses podem ser usados para forçar a ordem de avaliação a ocorrer em qualquer sequência.

15. Operadores com maior precedência são avaliados mais cedo. Para operadores da mesma pre-
importância, sua associatividade determina a ordem de avaliação

16. Todos os operadores binários, exceto os operadores de atribuição, são associativos à esquerda; operadores de
atribuição são associativos à direita.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Observação

Para cada exercício, analise cuidadosamente os requisitos do problema e projete


pense antes de codificar estratégias para resolvê-lo antes de codificar.

Observação

Antes de pedir ajuda, leia e entenda o programa e rastreie-o usando diversas entradas
representativas manualmente ou usando um depurador IDE. Você aprende a programar
aprender com os erros depurando seus erros.

Seções 3.3–3.8

*3.1 (Álgebra: resolver equações quadráticas) As duas raízes de uma equação quadrática
ax2 + bx + c = 0 pode ser obtido usando a seguinte fórmula:

-b + 2b2 - 4ac -b - 2b2 - 4ac


= e r2 =
r1 2a 2a

b2 - 4ac é chamado de discriminante da equação quadrática. Se for positivo, a equação


tem duas raízes reais. Se for zero, a equação tem uma raiz. Se for negativo, a equação
não tem raízes reais.

Escreva um programa que solicite ao usuário que insira valores para a, b e c e exiba o
resultado com base no discriminante. Se o discriminante for positivo, exiba duas raízes.
Se o discriminante for 0, exiba uma raiz. Caso contrário, exiba “A equação não tem raízes reais”.

Observe que você pode usar pow(x, 0.5) para calcular 2x. Aqui estão alguns exemplos de execução.

Insira a, b, c: 1,0 3 1 As raízes


são -0,381966 e -2,61803

Insira a, b, c: 1 2,0 1 A raiz é –


1

Digite a, b, c: 1 2 3
A equação não tem raízes reais
Machine Translated by Google

Exercícios de Programação 127

3.2 (Verificar números) Escreva um programa que solicite ao usuário que insira dois números inteiros e
verifique se o primeiro número é divisível pelo segundo. Aqui está um exemplo de execução:

Insira dois números inteiros: 2


3 2 não é divisível por 3

Insira dois números inteiros: 22


2 22 é divisível por 2

*3.3 (Álgebra: resolver 2 * 2 equações lineares) Você pode usar a regra de Cramer para resolver o
seguinte sistema 2 * 2 de equação linear:

machado + por = e ed - namorado


= de - ec
x= e
cx + dy = f anúncio - BC anúncio - BC

Escreva um programa que solicite ao usuário que digite a, b, c, d, e e f e exiba o resultado. Se


ad - bc for 0, informe que “A equação não tem solução”.

Insira a, b, c, d, e, f: 9,0 4,0 3,0 -5,0 -6,0 -21,0 x é -2,0 e y é 3,0

Digite a, b, c, d, e, f: 1,0 2,0 2,0 4,0 4,0 5,0 A equação não tem
solução

**3.4 (Verifique a velocidade) Escreva um programa que solicite ao usuário que insira a velocidade de um
veículo. Se a velocidade for inferior a 20, a exibição é muito lenta; se a velocidade for superior
a 80, a exibição é muito rápida; caso contrário, exiba corretamente.
*3.5 (Encontrar datas futuras) Escreva um programa que solicite ao usuário que insira um número inteiro
para o dia da semana de hoje (domingo é 0, segunda-feira é 1,... e sábado é 6). Além disso,
solicite ao usuário que insira o número de dias depois de hoje para um dia futuro e exiba o dia
da semana futuro. Aqui está um exemplo de execução:

Insira o dia de hoje: 1


Insira o número de dias decorridos desde hoje: 3
Hoje é segunda-feira e o dia futuro é quinta-feira

Insira o dia de hoje: 0


Insira o número de dias decorridos desde hoje: 31
Hoje é domingo e o dia futuro é quarta-feira

*3.6 (Aplicativo de saúde: IMC) Revise a Listagem 3.2, ComputeAndInterpretBMI.cpp, para permitir que o
usuário insira peso, pés e polegadas. Por exemplo, se uma pessoa tem 5 pés e 10 polegadas,
você inserirá 5 para pés e 10 para polegadas. Aqui está um exemplo de execução:
Machine Translated by Google

128 Capítulo 3 Seleções

Insira o peso em libras: 140 Insira


os pés: 5 Insira
as polegadas: 10 O
IMC é 20,087702275404553
Normal

*3.7 (Classificar três inteiros) Escreva um programa que solicite ao usuário que insira três inteiros e os
exiba em ordem não decrescente.
Nota de vídeo
Classifique três inteiros
*3.8 (Comparando números inteiros) Escreva um programa que solicite ao usuário que insira as arestas
de um triângulo e as compare. Se todas as arestas forem iguais, exiba Triângulo Equilátero;
se apenas duas arestas forem iguais, exiba Triângulo Isósceles e se nenhuma das arestas for
igual, exiba Triângulo Escaleno.

Seções 3.9–3.16
*3.9 (Exibir o dia e as horas restantes) Escreva um programa que solicite ao usuário que insira o número
do dia de uma semana e as horas passadas, e exiba o dia e as horas restantes. Por exemplo,
se o usuário digitou o número do dia 1 e as horas passaram de 20, o programa deverá exibir
Hoje é Domingo e Horas Restantes: 4. Se o usuário digitou o número do dia 7 e as horas
passaram de 2, o programa deverá exibir Hoje é Sábado e Horas Restantes 22.

3.10 (Jogo: Teste de multiplicação) A Listagem 3.4, SubtractionQuiz.cpp, gera aleatoriamente


uma questão de subtração. Revise o programa para gerar aleatoriamente uma questão
de multiplicação com dois números inteiros menores que 50.
*3.11 (Taxas de juros de empréstimos) Um banco usa a seguinte função para calcular a taxa de juros (em
porcentagem) para um determinado valor de empréstimo (em milhares de dólares).

13,5, se 1 6 a … 5
r(uma) =
12,5, se 5 6 a … 10
11,0,
d 15, se se
0,110
6 a6 …
a…1 50
Escreva um programa que solicite ao usuário o valor do empréstimo e exiba a taxa
de juros. Se o valor for inferior a US$ 10.000 ou superior a US$ 50.00.000, exiba a
mensagem “O empréstimo não pode ser fornecido”.
3.12 (Jogo: Par ou Ímpar) Escreva um programa que permita ao usuário adivinhar se um número
inteiro gerado aleatoriamente seria par ou ímpar. O programa gera aleatoriamente um
número inteiro e o divide por 2. O número inteiro é par se o resto for 0, caso contrário, é ímpar.
O programa solicita que o usuário insira uma estimativa e informa se a estimativa está
correta ou incorreta.
*3.13 (Aplicação financeira: calcular impostos) A Listagem 3.3, ComputeTax.cpp, fornece o código-fonte
para calcular impostos para arquivadores únicos. Complete a Listagem 3.3 para fornecer o
código-fonte completo.
**3.14 (Jogo: Predição) Escreva um programa que gere um número inteiro aleatório de dois dígitos.
O programa solicita que o usuário preveja o número gerado inserindo um número inteiro
de dois dígitos e, em seguida, determine a precisão da previsão do usuário de acordo
com as seguintes regras:
Se a previsão do usuário corresponder exatamente ao número gerado, a precisão será
de 100%. Se um dígito no número previsto do usuário corresponder a um dígito no
número gerado, a precisão será de 50%. Se nenhum dos dígitos do número previsto do
usuário corresponder ao número gerado, a precisão será de 0%.
*3.15 (Jogo: tesoura, pedra, papel) Escreva um programa que reproduza o popular jogo tesoura, pedra e
papel. (Uma tesoura pode cortar um papel, uma pedra pode derrubar uma tesoura e um papel pode
Machine Translated by Google

Exercícios de Programação 129

embrulhe uma pedra.) O programa gera aleatoriamente um número 0, 1 ou 2 representando


uma tesoura, uma pedra ou um papel. O programa solicita que o usuário insira um número 0, 1 ou 2
e exibe uma mensagem indicando se o usuário ou o computador ganha, perde ou empata. Aqui
estão exemplos de execuções:

tesoura (0), pedra (1), papel (2): 1 O computador é uma


tesoura. Você é uma rocha. Você ganhou

tesoura (0), pedra (1), papel (2): 2 O computador é


papel. Você também é papel. É um empate

**3.16 (Calcule a área de um triângulo equilátero) Escreva um programa que leia três arestas de um triângulo
e calcule a área se a entrada for válida. Caso contrário, mostra que a entrada é inválida. A
entrada é válida se todas as arestas do triângulo forem iguais.

*3.17 (Soma dos dígitos de um número inteiro) O Exercício de Programação 2.6 solicita que o usuário insira
um número inteiro entre 0 e 1000 e exibe a soma de todos os dígitos do número inteiro.
Escreva um programa que solicite ao usuário um número inteiro de três dígitos. O programa
exibe a soma de todos os dígitos do inteiro se a entrada for válida; caso contrário, exibe uma
mensagem indicando que o número inteiro não é um número de três dígitos e, portanto, é
inválido.

3.18 (Jogo: Multiplicação de quatro números) A Listagem 3.4, SubtractionQuiz.cpp, gera aleatoriamente
uma questão de subtração. Revise o programa para gerar aleatoriamente uma questão de
multiplicação com quatro números inteiros menores que 5.

Compreensivo
**3.19 (Geometria: ponto em um círculo?) Escreva um programa que solicite ao usuário que insira um ponto
(x, y) e verifique se o ponto está dentro do círculo centrado em (0, 0) com raio 10. Por exemplo ,
(4, 5) está dentro do círculo e (9, 9) está fora do círculo, conforme mostrado na Figura 3.7a.

eixo y eixo y
(9, 9)

(4, 5) (6, 4)
(2, 2)

(0, 0) eixo x (0, 0) eixo x

(a) (b)

Figura 3.7 (a) Pontos dentro e fora do círculo. (b) Pontos dentro e fora do retângulo.

(Dica: um ponto está no círculo se sua distância até (0, 0) for menor ou igual a 10.
2 2.
A fórmula para calcular a distância é 2(x2 - x1) + (y2 - y1) Teste seu
programa para cobrir todos os casos. Dois exemplos de execução são mostrados aqui:
Machine Translated by Google

130 Capítulo 3 Seleções

Insira um ponto com duas coordenadas: 4 5


O ponto (4, 5) está no círculo

Insira um ponto com duas coordenadas: 9 9


O ponto (9, 9) não está no círculo

**3.20 (Geometria: ponto em um retângulo?) Escreva um programa que solicite ao usuário que insira
um ponto (x, y) e verifique se o ponto está dentro do retângulo centrado em (0, 0) com
largura 10 e altura 5 Por exemplo, (2, 2) está dentro do retângulo e (6, 4) está fora do
retângulo, como mostra a Figura 3.7b. (Dica: um ponto está no retângulo se sua distância
horizontal até (0, 0) for menor ou igual a 10/2 e sua distância vertical até (0, 0) for menor
ou igual a 5/2 . Teste seu programa para cobrir todos os casos.) Aqui estão dois
exemplos de execução.

Insira um ponto com duas coordenadas: 2 2


O ponto (2, 2) está no retângulo

Insira um ponto com duas coordenadas: 6 4


O ponto (6, 4) não está no retângulo

**3.21 (Jogo: escolha uma carta) Escreva um programa que simule a escolha de uma carta de um
baralho de 52 cartas. Seu programa deve exibir a classificação (Ás, 2, 3, 4, 5, 6, 7, 8, 9,
10, Valete, Dama, Rei) e o naipe (Paus, Ouros, Copas , Espadas) da carta.
Aqui está um exemplo de execução do programa:

A carta que você escolheu é Valete de Copas

*3.22 (Geometria: ponto de interseção) Dois pontos na linha 1 são dados como (x1, y1) e (x2,
y2) e na linha 2 como (x3, y3) e (x4, y4), conforme mostrado na Figura 3.8a –b.

(x2, y2) (x2, y2) (x2, y2) (x3, y3)


(x3, y3)

(x3, y3)

(x4, y4)
(x1, y1) (x1, y1) (x4, y4) (x1, y1) (x4, y4)
(a) (b) (c)

Figura 3.8 Duas retas se cruzam em (aeb) e duas retas são paralelas em (c).

O ponto de intersecção das duas retas pode ser encontrado resolvendo a seguinte
equação linear:

(y1 - y2)x - (x1 - x2)y = ( y1 - y2 )x1 - (x1 - x2)y1

(y3 - y4)x - (x3 - x4 )y = (y3 - y4 )x3 - (x3 - x4)y3


Machine Translated by Google

Exercícios de Programação 131

Esta equação linear pode ser resolvida usando a regra de Cramer (ver Exercício de
Programação 3.3). Se a equação não tiver soluções, as duas retas serão paralelas (Figura
3.8c). Escreva um programa que solicite ao usuário que insira quatro pontos e exiba o ponto
de interseção. Aqui estão alguns exemplos de execução:

Insira x1, y1, x2, y2, x3, y3, x4, y4: 2 2 5 -1,0 4,0 2,0 -1,0 -2,0
O ponto de intersecção está em (2,88889, 1,1111)

Digite x1, y1, x2, y2, x3, y3, x4, y4: 2 2 7 6,0 4,0 2,0 -1,0 -2,0
As duas linhas são paralelas

**3.23 (Geometria: pontos em um triângulo?) Suponha que um triângulo retângulo seja colocado em um
plano conforme mostrado abaixo. O ponto de ângulo reto é colocado em (0, 0) e os outros dois
pontos são colocados em (200, 0) e (0, 100). Escreva um programa que solicite ao usuário que
insira um ponto com coordenadas x e y e determine se o ponto está dentro do triângulo.

(0, 100)
p2

p1

(0, 0) (200, 0)

Aqui estão os exemplos de execução:

Insira as coordenadas x e y de um ponto: 100,5 25,5


O ponto está no triângulo

Insira as coordenadas x e y de um ponto: 100,5 50,5


O ponto não está no triângulo

3.24 (Use os operadores && e || ) Escreva um programa que solicite ao usuário que insira um número
inteiro e determine se ele é divisível por 5 e 6, se é divisível por 5 ou 6 e se é divisível por 5 ou
6 , mas não ambos. Aqui está um exemplo de execução deste programa:

Insira um número inteiro: 10


10 é divisível por 5 e 6? falso
10 é divisível por 5 ou 6? verdadeiro
10 é divisível por 5 ou 6, mas não por ambos? verdadeiro

**3.25 (Geometria: dois retângulos) Escreva um programa que solicite ao usuário que insira as coordenadas
x, y centrais, largura e altura de dois retângulos e determine se o segundo retângulo está
dentro do primeiro ou se sobrepõe ao primeiro, conforme mostrado na Figura 3.9. Teste seu
programa para cobrir todos os casos.
Machine Translated by Google

132 Capítulo 3 Seleções


w1 w1

w2
w2
h1 h2 (x1, y1) h1 (x1, y1)
(x2, y2) h2
(x2, y2)

(a) (b)

Figura 3.9 (a) Um retângulo está dentro de outro. (b) Um retângulo se sobrepõe a outro.

Aqui estão os exemplos de execução:

Insira as coordenadas x, y do centro de r1, largura e altura: 2,5 4 2,5 43 Insira as


coordenadas x, y do centro de r2, largura e altura: 1,5 5 0,5 3 r2 está dentro de r1

Insira as coordenadas x, y do centro de r1, largura e altura: 1 2 3 5,5 Insira as


coordenadas x, y do centro de r2, largura e altura: 3 4 4,5 5 r2 se sobrepõe a r1

Insira as coordenadas x, y do centro de r1, largura e altura: 1 2 3 3


Insira as coordenadas x, y do centro de r2, largura e altura: 40 45 3 2 r2 não se
sobrepõe a r1

**3.26 (Geometria: dois círculos) Escreva um programa que solicite ao usuário que insira as coordenadas
centrais e os raios de dois círculos e determine se o segundo círculo está dentro do primeiro
ou se sobrepõe ao primeiro, como mostrado na Figura 3.10. (Dica: o círculo2 está dentro do
círculo1 se a distância entre os dois centros <= |r1 - r2| e o círculo2 se sobrepõe ao círculo1
se a distância entre os dois centros <= r1 + r2. Teste seu programa para cobrir todos os
casos.)

r1 r1

(x1, y1) (x1, y1)


r2
r2
(x2, y2)
(x2, y2)

(a) (b)

Figura 3.10 (a) Um círculo está dentro de outro círculo. (b) Um círculo se sobrepõe a outro círculo.
Machine Translated by Google

Exercícios de Programação 133

Aqui estão os exemplos de execução:

Insira as coordenadas x, y e o raio do centro do círculo1: 0,5 5,1 13 Insira as coordenadas x, y e o raio
do centro do círculo2: 1 1,7 4,5 o círculo2 está dentro do círculo1

Insira as coordenadas x, y e o raio do centro do círculo1: 3,4 5,7 5,5 Insira as coordenadas x, y e o raio
do centro do círculo2: 6,7 3,5 3 o círculo2 se sobrepõe ao círculo1

Insira as coordenadas x, y e o raio do centro do círculo1: 3,4 5,5 1 Insira as coordenadas x, y e o raio
do centro do círculo2: 5,5 7,2 1 o círculo2 não se sobrepõe ao círculo1

*3.27 (Hora atual) Revise o Exercício de Programação 2.8 para exibir a hora usando um relógio de 12 horas. Aqui está um exemplo de
execução:

Insira a diferença de fuso horário para GMT: -5


A hora atual é 4:50:34

*3.28 (Finanças: câmbio de moeda) Escreva um programa que solicite ao usuário que insira a taxa de câmbio da moeda em dólares
americanos para o RMB chinês. Solicita ao usuário que insira 0 para converter de dólares americanos em RMB chinês e
1 para converter de RMB chinês e dólares americanos. Solicita ao usuário que insira o valor em dólares americanos ou

RMB chinês para convertê-lo em RMB chinês ou dólares americanos, respectivamente. Aqui estão os exemplos de
execução:

Insira a taxa de câmbio de dólares para RMB: 6,81 Insira 0 para converter
dólares em RMB e 1 vice-versa: 0
Insira o valor em dólares: 100 $ 100
equivalem a 681 yuans

Insira a taxa de câmbio de dólares para RMB: 6,81 Insira 0 para converter
dólares em RMB e 1 vice-versa: 1 Insira o valor de RMB: 10.000 10.000,0 yuan é $
1.468,43

Insira a taxa de câmbio de dólares para RMB: 6,81


Digite 0 para converter dólares em RMB e 1 vice-versa: 5
Entrada incorreta
Machine Translated by Google

134 Capítulo 3 Seleções

*3.29 (Geometria: posição do ponto) Dada uma linha direcionada do ponto p0(x0, y0) a p1(x1, y1),
você pode usar a seguinte condição para decidir se um ponto p2(x2, y2) está à esquerda
do linha, à direita, ou na mesma linha (ver Figura 3.11):

> 0 p2 está no lado esquerdo da linha

(x1 – x0) * (y2 – y0) – (x2 – x0) * (y1 – y0) = 0 p2 está na mesma linha
< 0 p2 está no lado direito da linha

p1 p1 p1
p2

p2 p2

p0 p0 p0
(a) (b) (c)

A Figura 3.11 (a) p2 está à esquerda da linha. (b) p2 está à direita da linha. (c) p2 está na
mesma linha.

Escreva um programa que solicite ao usuário que insira os três pontos para p0, p1 e p2
e mostre se p2 está à esquerda da linha de p0 a p1, à direita ou na mesma linha. Aqui
estão alguns exemplos de execução:

Insira três pontos para p0, p1 e p2: 4,4 2 6,5 9,5 -5 4


p2 está no lado esquerdo da linha

Insira três pontos para p0, p1 e p2: 1 1 5 5 2 2


p2 está na mesma linha

Insira três pontos para p0, p1 e p2: 3,4 2 6,5 9,5 5 2,5
p2 está no lado direito da linha

*3,30 (Financeiro: compare custos) Suponha que você compre dois pacotes diferentes de arroz.
Você gostaria de escrever um programa para comparar o custo. O programa solicita ao
usuário que insira o peso e o preço de cada pacote e exibe aquele com melhor preço.
Aqui está um exemplo de execução:

Insira o peso e o preço do pacote 1: 50 24,59


Insira o peso e o preço do pacote 2: 25 11,99
O pacote 2 tem um preço melhor.

Insira o peso e o preço do pacote 1: 50 25


Insira o peso e o preço do pacote 2: 25 12,5
Dois pacotes têm o mesmo preço.
Machine Translated by Google

Exercícios de Programação 135

*3.31 (Geometria: ponto em segmento de reta) O Exercício de Programação 3.29 mostra como
testar se um ponto está em uma reta ilimitada. Revise o Exercício de Programação 3.29
para testar se um ponto está em um segmento de reta. Escreva um programa que solicite
ao usuário que insira os três pontos para p0, p1 e p2 e mostre se p2 está no segmento
de linha de p0 a p1. Aqui estão alguns exemplos de execução:

Insira três pontos para p0, p1 e p2: 1 1 2,5 2,5 1,5 1,5
(1,5, 1,5) está no segmento de linha de (1, 1) a (2,5, 2,5)

Insira três pontos para p0, p1 e p2: 1 1 2 2 3,5 3,5


(3.5, 3.5) não está no segmento de linha de (1, 1) a (2, 2)

*3.32 (Álgebra: forma de interceptação de inclinação) Escreva um programa que solicite ao usuário que insira as
coordenadas de dois pontos (x1, y1) e (x2, y2) e exiba a equação da linha na forma de interceptação
de inclinação, ou seja, y = mx + b. Para uma revisão das equações de linha, consulte www.purplemath
.com/modules/strtlneq.htm. m e b podem ser calculados usando a seguinte fórmula:

m = (y2 - y1)/(x2 - x1) b = y1 - mx1

Não exiba m se for 1 e não exiba b se for 0. Aqui está um exemplo de execução:

Insira as coordenadas para dois pontos: 1 1 0 0


A equação da reta para dois pontos (1, 1) e (0, 0) é y = x

Insira as coordenadas para dois pontos: 4,5 –5,5 6,6 –6,5 A equação da
linha para dois pontos (4,5, –5,5) e (6,6, –6,5) é y = –0,47619 × –3,35714

**3.33 (Ciência: dia da semana) A congruência de Zeller é um algoritmo desenvolvido por


Christian Zeller para calcular o dia da semana. A fórmula é

26(m + 1) k j
+k+ +
h = ¢q + 10 4 4 + 5jÿ % 7

onde

n h é o dia da semana (0: sábado, 1: domingo, 2: segunda, 3: terça,


4: Quarta, 5: Quinta, 6: Sexta).
n q é o dia do mês.
n m é o mês (3: março, 4: abril,..., 12: dezembro). Janeiro e fevereiro são contados
como os meses 13 e 14 do ano anterior.
ano
n j é o século (ou seja, ).
100

n k é o ano do século (ou seja, ano % 100).


Observe que a divisão na fórmula realiza uma divisão inteira. Escreva um programa
que solicite ao usuário que insira o ano, o mês e o dia do mês e exiba o nome do
dia da semana. Aqui estão alguns exemplos de execução:
Machine Translated by Google

136 Capítulo 3 Seleções

Insira o ano: (por exemplo, 2012): 2015


Insira o mês: 1-12: 1
Digite o dia do mês: 1-31:25
Dia da semana é domingo

Insira o ano: (por exemplo, 2012): 2012


Insira o mês: 1-12: 5
Digite o dia do mês: 1-31:12
Dia da semana é sábado

(Dica: janeiro e fevereiro são contados como 13 e 14 na fórmula, portanto, você precisa converter
a entrada do usuário 1 em 13 e 2 em 14 para o mês e alterar o ano para o ano anterior.)

3.34 (Ponto aleatório) Escreva um programa que exiba duas coordenadas aleatórias em um quadrado.
O quadrado está centrado em (0, 0) e tem lado 300.
**3.35 (Empresa: verifique ISBN-10) Um ISBN-10 (International Standard Book Number) consiste em 10 dígitos:
d1d2d3d4d5d6d7d8d9d10. O último dígito, d10, é uma soma de verificação, calculada a partir dos
outros nove dígitos usando a seguinte fórmula:

(d1 * 1 + d2 * 2 + d3 * 3 + d4 * 4 + d5 * 5 + d6 * 6 + d7 * 7

+ d8 * 8 + d9 * 9)% 11

Se a soma de verificação for 10, o último dígito será indicado como X de acordo com a convenção
ISBN-10. Escreva um programa que solicite ao usuário que insira os primeiros 9 dígitos e exiba o
ISBN de 10 dígitos (incluindo zeros à esquerda). Seu programa deve ler a entrada como um
número inteiro. Aqui estão exemplos de execuções:

Insira os primeiros 9 dígitos de um ISBN como número inteiro: 013601267


O número ISBN-10 é 0136012671

Insira os primeiros 9 dígitos de um ISBN como número inteiro: 013031997


O número ISBN-10 é 013031997X

3.36 (Número do palíndromo) Escreva um programa que solicite ao usuário que insira um número inteiro de
três dígitos e determine se é um número do palíndromo. Um número é palíndromo se for lido da
mesma forma da direita para a esquerda e da esquerda para a direita. Aqui está um exemplo de
execução deste programa:

Insira um número inteiro de três dígitos:


121 121 é um palíndromo

Insira um número inteiro de três dígitos:


123 123 não é um palíndromo
Machine Translated by Google

CAPÍTULO

4
Matemático
Funções,
Caracteres
e Strings

Objetivos
n Resolver problemas matemáticos utilizando funções matemáticas C++ (§4.2).

n Para representar caracteres usando o tipo char (§4.3).

n Para codificar caracteres usando código ASCII (§4.3.1).

n Para ler um caractere do teclado (§4.3.2).

n Para representar caracteres especiais usando as sequências de escape (§4.3.3).

n Para converter um valor numérico em um caractere e converter um caractere em um número inteiro (§4.3.4).

n Para comparar e testar caracteres (§4.3.5).

n Para programar usando caracteres (DisplayRandomCharacter,


Adivinhar aniversário) (§§4.4–4.5).

n Para testar e converter caracteres usando as funções de caracteres C++ (§4.6).

n Para converter um caractere hexadecimal em um valor decimal (HexDigit2Dec) (§4.7).

n Representar strings usando o tipo string e introduzir objetos e funções de instância


(§4.8).

n Para usar o operador subscrito para acessar e modificar caracteres em uma string
(§4.8.1).

n Para usar o operador + para concatenar strings (§4.8.2).

n Para comparar strings usando os operadores relacionais (§4.8.3).

n Para ler strings do teclado (§4.8.4).

n Para revisar o programa de loteria usando strings (LotteryUsingStrings) (§4.9).

n Para formatar a saída usando manipuladores de fluxo (§4.10).

n Para ler/escrever dados de/para um arquivo (§4.11).


Machine Translated by Google

138 Capítulo 4 Funções matemáticas, caracteres e strings

4.1 Introdução
O foco deste capítulo é apresentar funções matemáticas, caracteres e objetos de string e
Chave
Apontar usá-los para desenvolver programas.

Os capítulos anteriores introduziram técnicas fundamentais de programação; você aprendeu a escrever


programas simples para resolver problemas básicos. Este capítulo apresenta funções para realizar
operações matemáticas comuns. Você aprenderá como criar funções personalizadas no Capítulo 6.

Suponha que você precise estimar a área delimitada por quatro cidades, dadas as localizações GPS
(latitude e longitude) dessas cidades, conforme mostrado no diagrama a seguir. Como você escreveria
problema um programa para resolver esse problema? Você será capaz de escrever tal programa após concluir
este capítulo.

Charlotte (35.2270869, –80.8431267)

Atlanta
(33.7489954, –84.3879824)
Savana (32.0835407, –81.0998342)

Orlando (28.5383355, –81.3792365)

Como as strings são frequentemente usadas na programação, é benéfico apresentá-las


antecipadamente para que você possa começar a usá-las no desenvolvimento de programas úteis. Este
capítulo fornece uma breve introdução aos objetos string; você aprenderá mais sobre objetos e strings no Capítulo 10.

4.2 Funções Matemáticas


Chave
C++ fornece muitas funções úteis no cabeçalho cmath para executar funções matemáticas
Apontar comuns.

Uma função é um grupo de instruções que executa uma tarefa específica. Você já usou a função pow(a,
b) para calcular ab na Seção 2.8.4, “Operações com expoentes”, e a função rand()
função para gerar um número aleatório na Seção 3.9, “Gerando Números Aleatórios”. Esta seção
apresenta outras funções úteis. Elas podem ser categorizadas como funções trigonométricas, funções
expoentes e funções de serviço. As funções de serviço incluem as funções de arredondamento, mínimo,
máximo e absoluto.

4.2.1 Funções trigonométricas


C++ fornece as seguintes funções, conforme mostrado na Tabela 4.1, para executar funções
trigonométricas no cabeçalho cmath :

Tabela 4.1 Funções trigonométricas no cabeçalho cmath

Função Descrição

pecado (radianos) Retorna o seno trigonométrico de um ângulo em radianos.

cos(radianos) Retorna o cosseno trigonométrico de um ângulo em radianos

bronzeado (radianos) Retorna a tangente trigonométrica de um ângulo em radianos.

asin(a) Retorna o ângulo em radianos para o inverso do seno.

molestar Retorna o ângulo em radianos para o inverso do cosseno.

espere Retorna o ângulo em radianos para o inverso da tangente.


Machine Translated by Google

4.2 Funções Matemáticas 139

O parâmetro para sen, cos e tan é um ângulo em radianos. O valor de retorno para asin, acos e atan é um ângulo
em radianos no intervalo entre -p/2 e p/2. Um grau é igual a p/180 em radianos, 90 graus é igual a p/2 em radianos
e 30 graus é igual a p/6 em radianos.

Suponha que PI seja uma constante com valor 3,14159. Aqui estão alguns exemplos de uso dessas funções:

sin(0) retorna 0,0


sin(270 * PI / 180) retorna -1,0
sin(PI/ 6) retorna 0,5
sin(PI/ 2) retorna 1,0
cos(0) retorna 1,0
cos(PI/ 6) retorna 0,866
cos(PI/ 2) retorna 0
asin(0,5) retorna 0,523599 (o mesmo que ÿ/6)
acos(0,5) retorna 1,0472 (o mesmo que ÿ/3)
atan(1.0) retorna 0,785398 (o mesmo que ÿ/4)

4.2.2 Funções Expoente


Existem cinco funções relacionadas aos expoentes no cabeçalho cmath , conforme mostrado na Tabela 4.2.

Tabela 4.2 Funções de expoente no cabeçalho cmath

Função Descrição
x
exp(x) Retorna e elevado à potência de x (e ).

registro(x) Retorna o logaritmo natural de x (ln(x) = loge(x)).

log10(x) Retorna o logaritmo de base 10 de x (log10(x)).

pow (a, b) b
Retorna a elevado à potência de b (a ).
quadrado(x)
Retorna a raiz quadrada de x (2x) para x 7 = 0.

Suponha que E seja uma constante com valor 2,71828. Aqui estão alguns exemplos de uso dessas funções:

exp(1.0) retorna 2,71828


log(E) retorna 1,0
log10(10,0) retorna 1,0
pow(2,0, 3) retorna 8,0
sqrt(4.0) retorna 2,0
sqrt(10,5) retorna 3,24

4.2.3 Funções de Arredondamento


O cabeçalho cmath contém as funções para obtenção de arredondamentos conforme mostrado na Tabela 4.3.

Tabela 4.3 Funções de arredondamento no cabeçalho cmath

Função Descrição

teto (x) x é arredondado para o número inteiro mais próximo. Este número inteiro é retornado como um valor duplo.

chão (x) x é arredondado para o número inteiro mais próximo. Este número inteiro é retornado como um
valor duplo.
Machine Translated by Google

140 Capítulo 4 Funções matemáticas, caracteres e strings

Por exemplo,

ceil(2.1) retorna 3,0


ceil(2.0) retorna 2.0
ceil(-2,0) retorna -2,0
ceil(-2,1) retorna -2,0
piso(2.1) retorna 2,0
piso(2.0) retorna 2.0
piso(-2,0) retorna –2,0
piso(-2,1) retorna -3,0

4.2.4 Funções min, max e abs


As funções min e max retornam os números mínimo e máximo de dois números (int, long, float ou double).
Por exemplo, max(4,4, 5,0) retorna 5,0 e min(3, 2)
retorna 2.
A função abs retorna o valor absoluto do número (int, long, float ou
dobro). Por exemplo,

máximo(2, 3) retorna 3
máximo(2,5, 3,0) retorna 3,0
min(2,5, 4,6) retorna 2,5
abs(-2) retorna 2
abs(-2,1) retorna 2,1

Observação

As funções min, max e abs são definidas no cabeçalho cstdlib .

4.2.5 Estudo de caso: Calculando ângulos de um triângulo


Você pode usar as funções matemáticas para resolver muitos problemas computacionais. Dados os três
lados de um triângulo, por exemplo, você pode calcular os ângulos usando a seguinte fórmula:

x2, y2 A = acos((a * a - b * b - c * c) / (-2 * b * c))


B = acos((b * b - aa - c * * c) / (-2 * * c)) a
a C = acos((c * c - b * b - a * a) / (-2 * * b)) a
B
c
C
x3, y3
A b
x1, y1

Não se deixe intimidar pela fórmula matemática. Como discutimos na Listagem 2.11, ComputeLoan.cpp,
você não precisa saber como a fórmula matemática é derivada para escrever um programa para calcular
os pagamentos do empréstimo. Neste exemplo, dado o comprimento de três lados, você pode usar esta
fórmula para escrever um programa para calcular os ângulos sem saber como a fórmula é derivada. Para
calcular os comprimentos dos lados, precisamos de conhecer as coordenadas de três pontos de canto e
calcular as distâncias entre os pontos.
A Listagem 4.1 é um exemplo de programa que solicita ao usuário que insira as coordenadas xey
dos três pontos de canto em um triângulo e, em seguida, exibe os ângulos do triângulo.

Listagem 4.1 ComputeAngles.cpp


1 #include <iostream>
incluir cabeçalho cmath 2 #incluir <cmath>
3 usando namespace std;
4
Machine Translated by Google

4.2 Funções Matemáticas 141

5 int principal()
6{
// Solicita ao usuário que insira três pontos
cout << "Insira três pontos: ";
duplo x1, y1, x2, y2, x3, y3;
7 cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3; insira três pontos
8
9 // Calcula três lados
10 duplo a = sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3)); lados de cálculo
11 duplo b = sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
12 duplo c = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
13
14 //Obtém três ângulos em radianos
15 duplo A = acos((a * a - b * b - c duplo B = *c)/(-2 *b*c)); calcular ângulos
16 acos((b * b - a duplo C = acos((c * * a - cc * c) / (-2 * * c)); a
17 -b*b-a *a) /(-2 * *b)); a
18
19 //Mostra os ângulos em graus
20 const duplo PI = 3,14159; exibir resultado
21
" ""
cout << "Os três ângulos são << A * 180 / PI << << B * 180 / PI <<
""
22 << C * 180 / PI << endl;
23
24 retornar 0;
25 26 27 28 }

Insira três pontos: 1 1 6,5 1 6,5 2,5 Os três ângulos


são 15,2551 90,0001 74,7449

O programa solicita que o usuário insira três pontos (linha 10). Esta mensagem de aviso não é clara.
Você deve fornecer ao usuário instruções explícitas sobre como inserir esses pontos da seguinte forma:

cout << "Insira as coordenadas de três pontos separados"


<< "por espaços como x1 y1 x2 y2 x3 y3: ";

Observe que a distância entre dois pontos (x1, y1) e (x2, y2) pode ser calculada usando
2 2
a fórmula 2(x2 - x1) + (y2 - y1) . O programa aplica esta fórmula para calcular os três lados

(linhas 13–15) e depois aplica a fórmula para calcular os ângulos em radianos (linhas 18–20).
Os ângulos são exibidos em graus (linhas 24–25). Observe que 1 radiano equivale a 180/p graus.

4.1 Suponha que PI seja 3,14159 e E seja 2,71828, avalie as seguintes chamadas de função:
ÿVerificação de ponto

(a) quadrado (4,0) (j) piso (-2,5)


(b) pecado(2 * PI) (k) asin(0,5)
(c) cos(2 *PI) (l) acos(0,5)
(d) potência(2,0, 2) (m) espere (1,0)
(e) log(E) (n) teto(2,5)
(f) exp(1,0) (o) piso(2,5)
(g) máx(2, mín(3, 4)) (p) log10(10,0)
(h) quadrado(125,0) (q) potência(2,0, 3)
(i) teto (-2,5)

4.2 Verdadeiro ou falso? O argumento para funções trigonométricas é um ângulo em radianos.


Machine Translated by Google

142 Capítulo 4 Funções matemáticas, caracteres e strings

4.3 Escreva uma instrução que converta 47 graus em radianos e atribua o resultado a um
variável.

4.4 Escreva uma declaração que converta ÿ /7 em um ângulo em graus e atribua o resultado
para uma variável.

4.3 Tipos de Dados de Caracteres e Operações


Um tipo de dados de caractere representa um único caractere.
Chave
Apontar
Além de processar valores numéricos, você pode processar caracteres em C++. O tipo de dados de caractere, char,
tipo de caractere é usado para representar um único caractere. Um literal de caractere é colocado entre aspas simples. Considere o
seguinte código:

letra char = 'A';


char numChar = '4';

A primeira instrução atribui o caractere A à letra da variável char . A segunda afirmação


atribui o caractere de dígito 4 à variável char numChar.

Cuidado
caractere literal Uma string literal deve ser colocada entre aspas (" "). Um literal de caractere é um único caractere
colocado entre aspas simples (' '). Portanto, "A" é uma string e 'A'
é um personagem.

4.3.1 Código ASCII


Os computadores usam números binários internamente. Um caractere é armazenado em um computador como uma
codificação sequência de 0s e 1s. Mapear um caractere para sua representação binária é chamado de codificação. Existem
diferentes maneiras de codificar um caractere. A forma como os caracteres são codificados é definida por um esquema
de codificação.
ASCII A maioria dos computadores usa ASCII (American Standard Code for Information Interchange), um esquema de
codificação de 8 bits para representar todas as letras maiúsculas e minúsculas, dígitos, sinais de pontuação e
caracteres de controle. A Tabela 4.4 mostra o código ASCII para alguns caracteres comumente usados. O Apêndice
B, “O conjunto de caracteres ASCII”, fornece uma lista completa de caracteres ASCII e seus códigos decimais e
hexadecimais. Na maioria dos sistemas, o tamanho do tipo char é de 1 byte.

Tabela 4.4 Código ASCII para caracteres comumente usados

Personagens Código ASCII

'0' a '9' 48 a 57

'A a Z' 65 a 90

'A a Z' 97 a 122

Observação

incremento e decremento Os operadores de incremento e decremento também podem ser usados em variáveis char para
de caracteres
obter o caractere de código ASCII seguinte ou anterior. Por exemplo, as instruções a seguir exibem
o caractere b.

char ch = 'a';
cout << ++ch;
Machine Translated by Google

4.3 Tipos de Dados de Caracteres e Operações 143

4.3.2 Lendo um caractere do teclado


Para ler um caractere do teclado, use ler personagem

cout << "Digite um caractere: ";


char ;
cin >> ch; //Lê um personagem
cout << "O caractere lido é " << ch << endl;

4.3.3 Sequências de escape para caracteres especiais


Suponha que você queira imprimir uma mensagem entre aspas na saída. Você pode escrever uma declaração
como esta?

cout << "Ele disse "Programar é divertido"" << endl;

Não, esta instrução contém um erro de compilação. O compilador pensa que o segundo caractere de aspa
é o fim da string e não sabe o que fazer com o restante dos caracteres.
Para superar esse problema, C++ utiliza uma notação especial para representar caracteres especiais,
conforme mostrado na Tabela 4.5. Essa notação especial, chamada sequência de escape, consiste em uma
barra invertida (\) seguida por um caractere ou uma combinação de dígitos. Por exemplo, \t é uma sequência sequência de fuga
de escape para o caractere Tab. Os símbolos em uma sequência de escape são interpretados como um todo
e não individualmente. Uma sequência de escape é considerada um único caractere.

Tabela 4.5 Sequências de Escape

Sequência de fuga Nome Código ASCII Sequência de fuga Nome Código ASCII

\b Retrocesso 8 \r Retorno de transporte 13

\t Aba 9 \\ Barra invertida 92

\n Alimentação de linha 10 \" Citação dupla 34

\f Formfeed 12

Então, agora você pode imprimir a mensagem citada usando a seguinte instrução:

cout << "Ele disse \"Programar é divertido\"" << endl;

A saída é

Ele disse "Programar é divertido"


"
Observe que os símbolos \ e a juntos representam um personagem.
barra invertida \ são chamados de caracteres de escape. É um personagem especial. Para exibir isso personagem de fuga
caractere, você deve usar uma sequência de escape \\. Por exemplo, o seguinte código

cout << "\\t é um caractere de tabulação" << endl;

exibições

\t é um caractere de tabulação

Observação

Os personagens dos ', '\t', '\f', '\r' e '\n' são conhecidos como espaços em branco caractere de espaço em branco
personagens .

Observação

Ambas as instruções a seguir exibem uma string e movem o cursor para a próxima linha:

cout << "Bem-vindo ao C++\n";


cout << "Bem-vindo ao C++" << endl;

Entretanto, usar endl garante que a saída seja exibida imediatamente em todas as plataformas. \n vs. fim
Machine Translated by Google

144 Capítulo 4 Funções matemáticas, caracteres e strings

4.3.4 Casting entre tipos char e numéricos


Um char pode ser convertido em qualquer tipo numérico e vice-versa. Quando um número inteiro é convertido em
um char, apenas os 8 bits de dados inferiores são usados; a outra parte é ignorada. Por exemplo,

caractere c = 0XFF41; // O código hexadecimal 41 de 8 bits inferior é atribuído a c


cout << c; // variável c é o caractere A

Quando um valor de ponto flutuante é convertido em um char, o valor de ponto flutuante é primeiro convertido
em um int, que é então convertido em um char.

caractere c = 65,25; // 65 é atribuído à variável c


cout << c; // variável c é o caractere A

Quando um char é convertido em um tipo numérico, o ASCII do caractere é convertido no tipo especificado
tipo numérico. Por exemplo:

int i = 'A'; cout << // O código ASCII do caracter A é atribuído a i


eu; // variável i é 65

operadores numéricos O tipo char é tratado como se fosse um número inteiro do tamanho do byte. Todos os operadores numéricos podem
em caracteres
ser aplicados a operandos char . Um operando char é automaticamente convertido em um número se o outro
operando for um número ou caractere. Por exemplo, as seguintes afirmações

// O código ASCII para '2' é 50 e para '3' é 51


int i = '2' + '3'; cout << "eu
sou" << eu << endl; // agora tenho 101 anos

int j = 2 + 'a'; // O código ASCII para 'a' é 97


cout << "j é" << j << endl;
cout << j << " é o código ASCII para o caractere " static_cast<char>(j) << <<
endl;

mostrar

eu tenho 101
j é 99
99 é o código ASCII para o caractere c

Observe que o operador static_cast<char>(value) converte explicitamente um valor numérico em um caractere.

Conforme mostrado na Tabela 4.4, os códigos ASCII para letras minúsculas são números inteiros consecutivos
começando no código ASCII para 'a', depois para 'b', 'c', . . . e 'z'. O mesmo se aplica às letras maiúsculas e aos
caracteres numéricos. Além disso, o código ASCII para 'a' é maior que o código para 'A'. Você pode usar essas
propriedades para converter uma letra maiúscula em minúscula ou vice-versa. A Listagem 4.2 fornece um programa
que solicita ao usuário que digite uma letra minúscula e encontra sua letra maiúscula correspondente.

Listagem 4.2 ToUppercase.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 cout << "Digite uma letra minúscula: ";
7 char letra minúscula;
insira um caractere 8 cin >> letra minúscula;
Machine Translated by Google

4.3 Tipos de Dados de Caracteres e Operações 145

9 char letra maiúscula =


10 static_cast<char>('A' + (letra minúscula - 'a')); converter para maiúsculas
11
12 cout << "A letra maiúscula correspondente é "
13 << letra maiúscula << endl;
14
15 retornar 0;
16 17 }

Digite uma letra minúscula: b


A letra maiúscula correspondente é B

Observe que para uma letra minúscula ch1 e sua letra maiúscula correspondente ch2, ch1 - 'a'
é o mesmo que ch2 - 'A'. Portanto, ch2 = 'A' + ch1 - 'a'. Portanto, a letra maiúscula correspondente
para lowercaseLetter é static_cast<char>('A' + (lowercaseLetter - 'a')) (linha 11). Observe que as
linhas 10–11 podem ser substituídas por

char letra maiúscula = 'A' + (letra minúscula - 'a');

Como a letra maiúscula é declarada como um valor do tipo char, o C++ converte automaticamente o
valor int 'A' + (letra minúscula - 'a') para um valor char .

4.3.5 Comparando e Testando Caracteres


Dois caracteres podem ser comparados usando operadores relacionais da mesma forma que
comparamos dois números. Isso é feito comparando os códigos ASCII dos dois caracteres. Por exemplo,

'a' < 'b' é verdadeiro porque o código ASCII para 'a' (97) é menor que o código ASCII para
'b' (98).

'a' < 'A' é falso porque o código ASCII para 'a' (97) é maior que o código ASCII para 'A'
(65).

'1' < '8'é verdadeiro porque o código ASCII para '1' (49) é menor que o código ASCII para
'8' (56).

Freqüentemente, no programa, você precisa testar se um caractere é um número, uma letra, uma letra
maiúscula ou uma letra minúscula. Por exemplo, o código a seguir testa se um caractere ch é uma letra
maiúscula.

if (ch >= 'A' && ch <= 'Z')


cout << ch << "é uma letra maiúscula" << endl; senão if
(ch >= 'a' && ch <= 'z') cout << ch << "é
uma letra minúscula" << endl;
senão if (ch >= '0' && ch <= '9') cout << ch
<< "é um caractere numérico" << endl;

4.5 Use instruções de impressão do console para determinar o código ASCII para '1', 'A', 'B',
'a' e 'b'. Use instruções de impressão para determinar o caractere dos códigos decimais ÿVerificação de ponto

40, 59, 79, 85 e 90. Use instruções de impressão para determinar o caractere do código
hexadecimal 40, 5A, 71, 72 e 7A.

4.6 Os seguintes literais estão corretos para caracteres?


'1', '\t', '&', '\b', '\n'

4.7 Como você exibe os caracteres \ e "?


Machine Translated by Google

146 Capítulo 4 Funções matemáticas, caracteres e strings

4.8 Mostre a impressão do seguinte código:


int eu = '1';
int j = '1' + '2';
int k = 'a';
caractere c = 90;
"" "" ""
cout << eu << <<j<< <<k<< << c << endl;

4.9 Mostre a impressão do seguinte código:


caractere c = 'A';
int eu = c;

flutuar f = 1000,34f;
intj =f;

duplo d = 1000,34;
intk =d;

intl = 97;
char ch = eu;

cout << c << endl;


cout << i << endl;
cout << f << endl;
cout << j << endl;
cout << d << endl;
cout << k << endl;
cout << l << endl;
cout << ch << endl;

4.10 Mostre a saída do seguinte programa:


#include <iostream>
usando namespace std;

int principal()
{
caractere x = 'a';
char y = 'c';

cout << ++x << endl;


cout << y++ << endl;
cout << (x - y) << endl;

retornar 0;
}

4.4 Estudo de Caso: Gerando Caracteres Aleatórios


Um caractere é codificado usando um número inteiro. Gerar um caractere aleatório é gerar um
Chave
Apontar número inteiro.

Os programas de computador processam dados numéricos e caracteres. Você viu muitos exemplos
envolvendo dados numéricos. Também é importante compreender os personagens e como processá-los.
Esta seção fornece um exemplo de geração de caracteres aleatórios.
Cada caractere possui um código ASCII exclusivo entre 0 e 127. Gerar um caractere aleatório é gerar
um número inteiro aleatório entre 0 e 127. Na Seção 3.9, você aprendeu como gerar um número aleatório.
Lembre-se de que você pode usar a função srand(seed) para definir uma semente
Machine Translated by Google

4.4 Estudo de Caso: Gerando Caracteres Aleatórios 147

e use Rand() para retornar um número inteiro aleatório. Você pode usá-lo para escrever uma expressão simples
para gerar números aleatórios em qualquer intervalo. Por exemplo,

Retorna um número inteiro


rand()% 10
aleatório entre 0 e 9.

Retorna um número inteiro


50 + rand()% 50
aleatório entre 50 e 99.

Em geral,

Retorna um número aleatório


a + rand() % b
entre a e a + b, excluindo a + b.

Portanto, você pode usar a seguinte expressão para gerar um número inteiro aleatório entre 0 e 127:

rand() % 128

Agora vamos considerar como gerar uma letra minúscula aleatória. Os códigos ASCII para letras minúsculas são
números inteiros consecutivos começando com o código para 'a' e depois para 'b', 'c', . . . e 'z'. O código para 'a'
é

static_cast<int>('a')

Portanto, um número inteiro aleatório entre static_cast<int>('a') e static_cast<int>('z') é

static_cast<int>('a') + rand()
% (static_cast<int>('z') - static_cast<int>('a') + 1)

Lembre-se de que todos os operadores numéricos podem ser aplicados aos operandos char . O operando char é
convertido em um número se o outro operando for um número ou caractere. Assim, a expressão anterior pode
ser simplificada da seguinte forma:

('a' + rand)% ('z' - 'a' + 1)

e uma letra minúscula aleatória é

static_cast<char>('a' + rand() % ('z' - 'a' + 1))

Para generalizar a discussão anterior, um caractere aleatório entre quaisquer dois caracteres ch1
e ch2 com ch1 < ch2 podem ser gerados da seguinte forma:

static_cast<char>(ch1 + rand() % (ch2 – ch1 + 1))

Esta é uma descoberta simples, mas útil. A Listagem 4.3 fornece um programa que solicita ao usuário que insira
dois caracteres x e y com x <= y e exibe um caracter aleatório entre x e y.

Listagem 4.3 DisplayRandomCharacter.cpp


1 #include <iostream>
2 #incluir <cstdlib>
3 usando namespace std;

4 5 int principal()
6{
cout << "Insira um caractere inicial: ";
char startChar;
comer >> startChar;
7
8 9 10 11 cout << "Insira um caractere final: ";
Machine Translated by Google

148 Capítulo 4 Funções matemáticas, caracteres e strings


12 char finalChar;
13 comer >> endChar;
14
15 //Pegue um caractere aleatório
16 char randomChar = static_cast<char>(startChar + rand() % (endChar -
17 startChar + 1));
18
19 cout << "O caractere aleatório entre " << startChar << " é " << randomChar " e "
20 << endChar << << endl;
21
22 retornar 0;
23 }

Insira um caractere inicial: um


Insira um caractere final: z
O caractere aleatório entre a e z é p

O programa solicita que o usuário insira um caractere inicial (linha 9) e um caractere final (linha 13). Ele
obtém um caractere aleatório entre esses dois caracteres (pode incluir esses dois caracteres) nas linhas 16–
17.

4.11 Se a entrada para um caractere inicial e um caractere final for a mesma, que
ÿVerificação de ponto
caractere aleatório o programa exibirá?

4.5 Estudo de caso: Adivinhando aniversários


Adivinhar aniversários é um problema interessante com uma solução de programação simples.
Chave
Apontar
Você pode determinar o dia do mês em que seu amigo nasceu fazendo cinco perguntas.
Cada pergunta pergunta se o dia está em um dos cinco conjuntos de números a seguir.

= 19

1357 2367 4 567 8 9 10 11 16 17 18 19


9 11 13 15 10 11 14 15 12 13 14 15 12 13 14 15 20 21 22 23
17 19 21 23 18 19 22 23 20 21 22 23 24 25 26 27 24 25 26 27
25 27 29 31 26 27 30 31 28 29 30 31 28 29 30 31 28 29 30 31

Conjunto1 Conjunto 2 Conjunto3 Conjunto 4 Conjunto 5

O aniversário é a soma dos primeiros números dos conjuntos onde aparece o dia. Por exemplo, se o
aniversário for 19 anos, ele aparecerá no Conjunto1, Conjunto2 e Conjunto5. Os primeiros números nesses
três conjuntos são 1, 2 e 16. A soma deles é 19.
A Listagem 4.4 fornece um programa que solicita ao usuário que responda se o dia está em Set1 (linhas
10 a 16), em Set2 (linhas 22 a 28), em Set3 (linhas 34 a 40), em Set4 (linhas 46 a 52). e em Set5 (linhas 58–
64). Se o número estiver no conjunto, o programa adiciona o primeiro número do conjunto ao dia (linhas 19,
31, 43, 55, 67).

Listagem 4.4 GuessBirthday.cpp


Nota de vídeo 1 #include <iostream>
Adivinha aniversário 2 usando namespace std;

3 4 int principal()
Machine Translated by Google

4.5 Estudo de caso: Adivinhando aniversários 149

5{
dia interno = 0; // Dia a definir dia a definir
resposta de caractere ;

// Solicita ao usuário o Set1


6 cout << "Seu aniversário é no Set1?" << fim;
cout << "
7 1 3 5 7\n" <<
"
8 9 11 13 15\n" <<
9 "17 19 21 23\n" <<
10 "25 27 29 31" << fim;
11 cout << "Digite N/n para Não e S/y para Sim: ";
12 cin >> resposta;
13
14 if (resposta == 'S' || resposta == 's') no Conjunto1?

15 dia += 1;
16
17 // Solicita ao usuário o Set2
18 cout << "\nSeu aniversário está no Set2?" << fim;
cout << "
19 2 3 6 7\n" <<
20 "10 11 14 15\n" <<
21 "18 19 22 23\n" <<
22 "26 27 30 31" << fim;
23 24 25 26cout
27 << "Digite N/n para Não e S/y para Sim: ";
28 cin >> resposta;
29
30 if (resposta == 'S' || resposta == 's') no Conjunto2?

31 dia += 2;
32
33 // Solicita ao usuário o Set3
34 cout << "\nSeu aniversário está no Set3?" << fim;
cout << "
35 4 5 6 7\n" <<
36 "12 13 14 15\n" <<
37 "20 21 22 23\n" <<
38 "28 29 30 31" << fim;
39 cout << "Digite N/n para Não e S/y para Sim: ";
40 cin >> resposta;
41
42 if (resposta == 'S' || resposta == 's') no Conjunto3?

43 dia += 4;
44
45 // Solicita ao usuário Set4
46 cout << "\nSeu aniversário é no Set4?" << fim;
47 cout << "
8 9 10 11\n" <<
48 "12 13 14 15\n" <<
49 "24 25 26 27\n" <<
50 "28 29 30 31" << fim;
51 cout << "Digite N/n para Não e S/y para Sim: ";
52 cin >> resposta;
53
54 if (resposta == 'S' || resposta == 's') no Conjunto 4?

55 dia += 8;
56
57 // Solicita ao usuário Set5
58 cout << "\nSeu aniversário é no Set5?" << fim;
59 cout << "16 17 18 19\n" <<
60 "20 21 22 23\n" <<
61 "24 25 26 27\n" <<
62 "28 29 30 31" << fim;
63 cout << "Digite N/n para Não e S/y para Sim: ";
64 cin >> resposta;
65
Machine Translated by Google

150 Capítulo 4 Funções matemáticas, caracteres e strings


no Conjunto 5? 66 if (resposta == 'S' || resposta == 's')
67 dia += 16;
68
69 cout << "Seu aniversário é " << dia << endl;
70
71 retornar 0;
72}

Seu aniversário está no Set1?


1357
9 11 13 15
17 19 21 23
25 27 29 31
Insira N/n para Não e S/y para Sim: Y

Seu aniversário está no Set2?


2367
10 11 14 15
18 19 22 23
26 27 30 31
Insira N/n para Não e S/y para Sim: Y

Seu aniversário está no Set3?


4567
12 13 14 15
20 21 22 23
28 29 30 31
Insira N/n para Não e S/y para Sim: N

Seu aniversário está no Set4?


8 9 10 11
12 13 14 15
24 25 26 27
28 29 30 31
Insira N/n para Não e S/y para Sim: N

Seu aniversário está no Set5?


16 17 18 19
20 21 22 23
24 25 26 27
28 29 30 31
Digite N/n para Não e S/y para Sim: S Seu
aniversário é 19 anos

Linha# dia responder Saída


6 0
7 valor indefinido
16 E

19 1

28 E

31 3

40 N

52 N
Machine Translated by Google

4.6 Funções de Caracteres 151

Linha# dia responder Saída


64 E
67 19
69 Seu aniversário
tem 19 anos

Este jogo é fácil de programar. Você pode se perguntar como o jogo foi criado. A matemática por trás do matemática por trás do jogo
jogo é bastante simples. Os números não são agrupados por acidente -
a forma como são colocados nos cinco conjuntos é deliberada. Os números iniciais nos cinco conjuntos são 1,
2, 4, 8 e 16, que correspondem a 1, 10, 100, 1.000 e 10.000 em binário (os números binários são introduzidos
no Apêndice D, Sistemas Numéricos). Um número binário para inteiros decimais entre 1 e 31 possui no máximo
cinco dígitos, conforme mostrado na Figura 4.1a. Que seja b5b4b3b2b1. Assim, b5b4b3b2b1 = b50000 +
b4000 + b300 + b20 + b1, conforme mostrado na Figura 4.1b. Se o número binário de um dia tiver o dígito 1
em bk, o número deverá aparecer em Setk. Por exemplo, o número 19 é o binário 10011, portanto aparece no
Conjunto1, Conjunto2 e Conjunto5. É binário 1 + 10 + 10000 = 10011 ou decimal 1 + 2 + 16 = 19. O número
31 é binário 11111, portanto aparece em Conjunto1, Conjunto2, Conjunto3, Conjunto4 e Conjunto5.
É binário 1 + 10 + 100 + 1000 + 10000 = 11111 ou decimal 1 + 2 + 4 + 8 + 16 = 31.

Decimal Binário 10.000


b5 0 0 0 0
1 00001 1000
b4 0 0 0
2 00010 10.000 100
b3 0 0
3 00011 10 10
b2 0
... + 1 + 1
19 10011 + b1
10011 11111
...
b5 b4 b3 b2 b1
31 11111 19 31

(a) (b)

Figura 4.1 (a) Um número entre 1 e 31 pode ser representado usando um número binário de 5 dígitos.
(b) Um número binário de 5 dígitos pode ser obtido adicionando os números binários 1, 10, 100, 1.000 ou
10.000.

4.12 Se você executar a Listagem 4.4 GuessBirthday.cpp com entrada Y para Set1, Set3 e Set4 e
N para Set2 e Set5, qual será o aniversário? ÿVerificação de ponto

4.6 Funções dos Personagens


C++ contém as funções para trabalhar com caracteres.
Chave
Apontar
C++ fornece diversas funções para testar um caractere e para converter um caractere no arquivo de cabeçalho
<cctype> , conforme mostrado na Tabela 4.6. As funções de teste testam um único caractere e retornam
verdadeiro ou falso. Observe que eles realmente retornam um valor int . Um número inteiro diferente de zero
corresponde a verdadeiro e zero a falso. C++ também fornece duas funções para conversão de casos.

Tabela 4.6 Funções de Caracteres

Função Descrição

édígito(ch) Retorna verdadeiro se o caractere especificado for um dígito.

isalfa (ch) Retorna verdadeiro se o caractere especificado for uma letra.

isalnum(ch) Retornará verdadeiro se o caractere especificado for uma letra ou um dígito.

(contínuo)
Machine Translated by Google

152 Capítulo 4 Funções matemáticas, caracteres e strings

Tabela 4.6 (continuação)


Função Descrição

é inferior (ch) Retorna verdadeiro se o caractere especificado for uma letra minúscula.

jantar Retorna verdadeiro se o caractere especificado for uma letra maiúscula.

isespaço(ch) Retorna verdadeiro se o caractere especificado for um caractere de espaço em branco.

parainferior(ch) Retorna a letra minúscula do caractere especificado.

topo (ch) Retorna a letra maiúscula do caracter especificado.

A Listagem 4.5 é um programa para usar funções de caracteres.

Listagem 4.5 CharacterFunctions.cpp


1 #include <iostream>
incluir cctype 2 #incluir <cctype>
3 usando namespace std;

4 5 int principal()
6{
cout << "Digite um caractere: ";
char ;
caractere de entrada cin >> ch;
7
8 cout << "Você digitou" << ch << endl;
9
é minúsculo? 10 if (é inferior (ch)) {
11
12 cout << "É uma letra minúscula " cout << "Sua letra << fim;
13 maiúscula equivalente é " static_cast<char>(toupper(ch)) << endl; <<
converter para maiúsculas 14
15 }
é maiúsculo? 16 senão if (isupper(ch)) {
17
18 cout << "É uma letra maiúscula " << endl;
19 cout << "Sua letra minúscula equivalente é " <<
converter para minúsculas 20 static_cast<char>(tolower(ch)) << endl;
21 }
é dígito? 22 senão if (édígito(ch)) {
23
24 cout << "É um caractere de dígito" << fim;
25 }
26
27 retornar 0;
28 29 30 31 }

Digite um caractere: um
Você inseriu um
É uma letra minúscula
Sua letra maiúscula equivalente é A

Digite um caractere: T
Você digitou T
É uma letra maiúscula
Sua letra minúscula equivalente é t
Machine Translated by Google

4.7 Estudo de Caso: Convertendo um Dígito Hexadecimal em um Valor Decimal 153

Digite um caractere: 8
Você digitou 8
É um caractere de dígito

4.13 Qual função você usa para testar se um caractere é um dígito? uma letra? uma letra
minúscula? uma letra maiúscula? um dígito ou uma letra? ÿVerificação de ponto

4.14 Qual função você usa para converter uma letra em minúscula ou maiúscula?

4.7 Estudo de caso: Convertendo um dígito hexadecimal


em um valor decimal
Esta seção apresenta um programa que converte um dígito hexadecimal em um valor decimal.
Chave
Apontar
O sistema numérico hexadecimal tem 16 dígitos: 0–9, A–F. As letras A, B, C, D, E e F correspondem
aos números decimais 10, 11, 12, 13, 14 e 15. Agora escrevemos um programa que solicita ao usuário
que insira um dígito hexadecimal e exiba seu dígito correspondente. valor decimal, conforme mostrado
na Listagem 4.6.

Listagem 4.6 HexDigit2Dec.cpp


1 #include <iostream>
2 #incluir <cctype>
3 usando namespace std;

4 5 int principal()
6{
7 cout << "Insira um dígito hexadecimal: ";
char hexDigit;
cin >> hexadecimal; insira um caractere
8
9 hexDigit = topper(hexDigit); para maiúsculas
10 if (hexDigit <= 'F' && hexDigit >= 'A') { é A-F?
11
12 valor interno = 10 + hexDigit - 'A';
13 cout << "O valor decimal para dígito hexadecimal"
14 << dígito hexadecimal << "é" << valor << endl;
15 }
16 senão if (édígito(hexDigit)) { é 0–9?
17
18 cout << "O valor decimal para dígito hexadecimal"
19 << dígito hexadecimal << " é " << hexDigit << endl;
20 }
21 outro não é um dígito hexadecimal válido
22 {
23 cout << hexDigit << "é uma entrada inválida" << endl;
24 }
25
26 retornar 0;
27 28 29 }

Insira um dígito hexadecimal: b


O valor decimal para o dígito hexadecimal B é 11
Machine Translated by Google

154 Capítulo 4 Funções matemáticas, caracteres e strings

Insira um dígito hexadecimal: B


O valor decimal para o dígito hexadecimal B é 11

Insira um dígito hexadecimal: 8


O valor decimal para o dígito hexadecimal 8 é 8

Insira um dígito hexadecimal: T


T é uma entrada inválida

O programa lê um dígito hexadecimal como um caractere do console (linha 9) e obtém a letra maiúscula do
caractere (linha 11). Se o caractere estiver entre 'A' e 'F' (linha 12), o valor decimal correspondente é hexDigit –
'A' + 10 (linha 14). Observe que hexDigit – 'A' é 0 se hexDigit for 'A', hexDigit – 'A' é 1 se hexDigit for 'B' e
assim por diante. Quando dois caracteres realizam uma operação numérica, os códigos ASCII dos caracteres são
usados no cálculo.

O programa invoca a função isdigit(hexDigit) para verificar se hexDigit está entre '0' e '9' (linha 18). Nesse
caso, o dígito decimal correspondente é igual a hexDigit (linhas 20–21).

Se hexDigit não estiver entre 'A' e 'F' nem um caractere de dígito, o programa exibirá um
mensagem de erro (linha 25).

4.15 Qual linha do código testa se o caractere está entre '0' e '9'?
ÿVerificação de ponto
4.16 Se a entrada for f, qual é o valor exibido?

4.8 O tipo de string


Uma string é uma sequência de caracteres.
Chave
Apontar
O tipo char representa apenas um caractere. Para representar uma sequência de caracteres, use o tipo de dados
denominado string. Por exemplo, o código a seguir declara que a mensagem é uma string com o valor Programar
é divertido.

string mensagem = "Programar é divertido";

O tipo string não é um tipo primitivo. É conhecido como um tipo de objeto. Aqui mensagem
representa um objeto string com conteúdo Programar é divertido.
Os objetos são definidos usando classes. string é uma classe predefinida no arquivo de cabeçalho <string> .
Um objeto também é conhecido como instância de uma classe. Objetos e classes serão discutidos detalhadamente
no Capítulo 9. Por enquanto, você precisa saber apenas como criar um objeto string e como usar as funções
simples da classe string , conforme mostrado na Tabela 4.7.

Tabela 4.7 Funções Simples para Objetos String

Função Descrição

comprimento() Retorna o número de caracteres nesta string.


tamanho() O mesmo que comprimento().

em (índice) Retorna o caractere no índice especificado desta string.


Machine Translated by Google

4.8 A string Tipo 155

As funções na classe string só podem ser invocadas a partir de uma instância de string específica .
Por esse motivo, essas funções são chamadas de funções de instância. Por exemplo, você pode usar função de instância

a função size() na classe string para retornar o tamanho de um objeto string e usar a função at(index)
para retornar o caractere no índice especificado, conforme mostrado no código a seguir:

string mensagem = "ABCD";


cout << mensagem.length() << endl;
cout << mensagem.at(0) << endl;
string s = "Inferior";
cout << s.length() << endl;
cout << s.at(1) << endl;

Invocar message.length() retorna 4 e invocar message.at(0) retorna caractere


A. Invocar s.length() retorna 6 e invocar s.at(1) retorna o caractere o.
A sintaxe para invocar uma função de instância é objectName.functionName(arguments).
Uma função pode ter muitos argumentos ou nenhum argumento. Por exemplo, a função at(index)
possui um argumento, mas a função length() não possui argumentos.

Observação

Por padrão, uma string é inicializada como uma string vazia, ou seja, uma string que não string vazia
contém caracteres. Uma string literal vazia pode ser escrita como "". Portanto, as duas
afirmações a seguir têm o mesmo efeito:

cordas;
strings = "";

Observação

Para usar o tipo string, você precisa incluir o arquivo de cabeçalho <string> em seu programa.

4.8.1 Índice de String e Operador Subscrito


A função s.at(index) pode ser usada para recuperar um caractere específico em uma string s, onde o em (índice)
índice está entre 0 e s.length()–1. Por exemplo, message.at(0) retorna o caractere W, conforme
mostrado na Figura 4.2. Observe que o índice do primeiro caractere da string é 0.

Índices 0 1 2 3 4 5 6 7 8 9 10 11 12 13

mensagem Bem-vindo para C++

mensagem.at(0) mensagem.length é 14 mensagem.at(13)

Figura 4.2 Os caracteres em um objeto string podem ser acessados usando seu índice.

Por conveniência, C++ fornece o operador subscrito para acessar o caractere em um índice operador de subscrito
especificado em uma string usando a sintaxe stringName[index]. Você pode usar esta sintaxe para
recuperar e modificar o caractere em uma string. Por exemplo, o código a seguir define um novo
caractere P no índice 0 usando s[0] = 'P' e o exibe.

strings = "ABCD";
s[0] = 'P';
cout << s[0] << endl;
Machine Translated by Google

156 Capítulo 4 Funções matemáticas, caracteres e strings

Cuidado
intervalo de índice de string Tentar acessar caracteres fora dos limites de uma string é um erro de programação comum.
Para evitá-lo, certifique-se de não usar um índice além de s.length() – 1.
Por exemplo, s.at(s.length()) ou s[s.length()] causaria um erro.

4.8.2 Concatenando Strings


C++ fornece o operador + para concatenar duas strings. A instrução mostrada abaixo, por exemplo, concatena as
concatenação de strings strings s1 e s2 em s3:

string s3 = s1 + s2;

O operador += aumentado também pode ser usado para concatenação de strings. Por exemplo, o código a seguir
anexa a string "e programar é divertido" com a string "Bem-vindo ao C++" na mensagem.

mensagem += “e programar é divertido”;

Portanto, a nova mensagem é “Bem-vindo ao C++ e programar é divertido”.


Você também pode concatenar um caractere com uma string. Por exemplo,

strings = "ABC";
s+= 'D';

Portanto, o novo s é “ABCD”.

Cuidado
É ilegal concatenar duas strings literais. Por exemplo, o código a seguir está incorreto:

string cita = "Londres" + "Paris";

No entanto, o código a seguir está correto, porque primeiro concatena a string s com
"London" e depois a nova string é concatenada com "Paris".

strings = "Nova York";


string cita = s + "Londres" + "Paris";

4.8.3 Comparando Strings


Você pode usar os operadores relacionais ==, !=, <, <=, >, >= para comparar duas strings. Isso é feito comparando
seus caracteres correspondentes um por um, da esquerda para a direita. Por exemplo,

string s1 = "ABC";
string s2 = "ABE";
cout << (s1 == s2) << endl; // Exibe 0 (significa falso)
cout << (s1 != s2) << endl; // Exibe 1 (significa verdadeiro)
cout << (s1 > s2) << endl; // Exibe 0 (significa falso)
cout << (s1 >= s2) << endl; // Exibe 0 (significa falso)
cout << (s1 <s2) << endl; // Exibe 1 (significa verdadeiro)
cout << (s1 <= s2) << endl; // Exibe 1 (significa verdadeiro)

Considere avaliar s1 > s2. Os dois primeiros caracteres (A versus A) de s1 e s2 são comparados. Por
serem iguais, os dois segundos caracteres (B versus B) são comparados.
Como também são iguais, os terceiros dois caracteres (C versus E) são comparados. Como o caractere C é
menor que E, a comparação retorna 0.
Machine Translated by Google

4.8 A string Tipo 157

4.8.4 Lendo Strings


Uma string pode ser lida no teclado usando o objeto cin . Por exemplo, veja o seguinte
código:

1 cidade de corda;
2 cout << "Insira uma cidade: ";
3 cin >> cidade; //Lê a string cidade
4 cout << "Você digitou " << cidade << endl;

A linha 3 lê uma string para cidade. Essa abordagem para ler uma string é simples, mas há um
problema. A entrada termina com um caractere de espaço em branco. Se você quiser entrar em Nova
York, terá que usar uma abordagem alternativa. C++ fornece a função getline no arquivo de cabeçalho
de string , que lê uma string do teclado usando a seguinte sintaxe:

getline(cin, s, delimitarCaracter)

A função para de ler caracteres quando o caractere delimitador é encontrado. O delimitador é lido, mas
não armazenado na string. O terceiro argumento delimitCharacter possui um valor padrão ('\n').

O código a seguir usa a função getline para ler uma string.

1 cidade de corda; declarar uma string


2 cout << "Insira uma cidade: ";
3 getline(cin, cidade, '\n'); // O mesmo que getline(cin, cidade) leia uma string
4 cout << "Você digitou " << cidade << endl;

Como o valor padrão para o terceiro argumento na função getline é '\n', a linha 3 pode ser substituída
por

getline(cin,cidade); //Lê uma string

A Listagem 4.7 fornece um programa que solicita ao usuário que insira duas cidades e as exibe em
ordem alfabética.

Listagem 4.7 OrderTwoCities.cpp


1 #include <iostream>
2 #incluir <string> incluir string
3 usando namespace std;

4 5 int principal()
6{
7 string cidade1, cidade2;
8 cout << "Insira a primeira cidade: ";
getline(cin, cidade1); inserir cidade1
9 10 cout << "Insira a segunda cidade: ";
11 getline(cin, cidade2); inserir cidade2
12
13 cout << "As cidades em ordem alfabética são ";
14 if (cidade1 < cidade2) comparar duas cidades
""
15 cout << cidade1 << << cidade2 << endl;
16 outro
""
17 cout << cidade2 << << cidade1 << endl;
18
19 retornar 0;
20}
Machine Translated by Google

158 Capítulo 4 Funções matemáticas, caracteres e strings

Digite a primeira cidade: Nova York


Digite a segunda cidade: Boston
As cidades em ordem alfabética são Boston Nova York

Ao usar strings no programa, você deve sempre incluir o arquivo de cabeçalho da string (linha 2). Se a linha 9 for
substituída por cin >> cidade1, você não poderá inserir uma string com espaços para cidade1. Como o nome de uma
cidade pode conter múltiplas palavras separadas por espaços, o programa usa a função getline para ler uma string
(linhas 9, 11).

4.17 Escreva uma instrução que declare uma string chamada cidade com valor Chicago.
ÿVerificação de ponto
4.18 Escreva uma instrução que exiba o número de caracteres na string s.

4.19 Escreva uma instrução que altere o primeiro caractere da string s para 'P'.
4.20 Mostre a saída do seguinte código:

string s1 = "Bom dia";


string s2 = "Boa tarde";
cout << s1[0] << endl; cout
<< (s1 == s2? "verdadeiro": "falso") << endl; cout << (s1! =
s2? "verdadeiro": "falso") << endl; cout << (s1 > s2 ?
"verdadeiro": "falso") << endl; cout << (s1 >= s2 ?
"verdadeiro": "falso") << endl; cout << (s1 <s2 ?
"verdadeiro": "falso") << endl; cout << (s1 <= s2?
"verdadeiro": "falso") << endl;

4.21 Como você lê uma string que contém espaços?

4.9 Estudo de caso: Revisão do programa de loteria


usando strings
Um problema pode ser resolvido usando muitas abordagens diferentes. Esta seção reescreve o programa
Chave
Apontar de loteria da Listagem 3.7, Lottery.cpp, usando strings. O uso de strings simplifica este programa.

O programa de loteria da Listagem 3.7 gera um número aleatório de dois dígitos, solicita que o usuário insira um número
de dois dígitos e determina se o usuário ganha de acordo com as seguintes regras:

1. Se a entrada do usuário corresponder ao número da loteria na ordem exata, o prêmio será de US$ 10.000.

2. Se todos os dígitos inseridos pelo usuário corresponderem a todos os dígitos do número da loteria, o prêmio será
US$ 3.000.

3. Se um dígito na entrada do usuário corresponder a um dígito no número da loteria, o prêmio será de US$ 1.000.

O programa na Listagem 3.7 usa um número inteiro para armazenar o número. A Listagem 4.8 fornece um novo
programa que gera uma string aleatória de dois dígitos em vez de um número e recebe a entrada do usuário como uma
string em vez de um número.

Listagem 4.8 LotteryUsingStrings.cpp


1 #include <iostream>
2 #include <string> // para usar strings
Machine Translated by Google

4.9 Estudo de Caso: Revisão do Programa de Loteria Usando Strings 159

3 #include <ctime> // para função de tempo


4 #include <cstdlib> // para funções Rand e srand
5 usando namespace std;

6 7 int principal()
8{
loteria de cordas;
9 srand(tempo(0));
10 dígito interno = rand()% 10; //Gera o primeiro digito gerar o primeiro dígito
11 loteria += static_cast<char>(dígito + '0'); dígito = rand()% 10; // concatenar com uma
12 Gera o segundo digito string gerar o segundo dígito
13 loteria += static_cast<char>(dígito + '0'); concatenar com uma string
14
15 // Solicita ao usuário para inserir uma estimativa
16 cout << "Insira sua escolha de loteria (dois dígitos): ";
17 suposição de string; digite um palpite
18 cin >> adivinha;
19
20 cout << "O número da loteria é " << loteria << endl;
21
22 // Verifica o palpite
23 if (adivinha == loteria) Combinação exata?

24 cout << "Correspondência exata: você ganha $ 10.000" << endl;


25 senão if (adivinha[1] == loteria[0] && adivinha[0] == loteria[1]) corresponde a todos os dígitos?

26 27 cout << "Combine todos os dígitos: você ganha $ 3.000" << endl;
28 senão if (adivinha[0] == loteria[0] || adivinha[0] == loteria[1] corresponde a um dígito?
29 || palpite[1] == loteria[0] || palpite[1] == loteria[1])
30 cout << "Combine um dígito: você ganha $ 1.000" << endl;
31 outro sem correspondência

32 cout << "Desculpe, não corresponde" << endl;


33
34 retornar 0;
35 }

Digite sua escolha de loteria (dois dígitos): 00


O número da loteria é 00
Correspondência exata: você ganha US$ 10.000

Insira sua escolha de loteria (dois dígitos): 45


O número da loteria é 54
Combine todos os dígitos: você ganha $ 3.000

Insira sua escolha de loteria: 23


O número da loteria é 34
Acerte um dígito: você ganha US$ 1.000

Insira sua escolha de loteria: 23


O número da loteria é 14
Desculpe, não corresponde
Machine Translated by Google

160 Capítulo 4 Funções matemáticas, caracteres e strings

O programa gera o primeiro dígito aleatório (linha 11), converte-o em um caractere e concatena o caractere na loteria
de strings (linha 12). O programa então gera o segundo dígito aleatório (linha 13), converte-o em um caractere e
concatena o caractere na loteria de strings
(linha 14). Depois disso, a loteria contém dois dígitos aleatórios.
O programa solicita que o usuário insira uma estimativa como uma sequência de dois dígitos (linha 19) e verifica o
adivinhe o número da loteria nesta ordem:

1. Primeiro, verifique se o palpite corresponde exatamente ao da loteria (linha 24).

2. Caso contrário, verifique se a reversão do palpite corresponde à loteria (linha 26).

3. Caso contrário, verifique se há um dígito na loteria (linhas 28–29).

4. Caso contrário, nada corresponde e exibe “Desculpe, não corresponde” (linhas 31–32).

4.10 Formatando a saída do console


Você pode usar os manipuladores de fluxo para exibir a saída formatada no console.
Chave
Apontar
Nota de vídeo Muitas vezes é desejável exibir números em um determinado formato. Por exemplo, o código a seguir calcula juros,
Formatar saída do console dado o valor e a taxa de juros anual.

valor duplo = 12.618,98;


taxa de juros dupla = 0,0013;
juros duplos = valor * taxa de juros;
cout << "O interesse é" << juros << endl;

Os juros são 16,4047

Como o valor dos juros é monetário, é desejável exibir apenas dois dígitos após a vírgula. Para fazer isso, você pode
escrever o código da seguinte maneira:

valor duplo = 12.618,98;


taxa de juros dupla = 0,0013;
juros duplos = valor * taxa de juros;
cout << "O interesse é"
<< static_cast<char>(juros * 100) / 100,0 << endl;

O interesse é 16,4

No entanto, o formato ainda não está correto. Deve haver dois dígitos após a vírgula decimal (ou seja, 16,40 em vez
de 16,4). Você pode consertar isso usando funções de formatação, como esta:

valor duplo = 12.618,98;


taxa de juros dupla = 0,0013;
juros duplos = valor * taxa de juros;
juros são " << juros << endl; << fixo << setprecision(2) cout << "Os

Os juros são 16,40

Você já sabe como exibir a saída do console usando o objeto cout . C++ fornece funções adicionais para formatar
como um valor é exibido. Essas funções são chamadas de fluxo
Machine Translated by Google

4.10 Formatando a Saída do Console 161

manipuladores e estão incluídos no arquivo de cabeçalho iomanip . A Tabela 4.8 resume vários manipuladores
de fluxo úteis. manipulador de fluxo

Tabela 4.8 Manipuladores de fluxo usados com frequência

Operador Descrição

definir precisão (n) define a precisão de um número de ponto flutuante

fixo exibe números de ponto flutuante em notação de ponto fixo

ponto de exibição faz com que um número de ponto flutuante seja exibido com um ponto decimal
com zeros à direita, mesmo que não tenha parte fracionária

definir(largura) especifica a largura de um campo de impressão

esquerda justifica a saída à esquerda

certo justifica a saída à direita

4.10.1 Manipulador setprecision(n)


Você pode especificar o número total de dígitos exibidos para um número de ponto flutuante usando o
manipulador setprecision(n) , onde n é o número de dígitos significativos (ou seja, o número total de dígitos
que aparecem antes e depois do ponto decimal). Se um número a ser exibido tiver mais dígitos do que a
precisão especificada, ele será arredondado. Por exemplo, o código

número duplo = 12,34567;


""
cout << setprecision(3) << número << << setprecision(4) <<
""
número << << setprecision(5) << número << <<
""
setprecision(6) << número << endl;

exibições

12,3 ÿ 12,35 ÿ 12,346 ÿ 12,3457

onde a caixa quadrada (ÿ) denota um espaço em branco.


O valor do número é exibido usando precisão 3, 4, 5 e 6, respectivamente. Usando a precisão 3, 12,34567
é arredondado para 12,3. Usando a precisão 4, 12,34567 é arredondado para 12,35.
Usando precisão 5, 12,34567 é arredondado para 12,346. Usando a precisão 6, 12,34567 é arredondado para
12,3457.
O manipulador setprecision permanece em vigor até que a precisão seja alterada. Então,

número duplo = 12,34567;


cout << setprecision(3) << número << " "; cout << 9.34567 << <<
"" ""
121.3457 << << 0,2367 << final;

exibições

12,3 ÿ 9,35 ÿ 121 ÿ 0,237

A precisão é definida como 3 para o primeiro valor e permanece válida para os próximos dois valores,
porque não foi alterada.
Se a largura não for suficiente para um número inteiro, o manipulador setprecision será ignorado. Por
exemplo,

cout << setprecision(3) << 23456 << endl;

exibições

23456
Machine Translated by Google

162 Capítulo 4 Funções matemáticas, caracteres e strings

4.10.2 Manipulador fixo


Às vezes, o computador exibe automaticamente um grande número de ponto flutuante em notação
científica. Na máquina Windows, por exemplo, a instrução

cout << 232123434,357;

exibições

2.32123e+08

Você pode usar o manipulador fixo para forçar o número a ser exibido em notação não científica com um
número fixo de dígitos após o ponto decimal. Por exemplo,

cout << fixo << 232123434.357;

exibições

232123434.357000

Por padrão, o número fixo de dígitos após o ponto decimal é 6. Você pode alterá-lo usando o
manipulador fixo junto com o manipulador setprecision . Quando usado após o manipulador fixo , o
manipulador setprecision especifica o número de dígitos após o ponto decimal. Por exemplo,

duplo pagamento mensal = 345,4567;


duplo totalPagamento = 78676,887234;
cout << fixo << setprecision(2)
<< pagamento mensal << final
<< pagamento total << endl;

exibições

345,46
78676,89

4.10.3 Manipulador de ponto de exibição


Por padrão, os números de ponto flutuante que não possuem parte fracionária não são exibidos com ponto
decimal. Você pode usar o manipulador fixo para forçar os números de ponto flutuante a serem exibidos
com um ponto decimal e um número fixo de dígitos após o ponto decimal.
Alternativamente, você pode usar o manipulador showpoint junto com o setprecision
manipulador.
Por exemplo,

cout << setprecision(6);


cout << 1,23 << endl;
cout << ponto de exibição << 1,23 << endl;
cout << ponto de exibição << 123,0 << endl;

exibições

1.23
1,23000
123.000

A função setprecision(6) define a precisão como 6. Portanto, o primeiro número 1,23 é exibido como
1,23. Como o manipulador showpoint força o número de ponto flutuante a ser
Machine Translated by Google

4.10 Formatando a Saída do Console 163

exibido com um ponto decimal e zeros à direita, se necessário para preencher as posições, o segundo
número 1,23 é exibido como 1,23000 com zeros à direita e o terceiro número 123,0 é exibido como 123.000
com um ponto decimal e zeros à direita.

4.10.4 Manipulador setw(largura)


Por padrão, cout usa apenas o número de posições necessárias para uma saída. Você pode usar
setw(width) para especificar o número mínimo de colunas para uma saída. Por exemplo,

cout << setw(8) << "C++" << setw(6) << 101 << endl;
cout << setw(8) << "Java" << setw(6) << 101 << endl;
cout << setw(8) << "HTML" << setw(6) << 101 << endl;

exibições

86
C++ 101
Java 101
HTML 101

A saída é justificada à direita nas colunas especificadas. Na linha 1, setw(8) especifica que "C+
+" é exibido em oito colunas. Portanto, existem cinco espaços antes do C++. setw(6) especifica
que 101 é exibido em seis colunas. Portanto, existem três espaços antes de 101.
Observe que o manipulador setw afeta apenas a próxima saída. Por exemplo,

cout << setw(8) << "C++" << 101 << endl;

exibições

ÿÿÿÿÿC++101

O manipulador setw(8) afeta apenas a próxima saída "C++", não 101.


Observe que o argumento n para setw(n) e setprecision(n) pode ser uma variável inteira,
expressão ou constante.
Se um item exigir mais espaços do que a largura especificada, a largura será automaticamente
aumentou. Por exemplo, o seguinte código

cout << setw(8) << "Programação" << "#" << setw(2) << 101;

exibições

Programação#101

A largura especificada para Programação é 8, que é menor que seu tamanho real 11. A largura é
automaticamente aumentada para 11. A largura especificada para 101 é 2, que é menor que seu tamanho
real 3. A largura é automaticamente aumentada para 3.

4.10.5 Manipuladores esquerdo e direito


Observe que o manipulador setw usa justificação à direita por padrão. Você pode usar a esquerda
manipulador para justificar a saída à esquerda e use o manipulador direito para justificar a saída à direita.
Por exemplo,

cout << certo; cout <<


setw(8) << 1,23 << endl;
cout << setw(8) << 351,34 << endl;
Machine Translated by Google

164 Capítulo 4 Funções matemáticas, caracteres e strings

exibições

ÿÿÿÿ1.23
ÿÿ351.34

cout << esquerda;


cout << setw(8) << 1,23; cout
<< setw(8) << 351,34 << endl;

exibições

1.23ÿÿÿÿ351.34ÿÿ

4.22 Para usar manipuladores de fluxo, qual arquivo de cabeçalho você deve incluir?
ÿVerificação de ponto
4.23 Mostre a saída das seguintes afirmações.

cout << setw(10) << "C++" << setw(6) << 101 << endl; cout << setw(8)
<< "Java" << setw(5) << 101 << endl; cout << setw(6) << "HTML" <<
setw(4) << 101 << endl;

4.24 Mostre a saída das seguintes declarações:

número duplo = 93123,1234567; cout


<< setw(10) << setprecision(5) << número; cout << setw(10)
<< setprecision(4) << número; cout << setw(10) <<
setprecision(3) << número; cout << setw(10) << setprecision(8)
<< número;

4.25 Mostre a saída das seguintes declarações:

duplo pagamento mensal = 1345,4567; duplo


totalPagamento = 866,887234;

cout << setprecision(7); cout <<


pagamento mensal << endl; cout <<
pagamento total << endl;

cout << fixo << setprecision(2); cout <<


setw(8) << pagamento mensal << endl; cout << setw(8)
<< pagamento total << endl;

4.26 Mostre a saída das seguintes declarações:

cout << certo; cout


<< setw(6) << 21.23 << endl; cout << setw(6)
<< 51,34 << endl;

4.27 Mostre a saída das seguintes afirmações.

cout << esquerda;


cout << setw(6) << 21.23 << endl; cout <<
setw(6) << 51,34 << endl;

4.11 Entrada e saída simples de arquivos


Você pode salvar dados em um arquivo e ler os dados do arquivo posteriormente.
Chave
Apontar
Você usou o cin para ler a entrada do teclado e o cout para escrever a saída no console.
manipulador de fluxo Você também pode ler/gravar dados de/para um arquivo. Esta seção apresenta entrada e saída simples de
arquivos. A cobertura detalhada da entrada e saída de arquivos será apresentada no Capítulo 13.
Machine Translated by Google

4.11 Entrada e Saída Simples de Arquivo 165

4.11.1 Gravando em um arquivo


Para gravar dados em um arquivo, primeiro declare uma variável do tipo ofstream :

saída a jusante;

Para especificar um arquivo, invoque a função open do objeto de saída da seguinte forma:

saída.open("números.txt");

Esta instrução cria um arquivo chamado numbers.txt. Se este arquivo já existir, o conteúdo será destruído e um novo
arquivo será criado. Invocar a função open é associar um arquivo ao fluxo. No Capítulo 13, você aprenderá como verificar
se um arquivo existe antes de criá-lo.

Opcionalmente, você pode criar um objeto de saída de arquivo e abrir o arquivo em uma instrução como esta:

saída ofstream ("números.txt");

Para gravar dados, use o operador de inserção de fluxo (<<) da mesma forma que você envia dados para
o objeto cout . Por exemplo,

"" ""
saída << 95 << << 56 << << 34 << fim;

Esta instrução grava os números 95, 56 e 34 no arquivo. Os números são espaços separados,
conforme mostrado na Figura 4.3.

"" ""
saída << 95 << << 56 << << 34 << fim;

arquivo
95 56 34
pontuações.txt

Figura 4.3 O fluxo de saída envia dados para o arquivo.

Depois de terminar o arquivo, invoque a função close da saída da seguinte maneira:

saída.close();

Invocar a função close é necessário para garantir que os dados sejam gravados no arquivo antes do encerramento do
programa.
A Listagem 4.9 fornece o programa completo para gravação de dados em um arquivo.

Listagem 4.9 SimpleFileOutput.cpp


1 #include <iostream>
2 #include <fstream> incluir cabeçalho fstream
3 usando namespace std;

4 5 int principal()
6{
saída a jusante; declarar saída

7 //Cria um arquivo
8 saída.open("números.txt"); abrir arquivo
9
10 //Escreve números
"" ""
11 saída << 95 << << 56 << << 34; saída para arquivo
12
13 //fecha o arquivo
14 15 16 saída.close(); fechar arquivo
Machine Translated by Google

166 Capítulo 4 Funções matemáticas, caracteres e strings


17
18 cout << "Concluído" << endl;
19
20 retornar 0;
21}

incluindo cabeçalho <fstream> Como ofstream é definido no arquivo de cabeçalho fstream , a linha 2 inclui esse arquivo de cabeçalho.

4.11.2 Lendo de um arquivo


Para ler dados de um arquivo, primeiro declare uma variável do tipo ifstream :

entrada ifstream;

Para especificar um arquivo, invoque a função open da entrada da seguinte maneira:

input.open("números.txt");

Esta instrução abre um arquivo chamado numbers.txt para entrada. Se um arquivo que você tenta abrir não existir,
poderá surgir um erro inesperado. No Capítulo 13, você aprenderá como verificar se um arquivo existe ao abrir um
arquivo para entrada.
Opcionalmente, você pode criar um objeto de entrada de arquivo e abrir o arquivo em uma instrução como esta:

ifstream input("números.txt");

Para ler dados, use o operador de extração de fluxo (>>) da mesma forma que você lê dados do objeto cin . Por
exemplo,

entrada << pontuação1;


entrada << pontuação2;
entrada << pontuação3;

ou

entrada << pontuação1 << pontuação2 << pontuação3;

Essas declarações lêem três números do arquivo nas variáveis pontuação1, pontuação2 e pontuação3, conforme
mostrado na Figura 4.4.

entrada >> pontuação1; entrada >> pontuação2; entrada >> pontuação3;

arquivo
95 56 34
pontuações.txt

Figura 4.4 O fluxo de entrada lê dados do arquivo.

Depois de terminar o arquivo, invoque a função close da entrada da seguinte maneira:

input.close();

A Listagem 4.10 fornece o programa completo para gravar dados em um arquivo:

Listagem 4.10 SimpleFileInput.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 usando namespace std;

4 5 int principal()
Machine Translated by Google

Resumo do Capítulo 167

6{
entrada ifstream; declarar saída

//Abre um arquivo
7 input.open("números.txt"); abrir arquivo
8
9 int pontuação1, pontuação2, pontuação3;
10
11 //Lê os dados
12 entrada >> pontuação1; entrada do arquivo
13 entrada >> pontuação2;
14 entrada >> pontuação3;
15
16 cout << "A pontuação total é " << pontuação1 + pontuação2 + pontuação3 << endl;
17
18 //Fecha o arquivo
19 input.close(); fechar arquivo

20
21 cout << "Concluído" << endl;
22
23 retornar 0;
24 25 26 27 }

A pontuação total é 185


Feito

Como ifstream é definido no arquivo de cabeçalho fstream , a linha 2 inclui esse arquivo de cabeçalho. Você pode incluindo cabeçalho <fstream>
simplificar as declarações nas linhas 15–17 usando a seguinte declaração:

entrada >> pontuação1 >> pontuação2 >> pontuação3;

4.28 Como criar um objeto para leitura de dados do arquivo test.txt? Como você cria
um objeto para gravar dados no arquivo test.txt? ÿVerificação de ponto

4.29 Você pode substituir as instruções nas linhas 7 a 10 da Listagem 4.10 por uma instrução?

4.30 O que acontece se o arquivo já existir quando você abre um arquivo para saída?

Termos chave
Código ASCII 142 sequência de escape 145
tipo de caractere 142 função de instância 155
string vazia 155 operador de subscrito 155
codificação 142 caractere de espaço em branco 143
caractere de escape 143

Resumo do capítulo

1. C++ fornece as funções matemáticas sin, cos, tan, asin, acos, for, exp,
log, log10, pow, sqrt, cell, floor, min, max e abs para executar funções
matemáticas.

2. O tipo de caractere (char) representa um único caractere.


Machine Translated by Google

168 Capítulo 4 Funções matemáticas, caracteres e strings

3. O caractere \ é um caractere de escape e uma sequência de escape começa com o caractere de escape
caractere seguido por outro caractere ou uma combinação de dígitos.

4. C++ permite que você use sequências de escape para representar caracteres especiais como '\t'
e n'.

'
5. Os personagens dos ', '\t', '\f', '\r' e '\n' são conhecidos como espaços em branco
personagens.

6. C++ fornece as funções isdigit, isalpha, isalnum, islower, isupper, isspace


para testar se um caractere é um dígito, letra, dígito ou letra, minúscula,
maiúscula e espaço em branco. Ele também contém as funções tolower e
topper para retornar uma letra minúscula ou maiúscula.

7. Uma string é uma sequência de caracteres. Um valor de string é colocado entre aspas duplas
correspondentes ('). Um valor de caractere é colocado entre aspas simples correspondentes (').

8. Você pode declarar um objeto string usando o tipo string . Uma função que é invocada de
um objeto específico é chamado de função de instância.

9. Você pode obter o comprimento de uma string invocando sua função length() e recuperar um
caractere no índice especificado na string usando at(index).

10. Você pode usar o operador subscrito para recuperar ou modificar o caractere em uma string e pode
use o operador + para concatenar duas strings.

11. Você pode usar operadores relacionais para comparar duas strings.

12. Você pode formatar a saída usando manipuladores de fluxo definidos no cabeçalho iomanip .

13. Você pode criar um objeto ifstream para ler dados de um arquivo e um ofstream
objeto para gravar dados em um arquivo.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seção 4.2
4.1 (Geometria: área de um pentágono) Escreva um programa que solicite ao usuário que insira o
comprimento do centro de um pentágono até um vértice e calcule a área do pentágono, conforme
mostrado na figura a seguir.

R
Machine Translated by Google

Exercícios de Programação 169

5* s 2
A fórmula para calcular a área de um pentágono é Área = onde

4 * bronzeado¢5p ÿ,
p
s é o comprimento de um lado. O lado pode ser calculado usando a fórmula s = 2r sin ,
5
onde r é o comprimento do centro de um pentágono até um vértice. Arredonde dois dígitos
após a vírgula. Aqui está um exemplo de execução:

Insira o comprimento do centro até um vértice: 5,5 A área do pentágono


é 71,92

*4.2 (Geometria: distância do círculo máximo) A distância do círculo máximo é a distância


entre dois pontos na superfície de uma esfera. Sejam (x1, y1) e (x2, y2) a latitude
Nota de vídeo
e longitude geográfica de dois pontos. A distância do grande círculo entre os dois Grande distância do círculo
pontos pode ser calculada usando a seguinte fórmula:
d = raio * arcos(sin(x1) * sin(x2) + cos(x1) * cos(x2) * cos(y1 - y2))

Escreva um programa que solicite ao usuário que insira a latitude e a longitude de


dois pontos na Terra em graus e exiba a distância do grande círculo. O raio médio da
Terra é 6.378,1 km. Os graus de latitude e longitude na fórmula são para norte e
oeste. Use negativo para indicar os graus sul e leste. Aqui está um exemplo de execução:

Insira o ponto 1 (latitude e longitude) em graus: 39,55, -116,25 Insira o


ponto 2 (latitude e
longitude) em graus: 41,5, 87,37 A distância entre os dois pontos é

10691,79183231593 km

*4.3 (Calculando ângulos de triângulos) A Listagem 4.1, ComputeAngles.cpp, solicita que


o usuário insira as coordenadas xey dos três pontos de canto em um triângulo e,
em seguida, exibe os ângulos do triângulo. Escreva um programa que solicite ao
usuário que insira as coordenadas x e y de três vértices de dois triângulos, exiba
os ângulos do triângulo e compare se os ângulos de ambos os triângulos são
iguais.
4.4 (Geometria: área de um hexágono) A área de um hexágono pode ser calculada usando o
seguinte fórmula (s é o comprimento de um lado):

6* s 2
Área =

4 * bronzeado¢6p ÿ

Escreva um programa que solicite ao usuário que insira o lado de um hexágono e exiba sua área.
Aqui está um exemplo de execução:

Entre no lado: 5,5


A área do hexágono é 78,59
Machine Translated by Google

170 Capítulo 4 Funções matemáticas, caracteres e strings


*4.5 (Geometria: área de um polígono regular) Um polígono regular é um polígono de n lados
no qual todos os lados têm o mesmo comprimento e todos os ângulos têm o mesmo
grau (ou seja, o polígono é equilátero e equiângulo). A fórmula para calcular a área
de um polígono regular é

n*s 2
Área =

4 * bronzeado¢np ÿ

Aqui, s é o comprimento de um lado. Escreva um programa que solicite ao usuário que insira o
número de lados e seu comprimento de um polígono regular e exiba sua área. Aqui está um
exemplo de execução:

Insira o número de lados: 5 Insira o


lado: 6,5 A área do
polígono é 72,69

*4.6 (Ponto aleatório em um círculo) Escreva um programa que gere três pontos aleatórios
em um círculo centrado em (0, 0) com raio 40 e exiba três ângulos em um triângulo
formado por esses três pontos, como mostrado na Figura 4.5a. (Dica: gere um
ângulo aleatório a em radianos entre 0 e 2p, como mostrado na Figura 4.5b e o
ponto determinado por este ângulo é (r*cos(a), r*sin(a)).)

x = r × cos(ÿ) e y = r × sin(ÿ) posição 0 horas

60
R

65 R
a

(0, 0)
55

(a) (b) (c)

Figura 4.5 (a) Um triângulo é formado por três pontos aleatórios do círculo. (b) Um ponto
aleatório no círculo pode ser gerado usando um ângulo aleatório a. (c) Um pentágono está
centrado em (0, 0) com um ponto na posição 0 horas.

*4.7 (Coordenadas do ponto de canto) Suponha que um pentágono esteja centrado em (0, 0) com
um ponto na posição 0 horas, como mostrado na Figura 4.5c. Escreva um programa que
solicite ao usuário que insira o raio do círculo delimitador de um pentágono e exiba as
coordenadas dos cinco pontos de vértice do pentágono. Aqui está um exemplo de execução:

Insira o raio do círculo delimitador: 100 As coordenadas de


cinco pontos no pentágono são
(95.1057, 30.9017)
(0,000132679, 100)
(-95.1056, 30.9019)
(-58,7788, -80,9015)
(58,7782, -80,902)
Machine Translated by Google

Exercícios de Programação 171

Seções 4.3–4.7
*4.8 (Encontre o caractere de um código ASCII) Escreva um programa que receba um código ASCII (um número inteiro
entre 0 e 127) e exiba seu caractere. Aqui está uma amostra
correr:

Digite um código ASCII: 69


O personagem é E

*4.9 (Encontre o código ASCII de um caractere) Escreva um programa que receba um caractere e
exibe seu código ASCII. Aqui está um exemplo de execução:

Digite um caractere: E
O código ASCII para o caractere é 69

*4.10 (Vogal ou consoante?) Suponha que as letras A/a, E/e, I/i, O/o e U/u sejam vogais.
Escreva um programa que solicite ao usuário que digite uma letra e verifique se a letra é uma vogal ou
consoante. Aqui está um exemplo de execução:

Digite uma letra: B


B é uma consoante

Insira uma nota de letra: aa é uma


vogal

Insira uma nota por letra: # # é uma


entrada inválida

*4.11 (Converter uma letra maiúscula em minúscula) Escreva um programa que solicite ao usuário que digite uma letra
maiúscula e a converta em uma letra minúscula. Aqui está um exemplo de execução:

Digite uma letra maiúscula: T


A letra minúscula é t

*4.12 (Converter nota de letra em número) Escreva um programa que solicite ao usuário que insira uma nota de letra A/
a, B/b, C/c, D/d ou F/f e exiba seu valor numérico correspondente 4, 3 , 2, 1 ou 0. Aqui está um exemplo
de execução:

Insira uma nota de letra: B


O valor numérico para a nota B é 3

Insira uma nota de letra: b


O valor numérico para a nota b é 3

Insira uma nota de letra: T


T é uma nota inválida

4.13 (Hex para binário) Escreva um programa que solicite ao usuário que insira um dígito hexadecimal e
exibe seu número binário correspondente. Aqui está um exemplo de execução:
Machine Translated by Google

172 Capítulo 4 Funções matemáticas, caracteres e strings

Insira um dígito hexadecimal: B


O valor binário é 1011

Insira um dígito hexadecimal: G


G é uma entrada inválida

*4.14 (Decimal para hexadecimal) Escreva um programa que solicita ao usuário que insira um número inteiro
entre 0 e 15 e exibe seu número hexadecimal correspondente. Aqui estão alguns exemplos de execução:

Insira um valor decimal (0 a 15): 11


O valor hexadecimal é B

Insira um valor decimal (0 a 15): 5


O valor hexadecimal é 5

Insira um valor decimal (0 a 15): 31 31 é uma entrada


inválida

*4.15 (Teclados do telefone) O mapeamento padrão internacional de letras/números encontrado no telefone é


mostrado abaixo:

Escreva um programa que solicite ao usuário que digite uma letra e exiba o número correspondente.

Digite uma letra: A


O número correspondente é 2

Digite uma letra: a


O número correspondente é 2

Digite uma letra: + + é uma


entrada inválida

Seções 4.8–4.11
4.16 (Processar duas strings) Escreva um programa que solicite ao usuário que insira duas strings e exiba o
comprimento e o último caractere de cada uma.
Machine Translated by Google

Exercícios de Programação 173

4.17 (String do palíndromo) Escreva um programa que solicite ao usuário que insira uma string com
cinco letras minúsculas e determina se é um palíndromo.

*4.18 (Sequências aleatórias) Escreva um programa que gere uma string aleatória com seis caracteres
letras maiúsculas.

*4.19 (Ordenar três cidades) Escreva um programa que solicite ao usuário que insira três cidades e as exiba em
ordem crescente. Aqui está um exemplo de execução:

Digite a primeira cidade: Chicago


Digite a segunda cidade: Los Angeles
Digite a terceira cidade: Atlanta
As três cidades em ordem alfabética são Atlanta Chicago Los Angeles

*4.20 (Dias do mês) Escreva um programa que solicite ao usuário que insira o ano e as três primeiras letras do nome
do mês (com a primeira letra maiúscula) e exiba o número de dias do mês. Aqui está um exemplo de
execução:

Digite um ano: 2001


Digite um mês: janeiro
Janeiro de 2001 tem 31 dias

Digite um ano: 2001


Insira um mês: jan jan não é
o nome correto do mês

*4.21 (Graduação e status do aluno) Escreva um programa que solicite ao usuário que insira dois caracteres e exiba
a especialização e o status representados nos caracteres. O primeiro caractere indica a especialização
e o segundo é o caractere numérico 1, 2, 3, 4, que indica se o aluno é calouro, segundo ano, júnior ou
sênior. Suponha que os seguintes caracteres sejam usados para denotar as especialidades:

M: Matemática
C: Ciência da Computação
I: Tecnologia da Informação

Aqui está um exemplo de execução:

Insira dois caracteres: M1


Calouro em Matemática

Insira dois caracteres: C3


Ciência da Computação Júnior

Insira dois caracteres: T3


Código principal inválido

Digite dois caracteres: M7


Código de status inválido
Machine Translated by Google

174 Capítulo 4 Funções matemáticas, caracteres e strings

*4.22 (Aplicação financeira: folha de pagamento) Escreva um programa que leia as seguintes informações e imprima um extrato da folha de

pagamento: Nome do funcionário (por exemplo, Smith)

Número de horas trabalhadas em uma semana (por exemplo, 10)

Taxa de pagamento por hora (por exemplo, 9,75)

Taxa de retenção de imposto federal (por exemplo, 20%)

Taxa de retenção de imposto estadual (por exemplo, 9%)

Um exemplo de execução é mostrado abaixo:

Insira o nome do funcionário: Smith Insira o


número de horas trabalhadas em uma semana: 10 Insira a taxa de
pagamento por hora: 9,75 Insira a taxa de
retenção de imposto federal: 0,20 Insira a taxa de retenção de
imposto estadual: 0,09

Nome do funcionário: Smith


Horas trabalhadas: 10,0
Taxa de pagamento: $ 9,75
Salário bruto: $ 97,50
Deduções:
Retenção Federal (20,0%): $ 19,5
Retenção estatal (9,0%): $ 8,77
Dedução total: $ 28,27
Salário líquido: $ 69,22

*4.23 (Verificar SSN) Escreva um programa que solicite ao usuário que insira um número de Seguro Social no formato ddd-dd-dddd, onde d é um

dígito. Aqui estão exemplos de execuções:

Insira um SSN: 232-23-5435


232-23-5435 é um número de segurança social válido

Insira um SSN: 23-23-5435


23-23-5435 é um número de segurança social inválido
Machine Translated by Google

CAPÍTULO

5
rotações

Objetivos
n Para escrever programas que executam instruções repetidamente usando um while
laço (§5.2).

n Seguir a estratégia de concepção de circuitos para desenvolver circuitos (§§5.2.1–5.2.3).

n Para controlar um loop com a confirmação do usuário (§5.2.4).

n Para controlar um loop com valor sentinela (§5.2.5).

n Para obter entrada de um arquivo usando redirecionamento de entrada em vez de digitar


no teclado (§5.2.6).

n Para ler todos os dados de um arquivo (§5.2.7).

n Para escrever loops usando instruções do-while (§5.3).

n Para escrever loops usando instruções for (§5.4).

n Descobrir as semelhanças e diferenças entre três tipos de loop


declarações (§5.5).

n Para escrever loops aninhados (§5.6).

n Aprender as técnicas de minimização de erros numéricos (§5.7).

n Para aprender loops a partir de uma variedade de exemplos (GCD,


FutureTuition, MonteCarloSimulation, Dec2Hex) (§5.8).

n Implementar o controle do programa com pausa e continuação (§5.9).

n Escrever um programa que teste palíndromos (§5.10).

n Para escrever um programa que exiba números primos (§5.11).


Machine Translated by Google

176 Capítulo 5 Loops

5.1 Introdução
Um loop pode ser usado para instruir um programa a executar instruções repetidamente.
Chave
Apontar
problema Suponha que você precise exibir uma string (por exemplo, "Bem-vindo ao C++!") 100 vezes. Seria tedioso escrever
as seguintes afirmações 100 vezes:

cout << "Bem-vindo ao C++!\n";


100 vezes cout << "Bem-vindo ao C++!\n";
...
cout << "Bem-vindo ao C++!\n";

por que fazer loop? Então, como você resolve esse problema?
C++ fornece uma construção poderosa chamada loop que controla quantas vezes uma operação ou sequência
de operações é executada em sucessão. Usando uma instrução de loop, você simplesmente diz ao computador para
exibir uma string 100 vezes sem precisar codificar a instrução print 100 vezes, como segue:

contagem interna = 0;
enquanto (contagem < 100)
{
cout << "Bem-vindo ao C++!\n";
contar++;
}

A variável count é inicialmente 0. O loop verifica se (count <100) é verdadeiro. Nesse caso, ele executa o corpo do
loop para exibir a mensagem Bem-vindo ao C++! e contagem de incrementos
por 1. Ele executa repetidamente o corpo do loop até (contagem <100) se tornar falso (ou seja, quando a contagem
atingir 100). Neste ponto, o loop termina e a próxima instrução após a instrução do loop é executada.

Loops são construções que controlam execuções repetidas de um bloco de instruções. O conceito de looping é
fundamental para a programação. C++ fornece três tipos de instruções de loop: loops while , loops do-while e loops
for .

5.2 O loop while


Um loop while executa instruções repetidamente enquanto a condição é verdadeira.
Chave
Apontar
A sintaxe do loop while é

enquanto loop while (condição de continuação do loop) {

//Corpo do loop
Declarações);
}

A Figura 5.1a mostra o fluxograma do loop while. A parte do loop que contém as instruções a serem repetidas é
corpo do laço chamada de corpo do loop. Uma execução única de um corpo de loop é chamada de iteração (ou repetição) do loop.
iteração Cada loop contém uma condição de continuação do loop, uma expressão booleana que controla a execução do
condição de continuação do loop corpo. Ele é avaliado a cada vez para determinar se o corpo do loop é executado. Se sua avaliação for verdadeira,
o corpo do loop é executado; se sua avaliação for falsa, todo o loop termina e o controle do programa passa para a
instrução que segue o loop while .

O loop para exibir Welcome to C++! 100 vezes introduzido na seção anterior é um exemplo de loop while . Seu
fluxograma é mostrado na Figura 5.1b. A condição de continuação do loop é contagem <100 e o corpo do loop
contém as duas instruções a seguir:
Machine Translated by Google

5.2 O loop while 177

contagem = 0;

falso falso
Condição de (contagem <100)?
continuação do loop?

verdadeiro verdadeiro

Declarações) cout << "Bem-vindo ao C++!\n";


(corpo em loop) contar++;

(a) (b)

Figura 5.1 O loop while executa repetidamente as instruções no corpo do loop quando a condição de
continuação do loop é avaliada como verdadeira.

condição de continuação do loop


contagem interna = 0;
enquanto (contagem < 100)
{
cout << "Bem-vindo ao C++!\n"; corpo do laço
contar++; f
}

Neste exemplo, você sabe exatamente quantas vezes o corpo do loop precisa ser executado porque a variável
de controle count é usada para contar o número de execuções. Este tipo de loop é conhecido como loop controlado
por contador. circuito controlado por contador

Observação

A condição de continuação do loop deve sempre aparecer entre parênteses.


As chaves que envolvem o corpo do loop podem ser omitidas somente se o corpo do loop contiver uma
instrução ou nenhuma.

Aqui está outro exemplo para ajudar a entender como funciona um loop:

soma interna = 0, i = 1;
enquanto (eu <
10) {
soma = soma + i;
eu++;
}

cout << "soma é" << soma; // a soma é 45

Se i <10 for verdadeiro, o programa adiciona i à soma. A variável i é inicialmente definida como 1, depois é
incrementada para 2, 3 e até 10. Quando i é 10, i < 10 é falso, então o loop termina. Portanto, a soma é 1 + 2 + 3
+… + 9 = 45.
O que acontece se o loop for escrito incorretamente da seguinte maneira?

soma interna = 0, i = 1;
enquanto (eu <
10) {
soma = soma + i;
}

Este loop é infinito, porque i é sempre 1 e i <10 sempre será verdadeiro.


Machine Translated by Google

178 Capítulo 5 Loops

Observação

Certifique-se de que a condição de continuação do loop eventualmente se torne falsa


Loop infinito para que o loop termine. Um erro comum de programação envolve loops infinitos
(ou seja, o loop é executado para sempre). Se o seu programa demorar muito para ser executado e
não parar, ele poderá ter um loop infinito. Se você estiver executando o programa na janela de
comando, pressione CTRL+C para interrompê-lo.

Cuidado
Os programadores muitas vezes cometem o erro de executar um loop mais ou menos uma vez. Isso
erro off-by-one é comumente conhecido como erro off-by-one. Por exemplo, o loop a seguir exibe Welcome to C++
101 vezes em vez de 100 vezes. O erro está na condição, que deveria ser contagem <100 em vez
de contagem <= 100.

contagem interna = 0;
enquanto (contagem <= 100)
{
cout << "Bem-vindo ao C++!\n";
contar++;
}

Lembre-se de que a Listagem 3.4 SubtractionQuiz.cpp fornece um programa que solicita ao usuário que digite uma
resposta para uma pergunta sobre subtração. Usando um loop, você pode reescrever o programa para permitir que o
Nota de vídeo
usuário insira uma nova resposta até que ela esteja correta, conforme mostrado na Listagem 5.1.
Repita o teste de subtração

Listagem 5.1 RepeatSubtractionQuiz.cpp


1 #include <iostream>
incluir ctime 2 #include <ctime> // para função de tempo
incluir cstdlib 3 #include <cstdlib> // para funções Rand e srand
4 usando namespace std;

5 6 int principal()
7{
// 1. Gere dois números inteiros aleatórios de um dígito
definir uma semente srand(tempo(0));
gerar número1 8 int número1 = rand()% 10;
gerar número2 9 int número2 = rand()% 10;
10
11 // 2. Se número1 < número2, troque número1 por número2
12 se (número1 <número2)
13 {
trocar números 14 temperatura interna = número1;
15 número1 = número2;
16 número2 = temperatura;
17 }
18
19 // 3. Peça ao aluno que responda "O que é número1 - número2"
" - "
20 cout << "O que é" int << número1 << << número2 << "? ";
21 resposta;
digite a resposta 22 cin >> resposta;
23
24 // 4. Faça a pergunta repetidamente ao usuário até que ela esteja correta
verifique a resposta 25 while (número1 - número2! = resposta) {
26
27 cout << "Resposta errada. Tente novamente. O que é "
" - "
28 << número1 << << número2 << "? ";
digite a resposta 29 cin >> resposta;
30 }
31 32 33
Machine Translated by Google

5.2 O loop while 179

cout << "Você acertou!" << fim;

retornar 0;
34 35 36 37}

Quanto é 4 - 3? 4
Resposta errada. Tente novamente. Quanto é 4 - 3? 5
Resposta errada. Tente novamente. Quanto é 4 - 3? 1
Você conseguiu!

O loop nas linhas 27–32 solicita repetidamente ao usuário que insira uma resposta quando número1 -
número2 != resposta for verdadeiro. Quando number1 - number2 != answer for falso, o loop será encerrado.

5.2.1 Estudo de Caso: Adivinhando Números


O problema é adivinhar qual número um computador tem em mente. Você escreverá um programa que gera Nota de vídeo
aleatoriamente um número inteiro entre 0 e 100, inclusive. O programa solicita que o usuário insira um número Adivinhe um número

continuamente até que o número corresponda ao número gerado aleatoriamente. Para cada entrada do usuário,
o programa informa ao usuário se a entrada é muito baixa ou muito alta, para que o usuário possa fazer a
próxima estimativa de forma inteligente. Aqui está um exemplo de execução:

Adivinhe um número mágico entre 0 e 100

Digite seu palpite: 50


Seu palpite é muito alto

Digite seu palpite: 25


Seu palpite é muito baixo

Digite seu palpite: 42


Seu palpite é muito alto

Digite seu palpite: 39


Sim, o número é 39

O número mágico está entre 0 e 100. Para minimizar o número de palpites, primeiro digite 50. Se o seu palpite inteligente
palpite for muito alto, o número mágico estará entre 0 e 49. Se o seu palpite for muito baixo, o número mágico
estará entre 51 e 100. Portanto, você pode eliminar metade dos números da consideração após uma tentativa.

Como você escreve este programa? Você começa a codificar imediatamente? Não. É importante pensar
antes de codificar. Pense em como você resolveria o problema sem escrever um programa. Primeiro você pense antes de codificar
precisa gerar um número aleatório entre 0 e 100, inclusive, depois solicitar ao usuário que insira uma estimativa
e, em seguida, comparar a estimativa com o número aleatório.
É uma boa prática codificar de forma incremental, uma etapa de cada vez. Para programas que envolvem código incrementalmente
loops, se você não sabe como escrever um loop, você pode primeiro escrever o código para executar o loop
uma vez e depois descobrir como executar o código repetidamente em um loop. Para este programa, você
pode criar um rascunho inicial, conforme mostrado na Listagem 5.2.

Listagem 5.2 GuessNumberOneTime.cpp


1 #include <iostream>
2 #incluir <cstdlib>
Machine Translated by Google

180 Capítulo 5 Loops


3 #include <ctime> // Necessário para a função time
4 usando namespace std;

5 6 int principal()
7{
//Gera um número aleatório para ser adivinhado
8 9 srand(tempo(0));
gerar um número 10 número interno = rand()% 101;
11
12 cout << "Adivinhe um número mágico entre 0 e 100";
13
14 // Solicita ao usuário que adivinhe o número
15 cout << "\nDigite seu palpite: ";
16 int adivinha;
digite um palpite 17 cin >> adivinha;
18
palpite correto? 19 if (adivinhar == número)
20 cout << "Sim, o número é " << número << endl;
muito alto? 21 else if (adivinhar > número)
22 cout << "Seu palpite é muito alto" << endl;
muito baixo? 23 outro
24 cout << "Seu palpite é muito baixo" << endl;
25
26 retornar 0;
27 }

Quando você executa este programa, ele solicita que o usuário insira uma estimativa. Para permitir que o usuário insira um
Se você adivinhar repetidamente, você pode colocar o código nas linhas 15–24 em um loop da seguinte forma:

enquanto (verdadeiro)
{
// Solicita ao usuário que adivinhe o número
cout << "\nDigite seu palpite: ";
cin >> adivinha;

if (adivinhar == número)
cout << "Sim, o número é " else if << número << fim;
(adivinhar > número)
cout << "Seu palpite é muito alto" << endl;
outro
cout << "Seu palpite é muito baixo" << endl;
} // Fim do loop

Este loop solicita repetidamente que o usuário insira uma estimativa. No entanto, esse loop está incorreto porque nunca termina.
Quando a estimativa corresponde ao número, o loop deve terminar. Portanto, o loop pode ser revisado da seguinte forma:

while (adivinhe! = número)


{
// Solicita ao usuário que adivinhe o número
cout << "\nDigite seu palpite: ";
cin >> adivinha;

if (adivinhar == número)
cout << "Sim, o número é " else if << número << fim;
(adivinhar > número)
cout << "Seu palpite é muito alto" << endl;
outro
cout << "Seu palpite é muito baixo" << endl;
} // Fim do loop

O código completo é fornecido na Listagem 5.3.


Machine Translated by Google

5.2 O loop while 181

Listagem 5.3 GuessNumber.cpp


1 #include <iostream>
2 #incluir <cstdlib>
3 #include <ctime> // Necessário para a função time
4 usando namespace std;

5 6 int principal()
7{
8 //Gera um número aleatório para ser adivinhado
srand(tempo(0));
9 10 número interno = rand()% 101; gerar um número
11
12 cout << "Adivinhe um número mágico entre 0 e 100";
13
14 int estimativa = -1;
15 while (adivinhe! = número)
16 {
17 // Solicita ao usuário que adivinhe o número
18 cout << "\nDigite seu palpite: ";
19 cin >> adivinha; digite um palpite
20
21 if (adivinhar == número) palpite correto?
22 cout << "Sim, o número é " << número << endl;
23 else if (adivinhar > número) muito alto?
24 cout << "Seu palpite é muito alto" << endl;
25 outro muito baixo?
26 cout << "Seu palpite é muito baixo" << endl;
27 } // Fim do loop
28
29 retornar 0;
30 }

Número da linha adivinhar saída

10 39

14 —1

19 50
iteração 1
24 Seu palpite é muito alto

19 25
iteração 2
26 Seu palpite é muito baixo

19 12
iteração 3
24 Seu palpite é muito alto

19 39
iteração 4
22 Sim, o número é 39

O programa gera o número mágico na linha 10 e solicita ao usuário que digite uma estimativa
repetidamente em um loop (linhas 15 a 27). Para cada estimativa, o programa verifica se está correto,
muito alto ou muito baixo (linhas 21–26). Quando a estimativa estiver correta, o programa sai do loop
(linha 15). Observe que a estimativa é inicializada com -1. Inicializá-lo com um valor entre 0 e 100
seria errado, pois esse poderia ser o número adivinhado.
Machine Translated by Google

182 Capítulo 5 Loops

5.2.2 Estratégias de Design de Loop


Escrever um loop correto não é uma tarefa fácil para programadores novatos. Considere três etapas ao escrever um loop.

Passo 1: Identifique as afirmações que precisam ser repetidas.

Etapa 2: envolva essas instruções em um loop da seguinte maneira:

enquanto (verdadeiro)
{
Declarações;
}

Etapa 3: Codifique a condição de continuação do loop e adicione instruções apropriadas para controlar
o laço.

while (condição de continuação do loop) {

Declarações;
Instruções adicionais para controlar o loop;
}

5.2.3 Estudo de caso: Questionário de subtração múltipla


O programa de teste de subtração na Listagem 3.4, SubtractionQuiz.cpp, gera apenas uma pergunta para cada execução.
Você pode usar um loop para gerar perguntas repetidamente. Como você escreve o código para gerar cinco perguntas?
Siga a estratégia de design de loop. Primeiro, identifique as afirmações que precisam ser repetidas. São as instruções para
obter dois números aleatórios, solicitar ao usuário uma pergunta de subtração e avaliar a pergunta. Segundo, envolva as
instruções em um loop. Terceiro, adicione uma variável de controle de loop e a condição de continuação do loop para
executar o loop cinco vezes.

A Listagem 5.4 apresenta um programa que gera cinco perguntas e, depois que um aluno as responde, informa o
número de respostas corretas. O programa também exibe o tempo gasto na realização do teste, conforme mostrado no
exemplo de execução.

Listagem 5.4 SubtraçãoQuizLoop.cpp


1 #include <iostream>
2 #include <ctime> // Necessário para função time
3 #include <cstdlib> // Necessário para as funções srand e rand
4 usando namespace std;

5 6 int principal()
7{
contagem correta int contagem correta = 0; //Conta o número de respostas corretas
contagem total contagem interna = 0; //Conta o número de perguntas
obter horário de início 8 startTime longo = tempo(0);
9 const int NUMBER_OF_QUESTIONS = 5;
10
11 srand(tempo(0)); // Define uma semente aleatória
12
laço 13 enquanto (contagem < NUMBER_OF_QUESTIONS)
14 {
15 // 1. Gere dois números inteiros aleatórios de um dígito
16 int número1 = rand()% 10;
17 int número2 = rand()% 10;
18
19 20 21 // 2. Se número1 < número2, troque número1 por número2
Machine Translated by Google

5.2 O loop while 183


22 se (número1 <número2)
23 {
24 temperatura interna = número1;
25 número1 = número2;
26 número2 = temperatura;
27 }
28
29 // 3. Peça ao aluno para responder "o que é número1 – número2?"
" "
30 cout << "O que é " << número1 << << número2 << "? "; - exibir uma pergunta
31 resposta interna ;
32 cin >> resposta;
33
34 // 4. Classifique a resposta e exiba o resultado
35 if (número1 - número2 == resposta) { avaliar uma resposta
36
37 cout << "Você está correto!\n";
38 Contagem correta++; aumentar a contagem correta
39 }
40 outro
" - "
41 cout << "Sua resposta está errada.\n" << número1 << número2 << " <<
42 deveria ser " << (número1 - número2) << endl;
43
44 //Aumenta a contagem
45 contar++; aumentar variável de controle
46 }
47
48 endTime longo = tempo(0); obter horário de término

49 longo testTime = endTime - startTime; tempo de teste

50
51 cout << "A contagem correta é " " << correctCount << "\nO tempo de teste é " exibir resultado
52 << testTime << segundos\n";
53
54 retornar 0;
55 }

Quanto é 9 - 2? 7
Você está certo!

O que é 3 - 0? 3
Você está certo!

O que é 3 - 2? 1
Você está certo!

Quanto é 7 - 4? 4 Sua

resposta está errada.


7 - 4 deveria ser 3

Quanto é 7 - 5? 4 Sua

resposta está errada.


7 - 5 deveria ser 2

A contagem correta é 3
O tempo de teste é de 201 segundos
Machine Translated by Google

184 Capítulo 5 Loops

O programa usa a variável de controle count para controlar a execução do loop. contar
é inicialmente 0 (linha 9) e é aumentado em 1 em cada iteração (linha 45). Uma questão de subtração é exibida e processada
em cada iteração. O programa obtém o tempo antes do início do teste na linha 10, o tempo após o término do teste na linha
48 e calcula o tempo do teste na linha 49.

5.2.4 Controlando um Loop com Confirmação do Usuário O exemplo anterior executa o loop

cinco vezes. Se quiser que o usuário decida se deseja continuar, você pode oferecer uma confirmação do usuário. O modelo
confirmação do programa pode ser codificado da seguinte forma:

char continueLoop = 'Y';


enquanto (continueLoop == 'Y') {

//Executa o corpo do loop uma vez


...

// Solicita confirmação ao usuário


cout << "Digite Y para continuar e N para sair: ";
cin >> continueLoop;
}

Você pode reescrever a Listagem 5.4 com a confirmação do usuário para permitir que o usuário decida se deseja avançar
para a próxima pergunta.

5.2.5 Controlando um Loop com um Valor Sentinela Outra técnica comum para controlar

um loop é designar um valor especial ao ler e processar um conjunto de valores. Este valor de entrada especial, conhecido
valor sentinela como valor sentinela, significa o fim da entrada. Um loop que usa um valor sentinela para controlar sua execução é chamado
de loop controlado por sentinela.
circuito controlado por sentinela
A Listagem 5.5 fornece um programa que lê e calcula a soma de um número não especificado de inteiros. A entrada 0
significa o fim da entrada. Você precisa declarar uma nova variável para cada valor de entrada? Não. Basta usar uma variável
chamada data (linha 8) para armazenar o valor de entrada e usar uma variável chamada sum (linha 12) para armazenar o
total. Quando um valor for lido, atribua-o aos dados (linhas 9, 20) e adicione-o à soma (linha 15) se não for zero.

Listagem 5.5 SentinelValue.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 cout << "Insira um número inteiro (a entrada termina " <<
"se for 0): ";
dados internos ;
entrada 7 jantar >> encontro;
8
9 //Continua lendo os dados até que a entrada seja 0
10 soma interna = 0;
laço 11 enquanto (dados! = 0)
12 {
13 soma += dados;
14
15 //Leia os próximos dados
16 cout << "Insira um número inteiro (a entrada termina " <<
17 "se for 0): ";
18 19 20 jantar >> encontro;
Machine Translated by Google

5.2 O loop while 185


21 }

22 cout << "A soma é " << soma << endl; exibir resultado
23
24 retornar 0;
25 26 }

Insira um número inteiro (a entrada termina se for 0): 2


Insira um número inteiro (a entrada termina se for 0): 3
Insira um número inteiro (a entrada termina se for 0): 4
Insira um número inteiro (a entrada termina se for 0): 0
A soma é 9

Soma de dados da linha # Saída

9 2

12 0

15 2
iteração 1
20 3

15 5
iteração 2
20 4

17 9
iteração 3
20 0

23 A soma é 9

Se os dados não forem 0, eles serão adicionados à soma (linha 15) e os próximos itens dos dados de
entrada serão lidos (linhas 18–20). Se os dados forem 0, o loop termina. O valor de entrada 0 é o valor
sentinela para este loop. Observe que se a primeira entrada lida for 0, o corpo do loop nunca será executado e
a soma resultante será 0.

Cuidado
Não use valores de ponto flutuante para verificação de igualdade em uma expressão de controle de loop.
Como os valores de ponto flutuante são aproximações para alguns valores, usá-los pode resultar em
valores de contador imprecisos e em resultados imprecisos. erro numérico

Considere o seguinte código para calcular 1 + 0,9 + 0,8 + ... + 0,1:

item duplo = 1; soma dupla = 0;


while (item != 0) // Nenhum item de garantia será 0
{
soma += item;
item -= 0,1;
}
cout << soma << endl;

O item variável começa com 1 e é reduzido em 0,1 cada vez que o corpo do loop é executado.
O loop deve terminar quando item se tornar 0. Entretanto, não há garantia de que item será exatamente
0, porque a aritmética de ponto flutuante é aproximada. Este loop parece bom, mas na verdade é um
loop infinito.
Machine Translated by Google

186 Capítulo 5 Loops

5.2.6 Redirecionamentos de Entrada e Saída


Nota de vídeo
No exemplo anterior, se você tiver muitos dados para inserir, será complicado digitar no teclado. Você pode
Redirecionar entrada e saída
armazenar os dados separados por espaços em branco em um arquivo de texto, digamos input.txt, e executar
o programa usando o seguinte comando:

SentinelValue.exe <entrada.txt

redirecionamento de entrada Este comando é chamado de redirecionamento de entrada. O programa obtém a entrada do arquivo input.txt
em vez de fazer com que o usuário digite os dados no teclado em tempo de execução. Suponha que o conteúdo
do arquivo seja

2 3 4 5 6 7 8 9 12 23 32
23 45 67 89 92 12 34 35 3 1 2 4 0

O programa deve definir a soma como 518. Observe que SentinelValue.exe pode ser obtido usando o comando
do compilador de linha de comando:

g++ SentinelValue.cpp –o SentinelValue.exe

redirecionamento de saída Da mesma forma, o redirecionamento de saída pode enviar a saída para um arquivo em vez de exibi-la no
console. O comando para redirecionamento de saída é o seguinte:

SentinelValue.exe > saída.txt

O redirecionamento de entrada e saída pode ser usado no mesmo comando. Por exemplo, o comando a seguir
obtém a entrada de input.txt e envia a saída para output.txt:

SentinelValue.exe <entrada.txt> saída.txt

Execute o programa para ver qual conteúdo está em output.txt.

5.2.7 Lendo todos os dados de um arquivo


A Listagem 4.11 lê três números do arquivo de dados. Se você tiver muitos números para ler, terá que escrever um loop para
ler todos eles. Se você não sabe quantos números há no arquivo e deseja ler todos eles, como saber o final do arquivo?
Nota de vídeo
Ler arquivo
Você pode invocar o eof()
função no objeto de entrada para detectá-lo. A Listagem 5.6 revisa a Listagem 4.10 SimpleFileInput.cpp para ler
função eof
todos os números do arquivo numbers.txt.

Listagem 5.6 ReadAllData.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
//Abre um arquivo
abrir arquivo 7 ifstream input("pontuação.txt");
8
9 soma dupla = 0;
10 número duplo ;
fim do arquivo? 11 12 while (!input.eof()) // Continua se não for o fim do arquivo
Machine Translated by Google

5.2 O loop while 187


13 {
14 entrada >> número; //Lê os dados entrada do arquivo
15 cout << número << " "; // Mostrar dados
16 soma += número;
17 }
18
19 input.close(); fechar arquivo

20
21 cout << "\nEu sou " << soma << endl;
22
23 retornar 0;
24 }

95 56 34
A pontuação total é 185
Feito

O programa lê os dados em um loop (linhas 12–17). Cada iteração do loop lê um número.


O loop termina quando a entrada atinge o final do arquivo. fim do arquivo?

Quando não há mais nada para ler, eof() retorna verdadeiro. Para que este programa funcione
corretamente, não deve haver caracteres em branco após o último número do arquivo. No Capítulo 13,
discutiremos como melhorar o programa para lidar com casos incomuns com caracteres em branco após o
último número do arquivo.

5.1 Analise o código a seguir. A contagem <100 é sempre verdadeira, sempre falsa, ou às vezes
verdadeira ou às vezes falsa no Ponto A, Ponto B e Ponto C? ÿVerificação de ponto

contagem interna = 0;
enquanto (contagem < 100)
{
//Ponto A
cout << "Bem-vindo ao C++!\n";
contar++;
//Ponto B
}
//Ponto C

5.2 O que há de errado se o palpite for inicializado com 0 na linha 14 da Listagem 5.3?

5.3 Quantas vezes os seguintes corpos de loop são repetidos? Qual é a impressão de
cada ciclo?

int eu = 1; int eu = 1; int eu = 1;


enquanto (eu < 10) enquanto (eu < 10) enquanto (eu < 10)
se (eu% 2 == 0) se (eu% 2 == 0) se (eu++ % 2 == 0)
cout << i << endl; cout << i++ << endl; cout << i << endl;

(a) (b) (c)


Machine Translated by Google

188 Capítulo 5 Loops

5.4 Suponha que a entrada seja 2 3 4 5 0. Qual é a saída do código a seguir?

#include <iostream>
usando namespace std;

int main()

{ int número, max; cin


>> número; máximo
= número;

while (número! = 0) { cin

>> número; if (número


> max) max = número;

cout << "max é" cout << máximo << final;


<< "número" << número << fim;

retornar 0;
}

5.5 Qual é a saída do código a seguir? Explicar.

interno x = 80000000;

enquanto (x > 0)
x++;

cout << "x é" << x << endl;

5.6 Como você testa o final do arquivo ao ler dados de um arquivo?

5.3 O ciclo do-while


Um loop do-while é igual a um loop while , exceto que ele executa primeiro o corpo do loop e
Chave
Apontar depois verifica a condição de continuação do loop.

O loop do-while é uma variação do loop while . Sua sintaxe é a seguinte:

loop do-while
fazer {

//Corpo do loop;
Declarações);
} while (condição de continuação do loop);

Seu fluxograma de execução é mostrado na Figura 5.2.


O corpo do loop é executado primeiro. Em seguida, a condição de continuação do loop é avaliada. Se
a avaliação for verdadeira, o corpo do loop será executado novamente; caso contrário, o loop do-while
termina. A principal diferença entre um loop while e um loop do-while é a ordem em que a condição de
continuação do loop é avaliada e o corpo do loop é executado. Os loops while e do-while têm igual poder
expressivo. Às vezes um é mais conveniente que o outro. Por exemplo, você pode reescrever o loop while da
Listagem 5.5 usando um loop do-while , conforme mostrado na Listagem 5.7.
Machine Translated by Google

5.3 O ciclo do-while 189

Declarações)
(corpo em loop)

verdadeiro
Condição de
continuação do loop?

falso

Figura 5.2 O loop do-while executa primeiro o corpo do loop e depois


verifica a condição de continuação do loop para determinar se o loop deve
continuar ou terminar.

Listagem 5.7 TestDoWhile.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
5 //Continua lendo os dados até que a entrada seja 0
6 soma interna = 0;
7 dados internos = 0;
8
9 fazer laço
10 {
11 12 soma += dados;
13
14 //Leia os próximos dados
15 cout << "Insira um número inteiro (a entrada termina " "se <<
16 for 0): ";
17 jantar >> encontro; entrada
18 }
19 enquanto (dados! = 0);
20
21 cout << "A soma é " << soma << endl;
22
23 retornar 0;
24}

Insira um número inteiro (a entrada termina se for 0): 3


Insira um número inteiro (a entrada termina se for 0): 5
Insira um número inteiro (a entrada termina se for 0): 6
Insira um número inteiro (a entrada termina se for 0): 0
A soma é 14
Machine Translated by Google

190 Capítulo 5 Loops

O que aconteceria se soma e dados não fossem inicializados em 0? Isso causaria um erro de
sintaxe? Não. Isso causaria um erro lógico, porque soma e dados poderiam ser inicializados com qualquer valor.

Dica
Use o loop do-while se você tiver instruções dentro do loop que devem ser executadas
pelo menos uma vez, como no caso do loop do-while no programa TestDoWhile
anterior . Essas instruções devem aparecer antes do loop e também dentro dele se
você usar um loop while .

5.7 Suponha que a entrada seja 2 3 4 5 0. Qual é a saída do código a seguir?


ÿVerificação de ponto
#include <iostream>
usando namespace std;

int main()

{ int número, max; cin


>> número; máximo
= número;

faça {cin >> número;


if (número > max) max
= número; } while
(número! = 0);

cout << "max é" cout << máximo << final;


<< "número" << número << fim;

retornar 0;
}

5.8 Quais são as diferenças entre um loop while e um loop do-while ? Converta o
seguindo o loop while em um loop do-while .

soma interna =
0; número
interno ; cin >>
número; while (número!

= 0) {soma +=
número; cin >> número;
}

5.9 O que há de errado no código a seguir?

total interno = 0, num = 0;

fazer {

// Ler os próximos dados cout


<< "Insira um valor int, " "\nexit se a <<
entrada for 0: "; número interno ; cin
>> num;

total += num; }
enquanto (num! = 0);

cout << "O total é " << total << final;


Machine Translated by Google

5.4 O Loop for 191

5.4 O loop for


Um loop for possui uma sintaxe concisa para escrever loops.
Chave
Apontar
Muitas vezes você escreve um loop na seguinte forma comum:

i = valorinicial; // Inicializa variável de controle de loop


while (i < valor final) {

//Corpo do loop
...
eu++; // Ajusta a variável de controle do loop
}

Um loop for pode ser usado para simplificar o loop acima:

for (i = valorinicial; i < valorfinal; i++) {

//Corpo do loop
...
}

Em geral, a sintaxe de um loop for é mostrada abaixo:

for (ação inicial; condição de continuação do loop; ação após cada para loop
iteração)
{
//Corpo do loop;
Declarações);
}

O fluxograma do loop for é mostrado na Figura 5.3a.

Ação inicial eu = 0

falso falso
Condição de (eu <100)?
continuação do loop?

verdadeiro verdadeiro

Declarações) conta <<

(corpo em loop) "Bem-vindo ao C++\n";

Ação após cada iteração eu++

(a) (b)

Figura 5.3 Um loop for executa uma ação inicial uma vez, depois executa repetidamente as
instruções no corpo do loop e executa uma ação após uma iteração quando a condição de
continuação do loop é avaliada como verdadeira.
Machine Translated by Google

192 Capítulo 5 Loops

A instrução for-loop começa com a palavra-chave for, seguida por um par de parênteses envolvendo
ação inicial, condição de continuação do loop e ação após cada iteração, seguido pelo corpo
do loop entre colchetes. ação inicial, condição de continuação do loop e ação após cada iteração
são separadas por ponto e vírgula.

Um loop for geralmente usa uma variável para controlar quantas vezes o corpo do loop é executado
variável de controle e quando o loop termina. Isso é chamado de variável de controle. A ação inicial geralmente inicializa
uma variável de controle, a ação após cada iteração geralmente aumenta ou diminui a variável de
controle e a condição de continuação do loop testa se a variável de controle atingiu um valor de
término. Por exemplo, o loop for a seguir exibe Bem-vindo ao C++! 100 vezes:

int eu;
para (eu = 0; eu < 100; eu++) {

cout << "Bem-vindo ao C++!\n";


}

O fluxograma da declaração é mostrado na Figura 5.3b. O loop for inicializa i com 0,


em seguida, executa repetidamente a instrução de saída e avalia i++ enquanto i é menor que 100.
ação inicial A ação inicial, i condição de = 0, inicializa a variável de controle, i. O laço-
continuação, i <100, é uma expressão booleana. A expressão é avaliada logo após a inicialização e no início de cada
iteração. Se esta condição for verdadeira, o corpo do loop será executado. Se for falso, o loop termina e o controle do
programa passa para a linha seguinte ao loop.

ação após cada iteração A ação após cada iteração, i++, é uma instrução que ajusta a variável de controle. Esta instrução é executada
após cada iteração. Ele incrementa a variável de controle. Eventualmente, o valor da variável de controle deve forçar
a condição de continuação do loop
tornar-se falso. Caso contrário, o loop é infinito.
A variável de controle do loop pode ser declarada e inicializada no loop for . Aqui está um exemplo:

for (int i = 0; i < 100; i++) {

cout << "Bem-vindo ao C++!\n";


}

omitindo chaves Se houver apenas uma instrução no corpo do loop, como neste exemplo, os colchetes podem ser omitidos
conforme mostrado abaixo:

for (int i = 0; i < 100; i++) cout <<


"Bem-vindo ao C++!\n";

Dica
declarar variável de controle A variável de controle deve ser declarada dentro da estrutura de controle do loop ou antes do
loop. Se a variável de controle do loop for usada apenas no loop, e não em outro lugar, é
uma boa prática de programação declará-la na ação inicial do loop for . Se a variável for
declarada dentro da estrutura de controle do loop, ela não poderá ser referenciada fora do
loop. No código anterior, por exemplo, você não pode fazer referência a i fora do for
loop, porque é declarado dentro do loop for .

Observação

para variações de loop A ação inicial em um loop for pode ser uma lista de zero ou mais declarações de declaração
de variáveis separadas por vírgula ou expressões de atribuição. Por exemplo:
Machine Translated by Google

5.4 O Loop for 193


for (int i = 0, j = 0; i + j < 10; i++, j++) {

// Faça alguma coisa


}

A ação após cada iteração em um loop for pode ser uma lista de zero ou mais instruções
separadas por vírgula. Por exemplo:

for (int i = 1; i < 100; cout << i << endl, i++);

Este exemplo está correto, mas é um mau exemplo, pois dificulta a leitura do código.
Normalmente, você declara e inicializa uma variável de controle como uma ação inicial e
aumenta ou diminui a variável de controle como uma ação após cada iteração.

Observação

Se a condição de continuação do loop em um loop for for omitida, ela será


implicitamente verdadeira. Assim, a afirmação dada abaixo em (a), que é um loop infinito,
é a mesma que em (b). Para evitar confusão, porém, é melhor usar o loop equivalente em (c).

para ( ; { ;) de verdade; ) { enquanto (verdadeiro)

Equivalente Equivalente {

// Faça alguma coisa // Faça alguma coisa // Faça alguma coisa


} } Isto é melhor }

(a) (b) (c)

5.10 Os dois loops a seguir resultam no mesmo valor na soma?


ÿVerificação de ponto

for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; i++) {

soma += eu; soma += eu;


} }

(a) (b)

5.11 Quais são as três partes de um controle de loop for ? Escreva um loop for que imprima o
números de 1 a 100.

5.12 Suponha que a entrada seja 2 3 4 5 0. Qual é a saída do código a seguir?

#include <iostream>
usando namespace std;

int principal()
{
número interno , soma = 0, contagem;

for (contagem = 0; contagem < 5; contagem++)


{
cin >> número;
soma += número;
}
Machine Translated by Google

194 Capítulo 5 Loops


cout << "soma é " << soma << endl;
cout << "contagem é " << contagem << endl;

retornar 0;
}

5.13 O que a seguinte afirmação faz?


para ( ; { ;)

// Faça alguma coisa


}

5.14 Se uma variável for declarada no controle do loop for , ela poderá ser usada após o término do loop?

5.15 Converta a seguinte instrução de loop for em um loop while e em um loop do-while
laço:

soma longa = 0;
para (int i = 0; i <= 1000; i++)
soma = soma + i;

5.16 Conte o número de iterações nos seguintes loops:

contagem interna = 0; for (int contagem = 0;


enquanto (contagem contar <= n; contar++)
<n) { {
contar++; }
}

(a) (b)

contagem interna = 5; contagem interna = 5;


enquanto (contagem enquanto (contagem
<n) { <n) {
contar++; contar = contar + 3;
} }

(c) (d)

5.5 Qual circuito usar?


Você pode usar um loop for , um loop while ou um loop do-while , o que for mais conveniente.
Chave
Apontar
loop de pré-teste O loop while e o loop for são chamados de loops de pré-teste porque a condição de continuação é
ciclo pós-teste verificada antes que o corpo do loop seja executado. O loop do-while é chamado de loop pós-teste
porque a condição é verificada após a execução do corpo do loop. As três formas de instruções de loop –
while, do-while e for – são expressivamente equivalentes; isto é, você pode escrever um loop em qualquer
uma dessas três formas. Por exemplo, um loop while em (a) na figura a seguir sempre pode ser convertido
no loop for em (b).

while (condição de continuação do loop) { for ( ; condição de continuação do loop; )


Equivalente
{
//Corpo do loop //Corpo do loop
} }

(a) (b)
Machine Translated by Google

5.5 Qual circuito usar? 195


Um loop for em (a) na próxima figura pode geralmente ser convertido no loop while em (b), exceto
em certos casos especiais (veja o Ponto de Verificação 5.24 para tal caso).

for (ação inicial; condição ação inicial; while


de continuação do loop; ação após (condição de continuação do loop) {
Equivalente
cada iteração)
{ //Corpo do loop;
//Corpo do loop; ação após cada iteração;
} }

(a) (b)

Use a instrução de loop que for mais intuitiva e confortável para você. Em geral, um para
loop pode ser usado se o número de repetições for conhecido antecipadamente, como, por exemplo,
quando você precisa exibir uma mensagem 100 vezes. Um loop while pode ser usado se o número de
repetições não for fixo, como no caso de ler os números até que a entrada seja 0. Um loop do-while pode
ser usado para substituir um loop while se o corpo do loop tiver que ser executado antes que a condição
de continuação seja testada.

Cuidado
Adicionar um ponto e vírgula no final da cláusula for antes do corpo do loop é um erro comum, conforme
mostrado abaixo. Em (a), o ponto e vírgula significa o fim prematuro do loop.
O corpo do loop está realmente vazio, conforme mostrado em (b). (a) e (b) são equivalentes.

Erro Corpo Vazio

for (int i = 0; i < 10; i++); for (int i = 0; i < 10; i++) { };
{ {
cout << "eu sou" << eu << endl; cout << "eu sou" << eu << endl;
} }

(a) (b)

Da mesma forma, o loop em (c) também está errado. (c) é equivalente a (d).

Erro Corpo Vazio

int eu = 0; int eu = 0;
enquanto (eu < 10); enquanto (eu < 10) { };
{ {
cout << "eu é" i++; << eu << endl; cout << "eu é" i++; << eu << endl;

} }

(c) (d)

No caso do loop do-while , o ponto e vírgula é necessário para finalizar o loop.

int eu = 0;
fazer
{
cout << "eu é" i++; << eu << endl;

} enquanto (i < 10); Correto


Machine Translated by Google

196 Capítulo 5 Loops

5.17 Você pode converter um loop for em um loop while ? Liste as vantagens de usar para
ÿVerificação de ponto rotações.

5.18 Você sempre pode converter um loop while em um loop for ? Converta o seguinte loop while em
um loop for .

int eu = 1;
soma interna = 0;
enquanto (soma < 10.000)
{
soma = soma + i;
eu++;
}

5.19 Identifique e corrija os erros no código a seguir:


1 int principal() {
2

3 for (int i = 0; i < 10; i++);


4 soma += eu;

56 se (eu <j);
cout << i << endl;
outro
cout << j << endl;
7
8 enquanto (j < 10);
9 {
10 j++;
11 }
12
13 fazer {
14 j++;
15
16 } enquanto (j < 10)
17 18 19 20}

5.20 O que há de errado com os seguintes programas?

1int principal ( )
2{
3 for (int i = 0; i < 10; i++);
4 cout << i + 4 << endl;
5 }

5.6 Loops Aninhados


Um loop pode ser aninhado dentro de outro loop.
Chave
Apontar
loop aninhado Os loops aninhados consistem em um loop externo e um ou mais loops internos. Cada vez que o loop externo é
repetido, os loops internos são reiniciados e reiniciados.
A Listagem 5.8 apresenta um programa que usa loops for aninhados para exibir uma tabela de multiplicação.

Listagem 5.8 MultiplicationTable.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;
4
Machine Translated by Google

5.6 Loops Aninhados 197

5 int principal()
6{
"
cout << Tabela de Multiplicação\n"; título da tabela

cout << "--------------------------------\n";

7 //Mostra o título do número


"
8 conta << | ";
9 para (int j = 1; j <= 9; j++)
10 cout << setw(3) << j;
11
12 cout << "\n";
13
14 //Exibe o corpo da tabela
15 for (int i = 1; i <= 9; i++) { laço externo
16
"
17 cout << i << for | ";
18 (int j = 1; j <= 9; j++) { laço interno
19
20 // Exiba o produto e alinhe corretamente
21 cout << setw(3) << i * j;
22 }
23 cout << "\n";
24 25 26 27}
28
29 retornar 0;
30}

Tabela de multiplicação
1 234567 8 9
-----------------------------------------

1| 1 2 345 67 8 9
2| 2 46 8 10 12 14 16 18

3| 3 6 9 12 15 18 21 24 27

4| 4 8 12 16 20 24 28 32 36

5| 5 10 15 20 25 30 35 40 45

6| 6 12 18 24 30 36 42 48 54

7| 7 14 21 28 35 42 49 56 63

8| 8 16 24 32 40 48 56 64 72

9| 9 18 27 36 45 54 63 72 81

O programa exibe um título (linha 7) na primeira linha e traços (-) (linha 8) na segunda linha.
O primeiro loop for (linhas 12–13) exibe os números de 1 a 9 na terceira linha.
O próximo loop (linhas 18–27) é um loop for aninhado com a variável de controle i no
loop externo e j no loop interno. Para cada i, o produto i * j é exibido em uma linha no loop
interno, com j sendo 1, 2, 3,…, 9. O manipulador setw(3) (linha 24) especifica a largura de
cada número a ser exibido.

Observação

Esteja ciente de que um loop aninhado pode levar muito tempo para ser executado. Considere o seguinte
loop aninhado em três níveis:

para (int i = 0; i < 10000; i++)


para (int j = 0; j < 10000; j++)
para (int k = 0; k < 10000; k++)
Execute uma ação
Machine Translated by Google

198 Capítulo 5 Loops


A ação é realizada 1 trilhão de vezes. Se levar 1 microssegundo para executar a ação, o
tempo total para executar o loop será superior a 277 horas. Observe que 1 microssegundo
é um milionésimo (10–6) de segundo.

5.21 Quantas vezes a instrução print é executada?


ÿVerificação de ponto
for (int i = 0; i < 10; i++) for (int j = 0; j
< i; j++) cout << i * j << endl;

5.22 Mostre a saída dos seguintes programas. (Dica: desenhe uma tabela e liste as variáveis
nas colunas para rastrear esses programas.)

for (int i = 1; i < 5; i++) { int eu = 0;


enquanto (eu < 5)
int j = 0; {
enquanto (j <i) { para (int j = i; j > 1; j--)
cout << j << " ";
cout << j << " "; conta << “****” << fim;
j++; eu++;
} }
}

(a) (b)

int eu = 5; int eu = 1;
enquanto (eu >= fazer

1) { {
número interno = 1; número interno = 1
for (int j = 1; j <= i; j++) { para (int j = 1; j <= i; j++)
{
cout << num << "xxx"; cout << num << "G";
num *= 2; num += 2;
} }

cout << endl: cout << endl;


eu--; eu++;
} } enquanto (eu <= 5);

(c) (d)

5.7 Minimizando Erros Numéricos


Usar números de ponto flutuante na condição de continuação do loop pode causar erros numéricos
Chave
Apontar
erros.

erro numérico Erros numéricos envolvendo números de ponto flutuante são inevitáveis. Esta seção discute como minimizar
esses erros.
A Listagem 5.9 apresenta um exemplo somando uma série que começa com 0,01 e termina com 1,0.
Os números da série serão incrementados em 0,01, da seguinte forma: 0,01 + 0,02 + 0,03 e
breve.

Listagem 5.9 TestSum.cpp


1 #include <iostream>
2 usando namespace std;
Machine Translated by Google

5.8 Estudos de Caso 199

3 4 int principal()
5{
// Inicializa a soma
6 7 soma dupla = 0;

// Adicione 0,01, 0,02, . . . , 0,99, 1 para somar


8 para (duplo i = 0,01; i <= 1,0; i = i + 0,01) laço
9 soma += eu;
10
11 //Exibir resultado
12 cout << "A soma é " << soma << endl;
13
14 retornar 0;
15 16 17 }

A soma é 49,5

O resultado é 49,5, mas o resultado correto deveria ser 50,5. O que aconteceu? Para cada iteração no
loop, i é incrementado em 0,01. Quando o loop termina, o valor i é ligeiramente maior que 1 (não exatamente
1). Isso faz com que o último valor i não seja adicionado à soma. O problema fundamental é que os números
de ponto flutuante são representados por aproximação.
Para resolver o problema, use uma contagem inteira para garantir que todos os números sejam somados .
Aqui está o novo ciclo:

double CurrentValue = 0,01;

for (int contagem = 0; contagem < 100; contagem++) {

soma += ValorAtual;
ValorAtual += 0,01;
}

Após esse loop, a soma é 50,5.

5.8 Estudos de Caso


Loops são fundamentais na programação. A capacidade de escrever loops é essencial no
Chave
aprendizado de programação. Apontar

Se você consegue escrever programas usando loops, você sabe programar! Por esse motivo, esta seção
apresenta quatro exemplos adicionais de resolução de problemas usando loops.

5.8.1 Estudo de caso: Encontrando o Máximo Divisor Comum


O máximo divisor comum (MDC) dos dois inteiros 4 e 2 é 2. O MDC dos dois inteiros 16 e 24 é 8. Como você GCD
determina o MDC? Sejam os dois inteiros de entrada n1 e n2. Você sabe que o número 1 é um divisor
comum, mas pode não ser o maior. Assim, você pode verificar se k (para k = 2, 3, 4 e assim por diante) é um
divisor comum para n1 e n2, até que k seja maior que n1 ou n2. Armazene o divisor comum em uma variável
chamada mdc. Inicialmente, mdc
é 1. Sempre que um novo divisor comum é encontrado, ele se torna o novo GCD. Depois de verificar todos
os divisores comuns possíveis de 2 até n1 ou n2, o valor na variável gcd é o GCD. A ideia pode ser traduzida
no seguinte loop:

int mdc = 1; // O MDC inicial é 1


int k = 2; // Possível mdc
Machine Translated by Google

200 Capítulo 5 Loops


enquanto (k <= n1 && k <= n2)
{
if (n1 % k == 0 && n2 % k == 0) mdc = k; //
Atualiza o gcd
k++; // Próximo gcd possível
}

// Após o loop, mdc é o máximo divisor comum para n1 e n2

A Listagem 5.10 apresenta o programa que solicita ao usuário que insira dois números inteiros positivos e
encontre seu MDC.

Listagem 5.10 GreatestCommonDivisor.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
// Solicita ao usuário que insira dois números inteiros
6 7 cout << "Insira o primeiro número inteiro: ";
você é n1;
entrada 8 9 comer >> n1;
10
11 cout << "Digite o segundo inteiro: ";
12 interno n2;
entrada 13 comer >> n2;
14
mdc 15 int mdc = 1;
16 int k = 2;
17 enquanto (k <= n1 && k <= n2)
18 {
verificar divisor 19 se (n1% k == 0 && n2% k == 0)
20 mdc = k;
21 k++;
22 }
23
saída 24 cout << "O máximo divisor comum para " << n2 << << << n1 << "e"
25 gcd << endl; " é "
26
27 retornar 0;
28 }

Insira o primeiro número inteiro: 125


Insira o segundo número inteiro: 2525
O máximo divisor comum para 125 e 2525 é 25

Como você escreveria este programa? Você começaria imediatamente a escrever o código? Não.
pense antes de digitar É importante pensar antes de digitar. Pensar permite gerar uma solução lógica para o problema antes de
escrever o código. Depois de ter uma solução lógica, digite o código para traduzir a solução em um programa. A
tradução não é única. Por exemplo, você poderia usar um loop for para reescrever o código da seguinte maneira:

for (int k = 2; k <= n1 && k <= n2; k++) {

if (n1 % k == 0 && n2 % k == 0) mdc = k;

}
Machine Translated by Google

5.8 Estudos de Caso 201

Um problema geralmente tem múltiplas soluções, e o problema do MDC pode ser resolvido de várias múltiplas soluções
maneiras. O Exercício de Programação 5.16 sugere outra solução. Uma solução mais eficiente é usar o algoritmo
euclidiano clássico (ver www.cut-the-knot.org/blue/Euclid.shtml para mais informações).

Você pode pensar que um divisor para um número n1 não pode ser maior que n1/ 2, solicitando soluções erradas

você tente melhorar o programa usando o seguinte loop:

for (int k = 2; k <= n1 / 2 && k <= n2 / 2; k++) {

if (n1 % k == 0 && n2 % k == 0) mdc = k;

Esta revisão está errada. Você consegue encontrar o motivo? Consulte o Ponto de Verificação 5.23 para obter a resposta.

5.8.2 Estudo de caso: Prevendo a mensalidade futura


Suponha que a mensalidade de uma universidade seja de US$ 10.000 este ano e que a mensalidade aumente
7% a cada ano. Em quantos anos a mensalidade será duplicada?
Antes de escrever um programa para resolver esse problema, primeiro considere como resolvê-lo
manualmente. A mensalidade do segundo ano é a mensalidade do primeiro ano * 1,07. A mensalidade para um
ano futuro é a mensalidade do ano anterior * 1,07. Assim, a mensalidade de cada ano pode ser calculada da
seguinte forma:

mensalidade em dobro = 10.000; ano interno = 0; // Ano 0


mensalidade = mensalidade * 1,07; ano++; // Ano 1
mensalidade = mensalidade * 1,07; ano++; // Ano 2
mensalidade = mensalidade * 1,07; ano++; // Ano 3
...

Continue computando a mensalidade para um novo ano até que seja pelo menos 20.000. Até então você saberá
quantos anos serão necessários para que a mensalidade seja duplicada. Agora você pode traduzir a lógica no
seguinte loop:

mensalidade em dobro = 10.000; // Ano 0


ano interno = 0;
enquanto (mensalidade <
20.000) {
mensalidade = mensalidade * 1,07;
ano++;
}

O programa completo é mostrado na Listagem 5.11.

Listagem 5.11 FutureTuition.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;

4 5 int principal()
6{
mensalidade em dobro = 10.000; // Ano 1
7 8 int ano = 1;
9 enquanto (mensalidade < 20.000) laço
10 {
11 mensalidade = mensalidade * 1,07; mensalidade do próximo ano
12 ano++;
13 }
Machine Translated by Google

202 Capítulo 5 Loops


14
15 cout << "A mensalidade será dobrada em " " anos"<<<<
ano
endl;
<<
16 cout << setprecision(2) << fixo << showpoint <<
17 "A mensalidade será de $" << mensalidade <<" em "
18 << ano << "anos" << endl;
19
20 retornar 0;
21 }

Mensalidade será dobrada em 11 anos


A mensalidade será de $ 21.048,52 em 11 anos

O loop while (linhas 9–13) é usado para calcular repetidamente a mensalidade para um novo ano. O loop termina
quando a mensalidade é maior ou igual a 20.000.

5.8.3 Estudo de Caso: Simulação de Monte Carlo


A simulação de Monte Carlo usa números aleatórios e probabilidade para resolver problemas. Este método tem
uma ampla gama de aplicações em matemática computacional, física, química e finanças. Esta seção fornece um
exemplo de uso da simulação de Monte Carlo para estimar p.
Para estimar p usando o método de Monte Carlo, desenhe um círculo com seu quadrado delimitador conforme
mostrado abaixo:

e
1

1 1 x

Suponha que o raio do círculo seja 1. Portanto, a área do círculo é p e a área do quadrado é 4.
Gere aleatoriamente um ponto no quadrado. A probabilidade de o ponto cair no círculo é
circleArea / squareArea = ÿ / 4.
Escreva um programa que gere aleatoriamente 1.000.000 de pontos no quadrado e
deixe numberOfHits denotar o número de pontos que caem no círculo. Assim,
numberOfHits é aproximadamente 1.000.000 * (ÿ /4). p pode ser aproximado como 4 *
numberOfHits / 1000000. O programa completo é mostrado na Listagem 5.12.

Listagem 5.12 MonteCarloSimulation.cpp


1 #include <iostream>
2 #include <cstdlib> 3
#include <ctime>
4 usando namespace std;

5 6 int principal()
7{
const int NUMBER_OF_TRIALS = 1000000;
int númeroOfHits = 0;
8 srand(tempo(0));
9
10 para (int i = 0; i < NUMBER_OF_TRIALS; i++)
11 {
gerar pontos aleatórios 12 13 14 duplo x = rand() * 2,0 / RAND_MAX - 1;
Machine Translated by Google

5.8 Estudos de Caso 203

15 duplo y = rand() * 2.0 / RAND_MAX - 1;


16 se (x * y <= x1)+ y * verifique dentro do círculo
17 númeroDeHits++;
18 }
19
20 duplo pi = 4,0 * número de sucessos / NUMBER_OF_TRIALS; pi estimado
21 cout << "PI é " << pi << endl;
22
23 retornar 0;
24 }

PI é 3,14124

O programa gera repetidamente um ponto aleatório (x, y) no quadrado nas linhas 14–15.
Observe que RAND_MAX é o número máximo que pode ser retornado ao invocar Rand()
função. Portanto, rand() * 1.0 / RAND_MAX é um número aleatório entre 0,0 e 1,0, e 2,0 *
rand() / RAND_MAX é um número aleatório entre 0,0 e 2,0. Portanto, 2,0 * rand() / RAND_MAX
– 1 é um número aleatório entre -1,0 e 1,0.
Se x2 + y2 … 1, o ponto está dentro do círculo e numberOfHits é incrementado em 1. p é
aproximadamente 4 * numberOfHits / NUMBER_OF_TRIALS (linha 20).

5.8.4 Estudo de Caso: Convertendo Decimais em Hexadecimais


Hexadecimais são frequentemente usados na programação de sistemas de computadores (veja o
Apêndice D para uma introdução aos sistemas numéricos). Como você converte um número decimal
em um número hexadecimal? Converter um número decimal d em um número hexadecimal é encontrar
os dígitos hexadecimais hn, hn-1, hn-2, c, h2, h1 e h0 tais que

d = hn * 16n + hn-1 * 16n-1 + hn-2 * 16n-2 + + h2 * c

162 + h1 * 161 + h0 * 160

Esses dígitos hexadecimais podem ser encontrados dividindo sucessivamente d por 16 até
que o quociente seja 0. Os restos são h0, h1, h2, c, hn-2, hn-1 e hn. Os dígitos hexadecimais
incluem os dígitos decimais 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9, mais A, que é o valor decimal 10; B, que
é o valor decimal 11; C, que é 12; D, que é 13; E, que é 14; e F, que é 15.
Por exemplo, o número decimal 123 é 7B em hexadecimal. A conversão é feita da seguinte
maneira. Divida 123 por 16. O resto é 11 (B em hexadecimal) e o quociente é 7.
Continue dividindo 7 por 16. O resto é 7 e o quociente é 0. Portanto, 7B é o número hexadecimal
para 123.

0 7 Quociente

16 7 16 123
0 112
7 11 Restante

h1 h0

A Listagem 5.13 fornece um programa que solicita ao usuário que insira um número decimal e o converte em um
número hexadecimal como uma string.

Listagem 5.13 Dec2Hex.cpp


1 #include <iostream>
2 #incluir <string>
Machine Translated by Google

204 Capítulo 5 Loops

3 usando namespace std;

4 5 int principal()
6{
// Solicita ao usuário que insira um número inteiro decimal
cout << "Digite um número decimal: ";
inteiro decimal;
entrada decimal 7 cin >> decimal;
8
9 // Converte decimal em hexadecimal
10 string hexadecimal = "";
11
12 enquanto (decimal! = 0)
13 {
decimal para hexadecimal 14 int hexValor = decimal% 16;
15
16 // Converte um valor decimal em um dígito hexadecimal char
obter um caractere hexadecimal
17 hexChar = (hexValue <= 9 && hexValue >= 0) ?
18 static_cast<char>(hexValue + '0') :
19 static_cast<char>(hexValue - 10 + 'A');
20
adicionar à string hexadecimal 21 hex = hexChar + hex;
22 decimal=decimal/ 16;
23 }
24 25 26 27
28 cout << "O número hexadecimal é " << hexadecimal << endl;
29
30 retornar 0;
31 }

Insira um número decimal: 1234


O número hexadecimal é 4D2

Linha# decimal hexadecimal valor hexadecimal hexadecimal


""
13 1234

17 2
iteração 1 24 "2" 2

25 77

17 13
iteração 2 24 "D2" D

25 4

17 4
iteração 3 24 "4D2" 4

25 0

O programa solicita que o usuário insira um número inteiro decimal (linha 10), converte-o em um número
hexadecimal como uma string (linhas 13 a 26) e exibe o resultado (linha 28). Para converter um número decimal em
um número hexadecimal, o programa usa um loop para dividir sucessivamente o número decimal por 16 e obter seu
restante (linha 17). O restante é convertido em um caractere hexadecimal (linhas 20–22).
O caractere é então anexado à string hexadecimal (linha 24). A string hexadecimal está inicialmente vazia
Machine Translated by Google

5.9 Palavras-chave quebram e continuam 205

(linha 13). Divida o número decimal por 16 para remover um dígito hexadecimal do número (linha 25).
O loop termina quando o número decimal restante se torna 0.
O programa converte um hexValue entre 0 e 15 em um caractere hexadecimal. Se hexValor
está entre 0 e 9, ele é convertido em static_cast<char>(hexValue + '0') (linha 21).
Lembre-se de que ao adicionar um caractere a um número inteiro, o código ASCII do caractere é
usado na avaliação. Por exemplo, se hexValue for 5, static_cast<char>(hexValue + '0')
retorna o caractere 5 (linha 21). Da mesma forma, se hexValue estiver entre 10 e 15, ele será
convertido em static_cast<char>(hexValue - 10 + 'A') (linha 22). Por exemplo, se hexValue for 11,
static_cast<char>(hexValue - 10 + 'A') retornará o caractere B.

5.23 O programa funcionará se n1 e n2 forem substituídos por n1/2 e n2/2 na linha 17 da Listagem
5.10? ÿVerificação de ponto

5.24 Na Listagem 5.13, é correto alterar o código static_cast


<char>(hexValue + '0') para hexValue + '0' na linha 21?

5.25 Na Listagem 5.13, quantas vezes o corpo do loop é executado para um número decimal 245
e quantas vezes o corpo do loop é executado para um número decimal 3245?

5.9 Palavras-chave quebram e continuam


As palavras-chave break e continue fornecem controles adicionais em um loop.
Chave
Apontar
Nota Pedagógica
Duas palavras-chave, break e continue, podem ser usadas em instruções de loop para
fornecer controles adicionais. Usar break e continue pode simplificar a programação em alguns casos.
O uso excessivo ou indevido deles, entretanto, pode dificultar a leitura e a depuração dos
programas. (Nota para os instrutores: você pode pular esta seção sem afetar a compreensão
dos alunos sobre o restante do livro.)

Você usou a palavra-chave break em uma instrução switch . Você também pode usar break em um declaração de interrupção

loop para encerrá-lo imediatamente. A Listagem 5.14 apresenta um programa para demonstrar o efeito
do uso de break em um loop.

Listagem 5.14 TestBreak.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
soma interna = 0;
número interno = 0;

enquanto (número < 20)


6 {
7 número++;
8 soma += número;
9 se (soma >= 100)
10 quebrar; quebrar
11 }
12
13 cout << "O número é " cout << "A << número << fim;
14 soma é " << soma << endl;
15
16 retornar 0;
17 18 19 20 21 }
Machine Translated by Google

206 Capítulo 5 Loops

O número é 14
A soma é 105

O programa adiciona números inteiros de 1 a 20 nesta ordem para somar até que a soma seja maior ou
igual a 100. Sem as linhas 13 a 14, este programa calcularia a soma dos números de 1 a 20. Mas com as linhas
13 a 14, o loop termina quando a soma se torna maior ou igual a 100. Sem as linhas 13–14, a saída seria

O número é 20
A soma é 210

continuar Você também pode usar a palavra-chave continue em um loop. Quando encontrado, encerra a iteração
atual. O controle do programa vai para o final do corpo do loop. Em outras palavras, continue sai de uma
iteração, enquanto a palavra-chave break sai de um loop. O programa na Listagem 5.15 mostra o efeito de usar
continue em um loop.

Listagem 5.15 TestContinue.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
soma interna = 0;
número interno = 0;

enquanto (número < 20)


6 {
7 número++;
8 if (número == 10 || número == 11) continuar;
continuar 9
10 soma += número;
11 }
12
13 cout << "A soma é " << soma << endl;
14
15 retornar 0;
16 17 18 19 20 }

A soma é 189

continuar declaração O programa adiciona os números inteiros de 1 a 20 , exceto 10 e 11 para somar. A instrução continue é
executada quando o número se torna 10 ou 11. A instrução continue encerra a iteração atual para que o
restante da instrução no corpo do loop não seja executado; portanto, número
não é adicionado à soma quando é 10 ou 11.
Sem as linhas 12–13, a saída seria a seguinte:

A soma é 210
Machine Translated by Google

5.9 Palavras-chave quebram e continuam 207

Nesse caso, todos os números são somados , mesmo quando o número for 10 ou 11. Portanto, o resultado é 210.

Observação

A instrução continue está sempre dentro de um loop. Nos loops while e do-
while , a condição de continuação do loop é avaliada imediatamente após a
instrução continue . No loop for , a ação após cada iteração é executada e, em
seguida, a condição de continuação do loop é avaliada, imediatamente após a
instrução continue .

Você sempre pode escrever um programa sem usar break ou continue em loop. Consulte o Ponto de
Verificação 5.28. Em geral, é apropriado usar break e continue se seu uso simplifica a codificação e facilita a
leitura dos programas.
Suponha que você precise escrever um programa para encontrar o menor fator diferente de 1 para um número
inteiro n (suponha que n >= 2). Você pode escrever um código simples e intuitivo usando a instrução break da
seguinte forma:

fator interno = 2;
enquanto (fator <= n) {

se (n% fator == 0)
quebrar;
fator++;
}
cout << "O menor fator diferente de 1 para "
<< n << "é" << fator << endl;

Você pode reescrever o código sem usar break da seguinte maneira:

bool encontrado = falso;


fator interno = 2;
while (fator <= n && !encontrado) {

se (n% fator == 0)
encontrado = verdadeiro;
outro
fator++;
}
cout << "O menor fator diferente de 1 para "
<< n << "é" << fator << endl;

Obviamente, a instrução break torna este programa mais simples e fácil de ler. No entanto, você deve usar o
break e continuar com cautela. Muitas instruções break e continue produzirão um loop com muitos pontos de
saída e dificultarão a leitura do programa.

Observação

Algumas linguagens de programação, incluindo C++, possuem uma instrução goto . A instrução Vá para

goto transfere indiscriminadamente o controle para qualquer instrução no programa e a executa.


Isso torna seu programa vulnerável a erros. As instruções break e continue em
C++ são diferentes das instruções goto . Eles operam apenas em um loop ou switch
declaração. A instrução break sai do loop e a instrução continue sai da iteração
atual no loop.

5.26 Para que serve a palavra-chave break ? Para que serve a palavra-chave continuar ? Os seguintes
programas serão encerrados? Em caso afirmativo, forneça a saída. ÿVerificação de ponto
Machine Translated by Google

208 Capítulo 5 Loops

saldo interno = 1000; saldo interno = 1000;


enquanto (verdadeiro) enquanto (verdadeiro)
{ {
se (saldo < 9) se (saldo < 9)
quebrar; continuar;
saldo = saldo - 9; saldo = saldo - 9;
} }

cout << "Saldo é" equilíbrio << cout << "O equilíbrio é "
<< endl; << saldo << final;

(a) (b)

5.27 O loop for à esquerda é convertido no loop while à direita. O que está errado? Corrija isto.

for (int i = 0; i < 4; i++) { Convertido int eu = 0;


enquanto (eu <
se (i% 3 == 0) continuar; Errado 4) {
soma += eu; conversão se (i% 3 == 0) continuar;
} soma += eu;
eu++;
}

5.28 Reescreva os programas TestBreak e TestContinue nas Listagens 5.14 e 5.15 sem usar
break e continue.

5.29 Após a instrução break em (a) ser executada no loop seguinte, qual instrução é executada?
Mostre a saída. Depois que a instrução continue em (b) for executada no loop a seguir,
qual instrução será executada? Mostre a saída.

for (int i = 1; i < 4; i++) { for (int i = 1; i < 4; i++) {

for (int j = 1; j < 4; j++) { for (int j = 1; j < 4; j++) {

se (i * j > 2) se (i * j > 2)
quebrar; continuar;

cout << i * j << endl; cout << i * j << endl;


} }

cout << i << endl; cout << i << endl;


} }

(a) (b)

5.10 Estudo de Caso: Verificando Palíndromos


Esta seção apresenta um programa que testa se uma string é um palíndromo.
Chave
Apontar
Uma string é um palíndromo se for lida da mesma forma para frente e para trás. As palavras “mãe”,
“pai” e “meio-dia”, por exemplo, são palíndromos.
Como você escreve um programa para verificar se uma string é um palíndromo? Uma solução é
verificar se o primeiro caractere da string é igual ao último caractere. Nesse caso, verifique se o
segundo caractere é igual ao penúltimo caractere. Este processo continua até que uma
incompatibilidade seja encontrada ou todos os caracteres da string sejam verificados, exceto o
caractere do meio se a string tiver um número ímpar de caracteres.
Machine Translated by Google

5.10 Estudo de Caso: Verificando Palíndromos 209

Para implementar essa ideia, use duas variáveis, digamos low e high, para denotar a posição de dois
caracteres no início e no final de uma string s, conforme mostrado na Listagem 5.16 (linhas 13, 16).
Inicialmente, o mínimo é 0 e o máximo é s.length() — 1. Se os dois caracteres nessas posições
corresponderem, aumente o mínimo em 1 e diminua o máximo em 1 (linhas 27–28). Este processo
continua até que (baixo >= alto) ou uma incompatibilidade seja encontrada.

Listagem 5.16 TestPalindrome.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;

4 5 int principal()
6{
7 // Solicita ao usuário para inserir uma string
cout << "Digite uma string: ";
cordas;
8 getline(cin,s); sequência de entrada

9 10 11
12 //O índice do primeiro caractere da string
13 int baixo = 0;
14
15 //O índice do último caractere da string
16 int alto = s.comprimento() - 1;
17
18 bool isPalindrome = verdadeiro;
19 enquanto (baixo <alto) {
20
21 if (s[baixo] != s[alto]) { comparar dois personagens
22
23 isPalindrome = falso; // Não é um palíndromo não palíndromo
24 quebrar; ciclo de saída
25 }
26
27 baixo++;
28 alto--;
29 }
30
31 if (é Palíndromo)
32 cout << s << "é um palíndromo" << endl;
33 outro
34 cout << s << "não é um palíndromo" << endl;
35
36 retornar 0;
37 }

Digite uma string: abccba


abccba é um palíndromo

Digite uma string: abca abca


não é um palíndromo

O programa declara uma string (linha 9), lê uma string do console (linha 10) e verifica se a string é um
palíndromo (linhas 13–29).
A variável bool isPalindrome é inicialmente definida como true (linha 18). Ao comparar dois caracteres
correspondentes de ambas as extremidades da string, isPalindrome é definido como falso se o
Machine Translated by Google

210 Capítulo 5 Loops

dois caracteres diferem (linha 23). Neste caso, a instrução break é usada para sair do while
loop (linha 24).
Se o loop terminar quando low >= high, isPalindrome será verdadeiro, o que indica que a string é
um palíndromo.

5.11 Estudo de caso: Exibindo números primos


Esta seção apresenta um programa que exibe os primeiros 50 números primos em 5 linhas,
Chave
Apontar cada uma contendo 10 números.

Um número inteiro maior que 1 é primo se seu único divisor positivo for 1 ou ele mesmo. Por exemplo,
2, 3, 5 e 7 são números primos, mas 4, 6, 8 e 9 não são.
O programa pode ser dividido nas seguintes tarefas:

n Determine se um determinado número é primo.

n Para número = 2, 3, 4, 5, 6, . . . , teste se é primo.

n Conte os números primos.

n Exiba cada número primo e dez números por linha.

Obviamente, você precisa escrever um loop e testar repetidamente se um novo número é primo.
Se o número for primo, aumente a contagem em 1. A contagem é 0 inicialmente. Quando chega a 50,
o loop termina.
Aqui está o algoritmo:

Defina o número de números primos a serem impressos como


uma constante NUMBER_OF_PRIMES;
Use count para rastrear o número de números primos e
defina uma contagem inicial como 0;
Defina um número inicial como 2;

enquanto (contagem < NUMBER_OF_PRIMES)


{
Teste se o número é primo;

se o número for primo {

Exiba o número primo e aumente a contagem;


}

Aumentar o número em 1;
}

Para testar se um número é primo, verifique se ele é divisível por 2, 3, 4, até número/2.
Se um divisor for encontrado, o número não é primo. O algoritmo pode ser descrito da seguinte forma:

Use uma variável bool isPrime para indicar se


o número é primo; Defina isPrime como true inicialmente;

for (int divisor = 2; divisor <= número / 2; divisor++) {

if (número% divisor == 0) {

Defina isPrime como falso


Saia do ciclo;
}
}

O programa completo é dado na Listagem 5.17.


Machine Translated by Google

5.11 Estudo de caso: Exibindo números primos 211

Listagem 5.17 PrimeNumber.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;

4 5 int principal()
6{
const int NUMBER_OF_PRIMES = 50; //Número de primos a serem exibidos
7 8 const int NUMBER_OF_PRIMES_PER_LINE = 10; //Mostra 10 por linha
contagem interna = 0; //Conta o número de números primos contar números primos
9 10 número interno = 2; // Um número a ser testado quanto à primocidade
11
12 cout << "Os primeiros 50 números primos são \n";
13
14 // Encontra repetidamente números primos
15 enquanto (contagem < NUMBER_OF_PRIMES)
16 {
17 // Suponha que o número seja primo
18 bool éPrime = verdadeiro; // O número atual é primo? verifique a primazia
19
20 //Testa se o número é primo
21 for (int divisor = 2; divisor <= número / 2; divisor++)
22 {
23 if (número% divisor == 0)
24 {
25 //Se verdadeiro, o número não é primo
26 éPrime = falso; // Define isPrime como falso
27 quebrar; //Sai do loop for ciclo de saída
28 }
29 }
30
31 //Exibe o número primo e aumenta a contagem
32 if (éPrime) exibir se for primo
33 {
34 contar++; //Aumenta a contagem
35
36 se (contagem% NUMBER_OF_PRIMES_PER_LINE == 0)
37 //Exibe o número e avança para a nova linha
38 cout << setw(4) << número << endl;
39 outro
40 cout << setw(4) << número;
41 }
42
43 // Verifica se o próximo número é primo
44 número++;
45 }
46
47 retornar 0;
48}

Os primeiros 50 números primos são


2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229
Machine Translated by Google

212 Capítulo 5 Loops

Este é um programa complexo para programadores novatos. A chave para desenvolver uma solução
subproblema programática para este problema, e para muitos outros problemas, é dividi-lo em subproblemas e desenvolver
soluções para cada um deles. Não tente desenvolver uma solução completa na primeira tentativa. Em vez
disso, comece escrevendo o código para determinar se um determinado número é primo e, em seguida,
expanda o programa para testar se outros números são primos em um loop.
Para determinar se um número é primo, verifique se ele é divisível por um número entre 2 e número/2
inclusive. Se for assim, não é um número primo; caso contrário, é um número primo. Para um número primo,
exiba-o. Se a contagem for divisível por 10, avance para uma nova linha. O programa termina quando a
contagem chega a 50.
O programa usa a instrução break na linha 27 para sair do loop for assim que o número for considerado
não primo. Você pode reescrever o loop (linhas 21 a 29) sem usar a instrução break , como segue:

for (int divisor = 2; divisor <= número / 2 && isPrime; divisor++)

{
//Se verdadeiro, o número não é primo
if (número% divisor == 0) {

// Define isPrime como falso, se o número não for primo


éPrime = falso;
}
}

No entanto, usar a instrução break torna o programa mais simples e fácil de ler neste
caso.

Termos chave
declaração de quebra 205 condição de continuação do loop 176
continuar declaração 206 loop aninhado 196
loop do-while 188 erro off-by-one 178
para circuito 191 redirecionamento de saída 186
loop infinito 178 loop pós-teste 194
redirecionamento de entrada 186 loop de pré-teste 194
iteração 176 valor sentinela 184
laço 176 enquanto ciclo 176
corpo do laço 176

Resumo do capítulo
1. Existem três tipos de instruções de repetição: o loop while , o loop do-while e
o loop for .

2. A parte do loop que contém as instruções a serem repetidas é chamada de corpo do loop.

3. Uma execução única de um corpo de loop é chamada de iteração do loop.

4. Um loop infinito é uma instrução de loop que é executada infinitamente.

5. Ao projetar loops, você precisa considerar tanto a estrutura de controle do loop quanto a estrutura do loop.
corpo.
Machine Translated by Google

Exercícios de Programação 213

6. O loop while verifica primeiro a condição de continuação do loop . Se a condição for verdadeira, o corpo
do loop será executado; se for falso, o loop termina.

7. O loop do-while é semelhante ao loop while , exceto que o loop do-while executa primeiro o corpo do
loop e depois verifica a condição de continuação do loop para decidir se deve continuar ou terminar.

8. O loop while e o loop do-while são frequentemente usados quando o número de repetições
não está predeterminado.

9. Um valor sentinela é um valor especial que significa o fim do loop.

10. O loop for geralmente é usado para executar o corpo do loop um número fixo de vezes.

11. O controle do loop for tem três partes. A primeira parte é uma ação inicial que frequentemente inicializa
uma variável de controle. A segunda parte, a condição de continuação do loop, determina se o corpo do
loop deve ser executado. A terceira parte é executada após cada iteração e é frequentemente usada
para ajustar a variável de controle. Normalmente, as variáveis de controle do loop são inicializadas e
alteradas na estrutura de controle.

12. O loop while e o loop for são chamados de loops de pré-teste porque a condição de continuação
A configuração é verificada antes que o corpo do loop seja executado.

13. O loop do-while é chamado de loop pós-teste porque a condição é verificada após a execução do corpo do
loop.

14. Duas palavras-chave, break e continue, podem ser usadas em um loop.

15. A palavra-chave break encerra imediatamente o loop mais interno, que contém o break.

16. A palavra-chave continue apenas encerra a iteração atual.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Nota Pedagógica
Leia cada problema várias vezes até entendê-lo. Pense em como resolver o problema antes leia e pense antes de codificar
de começar a escrever código. Traduza sua lógica em um programa. Muitas vezes, um
problema pode ser resolvido de muitas maneiras diferentes. Os alunos são incentivados a
explorar várias soluções. explorar soluções

Seções 5.2–5.7
*5.1 (Contar números positivos e negativos e calcular a média dos números) Escreva um programa que leia
um número não especificado de inteiros, determine quantos valores positivos e negativos foram
lidos e calcule o total e a média dos valores de entrada (sem contar zeros). Seu programa termina
com a entrada 0. Exiba a média como um número de ponto flutuante. Aqui está um exemplo de
execução:
Machine Translated by Google

214 Capítulo 5 Loops

Insira um número inteiro, a entrada termina se for 0: 1 2 -1 3 0 O número de


positivos é 3
O número de negativos é 1
O total é 5
A média é 1,25

Insira um número inteiro, a entrada termina se for 0: 0

Nenhum número é inserido, exceto 0

5.2 (Repetir multiplicações) A Listagem 5.4, SubtractionQuizLoop.cpp, gera cinco questões de


subtração aleatórias. Revise o programa para gerar nove questões de multiplicação aleatória
para três números inteiros entre 1 e 5. Exiba a contagem correta e o tempo de teste.

5.3 (Conversão de milímetros para polegadas) Escreva um programa que exiba a seguinte tabela
(observe que 1 milímetro equivale a 0,039 polegadas):

Milímetros Polegadas
0,078
24 0,156
...
96 3.744
98 3.822

5.4 (Conversão de metros para pés) Escreva um programa que exiba a seguinte tabela (observe que 1
metro equivale a 3.280 pés):

Metros Pés
1 3.280
2 6.560
...
14 45.920
15 49.200

5.5 (Conversão de milímetros para polegadas e de polegadas para milímetros) Escreva um programa
que exiba as seguintes tabelas lado a lado (observe que 1 milímetro equivale a 0,039 polegadas):

Milímetros Polegadas | Polegadas Milímetros


0,078 | 10,156 | 2 25.641
24 51.282
...
98 3.822 | 49 | 50 1256.410
100 3.900 1282.051

5.6 (Conversão de metros para pés) Escreva um programa que exiba as seguintes tabelas lado a lado
(observe que 1 metro equivale a 3.280 pés):

Metros Pés |
Pés Metros
1 3.280 |
0,915
2 6.560 |
36 1.829
...
14 45.920 42 12.805
15 49.200 ||
45 13.720

5.7 (Usar funções trigonométricas) Imprima a tabela a seguir para exibir tan e cot
valores de graus de 0 a 60 com incrementos de 10 graus. Arredonde o valor para manter quatro
dígitos após a vírgula.
Machine Translated by Google

Exercícios de Programação 215

Grau 10 Seno/Cos Cos/Pecado


20 0,0000 informações

0,1736 5.6713
...
50 1.1918 0,8391
60 1.7320 0,5774

5.8 (Use a função exp ) Escreva um programa que imprima a seguinte tabela usando o
função exp :

Número Expoente
1
01 2.71828
...
8103.08
9 10 22026,5

**5.9 (Aplicação financeira: calcular o aluguel futuro do apartamento) Suponha que o aluguel de um apartamento
seja de $ 1.000 este ano e aumente 3% a cada ano. Escreva um programa que calcule o aluguel em
cinco anos e o aluguel total em um ano, começando daqui a cinco anos.

5.10 (Encontre o preço mais baixo) Escreva um programa que solicite ao usuário que insira o número de itens, o
nome e o preço de cada item e, finalmente, exiba o nome e o preço do item com o preço mais baixo.

*5.11 (Encontre os dois preços mais baixos) Escreva um programa que solicite ao usuário que insira o número de
itens, o nome e o preço de cada item e, finalmente, exiba o nome e o preço do item com a pontuação
mais baixa e do item com a segunda pontuação. menor preço.

5.12 (Encontre os números divisíveis por 3 e 4) Escreva um programa que exiba todos os números de 1 a 500,
5 por linha, que são divisíveis por 3 e 4. Os números são separados por exatamente um espaço.

5.13 (Encontre números divisíveis por 3 ou 6, mas não ambos) Escreva um programa que exiba todos os
números de 300 a 400, 5 por linha, que sejam divisíveis por 3 ou 6, mas não ambos.
Os números são separados por exatamente um espaço.

5.14 (Encontre o maior n tal que 2n <30.000)) Use um loop while para encontrar o maior
inteiro n tal que 2n é menor que 30.000.

5.15 (Encontre o menor n tal que 3n > 30.000) Use um loop while para encontrar o menor
inteiro n tal que 3n é maior que 30.000.

Seções 5.8–5.11

*5.16 (Calcular o máximo divisor comum) Outra solução para a Listagem 5.10 para encontrar o máximo divisor
comum (MDC) de dois inteiros n1 e n2 é a seguinte: Primeiro encontre d como o mínimo de n1 e n2,
depois verifique se d, d-1, d-2, . . . , 2 ou 1 é um divisor para n1 e n2 nesta ordem. O primeiro divisor
comum é o máximo divisor comum para n1 e n2. Escreva um programa que solicite ao usuário que
insira dois números inteiros positivos e exiba o GCD.

*5.17 (Exibir a tabela de caracteres ASCII) Escreva um programa que imprima todos os caracteres maiúsculos da
tabela de caracteres ASCII. Exibe 5 caracteres por linha. A tabela ASCII é mostrada no Apêndice B.
Os caracteres são separados por exatamente um espaço.

*5.18 (Primeiros cinco múltiplos de um inteiro) Escreva um programa que leia um inteiro e exiba
seus primeiros cinco múltiplos. Por exemplo, se o número inteiro de entrada for 10, a
saída deverá ser a seguinte: 10, 20, 30, 40, 50.
Machine Translated by Google

216 Capítulo 5 Loops

5.19 (Exibir pirâmide) Escreva um programa que solicite ao usuário que insira um número inteiro de 1 a 15 e
exiba uma pirâmide, conforme mostrado no exemplo a seguir:

Digite o número de linhas: 7


1
212
32124321 3
25432126543 34
21276543212 345
3456
34567

*5.20 (Exibir quatro padrões usando loops) Use loops aninhados que exibem os seguintes padrões em quatro
programas separados:

Padrão A Padrão B Padrão C Padrão D


123456 1 3 1
1 6 123 33 12
1 6 12345 333 123
1 6 1234567 33 1234
1 6 12345689 3 12345
123456 123456

**5.21 (Exibir números em um padrão de pirâmide) Escreva um loop for aninhado que imprima a seguinte saída:

abcdefghhgfedcba
abcdefggfedcba
abcdeffedcba
abcdeedcba
abcddcba
ABCBA
aba
ah
*5.22 (Exibir números não primos entre 1 e 100) Modifique a Listagem 5.17 para exibir todos os números não
primos entre 1 e 100. Exiba cinco números não primos por linha. Os números são separados por
exatamente um espaço.

Compreensivo
**5.23 (Aplicação financeira: compare empréstimos com diversas taxas de juros) Escreva um programa que permita
ao usuário inserir o valor do empréstimo e o período do empréstimo em número de anos e exiba os
pagamentos mensais e totais para cada taxa de juros, começando de 5% a 8%, com incremento de
1/8. Aqui está um exemplo de execução:

Valor do empréstimo: 10.000


Número de anos: 5
Taxa de juros Pagamento mensal Pagamento total
5,000% 188,71 11322,74
5,125% 189,28 11357.13
5,250% 189,85 11391,59
...
7,875% 202,17 12129,97
8,000% 202,76 12165,83

Para obter a fórmula para calcular o pagamento mensal, consulte a Listagem 2.11, ComputeLoan.cpp.
Machine Translated by Google

Exercícios de Programação 217

**5.24 (Aplicação financeira: cronograma de amortização do empréstimo) O pagamento mensal de


um determinado empréstimo paga o principal e os juros. Os juros mensais são calculados
Nota de vídeo
multiplicando a taxa de juros mensal e o saldo (o principal restante).
Exibir cronograma de empréstimo
O principal pago no mês é, portanto, o pagamento mensal menos os juros mensais.
Escreva um programa que permita ao usuário inserir o valor do empréstimo, o número de
anos e a taxa de juros e exiba o cronograma de amortização do empréstimo. Aqui está
um exemplo de execução:

Valor do empréstimo: 10.000


Número de anos: 1
Taxa de juros anual: 7

Pagamento Mensal: 865,26


Pagamento Total: 10383,21

Pagamento# Juros Saldo Principal


1 58,33 9193,07 806,93
2 53,62 8381,43 811,64
...
11 10h00 855,26 860,27
12 5h01 860,25 0,01

Observação

O saldo após o último pagamento não pode ser zero. Nesse caso, o último pagamento deverá ser o pagamento mensal normal mais o

saldo final.

Dica: Escreva um loop para exibir a tabela. Como o pagamento mensal é o mesmo para
cada mês, ele deve ser calculado antes do loop. O saldo é inicialmente o valor do
empréstimo. Para cada iteração no loop, calcule os juros e o principal e atualize o saldo.
O loop pode ficar assim:

for (i = 1; i <= númeroDeAnos * 12; i++) {

juros = taxa de juros mensal * saldo;


principal = pagamento mensal - juros;
saldo = saldo - principal;
cout << i << "\t\t" << juros
<< "\t\t" << principal << "\t\t" << saldo << endl;
}

*5.25 (Demonstrar erros de cancelamento) Um erro de cancelamento ocorre quando você está
manipulando um número muito grande com um número muito pequeno. O número
grande pode anular o número menor. Por exemplo, o resultado de 100000000,0 +
0,000000001 é igual a 100000000,0. Para evitar erros de cancelamento e obter
resultados mais precisos, selecione cuidadosamente a ordem de cálculo. Por
exemplo, ao calcular a série a seguir, você obterá resultados mais precisos
calculando da direita para a esquerda, em vez de da esquerda para a direita:
1 1 1
1+ + + c +
2 3 n

Escreva um programa que compare os resultados da soma das séries anteriores,


calculando da esquerda para a direita e da direita para a esquerda com n = 50.000.
Machine Translated by Google

218 Capítulo 5 Loops

*5.26 (Produto de uma série) Escreva um programa para calcular o produto da seguinte série:

1 1 1 1 1 1 1 1
* * * * * * c * *
5 9 13 17 21 25 93 97

**5.27 (Calcular p) Você pode aproximar p usando a seguinte série:

1 1 1 1 1
+ + + + c + 2
p = A6 * 11 + 4 9 16 25 n2

Escreva um programa que exiba o valor p para n = 100, 200, . . . e 600.


**5.28 (Calcular ex ) Você pode aproximar ex usando a seguinte série:

x x2 x3 x4 xn
ex = 1 + + + + + c +
1! 2! 3! 4! não!

Escreva um programa que solicite ao usuário que insira x e exiba o valor ex para n = 15.
(Dica: Porque n! = n * (n - 1) * c
* 2 * 1, então

1 1
é
não! n(n - 1)!

Inicialize ex e item como 1 e continue adicionando um novo item a ex. O novo


item é o item anterior multiplicado por x e dividido por n para n = 2, 3, 4,. . ., 15).
**5.29 (Exibir múltiplos de 10) Escreva um programa que exiba todos os múltiplos de 10,
15 por linha, de 4.000 a 4.500, separados por exatamente um espaço.
**5.30 (Exibir os domingos de um mês) Escreva um programa que solicite ao usuário que
insira o mês e o primeiro dia do mês e exiba todos os domingos daquele mês.
Por exemplo, se o usuário inseriu o mês 7 para julho e o primeiro dia 4 para
quarta-feira, seu programa deverá exibir a seguinte saída:

O primeiro dia deste mês é quarta-feira!


...
O próximo domingo deste mês é dia 26.

**5.31 (Exibir calendários) Escreva um programa que solicite ao usuário que insira o ano
e o primeiro dia do ano e exiba a tabela de calendário do ano no console.
Por exemplo, se o usuário digitou o ano 2013 e 2 para terça-feira, 1º de janeiro de
2013, seu programa deverá exibir o calendário para cada mês do ano, da seguinte
forma:

Janeiro de 2013
-----------------------------------------------

Sol Meu Terça-feira Coletar sex Sentado

1 2 3 4 5

6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
...
Machine Translated by Google

Exercícios de programação 219

dezembro de 2013
------------------------------------------------------------

Sol Meu ter qua Qui Sex Sábado


1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

*5.32 (Aplicação financeira: valor composto) Suponha que você economize US$ 100 por mês em uma conta
poupança com taxa de juros anual de 5%. Portanto, a taxa de juros mensal é 0,05/12 = 0,00417. Após
o primeiro mês, o valor na conta passa a ser

100 * (1 + 0,00417) = 100,417

Após o segundo mês, o valor na conta passa a ser

(100 + 100,417) * (1 + 0,00417) = 201,252

Após o terceiro mês, o valor da conta passa a ser

(100 + 201,252) * (1 + 0,00417) = 302,507

e assim por diante.

Escreva um programa que solicite ao usuário que insira um valor (por exemplo, 100), a taxa de juros
anual (por exemplo, 5) e o número de meses (por exemplo, 6) e exiba o valor na conta poupança após
o mês determinado.

*5.33 (Aplicação financeira: calcular o valor do CD) Suponha que você coloque US$ 10.000 em um CD com um
rendimento percentual anual de 5,75%. Depois de um mês, o CD vale

10.000 + 10.000 * 5,75/1.200 = 10.047,91

Depois de dois meses, o CD vale

10047,91 + 10047,91 * 5,75/1200 = 10096,06

Depois de três meses, o CD vale

10096,06 + 10096,06 * 5,75/1200 = 10144,43

e assim por diante.

Escreva um programa que solicite ao usuário que insira um valor (por exemplo, 10.000), o rendimento
percentual anual (por exemplo, 5,75) e o número de meses (por exemplo, 18) e exiba uma tabela
conforme mostrado na execução da amostra.

Insira o valor do depósito inicial: 10.000 Insira o rendimento


percentual anual: 5,75 Insira o período de vencimento
(número de meses): 18

Valor do CD do mês
1 10047,91
2 10096.06
...
17 10846,56
18 10898,54
Machine Translated by Google

220 Capítulo 5 Loops

*5.34 (Jogo: Predição) Revise o Exercício 3.14 para gerar um número aleatório de três dígitos. Os
três dígitos do número devem ser distintos. (Dica: gere o primeiro dígito. Use um loop
para gerar repetidamente o segundo e o terceiro dígitos até que sejam diferentes dos
outros dígitos.)
**5.35 (Série Fibonacci) A série Fibonacci é uma série que começa com 0 e 1 e tem a propriedade
de que cada termo seguinte é a soma dos dois termos anteriores.
Por exemplo, o terceiro número de Fibonacci é 1 , que é a soma de 0 e 1. O próximo é
2, que é a soma de 1 + 1. Escreva um programa que exiba os primeiros dez números
de uma série de Fibonacci.

***5.36 (Jogo: tesoura, pedra, papel) Revise o Exercício de Programação 3.15 para permitir que o
usuário jogue continuamente dez jogos. O programa deve exibir o número de vezes que
um usuário ganha, o computador ganha e o número de sorteios.
*5.37 (Somatório) Escreva um programa para calcular o seguinte somatório:

1 1 1 1
+ + + c +
23 99 - 23 93 23 93 - 23 87 23 87 - 23 81 23 9 - 23 3

**5.38 (número do palíndromo) Escreva um programa que solicita ao usuário que insira um número inteiro
e usa loops para simplificar o Exercício de Programação 3.36.
*5.39 (Aplicação financeira: encontre o lucro por item) Você acabou de abrir uma nova papelaria.
Seu lucro por item depende da quantidade total de item vendido.
O esquema mostrado abaixo é usado para determinar o lucro total:

Quantidade Lucro por item


0–1000 US$ 1

1001–5000 US$ 2

5001 e superior US$ 5

Observe que este é um lucro graduado. O lucro para vender até 1.000 itens é de $ 1, para
os próximos 4.000 itens é de $ 2 e além disso é de $ 5. Se a quantidade total do item
vendido for 10.000, o lucro será 1.000 * $ 1 + 4.000 * $ 2 + 5.000 * $ 5 = $ 34.000.
Sua meta é ganhar $ 50.000 por ano. Escreva um programa que use um do-while
loop para descobrir a quantidade mínima de item que você precisa vender para ganhar
$ 50.000.
5.40 (Simulação: par ou ímpar) Escreva um programa que gere um número inteiro aleatório cem
mil vezes e exiba o número de inteiros pares e ímpares.
**5.41 (Ocorrência de números máximos) Escreva um programa que leia inteiros, encontre o maior
deles e conte suas ocorrências. Suponha que a entrada termine com o número 0.
Suponha que você inseriu 3 5 2 5 5 5 0; o programa descobre que o maior é 5 e a
contagem de ocorrências para 5 é 4.

(Dica: mantenha duas variáveis, max e count. max armazena o número máximo atual
e count armazena suas ocorrências. Inicialmente, atribua o primeiro número a max e 1
a count. Compare cada número subsequente com max. Se o número for maior que
max, atribua-o a max e redefina a contagem para 1. Se o número for igual a max,
aumente a contagem em 1.)

Insira os números: 3 5 2 5 5 5 0
O maior número é 5
A contagem de ocorrências do maior número é 4
Machine Translated by Google

Exercícios de Programação 221

*5.42 (Aplicação financeira: encontre o lucro por item) Exercício de programação de reescrita
5.39 da seguinte forma:

n Use um loop while em vez de um loop do-while .


n Deixe o dono da loja inserir PROFIT_DESIRED em vez de fixá-lo como uma constante.

*5.43 (Simulação: contagem regressiva do relógio) Escreva um programa que solicite ao usuário que insira
o número de segundos, exiba uma mensagem a cada segundo e termine quando o tempo
expirar. Aqui está um exemplo de execução:

Insira o número de segundos: 3 2


segundos restantes
1 segundo restante
Parou

**5.44 (simulação de Monte Carlo) Um quadrado é dividido em quatro regiões menores, conforme mostrado
abaixo em (a). Se você lançar um dardo no quadrado 1.000.000 de vezes, qual é a
probabilidade de um dardo cair em uma região ímpar? Escreva um programa para simular o
processo e exibir o resultado.
(Dica: coloque o centro do quadrado no centro de um sistema de coordenadas, como mostrado
em (b). Gere aleatoriamente um ponto no quadrado e conte o número de vezes que um ponto
cai em uma região ímpar.)

2 2
3 1 3
1

4 4

(a) (b)

*5.45 (Matemática: combinações) Escreva um programa que exiba todas as combinações possíveis para
escolher dois números inteiros de 1 a 7. Além disso, exiba o número total de todas as
combinações.

12
13
...
...

O número total de todas as combinações é 21

*5.46 (Arquitetura do computador: operações em nível de bit) Um valor curto é armazenado em 16 bits.
Escreva um programa que solicite ao usuário que insira um número inteiro curto e exiba os 16
bits para o número inteiro. Aqui estão exemplos de execuções:

Insira um número inteiro: 5


Os bits são 0000000000000101

Insira um número inteiro: -5


Os bits são 1111111111111011
Machine Translated by Google

222 Capítulo 5 Loops


(Dica: você precisa usar o operador de deslocamento à direita bit a bit (>>) e o AND bit a bit
operador (&), que são abordados no Apêndice E.)
**5.47 (Estatísticas: calcular média e desvio padrão) Em aplicativos de negócios, muitas vezes você
é solicitado a calcular a média e o desvio padrão dos dados. A média é simplesmente
a média dos números. O desvio padrão é uma estatística que informa o quão firmemente
todos os vários dados estão agrupados em torno da média em um conjunto de dados.
Por exemplo, qual é a idade média dos alunos de uma turma? Quão próximas estão as
idades? Se todos os alunos tiverem a mesma idade, o desvio será 0.
Escreva um programa que solicite ao usuário que insira 10 números e exiba a
média e os desvios padrão desses números usando a seguinte fórmula:

n
n n
-¢ uma
eu= 1xiÿ 2
a XI 2xi _
= x1 + x2 + + xn n
c
eu=1 eu=1
média =
n n desvio = a a n-1
Aqui está um exemplo de execução:

Insira dez números: 1 2 3 4,5 5,6 6 7 8 9 10 A média é 5,61

O desvio padrão é 2,99794

*5.48 (Contar letras maiúsculas) Escreva um programa que solicite ao usuário que insira uma
string e exiba o número de letras maiúsculas na string. Aqui está um exemplo de execução:

Digite uma string: Programar é divertido


O número de letras maiúsculas é 3

*5.49 (Prefixo comum mais longo) Escreva um programa que solicite ao usuário que insira duas
strings e exiba o maior prefixo comum das duas strings. Aqui estão exemplos de execuções:

Digite s1: Programar é divertido


Digite s2: Programa usando um idioma
O prefixo comum é Programa

Digite s1: ABC


Digite s2:CBA
ABC e CBA não têm prefixo comum

*5.50 (Inverter uma string) Escreva um programa que solicite ao usuário que insira uma string e dis-
toca a corda na ordem inversa.

Insira uma string: ABCD


A string invertida é DCBA
Machine Translated by Google

Exercícios de Programação 223

*5.51 (Negócios: verifique ISBN-13) ISBN-13 é um novo padrão para identificação de livros. Ele usa 13 dígitos
d1d2d3d4d5d6d7d8d9d10d11d12d13. O último dígito d13 é uma soma de verificação, calculada a partir
dos outros dígitos usando a seguinte fórmula:

10 - (d1 + 3d2 + d3 + 3d4 + d5 + 3d6 + d7 + 3d8 + d9 + 3d10 + d11


+ 3d12)%10

Se a soma de verificação for 10, substitua-a por 0. Seu programa deverá ler a entrada como uma string.
Aqui estão exemplos de execuções:

Insira os primeiros 12 dígitos de um ISBN-13 como uma string: 978013213080


O número ISBN-13 é 9780132130806

Insira os primeiros 12 dígitos de um ISBN-13 como uma string: 978013213079


O número ISBN-13 é 9780132130790

Insira os primeiros 12 dígitos de um ISBN-13 como uma string: 97801320


97801320 é uma entrada inválida

*5.52 (Process string) Escreva um programa que solicita ao usuário que insira uma string e dis-
reproduz os personagens em posições de índice ímpares. Aqui está um exemplo de execução:

Insira uma string: ABeijing Chicago


BiigCiao

*5.53 (Contar vogais e consoantes) Suponha que as letras A, E, I, O e U sejam vogais.


Escreva um programa que solicite ao usuário que insira uma string e exiba o número de vogais e
consoantes na string.

Digite uma string: Programar é divertido


O número de vogais é 5
O número de consoantes é 11

**5.54 (Soma, média e produto de números) Escreva um programa que calcule a


soma, média e produto de números em um arquivo chamado numbers.txt.
**5.55 (Tutor de matemática) Escreva um programa que exiba um menu conforme mostrado no exemplo de execução.
Você pode inserir 1, 2, 3 ou 4 para escolher um teste de adição, subtração, multiplicação ou divisão.
Após a conclusão de um teste, o menu é exibido novamente. Você pode escolher outro teste ou digitar
5 para sair do sistema. Cada teste gera dois números aleatórios de um único dígito para formar uma
questão de adição, subtração, multiplicação ou divisão. Para uma subtração como número1 –
número2, número1 é maior ou igual a número2. Para uma questão de divisão como número1/
número2, número2 não é zero.
Machine Translated by Google

224 Capítulo 5 Loops

Menu principal
1: Adição 2:
Subtração 3:
Multiplicação 4: Divisão 5:
Sair Insira uma
opção: 1
O que é 1 + 7? 8 Correto

Menu principal
1: Adição 2:
Subtração 3:
Multiplicação 4: Divisão 5:
Sair Insira uma
opção: 1
O que é 4 + 0? 5 Sua
resposta está errada. A
resposta correta é 4

Menu principal
1: Adição 2:
Subtração 3:
Multiplicação 4: Divisão 5:
Sair Insira uma
opção: 4
O que é 4/5? 1 Sua resposta
está errada. A resposta
correta é 0

Menu principal
1: Adição
2: Subtração
3: Multiplicação
4: Divisão
5: Sair
Insira uma escolha:

*5.56 (Coordenadas do ponto de canto) Suponha que um polígono regular de n lados esteja
centrado em (0, 0) com um ponto na posição de 3 horas, como mostrado na Figura
5.4. Escreva um programa que solicite ao usuário que insira o número de lados, o raio
do círculo delimitador de um polígono e exiba as coordenadas dos pontos de vértice
do polígono.

R Posição das 3 horas R Posição das 3 horas

(0, 0) (0, 0)

Figura 5.4 Um polígono de n lados está centrado em (0, 0) com um ponto na posição
das 3 horas.
Machine Translated by Google

Exercícios de Programação 225

Aqui está um exemplo de execução:

Insira o número de lados: 6 Insira o raio do


círculo delimitador: 100 As coordenadas dos pontos no
polígono são (100, 0)

(50.0001, 86.6025)
(-49,9998, 86,6026)
(-100, 0,000265359)
(-50,0003, -86,6024)
(49.9996, -86.6028)

**5.57 (Verifique a carteira de estudante) Algumas faculdades impõem certas regras para a carteira de estudante. Suponha que
as regras da ID do estudante sejam as seguintes:

n Uma ID de estudante deve ter exatamente dez caracteres.


n Uma ID de estudante deve consistir apenas de dígitos e letras.
n Uma ID de estudante deve sempre começar com um dígito.

Escreva um programa que solicite ao aluno que insira uma ID de estudante e exiba uma ID de estudante válida
se as regras forem seguidas ou uma ID de estudante inválida caso contrário.
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

CAPÍTULO

6
Funções

Objetivos
n Definir funções com parâmetros formais (§6.2).

n Para definir/invocar funções de retorno de valor (§6.3).

n Para definir/invocar funções void (§6.4).

n Para passar argumentos por valor (§6.5).

n Desenvolver código reutilizável que seja modular, fácil de ler, fácil de depurar e fácil de
manter (§6.6).

n Para usar sobrecarga de funções e entender ambíguos


sobrecarga (§6.7).
n Usar protótipos de funções para declarar cabeçalhos de funções (§6.8).

n Para definir funções com argumentos padrão (§6.9).

n Para melhorar a eficiência do tempo de execução para funções curtas usando


funções inline (§6.10).

n Determinar o âmbito das variáveis locais e globais (§6.11).

n Passar argumentos por referência e compreender as diferenças entre passagem por


valor e passagem por referência (§6.12).

n Para declarar parâmetros const para evitar que sejam modificados


acidentalmente (§6.13).
n Para escrever uma função que converte um número hexadecimal em decimal
número (§6.14).
n Projetar e implementar funções usando refinamento gradual (§6.15).
Machine Translated by Google

228 Capítulo 6 Funções

6.1 Introdução
As funções podem ser usadas para definir código reutilizável e organizar e simplificar o código.
Chave
Apontar
Suponha que você precise encontrar a soma dos números inteiros de 1 a 10, de 20 a 37 e de 35 a 49,
problema respectivamente. Você pode escrever o código da seguinte maneira:

soma interna = 0;
para (int i = 1; i <= 10; i++)
soma += eu;
cout << "A soma de 1 a 10 é " << soma << endl;

soma = 0;
para (int i = 20; i <= 37; i++)
soma += eu;
cout << "A soma de 20 a 37 é " << soma << endl;

soma = 0;
para (int i = 35; i <= 49; i++)
soma += eu;
cout << "A soma de 35 a 49 é " << soma << endl;

Você deve ter observado que calcular essas somas de 1 a 10, de 20 a 37 e de 35 a 49 são muito
semelhantes, exceto que os números inteiros inicial e final são diferentes. Não seria bom se pudéssemos
escrever o código comum uma vez e reutilizá-lo? Podemos fazer isso definindo uma função e invocando-a.
por que usar funções?
O código anterior pode ser simplificado da seguinte forma:

definir função de soma 1 soma interna (int i1, int i2)


2{
soma interna = 0;
para (int i = i1; i <= i2; i++)
soma += eu;

soma de retorno ;
345678}
9
função principal 10 int principal()
11 {
invocar função de soma 12 cout << "A soma de 1 a 10 é " << sum(1, 10) << endl;
13 cout << "A soma de 20 a 37 é " << sum(20, 37) << endl;
14 cout << "A soma de 35 a 49 é " << sum(35, 49) << endl;
15
16 retornar 0;
17}

As linhas 1 a 8 definem a função chamada soma com dois parâmetros i1 e i2. As instruções na função
principal invocam sum(1, 10) para calcular a soma de 1 a 10, e sum(20, 37)
para calcular a soma de 20 a 37 e sum(35, 49) para calcular a soma de 35 a 49.
função Uma função é uma coleção de instruções agrupadas para realizar uma operação. Nos capítulos anteriores,
você aprendeu sobre funções como pow(a, b), rand(), srand(seed), time(0) e main(). Quando você chama a
função pow(a, b) , por exemplo, o sistema realmente executa as instruções da função e retorna o resultado.
Neste capítulo, você aprenderá como definir e usar funções e aplicar abstração de funções para resolver
problemas complexos.
Machine Translated by Google

6.2 Definindo uma Função 229

6.2 Definindo uma Função


Uma definição de função consiste em seu nome de função, parâmetros, tipo de valor de retorno Chave
e corpo. Apontar

A sintaxe para definir uma função é a seguinte:

returnValueType functionName(lista de parâmetros) {

// Corpo da função;
}

Vejamos uma função criada para descobrir qual dos dois inteiros é maior. Esta função, denominada max,
possui dois parâmetros int , num1 e num2, o maior dos quais é retornado pela função. A Figura 6.1 ilustra
os componentes desta função.

Definir uma função Invocar uma função

valor de retorno tipo nome da função parâmetros formais

função int máximo(int num1, int num2) int z = máx(x, y);


cabeçalho {

função resultado interno ; lista de parâmetros parâmetros reais


corpo (argumentos)
se (num1 > num2)
resultado = num1; função
outro
assinatura
resultado = num2;

resultado de retorno ; valor de retorno


}

Figura 6.1 Você pode definir uma função e invocá-la com argumentos.

O cabeçalho da função especifica o tipo de valor de retorno da função, o nome da função e os parâmetros. cabeçalho de função

Uma função pode retornar um valor. O returnValueType é o tipo de dados desse valor.
Algumas funções realizam operações desejadas sem retornar um valor. Nesse caso, returnValueType é a
palavra-chave void. Por exemplo, o returnValueType no srand
a função é nula. A função que retorna um valor é chamada de função de retorno de valor e a função que não função de retorno de valor
retorna um valor é chamada de função void. função vazia

As variáveis declaradas no cabeçalho da função são conhecidas como parâmetros formais ou parâmetro formal
simplesmente parâmetros. Um parâmetro é como um espaço reservado. Quando uma função é invocada, parâmetro
você passa um valor para o parâmetro. Este valor é referido como um parâmetro ou argumento real . A lista parâmetro real
de parâmetros refere-se ao tipo, ordem e número dos parâmetros de uma função. O nome da função e a lista argumento
de parâmetros juntos constituem a assinatura da função. Os parâmetros são opcionais; isto é, uma função lista de parâmetros

não pode conter parâmetros. Por exemplo, a função Rand() não possui parâmetros. assinatura de função

O corpo da função contém uma coleção de instruções que definem o que a função faz.
O corpo da função max usa uma instrução if para determinar qual número é maior e retorna o valor desse
número. Uma instrução de retorno usando a palavra-chave return
é necessário para que uma função que retorna valor retorne um resultado. A função termina quando uma
instrução return é executada.
Machine Translated by Google

230 Capítulo 6 Funções


Cuidado
No cabeçalho da função, você precisa declarar cada parâmetro separadamente. Por exemplo, max(int
num1, int num2) está correto, mas max(int num1, num2) está errado.

6.3 Chamando uma Função


Chamar uma função executa o código da função.
Chave
Apontar
Ao criar uma função, você define o que ela deve fazer. Para usar uma função, você deve chamá-la ou invocá- la.
Existem duas maneiras de chamar uma função, dependendo se ela retorna ou não um valor.
Se a função retornar um valor, uma chamada para essa função geralmente será tratada como um valor. Por
exemplo,

int maior = max(3, 4);

chama max(3, 4) e atribui o resultado da função à variável maior. Outro exemplo de tal chamada é

cout << máx(3, 4);

que imprime o valor de retorno da chamada de função max(3, 4).

Observação

Uma função que retorna valor também pode ser invocada como uma instrução em C++. Nesse caso, o
chamador simplesmente ignora o valor de retorno. Isso não é feito com frequência, mas é permitido se o
chamador não estiver interessado no valor retornado.

Quando um programa chama uma função, o controle do programa é transferido para a função chamada. A
função chamada é executada. Uma função chamada retorna o controle ao chamador quando sua instrução de
retorno é executada ou quando sua chave de fechamento de finalização de função é alcançada.
A Listagem 6.1 mostra um programa completo que é usado para testar a função max .

Nota de vídeo

A função máxima
Listagem 6.1 TestMax.cpp
1 #include <iostream>
2 usando namespace std;
3
4 // Retorna o máximo entre dois números 5 int max(int
definir função máxima num1, int num2) 6 {

resultado interno ;

se (num1 > num2)


7 resultado = num1;
8 outro
9 resultado = num2;
10
11 resultado de retorno ;
12 13 14 15 }
16
função principal 17 int principal()
18 {
19 int eu = 5;
20 int j = 2;
invocar máximo 21 int k = máx(i, j);
22 cout << "O máximo entre " << j << "é" << eu <<
23 "e" << k << final;
24
25 retornar 0;
26 }
Machine Translated by Google

6.3 Chamando uma Função 231

O máximo entre 5 e 2 é 5

Linha# eu
j k num1 num2 resultado

19 5

20 2

5 5 2

Invocando o máximo 7 indefinido

10 5

21 5

Este programa contém a função max e a função principal . A função principal é como qualquer outra função principal

função, exceto pelo fato de ser invocada pelo sistema operacional para executar o programa. Todas as outras
funções devem ser executadas por instruções de chamada de função.
Uma função deve ser definida antes de ser invocada. Como a função max é invocada pelo ordem de função
função principal , ela deve ser definida antes da função principal .
Quando a função max é invocada (linha 21), o valor 5 da variável i é passado para num1 e o valor 2 da função máxima

variável j é passado para num2 na função max . O fluxo de controle é transferido para a função max . A função
max é executada. Quando a instrução return na função max é executada, a função max retorna o controle ao
seu chamador (neste caso, o chamador é o principal
função). Este processo é ilustrado na Figura 6.2.

Passe o valor i

Passe o valor j

int principal() int máximo(int num1, int num2)


{ {
int eu = 5; resultado interno ;
int j = 2;
int k = máx(i, j); se (num1 > num2)
resultado = num1;
cout << "O máximo entre" outro
<< i << "e" + j + "é" resultado = num2;
<< k;
retornar 0; resultado de retorno ;
} }

Figura 6.2 Quando a função max é invocada, o fluxo de controle é transferido para ela. Assim que a função
max for concluída, ela retorna o controle ao chamador.

Cada vez que uma função é invocada, o sistema cria um registro de ativação (também chamado de quadro registro de ativação

de ativação) que armazena seus argumentos e variáveis para a função e coloca o registro de ativação em uma
área da memória conhecida como pilha de chamadas. Uma pilha de chamadas também é conhecida como pilha pilha

de execução, pilha de tempo de execução ou pilha de máquina e geralmente é abreviada para apenas “a pilha”.
Quando uma função chama outra função, o registro de ativação do chamador é mantido intacto e um novo
registro de ativação é criado para a nova chamada de função. Quando uma função termina seu trabalho e
retorna o controle ao seu chamador, seu registro de ativação é removido da pilha de chamadas.
Uma pilha de chamadas armazena os registros de ativação do tipo último a entrar, primeiro a sair. O registro
de ativação da função invocada por último é removido primeiro da pilha. Suponha que a função m1 chame a
função m2 e então m2 chame m3. O sistema de tempo de execução envia o registro de ativação de m1 para o
Machine Translated by Google

232 Capítulo 6 Funções

pilha, depois m2 e depois m3. Após a conclusão do m3 , seu registro de ativação é removido da
pilha. Após a conclusão do m2 , seu registro de ativação é removido da pilha. Após a conclusão de
m1 , seu registro de ativação é removido da pilha.
Compreender as pilhas de chamadas nos ajuda a compreender como as funções são invocadas.
As variáveis definidas na função principal são i, j e k. As variáveis definidas na função max são
num1, num2 e resultado. As variáveis num1 e num2 são definidas na assinatura da função e são
parâmetros da função. Seus valores são passados por meio de invocação de função. A Figura 6.3
ilustra os registros de ativação na pilha.

Registro de ativação para a


função max
resultado: 5
num2: 2
num1: 5
Registro de ativação para a Registro de ativação para a Registro de ativação para a
função principal função principal função principal
k: k: k: 5 A pilha está vazia
j: 2 j: 2 j: 2
eu: 5 eu: 5 eu: 5

(a) A função principal (b) O máximo (c) A função máxima é (d) A função principal
é invocado. função é invocada. terminou e o retorno está terminado.
o valor é enviado para k.

Figura 6.3 Quando a função max é invocada, o fluxo de controle é transferido para a função max . Assim que a função max for
concluída, ela retorna o controle ao chamador.

6.4 Funções nulas


Uma função void não retorna um valor.
Chave
Apontar
A seção anterior fornece um exemplo de função que retorna valor. Esta seção mostra como definir
e invocar uma função void. A Listagem 6.2 fornece um programa que define uma função chamada
printGrade e a invoca para imprimir a nota de uma determinada pontuação.

Listagem 6.2 TestVoidFunction.cpp


Nota de vídeo 1 #include <iostream>
função nula vs. função de retorno de valor
2 usando namespace std;
3
4 // Imprime a nota da pontuação 5 void
função printGrade printGrade(double score)
6{
7 se (pontuação >= 90,0)
8 cout << 'A' << endl;
9 senão se (pontuação >= 80,0)
10 cout << 'B' << endl;
11 senão se (pontuação >= 70,0)
12 cout << 'C' << endl;
13 senão se (pontuação >= 60,0)
14 cout << 'D' << endl;
15 outro
16 cout << 'F' << endl;
17}
18
função principal 19 int principal()
20 {
21 cout << "Insira uma pontuação: ";
22 pontuação dupla ;
23 cin >> pontuação;
24
Machine Translated by Google

6.4 Funções nulas 233

25 cout << "A nota é ";


26 printGrade(pontuação); invocar printGrade
27
28 retornar 0;
29 }

Insira uma pontuação: 78,5

A nota é C

A função printGrade é uma função nula. Não retorna nenhum valor. Uma chamada para uma invocar função void
função void deve ser uma instrução. Portanto, é invocado como uma instrução na linha 26 da função principal.
Como qualquer instrução C++, ela termina com ponto e vírgula.
Para ver as diferenças entre uma função void e uma função que retorna valor, vamos redesenhar a nulo vs. valor retornado
função printGrade para retornar um valor. Chamamos a nova função que retorna a nota, conforme
mostrado na Listagem 6.3, de getGrade.

Listagem 6.3 TestReturnGradeFunction.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Retorna a nota da pontuação 5 char
getGrade(double score) função getGrade
6{
7 se (pontuação >= 90,0)
8 retornar 'A';
9 senão se (pontuação >= 80,0)
10 retornar 'B';
11 senão se (pontuação >= 70,0)
12 retornar 'C';
13 senão se (pontuação >= 60,0)
14 retornar 'D';
15 outro
16 retornar 'F';
17 }
18
19 int principal() função principal
20 {
21 cout << "Insira uma pontuação: ";
22 pontuação dupla ;
23 cin >> pontuação;
24
25 cout << "A nota é ";
26 cout << getGrade(pontuação) << endl; invocar getGrade
27
28 retornar 0;
29 }

Insira uma pontuação: 78,5

A nota é C

A função getGrade definida nas linhas 5 a 17 retorna uma nota de caractere com base no valor
numérico da pontuação. O chamador invoca esta função na linha 26.
A função getGrade pode ser invocada por um chamador sempre que um caractere aparecer. O
A função printGrade não retorna nenhum valor. Deve ser invocado como uma declaração.
Machine Translated by Google

234 Capítulo 6 Funções

Nota
retornar na função void Uma instrução return não é necessária para uma função void, mas pode ser usada para
encerrar a função e retornar o controle ao chamador da função. A sintaxe é simplesmente

retornar;

Isto é raro, mas às vezes é útil para contornar o fluxo normal de controle em uma função
nula. Por exemplo, o código a seguir possui uma instrução return para encerrar a função
quando a pontuação for inválida.

// Imprime a nota da pontuação void


printGrade(double score) {

if (pontuação < 0 || pontuação > 100) {

cout << "Pontuação inválida";


retornar;
}

if (pontuação >= 90,0)


cout << 'A';
senão if (pontuação >= 80,0)
cout << 'B';
senão if (pontuação >= 70,0)
cout << 'C';
senão if (pontuação >= 60,0)
cout << 'D';
outro
cout << 'F';
}

Observação Ocasionalmente, você pode precisar encerrar o programa da função


imediatamente se ocorrer uma condição anormal. Isso pode ser feito invocando a função
exit(int) definida no cabeçalho cstdlib . Você pode passar qualquer número inteiro para
invocar esta função para indicar um erro no programa. Por exemplo, a função a seguir
encerra o programa se uma pontuação inválida for passada para a função.

// Imprime a nota da pontuação void


printGrade(double score) {

if (pontuação < 0 || pontuação > 100) {

cout << "Pontuação inválida" << endl;


saída(1);
}

if (pontuação >= 90,0)


cout << 'A';
senão if (pontuação >= 80,0)
cout << 'B';
senão if (pontuação >= 70,0)
cout << 'C';
senão if (pontuação >= 60,0)
cout << 'D';
outro
cout << 'F';
}
Machine Translated by Google

6.5 Passando Argumentos por Valor 235

6.1 Quais são os benefícios de usar uma função?

6.2 Como você define uma função? Como você invoca uma função? ÿVerificação de ponto

6.3 Como simplificar a função max da Listagem 6.1 usando uma expressão condicional?

6.4 Verdadeiro ou falso? Uma chamada para uma função com tipo de retorno void é sempre uma instrução
em si, mas uma chamada para uma função que retorna valor não pode ser uma instrução por si só.

6.5 Qual é o tipo de retorno de uma função principal ?

6.6 O que haveria de errado em não escrever uma declaração de retorno em um método de retorno de valor?
função? Você pode ter uma instrução de retorno em uma função void ? Será que o retorno
instrução na função a seguir causa erros de sintaxe?

vazio p (duplo x, duplo y)


{
""
conta << x << << e << fim;
retornar x + y;
}

6.7 Defina os termos parâmetro, argumento e assinatura de função.

6.8 Escreva cabeçalhos de função (não os corpos) para as seguintes funções:

a. Retorne uma comissão de vendas, considerando o valor das vendas e a taxa de comissão.

b. Exiba o calendário de um mês, considerando o mês e o ano.

c. Retorna uma raiz quadrada de um número.

d. Teste se um número é par e retorne verdadeiro se for.

e. Exibir uma mensagem um determinado número de vezes.

f. Devolva o pagamento mensal, considerando o valor do empréstimo, número de anos e anual


taxa de juro.

g. Retorne a letra maiúscula correspondente, dada uma letra minúscula.

6.9 Identifique e corrija os erros no seguinte programa:

int função1(int n) {

contagem << n;
}

função2(int n, m) {

n+=m;
função1(3,4);
}

6.5 Passando Argumentos por Valor


Por padrão, os argumentos são passados por valor aos parâmetros ao invocar uma função.
Chave
Apontar

O poder de uma função é sua capacidade de trabalhar com parâmetros. Você pode usar max para encontrar o
máximo entre quaisquer dois valores int . Ao chamar uma função, você precisa fornecer argumentos, que devem
ser fornecidos na mesma ordem que seus respectivos parâmetros na assinatura da função. Isso é conhecido como
associação de ordem de parâmetro. Por exemplo, a função a seguir imprime um caractere n vezes: associação de ordem de parâmetro
Machine Translated by Google

236 Capítulo 6 Funções


void nPrint(char ch, int n) {

para (int i = 0; i <n; i++)


cout << ch;
}

Você pode usar nPrint('a', 3) para imprimir 'a' três vezes. A instrução nPrint('a', 3) passa o parâmetro char
real, 'a', para o parâmetro ch; passa 3 para n; e imprime 'a'
três vezes. No entanto, a instrução nPrint(3, 'a') tem um significado diferente. Passa 3
para ch e 'a' para n.

6.10 O argumento pode ter o mesmo nome do seu parâmetro?


ÿVerificação de ponto
6.11 Identifique e corrija os erros no seguinte programa:

1 void nPrintln(string mensagem, int n) 2 {

3 interno n = 1;
4 para (int i = 0; i <n; i++)
cout << mensagem << endl;
5 6}

7 8 int principal()
9{
10 nPrintln(5, "Bem-vindo ao C++!");
11}

6.6 Modularizando Código


A modularização torna o código fácil de manter e depurar e permite que o código seja
Chave
Apontar reutilizado.

Funções podem ser usadas para reduzir código redundante e permitir a reutilização de código. As funções também

podem ser usadas para modularizar o código e melhorar a qualidade do programa.


Nota de vídeo
Modularizar código
A Listagem 5.10, GreatestCommonDivisor.cpp, fornece um programa que solicita ao usuário que insira
dois números inteiros e exibe seu máximo divisor comum. Você pode reescrever o programa usando uma
função, conforme mostrado na Listagem 6.4.

Listagem 6.4 GreatestCommonDivisorFunction.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Retorna o mdc de dois inteiros 5 int gcd(int
calcular o mdc n1, int n2)
6{
int mdc = 1; // O MDC inicial é 1
int k = 2; // Possível mdc

7 enquanto (k <= n1 && k <= n2)


8 {
9 se (n1% k == 0 && n2% k == 0)
10 mdc = k; //Atualiza o gcd
11 k++;
12 }
13
retornar mdc 14 retornar mdc; //Retorna mdc
15 16 17 18 }
Machine Translated by Google

6.6 Código de Modularização 237

19
20 int principal()
21 {
// Solicita ao usuário que insira dois números inteiros
22 23 cout << "Insira o primeiro número inteiro: ";
24 você é n1;
25 comer >> n1;
26
27 cout << "Digite o segundo inteiro: ";
28 interno n2;
29 comer >> n2;
30
31 cout << "O máximo divisor comum para" << n1 <<
32 "e" << n2 << "é" << gcd(n1, n2) << endl; invocar o mdc
33
34 retornar 0;
35 }

Insira o primeiro número inteiro: 45


Insira o segundo número inteiro: 75
O máximo divisor comum para 45 e 75 é 15

Ao encapsular o código para obtenção do GCD em uma função, este programa apresenta diversas
vantagens:

1. Isola o problema de cálculo do GCD do restante do código principal


função. Assim, a lógica fica clara e o programa fica mais fácil de ler.

2. Se houver erros no cálculo do GCD, eles ficarão confinados na função gcd , que
restringe o escopo da depuração.

3. A função gcd agora pode ser reutilizada por outros programas.

A Listagem 6.5 aplica o conceito de modularização de código para melhorar a Listagem 5.17,
PrimeNumber.cpp. O programa define duas novas funções isPrime e printPrimeNumbers. A função
isPrime verifica se um número é primo e a função printPrimeNumbers imprime números primos.

Listagem 6.5 PrimeNumberFunction.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;
4
5 // Verifica se o número é primo 6 bool
isPrime(int number) função isPrime
7{
for (int divisor = 2; divisor <= número / 2; divisor++)
{
8 if (número% divisor == 0)
9 {
10 //Se verdadeiro, o número não é primo
11 retorna falso; //número não é primo
12 }
13 }
14
15 retornar verdadeiro; //número é primo
16 17 18 }
Machine Translated by Google

238 Capítulo 6 Funções


19
imprimirPrimeNumbers 20 void printPrimeNumbers(int númeroOfPrimes)
função 21 {
22 const int NUMBER_OF_PRIMES = 50; //Número de primos a serem exibidos
23 const int NUMBER_OF_PRIMES_PER_LINE = 10; //Mostra 10 por linha
24 contagem interna = 0; //Conta o número de números primos
25 número interno = 2; // Um número a ser testado quanto à primocidade
26
27 // Encontra repetidamente números primos
28 while (contagem < númeroOfPrimes)
29 {
30 // Imprime o número primo e aumenta a contagem
invocar isPrime 31 if (éPrime(número))
32 {
33 contar++; //Aumenta a contagem
34
35 se (contagem% NUMBER_OF_PRIMES_PER_LINE == 0)
36 {
37 // Imprime o número e avança para a nova linha
38 cout << setw(4) << número << endl;
39 }
40 outro
41 cout << setw(4) << número;
42 }
43
44 // Verifica se o próximo número é primo
45 número++;
46 }
47 }
48
49 int principal()
50 {
51 cout << "Os primeiros 50 números primos são \n";
invocar printPrimeNumbers printPrimeNumbers(50);

retornar 0;
52 53 54 55 }

Os primeiros 50 números primos são


2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229

Dividimos um grande problema em dois subproblemas. Como resultado, o novo programa é


mais fácil de ler e depurar. Além disso, as funções printPrimeNumbers e isPrime podem ser
reutilizadas por outros programas.

6.7 Funções de Sobrecarga


A sobrecarga de funções permite definir funções com o mesmo nome, desde que suas
Chave
Apontar assinaturas sejam diferentes.

A função max usada anteriormente funciona apenas com o tipo de dados int . Mas e se você
precisar determinar qual dos dois números de ponto flutuante tem o valor maior? A solução
Machine Translated by Google

6.7 Funções de Sobrecarga 239

é criar outra função com o mesmo nome, mas com parâmetros diferentes, conforme mostrado no
código a seguir:

duplo máximo(duplo num1, duplo num2) {

se (num1 > num2)


retornar num1;
outro
retornar num2;
}

Se você chamar max com parâmetros int , a função max que espera parâmetros int será
invocada; se você chamar max com parâmetros duplos , a função max que espera double
parâmetros serão invocados. Isso é conhecido como sobrecarga de função; isto é, duas funções sobrecarga de função
têm o mesmo nome, mas listas de parâmetros diferentes dentro de um arquivo. O compilador C+
+ determina qual função é usada com base na assinatura da função.
O programa na Listagem 6.6 cria três funções. O primeiro encontra o número inteiro máximo, o
segundo encontra o máximo duplo e o terceiro encontra o máximo entre três valores duplos. Todas
as três funções são denominadas max.

Listagem 6.6 TestFunctionOverloading.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Retorna o máximo entre dois valores int 5 int max(int num1,
int num2) função máxima
6{
se (num1 > num2)
retornar num1;
outro
7 retornar num2;
8 9 10 11 }
12
13 // Encontre o máximo entre dois valores duplos 14 double
max(double num1, double num2) função máxima
15 {
16 se (num1 > num2)
17 retornar num1;
18 outro
19 retornar num2;
20 }
21
22 // Retorna o máximo entre três valores duplos 23 double
max(double num1, double num2, double num3) função máxima
24 {
25 retornar máx(máx(num1, num2), num3);
26}
27
28 int principal() função principal
29 {
30 // Invoca a função max com parâmetros int
31 cout << "O máximo entre 3 e 4 é " << max(3, 4) << endl; invocar máximo
32
33 // Invoca a função max com os parâmetros double
34 cout << "O máximo entre 3,0 e 5,4 é "
35 << max(3,0, 5,4) << endl; invocar máximo
36
37 // Invoca a função max com três parâmetros duplos
Machine Translated by Google

240 Capítulo 6 Funções


38 cout << "O máximo entre 3,0, 5,4 e 10,14 é "
invocar máximo 39 << máx(3,0, 5,4, 10,14) << endl;
40
41 retornar 0;
42 }

Ao chamar max(3, 4) (linha 31), a função max para encontrar o máximo de dois inteiros é invocada. Ao
chamar max(3.0, 5.4) (linha 35), a função max para encontrar o máximo de dois duplos é invocada. Ao
chamar max(3.0, 5.4, 10.14) (linha 39), a função max para encontrar o máximo de três valores duplos é
invocada.
Você pode invocar a função max com um valor int e um valor duplo , como max(2, 2.5)? Se possível,
qual das funções max é invocada? A resposta à primeira pergunta é sim. A resposta à segunda é que a
função max para encontrar o máximo de dois valores duplos é invocada. O valor do argumento 2 é
automaticamente convertido em um valor duplo
valor e passado para esta função.
Você pode estar se perguntando por que a função max(double, double) não é invocada para a chamada max(3, 4). Tanto
max(double, double) quanto max(int, int) são correspondências possíveis para max(3, 4). O compilador C++ encontra a função mais
específica para uma invocação de função. Como a função max(int, int) é mais específica que max(double, double), max(int, int) é
usado para invocar max(3, 4).

Dica
A sobrecarga de funções pode tornar os programas mais claros e legíveis. Funções que executam a
mesma tarefa com diferentes tipos de parâmetros devem receber os mesmos
nome.

Observação

Funções sobrecarregadas devem ter listas de parâmetros diferentes. Você não pode sobrecarregar
funções com base em diferentes tipos de retorno.

Às vezes, há duas ou mais correspondências possíveis para a invocação de uma função e o compilador
invocação ambígua não consegue determinar a correspondência mais específica. Isso é chamado de invocação ambígua. A
invocação ambígua causa um erro de compilação. Considere o seguinte código:

#include <iostream>
usando namespace std;

int maxNumber(int num1, double num2)


{
se (num1 > num2)
retornar num1;
outro
retornar num2;
}

double maxNumber(duplo num1, int num2)


{
se (num1 > num2)
retornar num1;
outro
retornar num2;
}

int principal()
{
cout << maxNumber(1, 2) << endl;

retornar 0;
}
Machine Translated by Google

6.8 Protótipos de Função 241

Tanto maxNumber(int, double) quanto maxNumber(double, int) são possíveis candidatos para
corresponder a maxNumber(1, 2). Como nenhum deles é mais específico, a invocação é ambígua, resultando
em um erro de compilação.
Se você alterar maxNumber(1, 2) para maxNumber(1, 2.0), ele corresponderá ao primeiro
função maxNumber . Portanto, não haverá erro de compilação.

Cuidado
As funções matemáticas estão sobrecarregadas no arquivo de cabeçalho <cmath> . Por exemplo, existem
três funções sobrecarregadas para sin:

flutuar pecado (flutuar)


duplo pecado (duplo)
pecado duplo longo (duplo longo)

6.12 O que é sobrecarga de função? Podemos definir duas funções que tenham a mesma
nome, mas tipos de parâmetros diferentes? Podemos definir duas funções em um programa que ÿVerificação de ponto

tenham nomes de funções e listas de parâmetros idênticos, mas tipos de valores de retorno
diferentes?

6.13 O que há de errado no programa a seguir?

vazio p (int i)
{
cout << i << endl;
}

int p(int j)
{
cout << j << endl;
}

6.14 Dadas duas definições de função,

duplo m (duplo x, duplo y)


duplo m (int x, duplo y)

responda as seguintes questões:

a. Qual das duas funções é invocada para


duplo z = m(4, 5);
b. Qual das duas funções é invocada para
duplo z = m(4, 5,4);
c. Qual das duas funções é invocada para

duplo z = m(4,5, 5,4);

6.8 Protótipos de Funções


Um protótipo de função declara uma função sem precisar implementá-la.
Chave
Apontar
Antes de uma função ser chamada, seu cabeçalho deve ser declarado. Uma maneira de garantir isso é colocar
protótipo de função
a definição antes de todas as chamadas de função. Outra abordagem é definir um protótipo de função antes
que a função seja chamada. Um protótipo de função, também conhecido como declaração de função, é um declaração de função

cabeçalho de função sem implementação. A implementação é dada posteriormente no programa.


Machine Translated by Google

242 Capítulo 6 Funções

A Listagem 6.7 reescreve a Listagem 6.6, TestFunctionOverloading.cpp, usando protótipos de função.


Três protótipos de função máxima são definidos nas linhas 5–7. Essas funções são chamadas
posteriormente na função principal . As funções são implementadas nas linhas 27, 36 e 45.

Listagem 6.7 TestFunctionPrototype.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Protótipo de função
protótipo de função 5 int máx(int num1, int num2);
protótipo de função 6 duplo máximo (duplo num1, duplo num2);
protótipo de função 7 duplo máximo (duplo num1, duplo num2, duplo num3);

função principal 8 9 int principal()


10 {
11 // Invoca a função max com parâmetros int
12 cout << "O máximo entre 3 e 4 é " max(3, 4) << endl; <<
invocar máximo 13
14
15 // Invoca a função max com os parâmetros double
16 cout << "O máximo entre 3,0 e 5,4 é "
invocar máximo 17 << max(3,0, 5,4) << endl;
18
19 // Invoca a função max com três parâmetros duplos
20 cout << "O máximo entre 3,0, 5,4 e 10,14 é "
invocar máximo 21 << máx(3,0, 5,4, 10,14) << endl;
22
23 retornar 0;
24}
25
26 // Retorna o máximo entre dois valores int 27 int max(int num1,
implementação de função int num2)
28 {
29 se (num1 > num2)
30 retornar num1;
31 outro
32 retornar num2;
33 }
34
35 // Encontre o máximo entre dois valores duplos 36 double
implementação de função max(double num1, double num2)
37 {
38 se (num1 > num2)
39 retornar num1;
40 outro
41 retornar num2;
42 }
43
44 // Retorna o máximo entre três valores duplos 45 double max(double
implementação de função num1, double num2, double num3)
46 {
47 retornar máx(máx(num1, num2), num3);
48}

Dica
omitindo nomes de parâmetros No protótipo, você não precisa listar os nomes dos parâmetros, apenas os tipos de parâmetros. O
compilador C++ ignora os nomes dos parâmetros. O protótipo informa ao compilador o nome da
função, seu tipo de retorno, o número de parâmetros e o tipo de cada parâmetro. Portanto, as linhas
5–7 podem ser substituídas por
Machine Translated by Google

6.9 Argumentos Padrão 243

int máximo(int, int);


duplo máximo(duplo, duplo);
duplo máximo(duplo, duplo, duplo);

Observação

Dizemos “definir uma função” e “declarar uma função”. Declarar uma função especifica definir vs. declarar funções
o que é uma função sem implementá-la. Definir uma função fornece um corpo de
função que implementa a função.

6.9 Argumentos Padrão


Você pode definir valores padrão para parâmetros em uma função.
Chave
Apontar
C++ permite declarar funções com valores de argumento padrão. Os valores padrão são passados para os
parâmetros quando uma função é invocada sem os argumentos.
A Listagem 6.8 demonstra como declarar funções com valores de argumento padrão e como invocar tais
funções.

Listagem 6.8 DefaultArgumentDemo.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Exibe a área de um círculo 5 void
printArea(double radius = 1) argumento padrão
6{
7 área dupla = raio * raio * 3,14159;
cout << "área é" << área << endl;
8 9}
10
11 int principal()
12 {
13 área de impressão(); invocar com padrão
14 printArea(4); invocar com argumento
15
16 retornar 0;
17 }

a área é 3,14159
a área é 50,2654

A linha 5 declara a função printArea com o parâmetro radius. radius tem um valor padrão 1. A linha 13
invoca a função sem passar um argumento. Neste caso, o valor padrão 1 é atribuído ao raio.

Quando uma função contém uma mistura de parâmetros com e sem valores padrão, aqueles com valores
padrão devem ser declarados por último. Por exemplo, as seguintes declarações são ilegais: argumentos padrão por último

void t1(int x, int y = 0, int z); // Ilegal


void t2(int x = 0, int y = 0, int z); // Ilegal

No entanto, as seguintes declarações são adequadas:

void t3(int x, int y = 0, int z = 0); // Jurídico


void t4(int x = 0, int y = 0, int z = 0); // Jurídico
Machine Translated by Google

244 Capítulo 6 Funções

Quando um argumento é deixado de fora de uma função, todos os argumentos que vêm depois dele também devem ser
deixados de fora. Por exemplo, as seguintes chamadas são ilegais:

t3(1, , 20);
t4(, , 20);

mas as seguintes chamadas estão bem:

t3(1); // Os parâmetros y e z recebem um valor padrão


t4(1, 2); // O parâmetro z recebe um valor padrão

6.15 Quais das seguintes declarações de função são ilegais?

ÿVerificação de ponto

void t1(int x, int y = 0, int z);


void t2(int x = 0, int y = 0, int z);
void t3(int x, int y = 0, int z = 0);
void t4(int x = 0, int y = 0, int z = 0);

6.10 Funções Inline


C++ fornece funções embutidas para melhorar o desempenho de funções curtas.
Chave
Apontar
Implementar um programa usando funções torna o programa fácil de ler e manter, mas as chamadas de função envolvem
eficiência sobrecarga de tempo de execução (isto é, colocar argumentos e registradores de CPU na pilha e transferir o controle de e
função embutida para uma função). C++ fornece funções embutidas
para evitar chamadas de função. Funções embutidas não são chamadas; em vez disso, o compilador copia o código da
função em linha no ponto de cada invocação. Para especificar uma função inline, preceda a declaração da função com a
palavra-chave inline , conforme mostrado na Listagem 6.9.

Listagem 6.9 InlineDemo.cpp


1 #include <iostream>
2 usando namespace std;

função embutida 3 4 inline void f(int mês, int ano) 5 {

cout << "mês é " << mês << endl;


cout << "ano é" << ano << endl;
678}
9
10 int principal()
11 {
12 int mês = 10, ano = 2008;
invocar função embutida 13 f(mês, ano); // Invoca a função in-line
invocar função embutida 14 f(9, 2010); // Invoca a função in-line
15
16 retornar 0;
17 }

mês é 10
o ano é 2008
mês é 9
o ano é 2010
Machine Translated by Google

6.11 Variáveis Locais Locais, Globais e Estáticas 245

No que diz respeito à programação, as funções inline são iguais às funções regulares, exceto que
são precedidas pela palavra-chave inline . No entanto, nos bastidores, o compilador C++ expande a
chamada de função embutida copiando o código da função embutida. Portanto, a Listagem 6.9 é
essencialmente equivalente à Listagem 6.10.

Listagem 6.10 InlineExpandedDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5 {6
7 int mês = 10, ano = 2008;
cout << "mês é " << mês << endl; cout << "ano é"
<< ano << endl;
cout << "mês é " << 9 << endl; Função inline expandida
8 cout << "ano é" << 2010 << endl;
9 10 11
12 retornar 0;
13}

mês é 10
o ano é 2008
mês é 9
o ano é 2010

Observação

Funções embutidas são desejáveis para funções curtas, mas não para funções longas que são para funções curtas
chamadas em vários locais de um programa, porque fazer múltiplas cópias aumentará não para funções longas
drasticamente o tamanho do código executável. Por esse motivo, C++ permite que os
compiladores ignorem a palavra-chave inline se a função for muito longa. Portanto, a palavra-
chave inline é apenas uma solicitação; cabe ao compilador decidir se deve honrá-lo ou ignorá-lo. decisão do compilador

6.16 O que é uma função embutida? Como você define uma função embutida?
ÿVerificação de ponto
6.17 Quando você deve usar uma função inline?

6.11 Variáveis locais, globais e estáticas


Uma variável pode ser declarada como local, global ou local estático em C++.
Chave
Apontar
Conforme mencionado na Seção 2.5, “Variáveis”, o escopo de uma variável é a parte do programa
onde a variável pode ser referenciada. Uma variável definida dentro de uma função é chamada de escopo da variável
variável local. C++ também permite usar variáveis globais. Eles são declarados fora de todas as variável local
funções e são acessíveis a todas as funções do seu escopo. As variáveis locais não possuem valores variável global
padrão, mas as variáveis globais são padronizadas como zero.
Uma variável deve ser declarada antes de poder ser usada. O escopo de uma variável local
começa na sua declaração e continua até o final do bloco que contém a variável. O escopo de uma
variável global começa na sua declaração e continua até o final do programa.
Um parâmetro é na verdade uma variável local. O escopo de um parâmetro de função cobre toda
a função.
A Listagem 6.11 demonstra o escopo das variáveis locais e globais.
Machine Translated by Google

246 Capítulo 6 Funções

Listagem 6.11 VariableScopeDemo.cpp


1 #include <iostream>
2 usando namespace std;

protótipo de função 3 4 void t1(); // Protótipo de função


5 void t2(); // Protótipo de função

6 7 int principal()
8{
t1();
9 10 t2();
11
12 retornar 0;
13}
14
variável global 15 inteiro ; // Variável global, padrão 0
16
17 vazio t1()
18 {
variável local 19 int x = 1;
20 cout << "x é" cout << x << endl;
21 << "y é" << e << fim;
incremento x x++;
incrementar y y++;
22 23 24 }

25 26 vazio t2()
27 {
variável local int x = 1;
cout << "x é" cout << x << endl;
<< "y é" << e << fim;
28 29 30 31 }

xé1
você é 0
xé1
você é 1

Uma variável global y é declarada na linha 15 com valor padrão 0. Esta variável é acessível nas funções
t1 e t2, mas não na função principal , porque a função principal é declarada antes de y ser declarado.

Quando a função principal invoca t1() na linha 9, a variável global y é incrementada (linha 23) e se torna
1 em t1. Quando a função principal invoca t2() na linha 10, a variável global y agora é 1.

Uma variável local x é declarada em t1 na linha 19 e outra é declarada em t2 na linha 28.


Embora tenham o mesmo nome, essas duas variáveis são independentes. Então, incrementando x
em t1 não afeta a variável x definida em t2.
Se uma função tiver uma variável local com o mesmo nome de uma variável global, apenas a variável local
variável pode ser vista na função.

Cuidado
É tentador declarar uma variável globalmente uma vez e depois usá-la em todas as funções.
No entanto, esta é uma má prática, porque modificar as variáveis globais pode levar a erros
Machine Translated by Google

6.11 Variáveis Locais Locais, Globais e Estáticas 247

que são difíceis de depurar. Evite usar variáveis globais. O uso de constantes globais é evite variáveis globais use
permitido, pois as constantes nunca são alteradas. constantes globais

6.11.1 O escopo das variáveis em um loop for Uma variável


declarada na parte de ação inicial de um cabeçalho de loop for tem seu escopo em todo o loop.
Porém, uma variável declarada dentro de um corpo de loop for tem seu escopo limitado no corpo do
loop desde sua declaração até o final do bloco que contém a variável, conforme mostrado na Figura para variável de controle de loop

6.4.

função vazia1 () {
.
.
for (int i = 1; i < 10; i++) {

.
O escopo de eu .
intj ;
O escopo de j .
.
.
}
}

Figura 6.4 Uma variável declarada na parte de ação inicial de um cabeçalho de loop for tem seu
escopo em todo o loop.

É comumente aceitável declarar uma variável local com o mesmo nome em diferentes blocos não
aninhados em uma função, como mostrado na Figura 6.5a, mas não é uma boa prática declarar uma
variável local duas vezes em blocos aninhados, mesmo que seja permitido em C++, conforme mostrado múltiplas declarações

na Figura 6.5b. Neste caso, i é declarado no bloco funcional e também no loop for . O programa pode
ser compilado e executado, mas é fácil cometer erros. Portanto, você deve evitar declarar a mesma
variável em blocos aninhados.

Não há problema em declarar i em Não é uma boa prática


dois blocos não aninhados declarar i em dois blocos de aninhamento

void function1() { int void function2() {

x = 1; int y = 1; int eu = 1;
soma interna = 0;

for (int i = 1; i < 10; i++) { x += i; } for (int i = 1; i < 10; i++) { soma += i; }

for (int i = 1; i < 10; i++) { y += i; } cout << i << endl; cout
<< soma << endl;
}

(a) (b)

Figura 6.5 Uma variável pode ser declarada múltiplas vezes em blocos não aninhados, mas você
deve evitar declará-las em blocos aninhados.
Machine Translated by Google

248 Capítulo 6 Funções

Cuidado
Não declare uma variável dentro de um bloco e tente usá-la fora do bloco.
Aqui está um exemplo de um erro comum:

for (int i = 0; i < 10; i++) {

cout << i << endl;

A última instrução causaria um erro de sintaxe, porque a variável i não está definida fora
do loop for .

6.11.2 Variáveis Locais Estáticas


Depois que uma função completa sua execução, todas as suas variáveis locais são destruídas. Essas
variável automática variáveis também são conhecidas como variáveis automáticas. Às vezes é desejável reter os valores
armazenados em variáveis locais para que possam ser usados na próxima chamada. C++ permite declarar
variável local estática variáveis locais estáticas. Variáveis locais estáticas são alocadas permanentemente na memória durante a
vida útil do programa. Para declarar uma variável estática, use a palavra-chave static.
A Listagem 6.12 demonstra o uso de variáveis locais estáticas.

Listagem 6.12 StaticVariableDemo.cpp


1 #include <iostream>
2 usando namespace std;

protótipo de função 3 4 void t1(); // Protótipo de função

5 6 int principal()
7{
invocar t1 t1();
t1();
8
9 retornar 0;
10 11 12 }
13
14 vazio t1()
15 {
variável local estática 16 int estático x = 1;
variável local 17 int y = 1;
incremento x 18 x++;
incrementar y 19 y++;
20 cout << "x é" cout << x << endl;
21 << "y é" << e << fim;
22 }

xé2
você é 2
xé3
você é 2

Uma variável local estática x é declarada na linha 16 com valor inicial 1. A inicialização de variáveis
estáticas acontece apenas uma vez na primeira chamada. Quando t1() é invocado pela primeira vez na linha
8, a variável estática x é inicializada em 1 (linha 16). x é incrementado para 2 (linha 18). Como x é estático
Machine Translated by Google

6.11 Variáveis Locais Locais, Globais e Estáticas 249

variável local, x é retido na memória após esta chamada. Quando t1() é invocado novamente na linha
9, x é 2 e é incrementado para 3 (linha 18).
Uma variável local y é declarada na linha 17 com valor inicial 1. Quando t1() é invocado pela
primeira vez na linha 8, y é incrementado para 2 (linha 19). Como y é uma variável local, ela é
destruída após esta chamada. Quando t1() é invocado novamente na linha 9, y é inicializado em 1 e
incrementado em 2 (linha 19).

6.18 Mostre a saída do seguinte código:


ÿVerificação de ponto
#include <iostream>
usando namespace std;

const duplo PI = 3,14159;

getArea duplo ( raio duplo)


{
raio de retorno * raio * PI;
}

void displayArea( raio duplo)


{
cout << getArea(raio) << endl;
}

int principal()
{
duplo r1 = 1;
duplo r2 = 10;
cout << getArea(r1) << endl;
displayArea(r2);
}

6.19 Identifique variáveis globais e locais no programa a seguir. Uma variável global tem um valor
padrão? Uma variável local tem um valor padrão? Qual será a saída do código?

#include <iostream>
usando namespace std;

intj ;

int principal()
{
int eu;
cout << "eu é" << eu << endl;
cout << "j é" << j << endl;
}

6.20 Identifique variáveis globais, variáveis locais e variáveis locais estáticas nos seguintes
programa. Qual será a saída do código?

#include <iostream>
usando namespace std;

int j = 40;

vazio p()
Machine Translated by Google

250 Capítulo 6 Funções

{
int eu = 5;
estático int j = 5;
eu++;
j++;

cout << "eu é" cout << eu << endl;


<< "j é" << j << endl;
}

int principal()
{
p();
p();
}

6.21 Identifique e corrija os erros no seguinte programa:

vazio p (int i)
{
int eu = 5;

cout << "eu sou" << eu << endl;


}

6.12 Passando Argumentos por Referência


Os parâmetros podem ser passados por referência, o que torna o parâmetro formal um alias
Chave
Apontar do argumento real. Assim, as alterações feitas nos parâmetros dentro da função também
foram feitas nos argumentos.

Quando você invoca uma função com um parâmetro, conforme descrito nas seções anteriores, o
passagem por valor valor do argumento é passado para o parâmetro. Isso é conhecido como passagem por valor. Se o
argumento for uma variável em vez de um valor literal, o valor da variável será passado para o
parâmetro. A variável não é afetada, independentemente das alterações feitas no parâmetro dentro
da função. Conforme mostrado na Listagem 6.13, o valor de x (1) é passado para o parâmetro n ao
invocar a função de incremento (linha 14). n é incrementado em 1 na função (linha 6), mas x não é
alterado, não importa o que a função faça.

Listagem 6.13 Increment.cpp


1 #include <iostream>
2 usando namespace std;

3 4 incremento vazio (int n) 5 6


{
incremento substantivo, masculino—
n++;
cout << "\tn dentro da função está " << n << final;
7 8}
9
10 int principal()
11 {
12 int x = 1;
13 cout << "Antes da chamada, x é " << x << endl;
invocar incremento 14 incremento(x);
15 cout << "após a chamada, x é " << x << endl;
16
17 retornar 0;
18}
Machine Translated by Google

6.12 Passando Argumentos por Referência 251

Antes da chamada, x é 1
n dentro da função é 2
após a chamada, x é 1

A passagem por valor tem sérias limitações. A Listagem 6.14 ilustra isso. O programa cria uma limitações de passagem por valor
função para trocar duas variáveis. A função swap é invocada passando dois argumentos.
No entanto, os valores dos argumentos não são alterados depois que a função é invocada.

Listagem 6.14 SwapByValue.cpp


1 #include <iostream>
2 usando namespace std;

3 4 // Tentativa de trocar duas variáveis não funciona! 5 troca nula


(int n1, int n2) função de troca
6{
cout << "\tDentro da função swap" << endl;
cout << "\tAntes de trocar n1 é " n2 é " << << n1 <<
"
n2 << endl;
7
8 //Troca n1 por n2
9 temperatura interna = n1;
10 n1 = n2;
11 n2 = temperatura;
12
13 cout << "\tApós a troca n1 é " << n2 << endl; << n1 <<
"
14 n2 é "
15 16 17 18 }
19
20 int principal() função principal
21 {
22 //Declara e inicializa variáveis
23 int num1 = 1;
24 int num2 = 2;
25
26 cout << "Antes de invocar a função swap, num1 é "
27 << num1 << "e num2 é" << num2 << endl;
28
29 // Invoca a função swap para tentar trocar duas variáveis
30 trocar(num1, num2); troca falsa
31
32 cout << "Depois de invocar a função swap, num1 é " " e num2 é " << num1 <<
33 << num2 << endl;
34
35 retornar 0;
36 }

Antes de invocar a função de troca, num1 é 1 e num2 é 2


Dentro da função de troca
Antes de trocar n1 é 1 n2 é 2
Depois de trocar n1 é 2 n2 é 1
Depois de invocar a função swap, num1 é 1 e num2 é 2

Antes da função swap ser invocada (linha 30), num1 é 1 e num2 é 2. Após a troca
função é invocada, num1 ainda é 1 e num2 ainda é 2. Seus valores não foram trocados.
Machine Translated by Google

252 Capítulo 6 Funções

Conforme mostrado na Figura 6.6, os valores dos argumentos num1 e num2 são passados para n1 e
n2, mas n1 e n2 têm seus próprios locais de memória independentes de num1 e num2. Portanto,
alterações em n1 e n2 não afetam o conteúdo de num1 e num2.

Os valores de num1 e num2 são


passado para n1 e n2. Executando troca
não afeta num1 e num2.

Registro de ativação para


a função swap

temperatura: n2: 2

n1:1

Registro de ativação para Registro de ativação para Registro de ativação para


a função principal a função principal a função principal A pilha está vazia
num2: 2 num2: 2 num2: 2
num1: 1 num1: 1 num1: 1

A função principal A função de troca A função de troca A função principal


é invocado. é invocado. está terminado. está terminado.

Figura 6.6 Os valores das variáveis são passados para os parâmetros da função.

Outra reviravolta é alterar o nome do parâmetro n1 em swap para num1. Que efeito isso tem?
Nenhuma mudança ocorre porque não faz diferença se o parâmetro e o argumento têm o mesmo nome.
O parâmetro é uma variável na função com espaço de memória próprio. A variável é alocada quando a
função é invocada e desaparece quando a função é retornada ao seu chamador.

A função swap tenta trocar duas variáveis. Porém, depois que a função é invocada, os valores das
variáveis não são trocados, porque os valores das variáveis são passados para os parâmetros. As
variáveis e parâmetros originais são independentes. Mesmo que os valores na função chamada sejam
alterados, os valores nas variáveis originais não são.
Então, podemos escrever uma função para trocar duas variáveis? Sim. A função pode fazer isso
variável de referência passando uma referência às variáveis. C++ fornece um tipo especial de variável – uma variável de
referência – que pode ser usada como um parâmetro de função para referenciar a variável original. Você
pode acessar e modificar os dados originais armazenados nessa variável por meio de sua variável de referência.
Uma variável de referência é um alias para outra variável. Para declarar uma variável de referência,
coloque o E comercial (&) na frente da variável ou após o tipo de dados da variável. Por exemplo, o
código a seguir declara uma variável de referência r que se refere à contagem de variáveis.

int &r = contagem;

ou equivalente,

int& r = contagem;

Observação

notação equivalente As seguintes notações para declarar variáveis de referência são equivalentes:

tipo de dados &refVar;


tipo de dados & refVar;
tipo de dados& refVar;

A última notação é mais intuitiva e afirma claramente que a variável refVar é do tipo dataType&.
Por esta razão, a última notação será usada neste livro.
Machine Translated by Google

6.12 Passando Argumentos por Referência 253

A Listagem 6.15 dá um exemplo de uso de variáveis de referência.

Listagem 6.15 TestReferenceVariable.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 contagem interna = 1;
7 int& r = contagem; declarar variável de referência
cout << "contagem é " << contagem << endl;
cout << "r é" << r << endl;
8
9 10 11 r++; usar variável de referência
12 cout << "contagem é " << contagem << endl;
13 cout << "r é" << r << endl;
14
15 contagem = 10; contagem de alterações
16 cout << "contagem é " << contagem << endl;
17 cout << "r é" << r << endl;
18
19 retornar 0;
20}

contagem é 1
ré1
contagem é 2
ré2
a contagem é 10
r é 10

A linha 7 declara uma variável de referência chamada r que é apenas um alias para contagem. Conforme
mostrado na Figura 6.7a, r e count fazem referência ao mesmo valor. A linha 11 incrementa r, que na verdade os
incrementos contam, uma vez que compartilham o mesmo valor, conforme mostrado na Figura 6.7b.

contar 1 contar 2

R R

(a) (b)

Figura 6.7 r e count compartilham o mesmo valor.

A linha 15 atribui 10 para contar. Já que count e r referem-se ao mesmo valor. Tanto a contagem quanto o r
são agora 10.
Você pode usar uma variável de referência como parâmetro em uma função e passar uma variável regular
para invocar a função. O parâmetro se torna um alias para a variável original. Isso é conhecido como passagem
por referência. Você pode acessar e modificar o valor armazenado na variável original através da variável de passagem por referência

referência. Para demonstrar o efeito da passagem por referência, vamos reescrever a função de incremento na
Listagem 6.13 conforme mostrado na Listagem 6.16.
Machine Translated by Google

254 Capítulo 6 Funções

Listagem 6.16 IncrementWithPassByReference.cpp


1 #include <iostream>
2 usando namespace std;

3 4 incremento vazio (int& n) 5 6


{
incremento substantivo, masculino—
n++;
cout << "n dentro da função é" << n << final;
7 8}
9
10 int principal()
11 {
12 int x = 1;
13 cout << "Antes da chamada, x é " << x << endl;
invocar incremento 14 incremento(x);
15 cout << "Após a chamada, x é " << x << endl;
16
17 retornar 0;
18}

Antes da chamada, x é 1
n dentro da função é 2
Após a chamada, x é 2

Invocar increment(x) na linha 14 passa a referência da variável x para a variável de referência n na função de
incremento . Agora n e x são iguais, conforme mostrado na saída.
Incrementar n na função (linha 6) é o mesmo que incrementar x. Portanto, antes de a função ser invocada, x é 1 e, depois,
x torna-se 2.
Passagem por valor e passagem por referência são duas maneiras de passar argumentos para os parâmetros de
uma função. A passagem por valor passa o valor para uma variável independente e a passagem por referência compartilha
compartilhamento de passagem a mesma variável. Semanticamente a passagem por referência pode ser descrita como passagem por compartilhamento.
Agora você pode usar parâmetros de referência para implementar uma função de troca correta , conforme mostrado
na Listagem 6.17.
Nota de vídeo

Passagem por referência


Listagem 6.17 SwapByReference.cpp
1 #include <iostream>
2 usando namespace std;
3
4 // Troca duas variáveis
variáveis de referência 5 troca nula (int& n1, int& n2)
6{
7 cout << "\tDentro da função swap" << endl;
8 cout << "\tAntes de trocar n1 é " n2 é " << n2 << endl; << n1 <<
"

9 10
11 //Troca n1 por n2
trocar 12 temperatura interna = n1;
13 n1 = n2;
14 n2 = temperatura;
15
16 cout << "\tApós a troca n1 é " n2 é " << n2 << endl; << n1 <<
"
17
18}
Machine Translated by Google

6.12 Passando Argumentos por Referência 255

19
20 int principal()
21 {
//Declara e inicializa variáveis
22 23 int num1 = 1;
24 int num2 = 2;
25
26 cout << "Antes de invocar a função swap, num1 é "
27 << num1 << " e num2 é " << num2 << endl;
28
29 // Invoca a função swap para tentar trocar duas variáveis
30 trocar(num1, num2); invocar função de troca
31
32 cout << "Depois de invocar a função swap, num1 é " " e num2 é " << num1 <<
33 << num2 << endl;
34
35 retornar 0;
36 }

Antes de invocar a função de troca, num1 é 1 e num2 é 2


Dentro da função de troca
Antes de trocar n1 é 1 n2 é 2
Depois de trocar n1 é 2 n2 é 1
Depois de invocar a função swap, num1 é 2 e num2 é 1

Antes da função swap ser invocada (linha 30), num1 é 1 e num2 é 2. Após a troca
função é invocada, num1 se torna 2 e num2 se torna 1. Seus valores foram trocados.
Conforme mostrado na Figura 6.8, as referências de num1 e num2 são passadas para n1 e
n2, então n1 e num1 são alias e n2 e num2 são alias. Trocar valores entre n1 e n2 é o mesmo
que trocar valores entre num1 e num2.

As referências de num1 e num2 são


passado para n1 e n2. n1 é um apelido
para num1 e n2 é um alias para num2.

Registro de ativação para


a função swap

temperatura: int& n2:


você& n1:

Registro de ativação para Registro de ativação para Registro de ativação para


a função principal a função principal a função principal A pilha está vazia
num2: 2 num2: 2 num2: 1
num1: 1 num1: 1 num1: 2

A função principal A função de troca A função de troca A função principal


é invocado. é invocado. está terminado. está terminado.

Figura 6.8 As referências das variáveis são passadas para os parâmetros da função.

Quando você passa um argumento por referência, o parâmetro formal e o argumento devem
têm o mesmo tipo. Por exemplo, no código a seguir, a referência da variável x é passada requer o mesmo tipo
Machine Translated by Google

256 Capítulo 6 Funções

para a função, o que é bom. Porém, a referência da variável y é passada para a função, o que é errado,
pois y e n são de tipos diferentes.

#include <iostream>
usando namespace std;

incremento vazio (duplo& n) {

n++;
}

int principal()
{
duplo x = 1; int y
= 1;

incremento(x);
incremento(y); // Não é possível invocar increment(y) com um argumento int

cout << "x é" cout << x << endl; <<


<< "y é" e << fim;

retornar 0;
}

requer variável Quando você passa um argumento por referência, o argumento deve ser uma variável. Quando você
passa um argumento por valor, o argumento pode ser um literal, uma variável, uma expressão ou até
mesmo o valor de retorno de outra função.

6.22 O que é passagem por valor? O que é passagem por referência? Mostre o resultado da seguinte
ÿVerificação de ponto programas:

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

void maxValue(int valor1, int valor2, int max) { if (valor1 > void maxValue(int valor1, int valor2, int& max) { if (valor1 >

valor2) max = valor1; senão valor2) max = valor1; senão


max = valor2; max = valor2;

} }

int principal() int principal()


{ {
int máximo = 0; int máximo = 0;
maxValue(1, 2, máx); cout maxValue(1, 2, máx); cout
<< "máximo é" << máximo << final; << "máximo é" << máximo << final;

retornar 0; retornar 0;
} }

(a) (b)
Machine Translated by Google

6.12 Passando Argumentos por Referência 257

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

void f(int i, int num) { vazio f(int& i, int num) {

for (int j = 1; j <= i; j++) { for (int j = 1; j <= i; j++) {

cout << num << " "; cout << num << " ";
num *= 2; num *= 2;
} }

cout << endl; cout << endl; }


}

int principal() int principal()


{ {
int eu = 1; int eu = 1;
enquanto (eu <= enquanto (eu <=
6) { 6) {
f(eu, 2); f(eu, 2);
eu+ eu++;
+; } }

retornar 0; retornar 0;
} }

(c) (d)

6.23 Um aluno escreveu a seguinte função para encontrar o número mínimo e máximo
entre dois valores a e b. O que há de errado no programa?
#include <iostream>
usando namespace std;

void minMax(duplo a, duplo b, duplo mínimo, duplo máximo) {

se (a < b) {

min = uma;
máximo = b;

senão { min = b;
máximo = uma;
}
}

int principal()
{
duplo a = 5, b = 6, min, max;
minMax(a,b,mín,máx);

cout << "min é" << min << "e máximo é" << máximo << final;

retornar 0;
}
Machine Translated by Google

258 Capítulo 6 Funções

6.24 Um aluno escreveu a seguinte função para encontrar o número mínimo e máximo entre
dois valores a e b. O que há de errado no programa?

#include <iostream>
usando namespace std;

void minMax(duplo a, duplo b, duplo& min, duplo& max) { if (a < b) { duplo

min = a; máximo

duplo = b; } outro {

mínimo duplo = b;
máximo duplo = a;
}
}

int principal()
{
duplo a = 5, b = 6, min, max;
minMax(a,b,mín,máx);

cout << "min é" << min << "e máximo é" << máximo << final;

retornar 0;
}

6.25 Para o Ponto de Verificação 6.24, mostre o conteúdo da pilha logo antes da função
minMax ser invocada, logo após inserir minMax, logo antes de minMax
retornar e logo após minMax retornar.

6.26 Mostre a saída do seguinte código:


#include <iostream>
usando namespace std;

vazio f(duplo& p) {

p+= 2;
}

int principal()
{
duplo x = 10; int
y = 10;

f(x);
f(s);

cout << "x é" cout << x << endl; <<


<< "y é" e << fim;

retornar 0;
}
Machine Translated by Google

6.14 Estudo de Caso: Convertendo Hexadecimais em Decimais 259

6.27 O que há de errado no programa a seguir?


#include <iostream>
usando namespace std;

vazio p(int& i)
{
cout << i << endl;
}

int p(int j)
{
cout << j << endl;
}

int principal()
{
int k = 5;
p(k);

retornar 0;
}

6.13 Parâmetros de Referência Constante


Você pode especificar um parâmetro de referência constante para evitar que seu valor seja
Chave
alterado acidentalmente. Apontar

Se o seu programa usa um parâmetro de passagem por referência e o parâmetro não é alterado na função,
você deve marcá-lo como constante para informar ao compilador que o parâmetro não deve ser alterado.
Para fazer isso, coloque a palavra-chave const antes do parâmetro na declaração da função. const
Tal parâmetro é conhecido como parâmetro de referência constante. Por exemplo, num1 e num2 são parâmetro de referência constante
declarados como parâmetros de referência constantes na função a seguir.

// Retorna o máximo entre dois números


int max(const int& num1, const int& num2) {

resultado interno ;

se (num1 > num2)


resultado = num1;
outro
resultado = num2;

resultado de retorno ;
}

Na passagem por valor, o parâmetro real e seu parâmetro formal são variáveis independentes.
Na passagem por referência, o parâmetro real e seu parâmetro formal referem-se à mesma variável. A
passagem por referência é mais eficiente do que a passagem por valor para tipos de objetos como strings, usar passagem por valor ou

porque os objetos podem ocupar muita memória. Entretanto, a diferença é insignificante para parâmetros de passagem por referência?

tipos primitivos como int e double. Portanto, se um parâmetro de tipo de dados primitivo não for alterado na
função, você deve simplesmente declará-lo como parâmetro de passagem por valor.

6.14 Estudo de Caso: Convertendo Hexadecimais em Decimais


Esta seção apresenta um programa que converte um número hexadecimal em um número decimal.
Chave
Apontar
A Seção 5.8.4, “Estudo de caso: convertendo decimais em hexadecimais”, fornece um programa que
converte um decimal em hexadecimal. Como você converte um número hexadecimal em decimal?
Machine Translated by Google

260 Capítulo 6 Funções

Dado um número hexadecimal hnhn-1hn-2 c h2h1h0, o valor decimal equivalente é

hn * 16n + hn-1 * 16n-1 + hn-2 * 16n-2 + c


+ h2 * 162 + h1 * 161 + h0 * 160

Por exemplo, o número hexadecimal AB8C é

10*163 +11*162 +8*161 +12*160 = 43916

Nosso programa solicitará que o usuário insira um número hexadecimal como uma string e o converta em um
decimal usando a seguinte função:

int hex2Dec(const string e hexadecimal)

Uma abordagem de força bruta consiste em converter cada caractere hexadecimal em um número decimal,
multiplicá-lo por 16i para obter um dígito hexadecimal na posição i e, em seguida, adicionar todos os itens para
obter o valor decimal equivalente para o número hexadecimal.
Observe que

hn * 16n + hn-1 * 16n-1 + hn-2 * 16n-2 + c


+ h1 * 161 + h0 * 160

= (c ((hn * 16 + hn-1) * 16 + hn-2) * 16 + c


+ h1) * 16 + h0

Algoritmo de Horner Essa observação, conhecida como algoritmo de Horner, leva ao seguinte código eficiente para converter
uma string hexadecimal em um número decimal:

int valor decimal = 0;


for (int i = 0; i < hex.size(); i++) {

char hexChar = hex[i];


valordecimal = valordecimal * 16 + hexCharToDecimal(hexChar);
}

Aqui está um traço do algoritmo para o número hexadecimal AB8C:

eu hexadecimal hexCharToDecimal valor decimal


(hexadecimal)

antes do ciclo 0

após a 1ª iteração 0 A 10 10

após a 2ª iteração 1 B 11 10*16+11

após a 3ª iteração 2 8 8 (10*16+11)*16+8

após a 4ª iteração 3 C 12 ((10*16+11)*16+8)*16+12

A Listagem 6.18 fornece o programa completo.

Listagem 6.18 Hex2Dec.cpp


1 #include <iostream>
2 #incluir <string>
3 #incluir <cctype>
4 usando namespace std;
5
6 // Converte um número hexadecimal como uma string em decimal
7 int hex2Dec(const string& hex);
Machine Translated by Google

6.14 Estudo de Caso: Convertendo Hexadecimais em Decimais 261

8
9 // Converte um caractere hexadecimal em um valor decimal
10 int hexCharToDecimal(char ch);
11
12 int principal()
13 {
14 // Solicita ao usuário que insira um número hexadecimal como uma string
15 cout << "Digite um número hexadecimal: ";
16 hexadecimal de corda;
17 cin >> hexadecimal; sequência de entrada

18
19 cout << "O valor decimal para número hexadecimal " " é " << hexadecimal

20 << << hex2Dec(hex) << endl; hexadecimal para decimal

21
22 retornar 0;
23 }
24
25 int hex2Dec ( string const e hexadecimal)
26 {
27 int valor decimal = 0;
28 for (não assinado i = 0; i < hex.size(); i++)
29 valordecimal = valordecimal * 16 + hexCharToDecimal(hex[i]);
30
31 return valordecimal;
32 }
33
34 int hexCharToDecimal(char ch) caractere hexadecimal para decimal

35 {
36 ch = topo(ch); //Muda para maiúscula para maiúsculas
37 se (ch >= 'A' && ch <= 'F')
38 retornar 10 + ch - 'A';
39 senão // ch é '0', '1', ..., return ch - '0'; ou '9'
40
41 }

Insira um número hexadecimal: AB8C


O valor decimal do número hexadecimal AB8C é 43916

Digite um número hexadecimal: af71


O valor decimal para o número hexadecimal af71 é 44913

O programa lê uma string do console (linha 17) e invoca a função hex2Dec para converter uma string
hexadecimal em número decimal (linha 20).
A função hex2Dec é definida nas linhas 25–32 para retornar um número inteiro. O parâmetro string é
declarado como const e passado por referência, pois a string não é alterada na função e economiza
memória ao passá-la como referência. O comprimento da string é determinado invocando hex.size() na
linha 28.
A função hexCharToDecimal é definida nas linhas 34–41 para retornar um valor decimal para um
caractere hexadecimal. O caractere pode estar em letras minúsculas ou maiúsculas. É convertido para
maiúsculas na linha 36. Lembre-se de que subtrair dois caracteres é subtrair seus códigos ASCII.
Por exemplo, '5' – '0' é 5.
Machine Translated by Google

262 Capítulo 6 Funções

6.15 Abstração de Função e Refinamento Stepwise


A chave para desenvolver software é aplicar o conceito de abstração.
Chave
Apontar
Você aprenderá muitos níveis de abstração neste livro. A abstração de funções é obtida separando o uso
de uma função de sua implementação. O cliente pode usar uma função sem saber como ela é
Nota de vídeo implementada. Os detalhes da implementação são encapsulados na função e ocultos do cliente que
Refinamento gradual invoca a função. Isso é conhecido como ocultação ou encapsulamento de informações. Se você decidir
abstração de função alterar a implementação, o programa cliente não será afetado, desde que você não altere a assinatura da
ocultação de informações função. A implementação da função fica oculta do cliente em uma “caixa preta”, conforme mostrado na
Figura 6.9.

Argumentos opcionais Retorno opcional


para entrada valor

Cabeçalho de Função

Caixa preta
Corpo funcional

Figura 6.9 O corpo da função pode ser pensado como uma caixa preta que contém a implementação
detalhada da função.

Você já usou a função Rand() para retornar um número aleatório, o tempo(0)


função para obter a hora atual e a função max para encontrar o número máximo. Você sabe como
escrever o código para invocar essas funções em seu programa, mas como usuário dessas funções, não
é necessário saber como elas são implementadas.
O conceito de abstração de função pode ser aplicado ao processo de desenvolvimento de programas.
dividir e conquistar Ao escrever um programa grande, você pode usar a estratégia “dividir e conquistar” , também conhecida
refinamento gradual como refinamento passo a passo, para decompô-lo em subproblemas. Os subproblemas podem ser
decompostos em outros menores e mais gerenciáveis.
Suponha que você escreva um programa que exiba o calendário de um determinado mês do ano. O
programa solicita que o usuário insira o ano e o mês e, em seguida, exibe todo o calendário do mês,
conforme mostrado na Figura 6.10.

Cabeçalho
agosto de 2013

Dom Seg Ter Qua Qui Sex Sáb


1 2 3
Corpo 4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

Figura 6.10 Após solicitar ao usuário que insira o ano e o mês, o programa exibe o calendário
daquele mês.

Vamos usar este exemplo para demonstrar a abordagem de dividir para conquistar.

6.15.1 Projeto de cima para baixo


Como você iniciaria esse programa? Você começaria a codificar imediatamente? Os programadores
iniciantes geralmente começam tentando descobrir a solução para cada detalhe. Embora os detalhes
sejam importantes no programa final, a preocupação com os detalhes nas fases iniciais pode bloquear a
Machine Translated by Google

6.15 Abstração de Função e Refinamento Stepwise 263

processo de resolução de problemas. Para fazer com que a solução de problemas flua sem problemas, este
exemplo começa usando a abstração de função para isolar detalhes do design; só mais tarde ele implementa
os detalhes.
Para este exemplo, o problema é primeiro dividido em dois subproblemas: obter informações do usuário e
imprimir o calendário do mês. Nesta fase, você deve se preocupar com o que os subproblemas irão alcançar,
e não com como obter informações e imprimir o calendário do mês. Você pode desenhar um gráfico de
estrutura para ajudar a visualizar a decomposição do problema (veja a Figura 6.11a).

imprimirCalendário
(principal) imprimir mês

leituraInput imprimir mês imprimirTítuloMês imprimirMêsBody

(a) (b)

Figura 6.11 (a) O gráfico de estrutura mostra que o problema printCalendar é dividido em dois
subproblemas, readInput e printMonth. (b) printMonth é dividido em dois subproblemas menores,
printMonthTitle e printMonthBody.

Você pode usar o objeto cin para ler a entrada do ano e do mês. O problema de imprimir o calendário de
um determinado mês pode ser dividido em dois subproblemas: imprimir o título do mês e imprimir o corpo do
mês, conforme mostrado na Figura 6.11b. O título do mês consiste em três linhas: mês e ano, uma linha
tracejada e os nomes dos sete dias da semana. Você precisa obter o nome do mês (por exemplo, janeiro) do
mês numérico (por exemplo, 1). Isso é feito em printMonthName (veja a Figura 6.12a).

imprimirMêsBody
imprimirTítuloMês

imprimirNomeMês getStartDay getNumberOfDaysInMonth

(a) (b)

Figura 6.12 (a) Para realizar printMonthTitle, você precisa de printMonthName. (b) O problema
printMonthBody é refinado em vários problemas menores.

Para imprimir o corpo do mês, você precisa saber qual dia da semana é o primeiro dia do mês (getStartDay)
e quantos dias o mês tem (getNumberOfDaysInMonth), conforme mostrado na Figura 6.12b. Por exemplo,
agosto de 2013 tem trinta e um dias e o primeiro dia do mês é quinta-feira, conforme mostra a Figura 6.10.

Como você conseguiria o dia de início de um mês? Existem várias maneiras de encontrá-lo. Suponha que
você saiba que o dia de início (startDay1800 = 3) de 1º de janeiro de 1800 foi quarta-feira.
Você poderia calcular o número total de dias (totalNumberOfDays) entre 1º de janeiro de 1800 e o dia de
início do mês civil. O cálculo é (totalNumberOfDays + startDay1800)% 7, porque cada semana tem sete dias.
Portanto, o problema getStartDay pode ser ainda mais refinado como getTotalNumberOfDays, conforme
mostrado na Figura 6.13a.
Para obter o número total de dias, você precisa saber se um ano é bissexto e quantos dias existem em
cada mês. Portanto, getTotalNumberOfDays é ainda mais refinado em dois subproblemas: isLeapYear e
getNumberOfDaysInMonth, conforme mostrado na Figura 6.13b. O gráfico de estrutura completo é mostrado
na Figura 6.14.
Machine Translated by Google

264 Capítulo 6 Funções

getTotalNumberOfDays

getStartDay
getNumberOfDaysInMonth

getTotalNumberOfDays é ano bissexto

(a) (b)

Figura 6.13 (a) Para realizar getStartDay, você precisa de getTotalNumberOfDays. (b)
O problema getTotalNumberOfDays é refinado em dois problemas menores.

imprimirCalendário
(principal)

leituraInput imprimir mês

imprimirTítuloMês imprimirMêsBody

imprimirNomeMês getStartDay

getTotalNumberOfDays

getNumberOfDaysInMonth

é ano bissexto

Figura 6.14 O gráfico de estrutura mostra o relacionamento hierárquico dos subproblemas do programa.

6.15.2 Implementação de cima para baixo ou de baixo para cima


Agora voltamos nossa atenção para a implementação. Em geral, um subproblema corresponde a uma
função na implementação, embora alguns subproblemas sejam tão simples que isso é desnecessário.
Você deve decidir quais módulos implementar como funções e quais combinar em outras funções. Tais
decisões devem basear-se na questão de saber se o programa geral será mais fácil de ler devido à sua
escolha. Neste exemplo, o subproblema readInput pode ser simplesmente implementado na função
principal .
implementação de cima para baixo Você pode usar uma implementação “de cima para baixo” ou “de baixo para cima” . A abordagem de
implementação de baixo para cima cima para baixo implementa uma função por vez no gráfico de estrutura, de cima para baixo. Stubs podem
esboço ser usados para funções que aguardam implementação. Um stub é uma versão simples, mas incompleta,
de uma função. Normalmente, um stub exibe uma mensagem de teste indicando que foi chamado e nada mais.
O uso de stubs permite testar a invocação da função de um chamador. Implemente o principal
função primeiro e, em seguida, use um stub para a função printMonth . Por exemplo, deixe printMonth
exibir o ano e o mês no esboço. Assim, seu programa pode começar assim:

#include <iostream>
#include <iomanip>
usando namespace std;
Machine Translated by Google

6.15 Abstração de Função e Refinamento Stepwise 265

void printMonth(int ano, int mês);


void printMonthTitle(int ano, int mês);
void printMonthName(int mês);
void printMonthBody(int ano, int mês);
int getStartDay(int ano, int mês);
int getTotalNumberOfDays(int ano, int mês);
int getNumberOfDaysInMonth(int ano, int mês);
bool éAnoLeap(int ano);

int principal()
{
// Solicita ao usuário para inserir o ano
cout << "Insira o ano completo (por exemplo, 2001): ";
ano interno ;
cin >> ano;

// Solicita ao usuário para inserir o mês


cout << "Insira o mês em número entre 1 e 12: ";
mês interno ;
cin >> mês;

//Imprime o calendário do mês do ano


printMês(ano, mês);

retornar 0;
}

void printMonth(int ano, int mês)


{
""
contagem << mês << << ano << fim;
}

Compile e teste o programa e corrija quaisquer erros. Agora você pode implementar o printMonth
função. Para funções invocadas a partir da função printMonth , você pode usar stubs.
A abordagem bottom-up implementa uma função por vez no gráfico de estrutura, de baixo para cima. abordagem de baixo para cima

Para cada função implementada, escreva um programa de teste, conhecido como driver, para testá-la. As motorista

abordagens de cima para baixo e de baixo para cima são boas. Ambos implementam funções de forma
incremental, ajudam a isolar erros de programação e facilitam a depuração. Às vezes eles podem ser
usados juntos.

6.15.3 Detalhes de Implementação


A função isLeapYear(int year) pode ser implementada usando o seguinte código:

return (ano % 400 == 0 || (ano % 4 == 0 && ano % 100 != 0));

Use as informações a seguir para implementar getTotalNumberOfDaysInMonth(int ano, int mês):

n Janeiro, março, maio, julho, agosto, outubro e dezembro têm 31 dias.

n Abril, junho, setembro e novembro têm 30 dias.

n Fevereiro tem 28 dias em um ano normal e 29 dias em um ano bissexto. Um ano normal,
portanto, tem 365 dias, e um ano bissexto tem 366.

Para implementar getTotalNumberOfDays(int ano, int mês), você precisa calcular o número total de
dias (totalNumberOfDays) entre 1º de janeiro de 1800 e o primeiro dia do mês civil. Você poderia encontrar
o número total de dias entre o ano de 1800 e
Machine Translated by Google

266 Capítulo 6 Funções

o ano civil e, em seguida, calcule o número total de dias anteriores ao mês civil no ano civil. A
soma desses dois totais é totalNumberOfDays.
Para imprimir um corpo, primeiro adicione algum espaço antes do dia de início e depois imprima as linhas de cada
semana, como mostrado em agosto de 2013 (veja a Figura 6.10).
O programa completo é dado na Listagem 6.19.

Listagem 6.19 PrintCalendar.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;
4
5 // Protótipos de funções
protótipo de função 6 void printMonth(int ano, int mês);
7 void printMonthTitle(int ano, int mês);
8 void printMonthName(int mês);
9 void printMonthBody(int ano, int mês);
10 int getStartDay(int ano, int mês);
11 int getTotalNumberOfDays(int ano, int mês);
12 int getNumberOfDaysInMonth(int ano, int mês);
13 bool éLeapYear(int ano);
14
função principal 15 int principal()
16 {
17 // Solicita ao usuário para inserir o ano
18 cout << "Insira o ano completo (ex.: 2001): ";
19 ano interno ;
ano de entrada 20 cin >> ano;
21
22 // Solicita ao usuário para inserir o mês
23 cout << "Insira o mês em número entre 1 e 12: ";
24 mês interno ;
mês de entrada 25 cin >> mês;
26
27 //Imprime o calendário do mês do ano
imprimir calendário 28 printMês(ano, mês);
29
30 retornar 0;
31 }
32
33 // Imprime o calendário de um mês do ano
imprimir mês 34 void printMonth(int ano, int mês)
35 {
36 //Imprime os títulos do calendário
37 printMonthTitle(ano,mês);
38
39 //Imprime o corpo do calendário
40 printMonthBody(ano,mês);
41 }
42
43 // Imprime o título do mês, por exemplo, maio de 1999
imprimir título do mês 44 void printMonthTitle(int ano, int mês)
45 {
46 printNomeMês(mês);
""
47 conta << << ano << fim;
48 conta << "-------------------------------------------" << fim;
"
49 conta << Dom Seg Ter Qua Qui Sex Sáb" << endl;
Machine Translated by Google

6.15 Abstração de Função e Refinamento Stepwise 267

50}
51
52 // Obtenha o nome em inglês do mês
53 void printMonthName(int mês)
54 {
55 mudar (mês)
56 {
57 caso 1:
58 cout << "Janeiro";
59 quebrar;
60 caso 2:
61 cout << "fevereiro";
62 quebrar;
63 caso 3:
64 cout << "Março";
65 quebrar;
66 caso 4:
67 cout << "Abril";
68 quebrar;
69 caso 5:
70 cout << "maio";
71 quebrar;
72 caso 6:
73 cout << "junho";
74 quebrar;
75 caso 7:
76 cout << "Julho";
77 quebrar;
78 caso 8:
79 cout << "Agosto";
80 quebrar;
81 caso 9:
82 cout << "setembro";
83 quebrar;
84 caso 10:
85 cout << "Outubro";
86 quebrar;
87 caso 11:
88 cout << "novembro";
89 quebrar;
90 caso 12:
91 cout << "Dezembro";
92 }
93 }
94
95 // Imprime o corpo do mês
96 void printMonthBody(int ano, int mês) imprimir corpo do mês
97 {
98 // Obtém o dia de início da semana para a primeira data do mês
99 int startDay = getStartDay(ano, mês);
100
101 // Obtém o número de dias do mês
102 int numberOfDaysInMonth = getNumberOfDaysInMonth(ano, mês);
103
104 // Preenche o espaço antes do primeiro dia do mês
105 int eu = 0;
106 for (i = 0; i < startDay; i++)
"
107 conta << ";
Machine Translated by Google

268 Capítulo 6 Funções


108
109 for (i = 1; i <= númeroDeDiasNoMês; i++)
110 {
111 cout << setw(4) << i;
112
113 if ((i + startDay)% 7 == 0)
114 cout << endl;
115 }
116 }
117
118 // Obtém o dia de início do primeiro dia do mês
comece o dia 119 int getStartDay(int ano, int mês)
120 {
121 // Obtém o número total de dias desde 01/01/1800
122 int diainicial1800 = 3;
123 int totalNumberOfDays = getTotalNumberOfDays(ano, mês);
124
125 //Retorna o dia de início
126 return (totalNumberOfDays + startDay1800)% 7;
127 }
128
129 // Obtém o número total de dias desde 1º de janeiro de 1800
getTotalNumberOfDays 130 int getTotalNumberOfDays(int ano, int mês)
131 {
132 total interno = 0;
133
134 // Obtém o total de dias de 1800 até o ano - 1
135 for (int i = 1800; i < ano; i++)
136 if (é ano bissexto (i))
137 total = total + 366;
138 outro
139 total = total + 365;
140
141 //Adiciona dias de janeiro ao mês anterior ao mês civil
142 for (int i = 1; i < mês; i++)
143 total = total + getNumberOfDaysInMonth(ano, i);
144
145 retorno total;
146}
147
148 // Obtém o número de dias em um mês
getNumberOfDaysInMonth 149 int getNumberOfDaysInMonth(int ano, int mês)
150 {
151 if (mês == 1 || mês == 3 || mês == 5 || mês == 7 ||
152 mês == 8 || mês == 10 || mês == 12)
153 retornar 31;
154
155 if (mês == 4 || mês == 6 || mês == 9 || mês == 11)
156 retornar 30;
157
158 if (mês == 2) retornar éAnoLeap(ano)? 29:28 ; _
159
160 retornar 0; //Se o mês estiver incorreto
161 }
162
163 // Determina se é um ano bissexto
é ano bissexto 164 bool éLeapYear(int ano)
Machine Translated by Google

6.15 Abstração de Função e Refinamento Stepwise 269

165 {
166 ano de retorno % 400 == 0 || (ano % 4 == 0 && ano % 100 != 0);
167}

Insira o ano completo (por exemplo, 2012):


2012 Insira o mês como um número entre 1 e 12: 3
Março de 2012
-----------------------------

Dom Seg Ter Qua Qui Sex Sáb


12 3
4 5 67 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

O programa não valida a entrada do usuário. Por exemplo, se o usuário digitasse um mês que não estivesse
no intervalo entre 1 e 12, ou um ano antes de 1800, o programa exibiria um calendário errado. Para evitar esse
erro, adicione uma instrução if para verificar a entrada antes de imprimir o calendário.
Este programa imprime calendários para um mês, mas pode ser facilmente modificado para imprimir
calendários para um ano. Embora só possa imprimir meses após janeiro de 1800, poderia ser modificado para
traçar o dia de um mês anterior a 1800.

6.15.4 Benefícios do refinamento gradual


O refinamento gradual divide um grande problema em subproblemas menores e gerenciáveis. Cada
subproblema pode ser implementado usando uma função. Essa abordagem torna o programa mais fácil de
escrever, reutilizar, depurar, testar, modificar e manter.

Programa mais simples

O programa de impressão do calendário é longo. Em vez de escrever uma longa sequência de instruções em
uma função, o refinamento gradual a divide em funções menores. Isso simplifica o programa e torna todo o
programa mais fácil de ler e entender.

Reutilizando funções

O refinamento gradual promove a reutilização de código dentro de um programa. A função isLeapYear é definida
uma vez e invocada a partir das funções getTotalNumberOfDays e getNumberOfDaysInMonth . Isso reduz
o código redundante.

Desenvolvimento, depuração e testes mais fáceis

Como cada subproblema é resolvido em uma função, uma função pode ser desenvolvida, depurada e testada desenvolvimento
individualmente. Isso isola os erros e facilita o desenvolvimento, a depuração e os testes. incremental e testes

Ao implementar um programa grande, use a abordagem de cima para baixo ou de baixo para cima. Não
escreva o programa inteiro de uma vez. Usar essas abordagens parece levar mais tempo de desenvolvimento
(porque você compila e executa o programa repetidamente), mas na verdade economiza tempo e facilita a depuração.

Facilitando melhor o trabalho em equipe

Como um grande problema é dividido em subprogramas, os subproblemas podem ser atribuídos a outros
programadores. Isso torna mais fácil para os programadores trabalharem em equipes.
Machine Translated by Google

270 Capítulo 6 Funções

Termos chave

parâmetro real 229 variável global 245


invocação ambígua 240 ocultação de informações 262
argumento 229 função embutida 244
variável automática 248 variável local 245
implementação de baixo para cima 264 lista de parâmetros 229
dividir e conquistar 262 passagem por referência 253
parâmetro formal (ou seja, parâmetro) 229 passagem por valor 250
abstração de função 262 variável de referência 252
declaração de função 241 escopo da variável 245
cabeçalho de função 229 variável local estática 248
sobrecarga de função 239 refinamento gradual 262
protótipo de função 241 esboço 264
assinatura de função 229 implementação de cima para baixo 264

Resumo do capítulo

1. Tornar os programas modulares e reutilizáveis é um objetivo da engenharia de software. Funções


pode ser usado para desenvolver código modular e reutilizável.

2. O cabeçalho da função especifica o tipo de valor de retorno, o nome da função e os parâmetros de


a função.

3. Uma função pode retornar um valor. O returnValueType é o tipo de dados do valor que
a função retorna.

4. Se a função não retornar um valor, returnValueType será a palavra-chave void.

5. A lista de parâmetros refere-se ao tipo, ordem e número dos parâmetros de uma função.

6. Os argumentos passados para uma função devem ter o mesmo número, tipo e
ordem como os parâmetros na assinatura da função.

7. O nome da função e a lista de parâmetros juntos constituem a assinatura da função.

8. Os parâmetros são opcionais; isto é, uma função não pode conter parâmetros.

9. Uma função que retorna valor deve retornar um valor quando a função for concluída.

10. Uma instrução return pode ser usada em uma função void para encerrar a função e retornar o controle ao
chamador da função.

11. Quando um programa chama uma função, o controle do programa é transferido para a função chamada.

12. Uma função chamada retorna o controle ao chamador quando sua instrução return é executada ou sua chave
de fechamento de finalização de função é atingida.

13. Uma função que retorna valor também pode ser invocada como uma instrução em C++. Nesse caso, o
chamador simplesmente ignora o valor de retorno.
Machine Translated by Google

Resumo do Capítulo 271

14. Uma função pode estar sobrecarregada. Isso significa que duas funções podem ter o mesmo nome
contanto que suas listas de parâmetros de função sejam diferentes.

15. Passagem por valor passa o valor do argumento para o parâmetro.

16. A passagem por referência passa a referência do argumento.

17. Se você alterar o valor de um argumento passado por valor em uma função, o valor não será
alterado no argumento após o término da função.

18. Se você alterar o valor de um argumento de passagem por referência em uma função, o valor será
alterado no argumento após o término da função.

19. Um parâmetro de referência constante é especificado usando a palavra-chave const para informar ao
compilador que seu valor não pode ser alterado na função.

20. O escopo de uma variável é a parte do programa onde a variável pode ser usada.

21. Variáveis globais são declaradas fora das funções e são acessíveis a todas as funções em
seu escopo.

22. Variáveis locais são definidas dentro de uma função. Depois que uma função completa sua execução, todas as
suas variáveis locais são destruídas.

23. Variáveis locais também são chamadas de variáveis automáticas.

24. Variáveis locais estáticas podem ser definidas para reter as variáveis locais para uso pela próxima função.
chamada de ação.

25. C++ fornece funções embutidas para evitar chamadas de função para execução rápida.

26. Funções inline não são chamadas; em vez disso, o compilador copia o código da função na linha em
o ponto de cada invocação.

27. Para especificar uma função inline, preceda a declaração da função com o inline
palavra-chave.

28. C++ permite declarar funções com valores de argumento padrão para passagem por valor
parâmetros.

29. Os valores padrão são passados para os parâmetros quando uma função é invocada sem o
argumentos.

30. A abstração de funções é obtida separando o uso de uma função de sua implementação.

31. Programas escritos como coleções de funções concisas são mais fáceis de escrever, depurar e manter.
manter e modificar o que seria de outra forma.

32. Ao implementar um programa grande, use a abordagem de codificação de cima para baixo ou de baixo para cima.

33. Não escreva o programa inteiro de uma vez. Essa abordagem parece levar mais tempo para codificação (porque
você está compilando e executando o programa repetidamente), mas na verdade economiza tempo e facilita
a depuração.
Machine Translated by Google

272 Capítulo 6 Funções

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 6.2–6.11

6.1 (Matemática: Números triangulares) Um número triangular é definido como m(m + 1)/2 para m = 1,
2, c e assim por diante.
, Portanto, os primeiros números são 1, 5, 12, 22,. . . .
Escreva uma função com o seguinte cabeçalho que retorne um número triangular:

int getTriangularNumber(int n)

Escreva um programa de teste que use esta função para exibir os primeiros 75 números
triangulares com 5 números em cada linha.

*6.2 (Média de dígitos em um número inteiro) Escreva uma função que calcule a média dos dígitos em
um número inteiro. Use o seguinte cabeçalho de função:

média duplaDígitos ( n longo)

Por exemplo, AverageDigits(936) retorna 6,0 ((9 + 3 + 6)/3). (Dica: use o operador % para
extrair dígitos e o operador / para remover o dígito extraído.
Por exemplo, para extrair 6 de 936, use 936% 10 (= 6). Para remover 6 de 936, use 936/10 (=
93). Use um loop para extrair e remover repetidamente o dígito até que todos os dígitos sejam
extraídos. Escreva um programa de teste que solicite ao usuário que insira um número inteiro
e exiba a soma de todos os seus dígitos.
**6.3 (inteiro Armstrong) Escreva as funções com os seguintes cabeçalhos:

// Retorna a soma dos cubos dos dígitos em um inteiro,


// ou seja, cubeOfDigits(131) retorna 13 +33 +13 =29
int cubeOfDigits(int número)

// Exibe se o inteiro é um inteiro Armstrong


void isArmstrong(int soma, int número)

Use cubeOfDigits para implementar isArmstrong. Um inteiro é um inteiro Armstrong se a


soma dos cubos de seus dígitos for igual ao próprio número. Escreva um programa de teste
que solicite ao usuário que insira um número inteiro e informe se é um número inteiro Armstrong.

*6.4 (Exibir dígitos pares em um inteiro) Escreva uma função com o seguinte cabeçalho para exibir os
dígitos pares em um inteiro:

void displayEven(int número)

Por exemplo, displayEven(345) exibe 4. Escreva um programa de teste que solicite ao usuário
que insira um número inteiro e exiba os dígitos pares nele.
*6.5 (Maior de três números) Escreva uma função com o seguinte cabeçalho para exibir o maior de três
números:

void displayLargest(
duplo num1, duplo num2, duplo num3)

Escreva um programa de teste que solicite ao usuário que insira três números e invoque a
função para exibir o maior deles.
Machine Translated by Google

Exercícios de Programação 273

*6.6 (Padrões de exibição) Escreva uma função para exibir um padrão como segue:

****************
**************
************
...
*

O cabeçalho da função é

void displayPattern(int n)

*6.7 (Aplicação financeira: calcular o valor do investimento futuro) Escreva uma função que calcule o
valor do investimento futuro a uma determinada taxa de juros durante um determinado
número de anos. O investimento futuro é determinado utilizando a fórmula do Exercício de
Programação 2.23.

Use o seguinte cabeçalho de função:

double futureInvestmentValue(
valor de investimento duplo , taxa de juros mensal dupla , anos inteiros )

Por exemplo, futureInvestmentValue(10000, 0,05/12, 5) retorna 12.833,59.


Escreva um programa de teste que solicite ao usuário o valor do investimento (por exemplo,
1.000) e a taxa de juros (por exemplo, 9%) e imprima uma tabela que exiba o valor futuro
para os anos de 1 a 30, conforme mostrado abaixo:

O valor investido: 1000


Taxa de juros anual: 9

Valor futuro em anos


1 1.093,80
2 1196,41
...
29 13467,25
30 14730,57

6.8 (Conversões entre Milímetros e Polegadas) Escreva as duas funções a seguir:

// Converte de milímetros para polegadas double


millimetersToInches(double millimeters)

// Converte de polegadas para milímetros double


inchToMillimeters(double inch)
A fórmula para a conversão é

milímetro = 0,39 * polegadas

Escreva um programa de teste que invoque essas funções para exibir as seguintes tabelas:

Milímetros 2 Polegadas |
Polegadas Milímetros
0,078 1 65.574
4 0,156 ||
2 81.967
...
98 3.822 49 1256,41
100 3.900 ||
50 1282.05
Machine Translated by Google

274 Capítulo 6 Funções

6.9 (Conversões entre Libras e Onças) Escreva as duas funções a seguir:

// Converte de libras para onças double


librasToOunces(double libras)

// Converte de onças para libras double


ouncesToPounds(double inch)

A fórmula para a conversão é

libra = 16 * Celsius onças


= 0,0625 * libra

Escreva um programa de teste que invoque essas funções para exibir as seguintes tabelas:

Libras Onças |
Onças 1 Libras
11 176 0,0625
12 192 ||
2 0,125
...
19 304 0,5625
20 320 ||
9 10 0,625

6.10 (Aplicação financeira: encontre o lucro por item) Use o esquema do Exercício de
programação 5.39 para escrever uma função que calcule o lucro por item. O cabeçalho
da função é:

cálculo duploProfitPerItem ( quantidade dupla)

Escreva um programa de teste que exiba a seguinte tabela:

Quantidade Lucro por item (em $) 1.000


1000 3.000
2000
...
9.000 29.000
10.000 34.000

6.11 (Exibir valores ASCII) Escreva uma função que imprima os valores ASCII dos caracteres
atores usando o seguinte cabeçalho:

void printASCII(char ch1, char ch2, int numberPerLine)

Esta função imprime os valores ASCII dos caracteres entre ch1 e ch2 com o número
especificado de caracteres por linha. Escreva um programa de teste que imprima 6
valores ASCII por linha de caracteres de 'a' a 'm'.
*6.12 (Soma de séries) Escreva uma função para calcular as seguintes séries:

1 1 1 1
f(n) = 3 + + c +
8 15 n(n + 2)

Escreva um programa de teste que exiba a seguinte tabela:

n f(n)
0,458333
24 0,566667
...
12 0,675824
14 0,685417
Machine Translated by Google

Exercícios de Programação 275

*6.13 (Estimativa p) p pode ser calculado usando a seguinte série:


1 1 1 1 1
+ + + + c +
f(n) = A6 * ¢1 + 4 9 16 25 n2 ÿ
Escreva uma função que retorne f(n) para um determinado n e escreva um programa de
teste que exiba a seguinte tabela:

f(n)
nº 2 2,73861
4 2,92261
6 2,99138
8 3,0273
10 3,04936
12 3,06429
14 3,07506
16 3,08319
18 3,08956
20 3,09467

*6.14 (Aplicação financeira: imprimir uma tabela de impostos) A Listagem 3.3, ComputeTax.cpp, é um
programa para calcular impostos. Escreva uma função para calcular impostos usando o seguinte
cabeçalho:

double computaTax( status int, double tributableIncome)

Use esta função para escrever um programa que imprima uma tabela de impostos para renda tributável de
US$ 50.000 a US$ 60.000 com intervalos de US$ 50 para todos os quatro status, como segue:

Tributável Casado Conjunto ou Casado Chefe de


Renda Viúva Solteira Qualificada(s) Separa uma Casa

50000 8688 6665 8688 7352


50050 8700 6673 8700 7365

...
59950 11175 8158 11175 9840
60000 11188 8165 11188 9852

*6.15 (Número de dias em fevereiro) Escreva uma função que retorne o número de dias em
fevereiro usando o seguinte cabeçalho:

int numberOfDaysInFebruary(int ano)

Escreva um programa de teste que exiba o número de dias de fevereiro do ano de


1985 . . . , até 1983.
*6.16 (Exibir matriz de 0s e 1s) Escreva uma função que exiba uma matriz n por n usando
o seguinte cabeçalho:

void printMatrix(int n)

Cada elemento é 0 ou 1, que é gerado aleatoriamente. Escreva um programa de teste que


solicite ao usuário que insira n e exiba uma matriz n por n . Aqui está um exemplo de execução:

Digite n: 3 0 1 0

000
111
Machine Translated by Google

276 Capítulo 6 Funções

6.17 (Validação e perímetro do Triângulo Equilátero) Implemente os dois seguintes


funções:

// Retorna verdadeiro se todos os lados do triângulo


// são iguais.
bool éValid( lado duplo1, lado duplo2 , lado duplo3 )

// Retorna o perímetro de um triângulo equilátero.


perímetro duplo ( lado duplo 1)

A fórmula para calcular o perímetro é perímetro = 3 * lado. Escreva um programa de


teste que leia três lados de um triângulo e calcule o perímetro se a entrada for válida.
Caso contrário, indique que a entrada é inválida.
6.18 (Modifique a função gcd ) A Listagem 6.4, GreatestCommonDivisorFunction.cpp, fornece
a função gcd(int n1, int n2) para calcular o GCD de dois números.
Modifique esta função para calcular o mínimo múltiplo comum (MCC) de dois números.
**6.19 (Número Armstrong reverso) Escreva um programa de teste que solicite ao usuário que insira
um número inteiro e verifique se é um número inteiro Armstrong. Se for um número de
Armstrong, o programa inverte os dígitos do número inteiro e verifica se o número inteiro
invertido também é um número de Armstrong. Caso contrário, mostre que o número inteiro
inserido pelo usuário não é um número Armstrong.
*6.20 (Geometria: posição do ponto) O Exercício de Programação 3.29 mostra como testar se um
ponto está no lado esquerdo de uma linha direcionada, à direita ou na mesma linha.
Escreva as seguintes funções:

/** Retorna verdadeiro se o ponto (x2, y2) estiver no lado esquerdo da * linha
direcionada de (x0, y0) para (x1, y1) */ bool leftOfTheLine(double
x0, double y0,
duplo x1, duplo y1, duplo x2, duplo y2)

/** Retorna verdadeiro se o ponto (x2, y2) estiver na mesma * linha


de (x0, y0) a (x1, y1) */ bool onTheSameLine(double
x0, double y0,
duplo x1, duplo y1, duplo x2, duplo y2)

/** Retorna verdadeiro se o ponto (x2, y2) estiver no


* segmento de linha de (x0, y0) a (x1, y1) */ bool
onTheLineSegment(double x0, double y0,
duplo x1, duplo y1, duplo x2, duplo y2)

Escreva um programa que solicite ao usuário que insira os três pontos para p0, p1 e p2 e
mostre se p2 está à esquerda da reta de p0 a p1, à direita, na mesma reta ou no segmento
de reta. Aqui estão alguns exemplos de execução:

Insira três pontos para p0, p1 e p2: 1 1 2 2 1,5 1,5 (1,5, 1,5) está no
segmento de linha de (1,0, 1,0) a (2,0, 2,0)

Insira três pontos para p0, p1 e p2: 1 1 2 2 3 3 (3,0, 3,0) está na


mesma linha de (1,0, 1,0) a (2,0, 2,0)

Insira três pontos para p0, p1 e p2: 1 1 2 2 1 1,5 (1,0, 1,5) está no lado
esquerdo da linha de (1,0, 1,0) a (2,0, 2,0)
Machine Translated by Google

Exercícios de Programação 277

Insira três pontos para p0, p1 e p2: 1 1 2 2 1 -1 (1,0, -1,0) está no


lado direito da linha
de (1,0, 1,0) a (2,0, 2,0)

**6.21 (Número par do palíndromo) Um número é um palíndromo se sua reversão for igual a ele mesmo. Um número par
de palíndromo é um número par e também um palíndromo. Escreva um programa que exiba os primeiros
50 números pares do palíndromo.
Exiba 5 números por linha e alinhe os números corretamente, como segue:

6 8 22
2 44 4 66 88 202 212
...

**6.22 (Jogo: dados) Craps é um jogo de dados popular jogado em cassinos. Escreva um programa para
jogue uma variação do jogo, como segue:

Jogue dois dados. Cada dado tem seis faces representando os valores 1, 2,… e 6, respectivamente.
Verifique a soma dos dois dados. Se a soma for 2, 3 ou 12 (chamados dados), você perde; se a soma for 7
ou 11 (chamado natural), você ganha; se a soma for outro valor (ou seja, 4, 5, 6, 8, 9 ou 10), um ponto é
estabelecido. Continue até rolar um 7
(você perde) ou o mesmo valor de pontos (você ganha).

Seu programa atua como um único jogador. Aqui estão alguns exemplos de execução:

Você rolou 5 + 6 = 11
Você ganha

Você rolou 1 + 2 = 3
Você perdeu

Você rolou 4 + 4 = 8
ponto é 8
Você rolou 6 + 2 = 8
Você ganha

Você rolou 3 + 2 = 5
ponto é 5
Você rolou 2 + 5 = 7
Você perdeu

**6.23 (Emirp) Um emirp (primo escrito ao contrário) é um número primo não palindrômico cuja reversão também é primo.
Por exemplo, 17 é primo e 71 é primo. Portanto, 17 e 71 são emirps. Escreva um programa que exiba os
Nota de vídeo
primeiros 100 emirps. Exiba 10 números por linha e alinhe-os corretamente, da seguinte maneira:
Encontre emirp prime

13 17 31 37 71 73 79 97 107 113 149 157 167 179 199 311


337 347 359 389
...

**6.24 (Jogo: Proporção de vitória para derrota no jogo de dados) Revise o Exercício de Programação 6.22 para executá-
lo 5.000 vezes e exibir a proporção de vitória para derrota.

**6.25 (Primo aditivo) Um número primo é chamado de primo aditivo se a soma de seus dígitos também for um número
primo. Escreva um programa que encontre os primeiros 25 números primos aditivos e exiba a saída no
seguinte formato:
Machine Translated by Google

278 Capítulo 6 Funções


Número primo 2 3 Soma de seus dígitos
2
3
...
11 2
...

**6.26 (Dia e horas restantes) O Exercício de Programação 3.9 exibe o dia atual e as horas restantes. Simplifique o
Exercício de Programação 3.9 usando uma função para obter o número do dia de uma semana e as
horas passadas.

**6.27 (Matemática: aproximar a raiz quadrada) Como é a função sqrt no cmath


biblioteca implementada? Existem diversas técnicas para implementá-lo. Uma dessas técnicas é
conhecida como método babilônico. Ele aproxima a raiz quadrada de um número, n, realizando
repetidamente um cálculo usando a seguinte fórmula:

nextGuess = (lastGuess + (n / lastGuess)) / 2

Quando nextGuess e lastGuess são quase idênticos, nextGuess é a raiz quadrada aproximada. A
estimativa inicial pode ser qualquer valor positivo (por exemplo, 1). Este valor será o valor inicial para
lastGuess. Se a diferença entre nextGuess
e lastGuess for menor que um número muito pequeno, como 0,0001, você pode afirmar que nextGuess
é a raiz quadrada aproximada de n. Caso contrário, nextGuess torna-se lastGuess e o processo de
aproximação continua. Implemente a seguinte função que retorna a raiz quadrada de n:

quadrado duplo (int n)

*6.28 (Dígito inteiro par ou ímpar) Escreva uma função que verifique se um número inteiro é um
dígito par ou um número inteiro de dígito ímpar usando o seguinte cabeçalho:

int getType(int n)

Por exemplo, getType(39) é um número inteiro de dígito par e getType(5) é um número inteiro de dígito
ímpar. Escreva um programa de teste que solicite ao usuário que insira um número inteiro e exiba seu
tipo.

*6.29 (Média de casas pares) Escreva uma função que retorne a média dos dígitos nas casas pares de um inteiro
usando o seguinte cabeçalho:

duplo avgOfEvenPlaces(int n)

Por exemplo, avgOfEvenPlaces(5856) retorna 7,0 e avgOfEvenPlaces(131)


retorna 3,0. Escreva um programa de teste que solicite ao usuário que insira um número inteiro e exiba
a média dos dígitos nas casas pares desse número inteiro.

Seções 6.12–6.15

*6.30 (Pesquisar um caractere) Escreva uma função que procure um caractere específico em um
string usando o seguinte cabeçalho:

pesquisa nula (string& s, char& chave)

Escreva um programa de teste que solicite ao usuário que insira a string e um caractere e exiba se o
caractere for encontrado.

*6.31 (Multiplicar por um valor constante) Escreva uma função com o seguinte cabeçalho para multiplicar
os três números com um valor constante:

multiplicação nula (double& num1, double& num2, double& num3 int constante)

Escreva um programa de teste que solicite ao usuário que insira três números e uma constante para
multiplicá-los e exiba o resultado.
Machine Translated by Google

Exercícios de Programação 279

*6.32 (Álgebra: resolver equações quadráticas) As duas raízes de uma equação quadrática
ax2 + bx + x = 0 pode ser obtido usando a seguinte fórmula:

-b + 2b2 - 4ac -b - 2b2 - 4ac


= =
r1 e r2
2a 2a

Escreva uma função com o seguinte cabeçalho

void resolveQuadraticEquation(duplo a, duplo b, duplo c, duplo&


discriminante, duplo& r1, duplo& r2)

b2 - 4ac é chamado de discriminante da equação quadrática. Se o discriminante for menor


que 0, a equação não tem raízes. Neste caso, ignore o valor em r1 e r2.

Escreva um programa de teste que solicite ao usuário que insira valores para a, b e c e
exiba o resultado com base no discriminante. Se o discriminante for maior ou igual a 0,
exiba as duas raízes. Se o discriminante for igual a 0, exiba a raiz única. Caso contrário,
exiba "a equação não tem raízes". Veja o Exercício de Programação 3.1 para exemplos
de execução.
*6.33 (Álgebra: resolver 2 * 2 equações lineares) Você pode usar a regra de Cramer para resolver o
seguinte sistema 2 * 2 de equações lineares:

machado + por = e ed - namorado


= de - ec
x= e
cx + dy = f anúncio - BC anúncio - BC

Escreva uma função com o seguinte cabeçalho:

void resolverEquação(duplo a, duplo b, duplo c, duplo d,


duplo e, duplo f, duplo& x, duplo& y, bool& isSolúvel)

Se ad - bc for 0, a equação não tem solução e isSolvable deve ser falsa.


Escreva um programa que solicite ao usuário que digite a, b, c, d, e e f e exiba o resultado.
Se ad - bc for 0, informe que “A equação não tem solução”. Veja o Exercício de
Programação 3.3 para exemplos de execução.
***6.34 (Data e hora atuais) Invocar time(0) retorna o tempo decorrido em milissegundos desde a meia-
noite de 1º de janeiro de 1970. Escreva um programa que exiba a data e a hora. Aqui
está um exemplo de execução:

A data e hora atuais é 16 de maio de 2009 10:34:23

**6.35 (Geometria: interseção) Suponha que dois segmentos de linha se cruzam. Os dois pontos finais para o primeiro
segmento de linha são (x1, y1) e (x2, y2) e para o segundo segmento de linha são (x3, y3) e (x4, y5).
Nota de vídeo
Escreva a seguinte função que retorna o ponto de intersecção se as duas linhas se cruzarem:
Encontre o ponto de intersecção

void intersectPoint(duplo x1, duplo y1, duplo x2, duplo y2,


duplo x3, duplo y3, duplo x4, duplo y4, duplo& x,
duplo& y, bool& isIntersecting)

Escreva um programa que solicite ao usuário que insira esses quatro pontos finais e exiba
o ponto de interseção. (Dica: Use a função para resolver equações lineares 2 * 2 no
Exercício de Programação 6.33.)
Machine Translated by Google

280 Capítulo 6 Funções

Insira as extremidades do primeiro segmento de linha: 2,0 2,0 0 0 Insira as


extremidades do segundo segmento de linha: 0 2,0 2,0 0 O ponto de interseção é:
(1, 1)

Insira as extremidades do primeiro segmento de linha: 2,0 2,0 0 0 Insira as


extremidades do segundo segmento de linha: 3 3 1 1 As duas linhas não se
cruzam

6.36 (Formatar um inteiro) Escreva uma função com o seguinte cabeçalho para formatar um inteiro positivo com a
largura especificada:

formato de string ( número interno, largura interna )

A função retorna uma string para o número com um ou mais prefixos 0s. O tamanho da string é a largura.
Por exemplo, format(34, 4) retorna 0034 e format(34, 5) retorna 00034. Se o número for maior que a
largura, a função retornará a representação de string do número. Por exemplo, formato(34, 1)

retorna 34.

Escreva um programa de teste que solicite ao usuário que insira um número e sua largura e exiba uma
string retornada invocando format(number, width).

**6.37 (Financeiro: validação de número de cartão de crédito) Os números de cartão de crédito seguem certos padrões.
O número do cartão de crédito deve ter entre 13 e 16 dígitos. O número deve começar com o seguinte:

nº 4 para cartões Visa


n 5 para cartões MasterCard

n 37 para cartões American Express


nº 6 para cartões Discover

Em 1954, Hans Luhn, da IBM, propôs um algoritmo para validar números de cartão de crédito. O algoritmo
é útil para determinar se um número de cartão foi inserido corretamente ou digitalizado corretamente por
um scanner. Quase todos os números de cartão de crédito são gerados após esta verificação de validade,
comumente conhecida como verificação Luhn ou verificação Mod 10. Pode ser descrito da seguinte forma.
(Para ilustração, considere o número do cartão 4388576018402626.)

1. Dobre cada segundo dígito da direita para a esquerda. Se a duplicação de um dígito resultar em um
número de dois dígitos, adicione os dois dígitos para obter um número de um único dígito.

4388576018402626

2*2=4
2*2=4
4*2=8
1*2=2
6 * 2 = 12 (1 + 2 = 3)
5 * 2 = 10 (1 + 0 = 1)
8 * 2 = 16 (1 + 6 = 7)
4*2=8

2. Agora adicione todos os números de um dígito da Etapa 1. 4


+ 4 + 8 + 2 + 3 + 1 + 7 + 8 = 37

3. Adicione todos os dígitos nos lugares ímpares, da direita para a esquerda, no número do cartão.
6 + 6 + 0 + 8 + 0 + 7 + 8 + 3 = 38
Machine Translated by Google

Exercícios de Programação 281

4. Some os resultados da Etapa 2 e da Etapa 3.


37 + 38 = 75

5. Se o resultado do Passo 4 for divisível por 10, o número do cartão é válido; caso contrário, é inválido.
Por exemplo, o número 4388576018402626 é inválido, mas o número 4388576018410707 é válido.

Escreva um programa que solicite ao usuário que insira um número de cartão de crédito como uma string.
Exibir se o número é válido. Projete seu programa para usar as seguintes funções:

// Retorna verdadeiro se o número do cartão for válido


bool éValid ( string const e número do cartão)

// Obtém o resultado do Passo 2


int sumOfDoubleEvenPlace(const string& cardNumber)

// Retorna este número se for um único dígito, caso contrário,


//retorna a soma dos dois dígitos
int getDigit(int número)

// Retorna a soma dos dígitos ímpares do número do cartão


int sumOfOddPlace(const string& cardNumber)

// Retorna verdadeiro se substr for o prefixo de cardNumber


bool começaCom(const string& cardNumber, const string& substr)

*6.38 (Decimal para octal) Escreva uma função que analisa um número decimal em um octal
número. O cabeçalho da função é o seguinte:

int dec2Octal(const int&decimal)

Escreva um programa de teste que solicite ao usuário que insira um número decimal, use a função
dec2Octal para analisá-lo em um número octal equivalente e exiba o número octal.

*6.39 (Octal para decimal) Escreva uma função que retorne um número decimal de um octal
número. O cabeçalho da função é o seguinte:

int octal2Dec(const int& octalNumber)

Por exemplo, o número octal 345 é 229 (3 * 82 + 4 * 81 + 5 * 80 = 229). Portanto, octal2Dec("345")


retorna 229. Escreva um programa de teste que solicite ao usuário que insira um número octal e exiba
seu valor decimal equivalente.

**6.40 (Binário para Octal) Escreva uma função que retorne um número octal de um número binário
número. O cabeçalho da função é o seguinte:

int bin2Octal( string const e string binária)

Escreva um programa de teste que solicite ao usuário que insira um número binário como uma string e
exiba o valor octal correspondente como uma string.

**6.41 (Octal para binário) Escreva uma função que retorne uma string binária de um número octal.
O cabeçalho da função é o seguinte:

string octal2Binary(const int& octalNumber)

Escreva um programa de teste que solicite ao usuário que insira um número octal, use a função
octal-2Binary para analisá-lo em uma string binária equivalente e exiba a string binária.
Machine Translated by Google

282 Capítulo 6 Funções

*6.42 (Exibir a string mais longa) Escreva a função mais longa usando o seguinte cabeçalho de
função para retornar a string mais longa entre duas strings:

string mais longa (const string& s1, const string& s2)

Escreva um programa de teste que solicite ao usuário que insira duas strings e exiba a
string mais longa se a entrada for válida. Por exemplo, se duas strings forem Bem-vindo e
Programação, a saída será Programação.
**6.43 (Verificar substrings) Escreva a seguinte função para verificar se a string s1 é uma substring da
string s2. A função retorna o primeiro índice em s2 se houver correspondência. Caso
contrário, retorne -1.

int indexOf(const string& s1, const string& s2)

Escreva um programa de teste que leia duas strings e verifique se a primeira string é uma
substring da segunda string. Aqui está um exemplo de execução do programa:

Digite a primeira string: bem-vindo Digite


a segunda string: Sejam bem-vindos! indexOf("bem-
vindo", "Bem-vindo!") é 3

Digite a primeira string: bem vindo Digite


a segunda string: Convidamos você! indexOf("bem-
vindo", "Nós convidamos você!") é –1

*6.44 (Ocorrências de um caractere especificado) Escreva uma função que encontre o número de
ocorrências de um caractere especificado na string usando o seguinte cabeçalho:

contagem int (const string& s, char a)

Por exemplo, count("Bem-vindo", 'e') retorna 2. Escreva um programa de teste que leia
uma string e um caractere e exiba o número de ocorrências do caractere na string. Aqui
está um exemplo de execução do programa:

Insira uma string: Bem-vindo ao C++


Insira um caractere: oo
aparece em Welcome to C++ 2 vezes

***6.45 (Ano, mês e dia atuais) Escreva um programa que exiba o ano, mês e dia atuais usando a função
time(0) . Aqui está um exemplo de execução do programa:

A data atual é 17 de maio de 2012

**6.46 (Swap case) Escreva a seguinte função que retorna uma nova string na qual as letras maiúsculas
são alteradas para minúsculas e as minúsculas para maiúsculas.
Machine Translated by Google

Exercícios de Programação 283

string swapCase(const string& s)

Escreva um programa de teste que solicite ao usuário que insira uma string e invoque esta função e
exiba o valor de retorno desta função. Aqui está um exemplo de execução:

Digite uma string: estou aqui


A nova string é: ESTOU AQUI

**6.47 (Teclados de telefone) O mapeamento padrão internacional de letras/números para telefones é mostrado no
Exercício de Programação 4.15. Escreva uma função que retorne um número, dada uma letra maiúscula,
como segue:

int getNumber(char letra maiúscula)

Escreva um programa de teste que solicite ao usuário que insira um número de telefone como uma string.
O número de entrada pode conter letras. O programa converte uma letra (maiúscula ou minúscula) em
um dígito e deixa todos os outros caracteres intactos. Aqui está um exemplo de execução do programa:

Digite uma string: 1-800-Flowers


1-800-3569377

Insira uma string: 1800flores


18003569377
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

CAPÍTULO

7
Unidimensional
Matrizes
e cordas C
Objetivos
n Descrever porque um array é necessário na programação (§7.1).

n Para declarar arrays (§7.2.1).

n Para acessar elementos do array usando índices (§7.2.2).

n Para inicializar os valores em um array (§7.2.3).

n Para programar operações comuns de array (exibição de arrays, soma de todos os elementos,
localização de elementos mínimos e máximos, embaralhamento aleatório, deslocamento
de elementos) (§7.2.4).

n Aplicar matrizes no desenvolvimento de aplicativos (LottoNumbers,


DeckOfCards) (§§7.3–7.4).

n Para definir e invocar funções com argumentos de array (§7.5).

n Para definir um parâmetro de array const para evitar que ele


seja alterado (§7.6).

n Para retornar um array passando-o como argumento (§7.7).

n Para contar ocorrências de cada letra em uma matriz de caracteres


(CountLettersInArray) (§7.8).

n Para pesquisar elementos usando o algoritmo de pesquisa linear (§7.9.1) ou binário


(§7.9.2).

n Para ordenar um array usando a ordenação por seleção (§7.10).

n Para representar strings usando strings C e usar funções de string C (§7.11).


Machine Translated by Google

286 Capítulo 7 Matrizes unidimensionais e strings C

7.1 Introdução
Um único array pode armazenar uma grande coleção de dados.
Chave
Apontar
Freqüentemente, você terá que armazenar um grande número de valores durante a execução de um programa.
Suponha, por exemplo, que você precise ler 100 números, calcular sua média e determinar quantos números estão
problema acima da média. Primeiro, seu programa lê os números e calcula sua média, e então compara cada número com a
média para determinar se está acima da média. Para conseguir isso, todos os números devem ser armazenados em
variáveis. Você tem que declarar 100 variáveis e escrever repetidamente código quase idêntico 100 vezes. Escrever
um programa desta forma seria impraticável. Então, como você resolve esse problema?

É necessária uma abordagem eficiente e organizada. C++ e a maioria das outras linguagens de alto nível
variedade? fornecem uma estrutura de dados, o array, que armazena uma coleção sequencial de tamanho fixo de elementos do
mesmo tipo. No presente caso, você pode armazenar todos os 100 números em um array e acessá-los por meio de
uma única variável de array. A solução é dada na Listagem 7.1.

Listagem 7.1 AnalisarNumbers.cpp


1 #include <iostream>
2 usando namespace std;
3 números variedade

4 int principal() {
5 números[0]:
6 const int NUMBER_OF_ELEMENTS = 100; números números[1]:
7 duplos [NUMBER_OF_ELEMENTS]; soma dupla = números[2]:
declarar matriz
0; .
números[eu]: .
8 for (int i = 0; i < NUMBER_OF_ELEMENTS; i++) { .
9 números[97]:
10 cout << "Digite um novo número: "; cin >> números[98]:
armazenar número em array 11 números[i]; soma += números[99]:
12 números[i];
13 }
14
obter média 15 média dupla = soma / NUMBER_OF_ELEMENTS;
16
17 contagem interna = 0; // O número de elementos acima da média
18 for (int i = 0; i < NUMBER_OF_ELEMENTS; i++)
acima da média? 19 if (números[i] > média)
20 contar++;
21
22 23 24 cout << "A média é " << média << endl;
"
25 cout << "Número de elementos acima da média << contagem << endl;
26
27 retornar 0;
28 }

O programa declara um array de 100 elementos na linha 7, armazena números no array da linha 13, adiciona
cada número à soma na linha 11 e obtém a média na linha 17. Em seguida, ele compara cada número do array com
a média de conte o número de valores acima da média (linhas 19–22).

Você será capaz de escrever este programa após concluir este capítulo. Este capítulo apresenta arrays
unidimensionais. O Capítulo 8 apresentará matrizes bidimensionais e multidimensionais.
Machine Translated by Google

7.2 Noções básicas de matriz 287

7.2 Noções básicas de matriz

Uma matriz é usada para armazenar vários valores do mesmo tipo. Um elemento em um array pode Chave
ser acessado usando um índice. Apontar

Um array é usado para armazenar uma coleção de dados, mas muitas vezes é mais útil pensar em um array como uma coleção de índice

variáveis do mesmo tipo. Em vez de declarar variáveis individuais, como número0, número1, . . . e número99, você declara uma
matriz com um nome como números e usa números[0], números[1], . e números[99] para representar variáveis individuais. Esta

. .usando
seção apresenta como declarar arrays e acessar elementos de array , índices.

7.2.1 Declarando Matrizes


Para declarar um array, você precisa especificar o tipo e tamanho do elemento usando a seguinte sintaxe:

elementType nomeArray[TAMANHO];

O elementType pode ser qualquer tipo de dados e todos os elementos da matriz terão o mesmo tipo de
dados. O SIZE, conhecido como declarador de tamanho de array, deve ser uma expressão avaliada como declarador de tamanho de array

um número inteiro constante maior que zero. Por exemplo, a instrução a seguir declara um array de 10
elementos duplos :

double minhaLista[10];

O compilador aloca espaço para 10 elementos duplos para o array myList. Quando um array é
declarado, seus elementos recebem valores arbitrários. Para atribuir valores usamos a seguinte sintaxe: valores iniciais arbitrários

arrayNome[índice] = valor;

Por exemplo, o código a seguir inicializa o array:

minhaLista[0] = 5,6;
minhaLista[1] = 4,5;
minhaLista[2] = 3,3;
minhaLista[3] = 13,2;
minhaLista[4] = 4,0;
minhaLista[5] = 34,33;
minhaLista[6] = 34,0;
minhaLista[7] = 45,45;
minhaLista[8] = 99,993;
minhaLista[9] = 111,23;

A matriz é mostrada na Figura 7.1.

Observação

O tamanho do array usado para declarar um array deve ser uma expressão constante no C++ padrão. tamanho constante

Por exemplo, o seguinte código é ilegal:

tamanho interno = 4;
double minhaLista[tamanho]; // Errado

Mas está tudo bem se SIZE for uma constante como segue:

const int TAMANHO = 4;


double minhaLista[TAMANHO]; // Correto
Machine Translated by Google

288 Capítulo 7 Matrizes unidimensionais e strings C

double minhaLista[10];

minhaLista[0] 5.6

minhaLista[1] 4,5

minhaLista[2] 3.3

minhaLista[3] 13.2

minhaLista[4] 4,0

Elemento da matriz no
minhaLista[5] 34,33 Valor do elemento
índice 5

minhaLista[6] 34,0

minhaLista[7] 45,45

minhaLista[8] 99.993

minhaLista[9] 111.23

Figura 7.1 O array myList possui 10 elementos do tipo double e índices int de 0 a 9.

Dica
declarando juntos Se os arrays tiverem o mesmo tipo de elemento, eles poderão ser declarados juntos, como segue:

elementType nomeArray1[tamanho1], nomeArray2[tamanho2], ...,


arrayNome[tamanhoN];

As matrizes são separadas por vírgulas. Por exemplo,

lista dupla1 [10], lista2[25];

7.2.2 Acessando Elementos do Array


índice de matriz Os elementos do array são acessados através do índice inteiro. Os índices de array são baseados
Baseado em 0 em 0; isto é, eles vão de 0 a arraySize-1. O primeiro elemento recebe o índice 0, o segundo
elemento recebe o índice 1 e assim por diante. No exemplo da Figura 7.1, myList contém 10
valores duplos e os índices vão de 0 a 9.
Cada elemento da matriz é representado usando a seguinte sintaxe:

arrayNome[índice];

Por exemplo, myList[9] representa o último elemento do array myList. Observe que o declarador
de tamanho é usado para indicar o número de elementos ao declarar o array. Um índice de array é
usado para acessar um elemento específico em um array.
Cada elemento do array, quando acessado por seu índice, pode ser usado da mesma forma que
uma variável regular. Por exemplo, o código a seguir adiciona os valores em myList[0] e myList[1]
a myList[2].

minhaLista[2] = minhaLista[0] + minhaLista[1];

O código a seguir incrementa myList[0] em 1:

minhaLista[0]++;

O código a seguir invoca a função max para retornar o número maior entre
minhaLista[1] e minhaLista[2]:

cout << max(minhaLista[1], minhaLista[1]) << endl;


Machine Translated by Google

7.2 Noções básicas de matriz 289

O loop a seguir atribui 0 a myList[0], 1 a myList[1], . . . e 9 para minhaLista[9]:

for (int i = 0; i < 10; i++) {

minhaLista[i] = i;
}

Cuidado
Acessar elementos do array usando índices além dos limites (por exemplo, myList[-1] e
myList[10]) causa um erro fora dos limites. Fora dos limites é um erro grave. Infelizmente, o erro fora dos limites
compilador C++ não relata isso. Tenha cuidado para garantir que os índices da matriz estejam
dentro dos limites.

7.2.3 Inicializadores de array


C++ possui uma notação abreviada, conhecida como inicializador de array, que declara e inicializa inicializador de matriz

um array em uma única instrução, usando a seguinte sintaxe:

elementType arrayName[arraySize] = {valor0, valor1, ..., valork};

Por exemplo,

double minhaLista[4] = {1,9, 2,9, 3,4, 3,5};

Esta instrução declara e inicializa o array myList com quatro elementos, tornando-o equivalente às
instruções mostradas abaixo:

double minhaLista[4];
minhaLista[0] = 1,9;
minhaLista[1] = 2,9;
minhaLista[2] = 3,4;
minhaLista[3] = 3,5;

Cuidado
Usando um inicializador de array, você deve declarar e inicializar o array em uma instrução.
Dividi-lo causaria um erro de sintaxe. Assim, a próxima afirmação está errada:

double minhaLista[4];
minhaLista = {1,9, 2,9, 3,4, 3,5};

Observação

C++ permite omitir o tamanho do array ao declarar e criar um array usando um inicializador. Por tamanho implícito
exemplo, a seguinte declaração é adequada:

double minhaLista[] = {1,9, 2,9, 3,4, 3,5};

O compilador descobre automaticamente quantos elementos existem no array.

Observação

C++ permite inicializar uma parte do array. Por exemplo, a instrução a seguir atribui valores 1,9,
inicialização parcial
2,9 aos dois primeiros elementos da matriz. Os outros dois elementos serão definidos como zero.

double minhaLista[4] = {1,9, 2,9};

Observe que se um array for declarado, mas não inicializado, todos os seus elementos conterão
“lixo”, como todas as outras variáveis locais.
Machine Translated by Google

290 Capítulo 7 Matrizes unidimensionais e strings C

7.2.4 Processando Matrizes


Ao processar elementos de array, você frequentemente usará um loop for — pelos seguintes motivos:

n Todos os elementos de um array são do mesmo tipo. Eles são processados uniformemente da mesma maneira
usando um loop.

n Como o tamanho do array é conhecido, é natural usar um loop for .

Suponha que o array seja declarado da seguinte forma:

const int ARRAY_SIZE = 10;


double minhaLista[ARRAY_SIZE];

Aqui estão 10 exemplos de processamento de matrizes:

1. Inicializando arrays com valores de entrada: O loop a seguir inicializa o array myList com
valores de entrada do usuário:

cout << "Entrar" << ARRAY_SIZE << "valores: ";


for (int i = 0; i < ARRAY_SIZE; i++) cin >>
minhaLista[i];

2. Inicializando arrays com valores aleatórios: O loop a seguir inicializa o array myList
com valores aleatórios entre 0 e 99:

for (int i = 0; i < ARRAY_SIZE; i++) {

minhaLista[i] = rand() % 100;


}

3. Imprimindo arrays: Para imprimir um array, você deve imprimir cada elemento nele, usando um loop como o
seguindo:

for (int i = 0; i < ARRAY_SIZE; i++) {

cout << minhaLista[i] << " ";


}

4. Copiando arrays: suponha que você tenha dois arrays, list e myList. Você pode copiar myList
listar usando uma sintaxe como a seguinte ?

lista = minhaLista;

Isso não é permitido em C++. Você deve copiar elementos individuais de um array para outro da seguinte maneira:

for (int i = 0; i < ARRAY_SIZE; i++) {

lista[i] = minhaLista[i];
}

5. Somar todos os elementos: Use uma variável chamada total para armazenar a soma. Inicialmente, total
é 0. Adicione cada elemento da matriz ao total usando um loop como este:

total duplo = 0;
for (int i = 0; i < ARRAY_SIZE; i++) {

total += minhaLista[i];
}

6. Encontrando o maior elemento: Use uma variável chamada max para armazenar o maior elemento. Inicialmente,
max é myList[0]. Para encontrar o maior elemento na matriz myList, compare cada elemento com max e atualize
max se o elemento for maior que max.
Machine Translated by Google

7.2 Noções básicas de matriz 291

double max = minhaLista[0];


for (int i = 1; i < ARRAY_SIZE; i++) {

if (minhaLista[i] > max) max = minhaLista[i];


}

7. Encontrando o menor índice do maior elemento: Muitas vezes você precisa localizar o maior elemento
em uma matriz. Se uma matriz tiver vários elementos com o mesmo valor maior, encontre o menor
índice desse elemento. Suponha que a matriz myList seja {1, 5, 3, 4, 5, 5}. Portanto, o maior
elemento é 5 e o menor índice de 5 é 1. Use uma variável chamada max para armazenar o maior
elemento e uma variável chamada indexOfMax para denotar o índice do maior elemento.
Inicialmente max é myList[0] e indexOfMax é 0. Compare cada elemento em myList com max.
Se o elemento for maior que max, atualize max e indexOfMax.

double max = minhaLista[0];


int indexOfMax = 0;

for (int i = 1; i < ARRAY_SIZE; i++) {

if (minhaLista[i] > max) {

max = minhaLista[i];
indexOfMax = i;
}
}

Qual será a consequência se (myList[i] > max) for substituído por (myList[i] >= max)?

8. Embaralhamento aleatório: Em muitos aplicativos, você precisa reordenar os elementos de uma


matriz aleatoriamente. Isso é chamado de embaralhamento. Para fazer isso, para cada elemento
minhaLista[i], gere aleatoriamente um índice j e troque minhaLista[i] por minhaLista[j], como segue:

srand(tempo(0));
minha lista
[0]
for (int i = ARRAY_SIZE - 1; i > 0; i--) {
eu

.
[1]

// Gera um índice j aleatoriamente com 0 <= j <=i int j = rand() % (i + 1); [eu]
.

trocar

// Troque minhaLista[i] por minhaLista[j] double Um índice aleatório [j]


temp = minhaLista[i]; minhaLista[i] = [n1]
minhaLista[j]
minhaLista[j] = temp;
}

9. Deslocar elementos: Às vezes você precisa deslocar os elementos para a esquerda ou para a direita. Por exemplo,
você pode deslocar os elementos uma posição para a esquerda e preencher o último elemento com o primeiro
elemento:

temperatura dupla = minhaLista[0]; //Mantém o primeiro elemento

// Desloca os elementos para a esquerda minha lista


for (int i = 1; i < ARRAY_SIZE; i++) {

minhaLista[i - 1] = minhaLista[i];
}

// Move o primeiro elemento para preencher a última posição


minhaLista[ARRAY_SIZE - 1] = temp;
Machine Translated by Google

292 Capítulo 7 Matrizes unidimensionais e strings C

10. Simplificando a codificação: Arrays podem ser usados para simplificar a codificação para determinadas
tarefas. Por exemplo, suponha que você queira obter o nome em inglês de um determinado mês pelo seu
número. Se os nomes dos meses forem armazenados em uma matriz, o nome do mês de um determinado
mês poderá ser acessado simplesmente por meio do índice. O código a seguir solicita que o usuário insira
um número de mês e exibe o nome do mês:

string meses[] = {"Janeiro", "Fevereiro", ..., "Dezembro"};


cout << "Insira o número do mês (1 a 12): ";
int númeroMês;
cin >> númeroMês;
cout << "O mês é " << meses[Númeromês - 1] << endl;

Se você não usasse a matriz meses , teria que determinar o nome do mês usando uma instrução if-else
multidirecional longa , como segue:

if (mêsNúmero == 1)
cout << "O mês é janeiro" << endl;
senão if (mêsNúmero == 2)
cout << "O mês é fevereiro" << endl;
...
else
cout << "O mês é dezembro" << endl;

Cuidado
Os programadores muitas vezes referenciam erroneamente o primeiro elemento em uma matriz
erro off-by-one com índice 1. Isso é chamado de erro off-by-one. É um erro comum em um loop usar <= onde <
deveria ser usado. Por exemplo, o seguinte loop está errado:

para (int i = 0; i <= ARRAY_SIZE; i++)


cout << lista[i] << " ";

O <= deve ser substituído por <.

Dica
verificando os limites do índice Como o C++ não verifica o limite do array, você deve prestar atenção especial para garantir que os
índices estejam dentro do intervalo. Verifique a primeira e a última iteração em um loop para ver se
os índices estão no intervalo permitido.

7.1 Como você declara um array? Qual é a diferença entre um declarador de tamanho de array e um índice de
ÿVerificação de ponto array?

7.2 Como você acessa os elementos de um array? Você pode copiar um array a para b usando b = a?

7.3 A memória é alocada quando um array é declarado? Os elementos da matriz têm


valores padrão? O que acontece quando o código a seguir é executado?

números internos [30];


cout << "números[0] é " << números[0] << endl;
cout << "números[29] é " << números[29] << endl;
cout << "números[30] é " << números[30] << endl;

7.4 Indique verdadeiro ou falso para as seguintes afirmações:

n Cada elemento em uma matriz tem o mesmo tipo.

n O tamanho do array é fixo após ser declarado.

n O declarador do tamanho do array deve ser uma expressão constante.

n Os elementos do array são inicializados quando um array é declarado.


Machine Translated by Google

7.3 Problema: Números de Loteria 293

7.5 Quais das seguintes afirmações são declarações de array válidas?

duplo d[30];
char[30] r;
int eu[] = (3, 4, 3, 2);
float f[] = {2,3, 4,5, 6,6};

7.6 Qual é o tipo de índice de array? Qual é o menor índice? Qual é a representação
do terceiro elemento em uma matriz chamada a?

7.7 Escreva instruções C++ para fazer o seguinte:

a. Declare uma matriz para conter 10 valores duplos.

b. Atribua o valor 5,5 ao último elemento da matriz.

c. Exiba a soma dos dois primeiros elementos.

d. Escreva um loop que calcule a soma de todos os elementos do array.

e. Escreva um loop que encontre o elemento mínimo no array.

f. Gere aleatoriamente um índice e exiba o elemento nesse índice na matriz.

g. Use um inicializador de array para declarar outro array com valores iniciais 3,5, 5,5, 4,52,
e 5.6.

7.8 O que acontece quando seu programa tenta acessar um elemento do array com um
índice inválido?

7.9 Identifique e corrija os erros no código a seguir:

1 int principal() {

duplo[100] r;

for (int i = 0; i < 100; i++);


r(i) = rand() % 100;
234567}

7.10 Qual é a saída do código a seguir?

lista interna [] = {1, 2, 3, 4, 5, 6};

para (int i = 1; i < 6; i++)


lista[i] = lista[i - 1];

para (int i = 0; i < 6; i++)


cout << lista[i] << " ";

7.3 Problema: Números de Loteria


O problema é escrever um programa que verifique se todos os números de entrada cobrem de 1 a 99.
Chave
Apontar
Cada bilhete de loteria Pick-10 tem 10 números únicos variando de 1 a 99. Suponha que você compre muitos
bilhetes e gostaria que eles cobrissem todos os números de 1 a 99. Escreva um programa que leia os números
dos bilhetes de um arquivo e verifique se todos os números estão cobertos. Suponha que o último número do
arquivo seja 0. Suponha que o arquivo contenha os números

80 3 87 62 30 90 10 21 46 27
12 40 83 9 39 88 95 59 20 37
80 40 87 67 31 90 11 24 56 77
11 48 51 42 8 74 1 41 36 53
Machine Translated by Google

294 Capítulo 7 Matrizes unidimensionais e strings C


52 82 16 72 19 70 44 56 29 33 54 64 99 14 23
22 94 79 55 2 60 86 34 4 31 63 84 89 7 78 43
93 97 45 25 38 28 26 85 49 47 65 57 67 73
69 32 71 24 66 92 98 96 77 6 75 17 61 58 13
35 81 18 15 5 68 91 50 76 0

Seu programa deve exibir

Os ingressos cobrem todos os números

Suponha que o arquivo contenha os números

11 48 51 42 8 74 1 41 36 53 52 82 16 72 19
70 44 56 29 33 0

Seu programa deve exibir

Os ingressos não cobrem todos os números

Como você marca um número como coberto? Você pode declarar um array com 99 elementos bool.
Cada elemento da matriz pode ser usado para marcar se um número é coberto. Deixe o array ser isCovered.
Inicialmente, cada elemento é falso, conforme mostrado na Figura 7.2a. Sempre que um número é lido, seu
elemento correspondente é definido como verdadeiro. Suponha que os números inseridos sejam 1, 2, 3, 99, 0.
Quando o número 1 é lido, isCovered[1 - 1] é definido como verdadeiro (veja a Figura 7.2b). Quando o número 2
é lido, isCovered[2 - 1] é definido como verdadeiro (veja a Figura 7.2c). Quando o número 3 é lido, isCovered[3 -
1] é definido como verdadeiro (veja a Figura 7.2d). Quando o número 99 for lido, defina isCovered[99 - 1] como
true (veja a Figura 7.2e).

está coberto está coberto está coberto está coberto está coberto

[0] falso [0] verdadeiro


[0] verdadeiro
[0] verdadeiro
[0] verdadeiro

[1] falso [1] falso [1] verdadeiro


[1] verdadeiro
[1] verdadeiro

[2] falso [2] falso [2] falso [2] verdadeiro


[2] verdadeiro

[3] falso [3] falso [3] falso [3] falso [3] falso

. . . . .

. . . . .

. . . . .

[97] falso [97] falso [97] falso [97] falso [97] falso

[98] falso [98] falso [98] falso [98] falso [98] verdadeiro

(a) (b) (c) (d) (e)

Figura 7.2 Se o número i aparecer em um bilhete de loteria, isCovered[i-1] será definido como verdadeiro.

O algoritmo do programa pode ser descrito da seguinte forma:

para cada número k lido do arquivo,


marque o número k como coberto pela configuração isCovered[k – 1] true;

se todo isCovered[i] for verdadeiro


Os ingressos cobrem todos os outros números

Os ingressos não cobrem todos os números


Machine Translated by Google

7.3 Problema: Números de Loteria 295

O programa completo é dado na Listagem 7.2.

Listagem 7.2 LottoNumbers.cpp


1 #include <iostream> Nota de vídeo
2 usando namespace std; números de loteria

3 4 int principal()
5{
bool éCovered[99]; declarar matriz
6 7 número interno ; //número lido de um arquivo

// Inicializa o array
8 para (int i = 0; i < 99; i++)
9 10 11 estáCoberto[i] = falso; inicializar matriz
12
13 // Leia cada número e marque seu elemento correspondente coberto
14 cin >> número; ler número
15 enquanto (número! = 0)
16 {
17 estáCoberto[número - 1] = verdadeiro; marcar número coberto
18 cin >> número; ler número
19 }
20
21 //Verifica se está tudo coberto
22 bool allCovered = verdadeiro; // Assuma tudo coberto inicialmente
23 para (int i = 0; i < 99; i++)
24 if (!isCovered[i]) { verifique tudoCoberto?
25
26 tudo coberto = falso; //Encontre um número não coberto
27 quebrar;
28 }
29
30 //Exibir resultado
31 if (todos cobertos)
32 cout << "Os tickets cobrem todos os números" << endl;
33 outro
34 cout << "Os tickets não cobrem todos os números" << endl;
35
36 retornar 0;
37 }

Suponha que você tenha criado um arquivo de texto chamado LottoNumbers.txt que contém os dados de entrada 2 5 6 5 4
3 23 43 2 0. Você pode executar o programa usando o seguinte comando:

g++ LottoNumbers.cpp –o LottoNumbers.exe


LottoNumbers.exe < LottoNumbers.txt

O programa pode ser rastreado da seguinte forma:

Elementos representativos na matriz isCovered

Linha# [1] [2] [3] [4] [5] [22] [42] número tudo coberto

11 falso falso falso falso falso falso falso falso


14 2

17 verdadeiro

18 5

17 verdadeiro

(contínuo )
Machine Translated by Google

296 Capítulo 7 Matrizes unidimensionais e strings C

Elementos representativos na matriz isCovered

Linha# [1] [2] [3] [4] [5] [22] [42] número tudo coberto

18 6

17 verdadeiro

18 5

17 verdadeiro

18 4

17 verdadeiro

18 3

17 verdadeiro

18 23

17 verdadeiro

18 43

17 verdadeiro

18 2

17 verdadeiro

18 0

22 verdadeiro

24(eu=0) falso

O programa declara um array de 99 elementos bool (linha 6) e inicializa cada elemento como falso (linhas 10–11). Lê
o primeiro número do arquivo (linha 14). O programa então repete as seguintes operações em um loop:

n Se o número não for zero, defina seu valor correspondente na matriz isCovered como true
(linha 17);

n Leia o próximo número (linha 18).

Quando a entrada é 0, a entrada termina. O programa verifica se todos os números estão cobertos nas linhas 22–28 e
exibe o resultado nas linhas 31–34.

7.4 Problema: Baralho de Cartas


O problema é criar um programa que selecione aleatoriamente quatro cartas de um baralho de 52 cartas.
Chave
Apontar

Todas as cartas podem ser representadas usando um array denominado baralho, preenchido com valores iniciais de 0 a
51, como segue:

convés interno [52];

// Inicializa os cartões
para (int i = 0; i < NUMBER_OF_CARDS; i++)
baralho[i] = i;

Os números das cartas de 0 a 12, 13 a 25, 26 a 38, 39 a 51 representam 13 espadas, 13 copas,


13 ouros e 13 paus, respectivamente, conforme mostrado na Figura 7.3. cardNumber / 13
determina o naipe da carta e cardNumber % 13 determina o valor da carta, conforme mostrado na Figura 7.4.
Depois de embaralhar o baralho, escolha as primeiras quatro cartas do baralho.
Machine Translated by Google

7.4 Problema: Baralho de Cartas 297

área coberta área coberta

0 [0] 0 [0] A carta número 6 é o 7


. . . 6
[1] (6% 13 = 6) de Espadas (6/13 = 0)
. 13 espadas ( ) . . 48
[2]
. . . 11 24
[3]
. A carta número 48 é o 10
12 [12] 12 [4]
13 13 . (48% 13 = 9) de Paus (48/13 = 3)
[13] [5]
. . . . .
. . . . .
13 Corações ( ) O cartão número 11 é o
. . . . .
Embaralhamento aleatório . Dama (11% 13 = 11) de Espadas (11/13 = 0)
25 [25] 25 [25]
26 [26] 26 [26] .
. . . . .
. . . . . O cartão número 24 é o
13 diamantes ( )
. . . . . Rainha (24% 13 = 11) de Copas (24/13 = 1)
38 [38] 38 [38] .
39 [39] 39 [39] .
. . . . .
. . . . .
13 Clubes ( )
. . . . .
51 [51] 51 [51] .

Figura 7.3 Cinquenta e duas cartas são armazenadas em um conjunto denominado baralho.

0 Ás

1 2
0 Espadas
.
1 Corações
número do cartão / 13 = número do cartão% 13 = .
2 Diamantes
10 Jack
3 Clubes
11 Rainha

12 Rei

Figura 7.4 Um número de cartão identifica um cartão.

A Listagem 7.3 fornece a solução para o problema.

Listagem 7.3 DeckOfCards.cpp


1 #include <iostream> 2 #include
<ctime> 3 #include <string>
4 usando namespace std; 5 6
int principal() 7 { 8 9 10 11 12 13 14

15 16 17 18 19

const int NUMBER_OF_CARDS = 52; baralho


interno [NUMBER_OF_CARDS]; string declarar o deck do array
suit[] = {"Espadas", "Corações", "Diamantes", "Paus"}; string classificações[] = {"Ás", "2", declarar os naipes do array
"3", "4", "5", "6", "7", "8", "9", declarar as classificações do array
"10", "Jack", "Rainha", "Rei"};

// Inicializa cartas para (int i =


0; i < NUMBER_OF_CARDS; i++) deck[i] = i;
inicializar deck

// Embaralha as cartas baralho embaralhado

srand(time(0));
Machine Translated by Google

298 Capítulo 7 Matrizes unidimensionais e strings C


20 para (int i = 0; i < NUMBER_OF_CARDS; i++)
21 {
22 //Gera um índice aleatoriamente
23 índice interno = rand()% NUMBER_OF_CARDS;
24 temperatura interna = deck[i];
25 baralho[i] = baralho[índice];
26 deck[índice] = temp;
27 }
28
29 //Mostra as quatro primeiras cartas
30 para (int i = 0; i < 4; i++)
31 {
terno de exibição 32 string naipe = naipes[baralho[i] / 13];
classificação de exibição 33 string classificação = classificações[deck[i] % 13];
34 cout << "Número do cartão " << baralho[i] << ": "
35 << classificação << " de " << terno << endl;
36 }
37
38 retornar 0;
39 }

Carta número 6: 7 de Espadas


Carta número 48: 10 de Paus
Carta número 11: Dama de Espadas
Cartão número 24: Rainha de Copas

O programa define um baralho array para 52 cartas (linha 9). O deck é inicializado com valores de 0 a 51 nas
linhas 15–16. O valor do baralho 0 representa a carta Ás de Espadas, 1 representa a carta 2 de Espadas, 13
representa a carta Ás de Copas e 14 representa a carta 2 de Copas. As linhas 20–27 embaralham o baralho
aleatoriamente. Depois que um baralho é embaralhado, deck[i] contém um valor arbitrário. deck[i] / 13 é 0, 1, 2
ou 3, o que determina um naipe (linha 32). deck[i] % 13 é um valor entre 0 e 12, que determina uma classificação
(linha 33). Se os ternos
array não está definido, você teria que determinar o naipe usando um longo if-else multiway
declaração da seguinte forma:

se (baralho[i] / 13 == 0)
cout << "naipe é Espadas" << endl;
senão se (baralho[i] / 13 == 1)
cout << "suit is Heart" << endl;
senão se (baralho[i] / 13 == 2)
cout << "naipe é de Ouros" << endl;
outro
cout << "naipe é de Paus" << endl;

Com suit = {"Spades", "Hearts", "Diamonds", "Clubs"} declarado como uma matriz, suit[deck / 13] fornece
o naipe do baralho. O uso de arrays simplifica bastante a solução para este programa.

7.5 Passando Arrays para Funções


Quando um argumento de array é passado para uma função, seu endereço inicial é passado para o
Chave
Apontar parâmetro de array na função. Tanto o parâmetro quanto o argumento referem-se ao
mesma matriz.

Assim como você pode passar valores únicos para uma função, você também pode passar um array inteiro para
uma função. A Listagem 7.4 dá um exemplo para demonstrar como declarar e invocar este tipo de função.
Machine Translated by Google

7.5 Passando Arrays para Funções 299

Listagem 7.4 PassArrayDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 void printArray(int lista[], int arraySize); // Protótipo de função protótipo de função

5 6 int principal()
7{
8 números internos [5] = {1, 4, 3, 6, 8}; declarar matriz
printArray(números, 5); //Invoca a função invocar função
9 10
11 retornar 0;
12}
13
14 void printArray(int lista[], int arraySize) implementação de função
15 {
16 for (int i = 0; i < arraySize; i++)
17 {
18 cout << lista[i] << " ";
19 }
20}

14368

No cabeçalho da função (linha 14), int list[] especifica que o parâmetro é um array inteiro de qualquer
tamanho. Portanto, você pode passar qualquer array inteiro para invocar esta função (linha 9).
Observe que os nomes dos parâmetros nos protótipos de funções podem ser omitidos. Portanto, o
protótipo da função pode ser declarado sem os nomes dos parâmetros list e arraySize da seguinte forma:

void printArray(int [], int); // Protótipo de função

Observação

Normalmente quando você passa um array para uma função, você também deve passar seu passando tamanho junto com array
tamanho em outro argumento, para que a função saiba quantos elementos existem no array. Caso
contrário, você terá que codificar isso na função ou declará-lo em uma variável global. Nenhum
dos dois é flexível ou robusto.

C++ usa passagem por valor para passar argumentos de array para uma função. Existem diferenças passagem por valor
importantes entre passar os valores de variáveis de tipos de dados primitivos e passar arrays.

n Para um argumento de tipo primitivo, o valor do argumento é passado.

n Para um argumento do tipo array, o valor do argumento é o endereço de memória inicial de um


array; este valor é passado para o parâmetro array na função.
Semanticamente, pode ser melhor descrito como passagem por compartilhamento, ou seja, o
array na função é igual ao array que está sendo passado. Assim, se você alterar o array na
função, verá a mudança fora da função. A Listagem 7.5 dá um exemplo que demonstra esse
efeito.

Listagem 7.5 EffectOfPassArrayDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 vazio m(int, int []); protótipo de função


5
Machine Translated by Google

300 Capítulo 7 Matrizes unidimensionais e strings C

6int principal ( )
7{
int x = 1; // x representa um valor int
int y[10]; // y representa um array de valores int
8 y[0] = 1; // Inicializa y[0]
9
passar matriz y 10 m(x, y); // Invoca m com argumentos x e y
11
12 cout << "x é " cout << x << endl;
13 << "y[0] é " << y[0] << endl;
14
15 retornar 0;
16 17 18 }
19
20 void m(número int , números int [])
21 {
número = 1001; //Atribuir um novo valor ao número
modificar matriz números[0] = 5555; //Atribuir um novo valor aos números[0]
22 23 24}

xé1
y[0] é 5555

Você verá que depois que a função m é invocada, x permanece 1, mas y[0] é 5555. Isso ocorre
porque o valor de x é copiado para número, e x e número são variáveis independentes, mas y e
números fazem referência ao mesmo variedade. os números podem ser considerados um alias para o array y.

7.6 Prevenindo Mudanças de Argumentos de


Array em Funções
Você pode definir o parâmetro de array const em uma função para evitar que ele seja alterado
Chave
Apontar em uma função.

Passar um array apenas passa o endereço de memória inicial do array. Os elementos da matriz não são
copiados. Isso faz sentido para conservar espaço de memória. No entanto, usar argumentos de array
pode levar a erros se sua função alterar acidentalmente o array. Para evitar isso, você pode colocar a
matriz const palavra-chave const antes do parâmetro array para informar ao compilador que o array não pode ser
alterado. O compilador reportará erros se o código na função tentar modificar o array.

A Listagem 7.6 fornece um exemplo que declara uma lista de argumentos de array const na função p
(linha 4). Na linha 7, a função tenta modificar o primeiro elemento do array. Este erro é detectado pelo
compilador, conforme mostrado no exemplo de saída.

Listagem 7.6 ConstArrayDemo.cpp


1 #include <iostream>
2 usando namespace std;

argumento de matriz const 3 4 void p(const int lista[], int arraySize)


{
//Modifica array acidentalmente
tentar modificar lista[0] = 100; // Erro de compilação!
5678}
9
Machine Translated by Google

7.7 Retornando Arrays das Funções 301

10 int principal()
11 {
12 números internos [5] = {1, 4, 3, 6, 8};
13 p(números, 5);
14
15 retornar 0;
16 }

Compilado usando
Visual C++ 2012

erro C3892: “lista”: você não pode atribuir a uma variável que é const

Compilado usando
GNU C++

ConstArrayDemo.cpp:7: erro: atribuição de local somente leitura

Observação

Se você definir um parâmetro const em uma função f1 e esse parâmetro for passado parâmetros const em cascata
para outra função f2, o parâmetro correspondente na função f2 deverá ser declarado
const para consistência. Considere o seguinte código:

void f2(int lista[], tamanho interno )


{
// Faça alguma coisa
}

void f1(const int lista[], tamanho interno )


{
// Faça alguma coisa
f2(lista, tamanho);
}

O compilador relata um erro porque a lista é const em f1 e é passada para f2, mas
não é const em f2. A declaração da função para f2 deve ser

void f2(const int lista[], tamanho interno )

7.7 Retornando Arrays de Funções


Para retornar um array de uma função, passe-o como parâmetro em uma função.
Chave
Apontar
Você pode declarar uma função para retornar um valor de tipo primitivo ou um objeto. Por exemplo,
// Retorna a soma dos elementos da lista int sum(const int
list[], int size)

Você pode retornar um array de uma função usando uma sintaxe semelhante? Por exemplo, você pode
tentar declarar uma função que retorne um novo array que seja uma reversão de um array, como segue:
// Retorna a reversão da lista int[]
reverse(const int list[], int size)

Isso não é permitido em C++. No entanto, você pode contornar essa restrição passando dois argumentos
de array na função:

// newList é a inversão da lista


void reverso(const int list[], int newList[], int size)
Machine Translated by Google

302 Capítulo 7 Matrizes unidimensionais e strings C

O programa é dado na Listagem 7.7.

Listagem 7.7 ReverseArray.cpp


Nota de vídeo
1 #include <iostream>
Matriz reversa
2 usando namespace std;

3 4 // newList é a inversão da lista


função reversa 5 void reverso(const int list[], int newList[], int size)
6{
7 for (int i = 0, j = tamanho - 1; i < tamanho; i++, j--)
{
reverter para newList novaLista[j] = lista[i];
8 }
9 10 11}
12
matriz de impressão 13 void printArray(const int list[], int size)
14 {
15 for (int i = 0; i <tamanho; i++)
16 cout << lista[i] << " ";
17}
18
19 int principal()
20 {
21 const int TAMANHO = 6;
declarar matriz original 22 lista interna [] = {1, 2, 3, 4, 5, 6};
declarar nova matriz 23 int novaLista[TAMANHO];
24
invocar reverso 25 inverter(lista, novaLista, TAMANHO);
26
27 cout << "A matriz original: ";
imprimir matriz original 28 printArray(lista, TAMANHO);
29 cout << endl;
30
31 cout << "O array invertido: ";
imprimir matriz invertida 32 printArray(novaLista, TAMANHO);
33 cout << endl;
34
35 retornar 0;
36 }

A matriz original: 1 2 3 4 5 6
A matriz invertida: 6 5 4 3 2 1

A função reversa (linhas 5–11) usa um loop para copiar o primeiro elemento, segundo, . . . , e
assim por diante na matriz original até o último elemento, penúltimo, . . . , na nova matriz, conforme
mostrado no diagrama a seguir:

lista

nova lista
Machine Translated by Google

7.7 Retornando Arrays das Funções 303

Para invocar esta função (linha 25), você deve passar três argumentos. O primeiro argumento é o array original,
cujo conteúdo não é alterado na função. O segundo argumento é o novo array, cujo conteúdo é alterado na função.
O terceiro argumento indica o tamanho do array.

7.11 Quando um array é passado para uma função, um novo array é criado e passado para a função.
ção. Isso é verdade?
ÿVerificação de ponto

7.12 Mostre a saída dos dois programas a seguir:

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

vazio m(int x, int y[]) { void reverso(int lista[], int tamanho) {

x = 3; for (int i = 0; i < tamanho / 2; i++) {


y[0] = 3;
} temperatura interna =
lista[i]; lista[i] = lista[tamanho - 1 - i];
int principal() lista[tamanho - 1 - i] = temp;
{ }
número interno = 0; }
números internos [1];
int principal()
m(número, números); {
lista interna [] = {1, 2, 3, 4, 5}; tamanho
cout << "número é" << número interno = 5;
<< números[0] é" << números[0]; "e reverso(lista, tamanho);
for (int i = 0; i < tamanho; i++) cout <<
retornar 0; lista[i] << " ";
}
retornar 0;
}

(a) (b)

7.13 Como evitar que o array seja modificado acidentalmente em uma função?

7.14 Suponha que o código a seguir seja escrito para inverter os caracteres em uma string, explique
por que está errado.

strings = "ABCD"; for


(int i = 0, j = s.size() - 1; i < s.size(); i++, j--) {

// Troca s[i] por s[j] char temp


= s[i]; s[i] = s[j]; s[j] =
temperatura;

cout << "A string invertida é " << s << endl;


Machine Translated by Google

304 Capítulo 7 Matrizes unidimensionais e strings C

7.8 Problema: Contando as Ocorrências de Cada Letra


Esta seção apresenta um programa para contar as ocorrências de cada letra em um array de
Chave
Apontar caracteres.

O programa faz o seguinte:

1. Gere 100 letras minúsculas aleatoriamente e atribua-as a um array de caracteres, conforme mostrado
na Figura 7.5a. Conforme discutido na Seção 4.4, “Estudo de caso: Gerando caracteres aleatórios”,
uma letra minúscula aleatória pode ser gerada usando

static_cast<char>('a' + rand() % ('z' - 'a' + 1))

2. Conte as ocorrências de cada letra do array. Para fazer isso, declare um array, digamos, contagens
de 26 valores int , cada um dos quais conta as ocorrências de uma letra, como mostrado na Figura 7.5b.
Ou seja, counts[0] conta o número de a's, counts[1] conta o número de b's e
breve.

caracteres[0] conta[0]
caracteres[1] conta[1]
… … … …
… … … …

caracteres[98] conta[24]
caracteres[99] conta[25]
(a) (b)

Figura 7.5 O array chars armazena 100 caracteres e o array counts armazena 26 contagens, cada uma
contando as ocorrências de uma letra.

A Listagem 7.8 fornece o programa completo.

Listagem 7.8 CountLettersInArray.cpp


1 #include <iostream>
2 #incluir <ctime>
3 usando namespace std;

26 letras 4 5 const int NUMBER_OF_LETTERS = 26;


cem cartas 6 const int NUMBER_OF_RANDOM_LETTERS = 100;
protótipos de função 7 void createArray(char []);
8 void displayArray(const char []);
9 void countLetters(const char [], int []);
10 void displayCounts(const int []);
11
12 int principal()
13 {
14 //Declara e cria um array
matriz de caracteres 15 char caracteres[NUMBER_OF_RANDOM_LETTERS];
16
17 // Inicializa o array com letras minúsculas aleatórias
atribuir letras aleatórias 18 criarArray(caracteres);
19
20 //Mostra a matriz
"
21 cout << "As letras minúsculas são: << fim;
matriz de exibição 22 displayArray(chars);
Machine Translated by Google

7.8 Problema: Contando as Ocorrências de Cada Letra 305


23
24 //Conta as ocorrências de cada letra
25 contagens internas [NUMBER_OF_LETTERS]; matriz de contagens
26
27 //Conta as ocorrências de cada letra
28 contagemLetras(caracteres, contagens); contar letras
29
30 //Exibe contagens
"
31 cout << "\nAs ocorrências de cada letra são: displayCounts(counts); << fim;
32 contagens de exibição
33
34 retornar 0;
35 }
36
37 // Cria um array de caracteres
38 void createArray(char chars[])
39 { inicializar matriz
40 // Cria letras minúsculas aleatoriamente e atribui
41 // eles para o array
42 srand(tempo(0)); definir uma nova semente

43 para (int i = 0; i < NUMBER_OF_RANDOM_LETTERS; i++)


44 chars[i] = static_cast<char>('a' + rand() % ('z' - 'a' + 1)); carta aleatória
45}
46
47 // Exibe o array de caracteres
48 void displayArray(const char chars[])
49 {
50 //Exibe os caracteres do array 20 em cada linha
51 para (int i = 0; i < NUMBER_OF_RANDOM_LETTERS; i++)
52 {
53 se ((eu + 1)% 20 == 0)
""
54 cout << chars[i] << else << fim;
55
56 cout << caracteres[i] << " ";
57 }
58 }
59
60 // Conta as ocorrências de cada letra
61 letras de contagem nulas (const char chars[], int contagens[]) carta de contagem

62 {
63 // Inicializa o array
64 for (int i = 0; i <NÚMERO_DE_LETRAS; i++)
65 contagens[i] = 0;
66
67 // Para cada letra minúscula do array, conte-a
68 for (int i = 0; i < NUMBER_OF_RANDOM_LETTERS; i++)
69 conta[chars[i] - 'a'] ++;
70 }
71
72 // Exibir contagens
73 void displayCounts(const int contagens[])
74 {
75 for (int i = 0; i <NÚMERO_DE_LETRAS; i++)
76 {
77 se ((eu + 1)% 10 == 0)
""
cout << conta[i] << else << static_cast<char>(i + 'a') << endl; lançar para char

""
78 cout << conta[i] << << static_cast<char>(i + 'a') << " ";
79 }
80 81 82 }
Machine Translated by Google

306 Capítulo 7 Matrizes unidimensionais e strings C

As letras minúsculas são:


pyaounsuibthygwqlbyox vbrighixwvcgraspyiznf
jvcjcacvlajrxrdtwqmay evmkdmemojvkmevtarmou
vdhfooxdgiuwriqh

As ocorrências de cada letra são: 6 a 3 b 4 c 4


d3e2f4g4h6i4j
2k2l6m2n6o2p3q6r2s3t
4 você 8 v 4 w 4 x 5 y 1 z

A função createArray (linhas 38–45) gera um array de 100 letras minúsculas aleatórias e as atribui em
caracteres do array. A função countLetters (linhas 61–70) conta a ocorrência de letras em caracteres e
armazena as contagens no array counts. Cada elemento em contagens armazena o número de ocorrências de
uma letra. A função processa cada letra do array e aumenta sua contagem em um. Uma abordagem de força bruta
para contar as ocorrências de cada letra pode ser a seguinte:

for (int i = 0; i < NUMBER_OF_RANDOM_LETTERS; i++) if (chars[i]


== 'a') conta[0]++;

senão if (chars[i] == 'b') conta[1]+


+;
...

Mas uma solução melhor é dada nas linhas 68–69.

for (int i = 0; i < NUMBER_OF_RANDOM_LETTERS; i++)


conta[chars[i] - 'a']++;

Se a letra (chars[i]) for 'a', a contagem correspondente será counts['a' - 'a']


(ou seja, contagens[0]). Se a letra for 'b', a contagem correspondente será contagens['b' - 'a']
(ou seja, contagens[1]) já que o código ASCII de 'b' é um a mais que o de 'a'. Se a letra for 'z', a contagem
correspondente será counts['z' - 'a'] (ou seja, counts[25]) já que o código ASCII de 'z' é 25 a mais que o de 'a'.

7.9 Pesquisando Matrizes


Se uma matriz for classificada, a pesquisa binária é mais eficiente do que a pesquisa linear para encontrar
Chave
Apontar um elemento na matriz.

Pesquisar é o processo de procurar um elemento específico em uma matriz — por exemplo, descobrir se uma
determinada pontuação está incluída em uma lista de pontuações. Pesquisar é uma tarefa comum na programação
pesquisa linear de computadores. Muitos algoritmos e estruturas de dados são dedicados à pesquisa. Esta seção discute duas
pesquisa binária abordagens comumente usadas: pesquisa linear e pesquisa binária.

7.9.1 A abordagem de pesquisa linear


A abordagem de pesquisa linear compara a chave do elemento- chave sequencialmente com cada elemento da
matriz. A função continua fazendo isso até que a chave corresponda a um elemento do array ou o array se esgote.
Se for feita uma correspondência, a pesquisa linear retorna o índice do elemento no
Machine Translated by Google

7.9 Pesquisando Matrizes 307

matriz que corresponde à chave. Caso contrário, a pesquisa retornará -1. A função linearSearch na Listagem 7.9
fornece a solução:

Listagem 7.9 LinearSearch.cpp


int linearSearch(const int list[], int key, int arraySize) { animação de pesquisa linear ativada

Site complementar
for (int i = 0; i < arraySize; i++) {

...
if (chave == lista[i]) [0] [1] [2]
retornar eu; lista
}
retornar -1; chave Compare chave com lista[i] para i = 0, 1, ...
}

Rastreie a função usando as seguintes instruções:

lista interna [] = {1, 4, 4, 2, 5, -3, 6, 2};


int i = linearSearch(lista, 4, 8); // Retorna 1
int j = linearSearch(lista, -4, 8); // Retorna -1
int k = linearSearch(lista, -3, 8); // Retorna 5

A função de pesquisa linear compara a chave com cada elemento da matriz. Os elementos da matriz podem
estar em qualquer ordem. Em média, o algoritmo terá que comparar metade dos elementos antes de encontrar a
chave, se ela existir. Como o tempo de execução de uma pesquisa linear aumenta linearmente à medida que o
número de elementos do array aumenta, a pesquisa linear é ineficiente para um array grande.

7.9.2 A abordagem de pesquisa binária


A pesquisa binária é outra abordagem de pesquisa comum para uma lista de valores. Requer que os elementos do
array já estejam ordenados. Suponha que a matriz esteja em ordem crescente.
A pesquisa binária primeiro compara a chave com o elemento no meio do array. Considere os seguintes casos:

n Se a chave for menor que o elemento do meio, você só precisa continuar a pesquisar no
primeira metade da matriz.

n Se a chave for igual ao elemento do meio, a pesquisa termina com uma correspondência.

n Se a chave for maior que o elemento do meio, você só precisa continuar a pesquisar em
a segunda metade da matriz.

Claramente, a função de busca binária elimina pelo menos metade do array após cada comparação. Às vezes
você elimina metade dos elementos e às vezes elimina metade mais um. Suponha que o array tenha n elementos.
Por conveniência, seja n uma potência de 2. Após a primeira comparação, n/2 elementos são deixados para
pesquisa posterior; após a segunda comparação, (n/2)/2 elementos são deixados para pesquisa adicional. Após a k-
ésima comparação, n/2k elementos são deixados para pesquisa adicional. Quando k = log2n, resta apenas um
elemento na matriz e você precisa apenas de mais uma comparação. Na pior das hipóteses, portanto, ao usar a
abordagem de pesquisa binária, você precisa de comparações log2n+1 para encontrar um elemento na matriz
classificada. No pior caso, para uma lista de 1.024 (210 ) elementos, a pesquisa binária requer apenas onze
comparações, enquanto uma pesquisa linear requer 1.024. A parte do array que está sendo pesquisada diminui pela
metade após cada comparação. Deixe low e high denotarem, respectivamente, o primeiro índice e o último índice
do subarray.
Inicialmente, low é 0 e high é listSize–1. Deixe mid denotar o índice do elemento intermediário.
Então médio é (baixo + alto)/2. A Figura 7.6 mostra como encontrar a chave 11 na lista {2, 4, 7, 10, 11, 45, 50, animação de pesquisa
59, 60, 66, 69, 70, 79} usando pesquisa binária. binária no site complementar
Machine Translated by Google

308 Capítulo 7 Matrizes unidimensionais e strings C

a chave é 11 baixo meio alto

chave <50 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10][11][12]
lista 2 4 7 10 11 45 50 59 60 66 69 70 79

baixo meio alto

[0] [1] [2] [3] [4] [5]

chave > 7 lista 2 4 7 10 11 45

baixo médio alto

[3] [4] [5]

chave == 11 lista 10 11 45

Figura 7.6 A pesquisa binária elimina metade da lista de considerações adicionais após cada comparação.

Agora você sabe como funciona a pesquisa binária. A próxima tarefa é implementá-lo. Não se apresse em fazer
uma implementação completa. Implemente-o de forma incremental, um passo de cada vez. Você pode começar
com a primeira iteração da pesquisa, conforme mostrado na Figura 7.7a. Ele compara a chave com o elemento do
meio na lista cujo índice baixo é 0 e o índice alto é listSize - 1. Se chave <lista[mid], defina o índice alto como
mid - 1; se key == list[mid], uma correspondência é encontrada e retorna mid; se chave > lista[mid], defina o
índice baixo como mid + 1.

int pesquisa binária(const int int binarySearch(const int lista[],


lista[], tamanholista) listSize)
{ {
int baixo = 0; int baixo = 0;
int alto = tamanholista - 1; int alto = tamanholista - 1;

enquanto (baixo <= alto)


{
int médio = (baixo + alto)/ 2; int médio = (baixo + alto)/ 2;
if (chave <lista[meio]) if (chave <lista[meio])
alto = médio - 1; alto = médio - 1;
senão if (chave == lista[meio]) senão if (chave == lista[meio])
retornar no meio; retornar no meio;
outro outro
baixo = médio + 1; baixo = médio + 1;
}

retornar -1;
} }

(a) Versão 1 (b) Versão 2

Figura 7.7 A pesquisa binária é implementada de forma incremental.

A seguir, considere como implementar a função para realizar a pesquisa repetidamente adicionando um loop,
conforme mostrado na Figura 7.7b. A pesquisa termina se a chave for encontrada ou se a chave não for encontrada.
Observe que quando baixo > alto, a chave não está na matriz.
Machine Translated by Google

7.9 Pesquisando Matrizes 309

Quando a chave não é encontrada, low é o ponto de inserção onde uma chave seria inserida para
manter a ordem da lista. É mais útil retornar o ponto de inserção do que -1. A função deve retornar um
valor negativo para indicar que a chave não está na lista. Ele pode simplesmente retornar –baixo? Não.
Se a chave for menor que lista[0], low seria 0. -0 é 0. Isso indicaria que a chave corresponde à lista[0].
Uma boa opção é deixar a função retornar –low – 1 se a chave não estiver na lista. Retornar –low – 1
não indica apenas que a chave não está na lista, mas também onde a chave seria inserida na lista.

A função de busca binária é implementada na Listagem 7.10.

Listagem 7.10 BinarySearch.cpp


1 int binarySearch(const int list[], int key, int listSize)
2{
3 int baixo = 0;
4 int alto = tamanholista - 1;
5

6 enquanto (alto >= baixo)


7 {
int médio = (baixo + alto)/ 2;
if (chave <lista[meio])
8 alto = médio - 1;
9 10 11 senão if (chave == lista[meio])
12 retornar no meio; combinação encontrada

13 outro
14 baixo = médio + 1;
15 }
16
17 retornar -baixo - 1; sem correspondência

18 }

A pesquisa binária retorna o índice da chave de pesquisa se estiver contida na lista (linha 12).
Caso contrário, retorna –low – 1 (linha 17). O que acontece se (high >= low) na linha 6 for substituído
por (high > low)? A pesquisa perderia um possível elemento correspondente. Considere uma lista com
apenas um elemento. A pesquisa perderia o elemento. A função ainda funciona se houver elementos
duplicados na lista? Sim, desde que os elementos estejam ordenados em ordem não decrescente na
lista. A função retorna o índice de um dos elementos correspondentes se o elemento estiver na lista.

Para entender melhor esta função, trace-a com as seguintes afirmações e identifique baixos
e alto quando a função retorna.

lista interna [] = {2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79};
int i = buscabinária(lista, 2, 13); //Retorna 0
int j = buscabinária(lista, 11, 13); //Retorna 4
int k = buscabinária(lista, 12, 13); // Retorna –6
int l = buscabinária(lista, 1, 13); // Retorna –1
int m = buscabinária(lista, 3, 13); // Retorna –2

Aqui está a tabela que lista os valores baixos e altos quando a função é encerrada e o valor
retornado ao invocar a função.

Função baixo alto Valor retornado

binarySearch (lista, 2, 13) 0 1 0

binarySearch (lista, 11, 13) 3 5 4

binarySearch (lista, 12, 13) 5 4 –6

binarySearch (lista, 1, 13) 0 –1 –1

binarySearch (lista, 3, 13) 1 0 –2


Machine Translated by Google

310 Capítulo 7 Matrizes unidimensionais e strings C


Observação

A pesquisa linear é útil para encontrar um elemento em um array pequeno ou não classificado, mas é ineficiente

benefícios da pesquisa binária para arrays grandes. A pesquisa binária é mais eficiente, mas requer que o array seja pré-classificado.

7.10 Classificando Matrizes


A classificação, assim como a pesquisa, é uma tarefa comum na programação de computadores. Muitos
Chave
Apontar algoritmos diferentes foram desenvolvidos para classificação. Esta seção apresenta um algoritmo de
ordenação por seleção classificação intuitivo: classificação por seleção.

Suponha que você queira classificar uma lista em ordem crescente. A classificação por seleção encontra o
menor número na lista e o troca pelo primeiro. Em seguida, ele encontra o menor número restante e o troca
animação de classificação por
pelo próximo ao primeiro, e assim por diante, até que reste apenas um único número. A Figura 7.8 mostra
seleção no site complementar
como ordenar a lista {2, 9, 5, 4, 8, 1, 6} usando ordenação por seleção.

trocar

Nota de vídeo
Selecione 1 (o menor) e troque-o 29 5 4 8 16
Ordenação por seleção
com 2 (o primeiro) na lista
trocar

Selecione 2 (o menor) e troque-o


O número 1 está agora no 1 9 5482 6 com 9 (o primeiro) no restante
posição correta e, portanto, não lista
mais tempo precisa ser considerado.

trocar

Selecione 4 (o menor) e troque-o


O número 2 está agora no 1 2 548 96 com 5 (o primeiro) no restante
posição correta e, portanto, não lista
mais tempo precisa ser considerado.

O número 4 está agora no 1 245 896 5 é o menor e está à direita


posição. Nenhuma troca é necessária
posição correta e, portanto, não
mais tempo precisa ser considerado.

trocar

Selecione 6 (o menor) e troque-o


O número 5 está agora no 1 24 5896 com 8 (o primeiro) no restante
posição correta e, portanto, não lista
mais tempo precisa ser considerado.

trocar

Selecione 8 (o menor) e troque-o


O número 6 está agora no 1 456
2 98 com 9 (o primeiro) no restante
posição correta e, portanto, não lista
mais tempo precisa ser considerado.

Como existe apenas um elemento


O número 8 está agora no 1 245689 permanecendo na lista, sort é
posição correta e, portanto, não concluído
mais tempo precisa ser considerado.

Figura 7.8 A classificação por seleção seleciona repetidamente o menor número e o troca pelo primeiro número da lista
restante.
Machine Translated by Google

7.10 Classificando Matrizes 311

Você sabe como funciona a abordagem de classificação por seleção. A tarefa agora é implementá-lo em C++.
Para iniciantes, é difícil desenvolver uma solução completa na primeira tentativa. Você pode começar a escrever
o código da primeira iteração para encontrar o menor elemento da lista e trocá-lo pelo primeiro elemento, e então
observar o que seria diferente para a segunda iteração, a terceira e assim por diante. O insight fornecido permitirá
que você escreva um loop que generalize todas as iterações.

A solução pode ser descrita da seguinte forma:

for (int i = 0; i < tamanholista - 1; i++) {

selecione o menor elemento em list[i..listSize-1];


troque o menor por list[i], se necessário;
// list[i] está na posição correta.
// A próxima iteração se aplica a list[i+1..listSize-1]
}

A Listagem 7.11 implementa a solução.

Listagem 7.11 SelectionSort.cpp


1 seleção nulaSort ( lista dupla[], int listSize)
2{
for (int i = 0; i <listSize - 1; i++)
34 {
// Encontre o mínimo na lista[i..listSize-1]
5 double currentMin = lista[i];
6 int currentMinIndex = i;
7
8 for (int j = i + 1; j <listSize; j++)
9 {
10 if (Min atual > lista[j])
11 12 {
13 atualMin = lista[j];
14 índiceMin atual = j;
15 }
16 }
17
18 // Troque list[i] por list[currentMinIndex] se necessário;
19 if (currentMinIndex! = i)
20 {
21 lista[currentMinIndex] = lista[i];
22 lista[i] = atualMin;
}
23 }
24 25}

A função selectionSort(double list[], int listSize) classifica qualquer array de elementos duplos . A função é
implementada com um loop for aninhado . O loop externo (com a variável de controle de loop i) (linha 3) é iterado
para encontrar o menor elemento na lista, que varia de list[i] a list[listSize - 1], e trocá-lo por list[ i].A variável i é
inicialmente 0. Após cada iteração do loop externo, list[i] está no lugar certo.

Eventualmente, todos os elementos são colocados no lugar certo; portanto, toda a lista é classificada. Para
entender melhor esta função, trace-a com as seguintes afirmações:

lista dupla [] = {1, 9, 4,5, 6,6, 5,7, -4,5};


seleçãoSort(lista, 6);
Machine Translated by Google

312 Capítulo 7 Matrizes unidimensionais e strings C

7.15 Use a Figura 7.6 como exemplo para mostrar como aplicar a abordagem de pesquisa binária para
ÿVerificação de ponto pesquisar a chave 10 e a chave 12 na lista {2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70 , 79}.

7.16 Use a Figura 7.8 como exemplo para mostrar como aplicar a abordagem de seleção-ordenação para
classificar {3,4, 5, 3, 3,5, 2,2, 1,9, 2}.

7.17 Como você modifica a função selectionSort na Listagem 7.11 para classificar números em ordem
decrescente?

7.11 Cordas C
String C é uma matriz de caracteres que termina com o caractere terminador nulo '\0'.
Chave
Apontar Você pode processar strings C usando funções de string C na biblioteca C++.

Nota Pedagógica
Strings C vs. tipo de string A string C é popular na linguagem C, mas foi substituída por um tipo de string mais
robusto, conveniente e útil em C++. Por esse motivo, o tipo string , apresentado no
Capítulo 4, é usado para processar strings neste livro. O objetivo de apresentar strings
C nesta seção é fornecer exemplos e exercícios adicionais usando arrays e permitir
que você trabalhe com programas C legados.

String C
Uma string C é uma matriz de caracteres que termina com o terminador nulo ('\0'), que indica onde
terminador nulo
uma string termina na memória. Lembre-se de que um caractere que começa com o símbolo de barra
invertida (\) é uma sequência de escape na Seção 4.3.3, “Sequências de escape para caracteres
especiais”. Os símbolos \ e 0 (zero) juntos representam um caractere. Este caractere é o primeiro caractere
da tabela ASCII.
Cada string literal é uma string C. Você pode declarar um array inicializado com uma string literal.
Por exemplo, a instrução a seguir cria uma matriz para uma string C que contém os caracteres 'D', 'a', 'l', 'l',
'a', 's' e '\0', conforme mostrado em Figura 7.9.

char cidade[7] = "Dallas";

'D' 'a' 'eu' 'eu' 'a' 'é' '\0'


cidade[0] cidade[1] cidade[2] cidade[3] cidade[4] cidade[5] cidade[6]

Figura 7.9 Um array de caracteres pode ser inicializado com uma string C.

Observe que o tamanho do array é 7 e o último caractere do array é '\0'. Há uma diferença sutil entre
uma string C e uma matriz de caracteres. Por exemplo, as duas declarações a seguir são diferentes:

char cidade1[] = "Dallas"; //string C


char cidade2[] = {'D', 'a', 'l', 'l', 'a', 's'}; // Não é uma string C

A primeira instrução é uma string C e a segunda instrução é apenas uma matriz de caracteres. O primeiro
possui 7 caracteres incluindo o último terminador nulo e o último possui 6 caracteres.

7.11.1 Entrada e Saída de Strings C


Produzir uma string C é simples. Suponha que s seja um array para uma string C. Para exibi-lo no console,
basta usar

cout << s;
Machine Translated by Google

7.11 Cordas Dó 313

Você pode ler uma string C no teclado da mesma forma que lê um número. Por exemplo, considere
o seguinte código:

1 caractere cidade[7]; declarar uma matriz


2 cout << "Insira uma cidade: ";
3 cin >> cidade; //Lê o array cidade leia string C
4 cout << "Você digitou " << cidade << endl;

Ao ler uma string em um array, deixe espaço para o caractere terminador nulo.
Como a cidade tem tamanho 7, sua entrada não deve exceder 6 caracteres. Essa abordagem para ler uma tamanho de entrada

string é simples, mas há um problema. A entrada termina com um caractere de espaço em branco. Você não
pode ler uma string que contenha um espaço. Suponha que você queira entrar em Nova York; então você deve
usar uma abordagem alternativa. C++ fornece a função cin.getline no iostream
arquivo de cabeçalho, que lê uma string em um array. A sintaxe da função é a seguinte:

cin.getline(char array[], tamanho interno , char delimitChar)

A função para de ler caracteres quando o caractere delimitador é encontrado ou quando o número de
caracteres de tamanho - 1 foi lido. O último caractere da matriz é reservado para o terminador nulo ('\0'). Se o
delimitador for encontrado, ele será lido, mas não será armazenado na matriz. O terceiro argumento delimitChar
possui um valor padrão ('\n'). O código a seguir usa a função cin.getline para ler uma string:

1 caractere cidade[30]; declarar matriz


2 cout << "Insira uma cidade: "; // ou seja, Nova York
3 cin.getline(cidade, 30, '\n'); //Lê o array cidade string para array
4 cout << "Você digitou " << cidade << endl;

Como o valor padrão para o terceiro argumento na função cin.getline é '\n', a linha 3 pode ser substituída
por

cin.getline(cidade, 30); //Lê o array cidade

7.11.2 Funções de string C


Dado que uma string C termina com um terminador nulo, C++ pode utilizar esse fato para processar strings C processando string C
de forma eficiente. Ao passar uma string C para uma função, você não precisa passar seu comprimento, porque
o comprimento pode ser obtido contando todos os caracteres da esquerda para a direita no array até que o
caractere terminador nulo seja alcançado. Aqui está a função para obter o comprimento de uma string C:

strlen int não assinado (char s[])


{
int eu = 0;
para ( ; s[i] != '\0'; i++);
retornar eu;
}

Na verdade, strlen e diversas outras funções são fornecidas na biblioteca C++ para processamento de
strings C, conforme mostrado na Tabela 7.1.

Observação

size_t é um tipo C++. Para a maioria dos compiladores, é o mesmo que unsigned int. digite tamanho_t

Todas essas funções são definidas no arquivo de cabeçalho cstring , exceto as funções de
conversão atoi, atof, atol e itoa que são definidas na função cstdlib .
Machine Translated by Google

314 Capítulo 7 Matrizes unidimensionais e strings C

Tabela 7.1 Funções de String


Função Descrição

tamanho_t strlen(char s[]) Retorna o comprimento da string, ou seja, o número de caracteres


antes do terminador nulo.

strcpy(char s1[], const char s2[]) Copia a string s2 para a string s1.

strncpy(char s1[], const char Copia os primeiros n caracteres da string s2 para a string s1.
s2[], tamanho_t n)
strcat(char s1[], const char s2[]) Acrescenta a string s2 a s1.

strncat(char s1[], const char Acrescenta os primeiros n caracteres da string s2 a s1.


s2[], tamanho_t n)
int strcmp(char s1[], const char Retorna um valor maior que 0, 0 ou menor que 0 se s1 for maior,
s2[]) igual ou menor que s2 com base no código numérico dos caracteres.

int strncmp(char s1[], const char O mesmo que strcmp, mas compara até n número de caracteres em s1
s2[], tamanho_t n) com aqueles em s2.

int atoi(char s[]) Retorna um valor int para a string.

duplo atof(char s[]) Retorna um valor duplo para a string.

atol longo(char s[]) Retorna um valor longo para a string.

void itoa(int valor, char s[], int Obtém um valor inteiro para uma string com base na base
raiz) especificada.

7.11.3 Copiando Strings Usando strcpy e strncpy


strcpy A função strcpy pode ser usada para copiar uma string de origem no segundo argumento para uma
string de destino no primeiro argumento. A sequência de destino já deve ter recebido memória suficiente
para que a função funcione. Um erro comum é copiar uma string C usando um código como este:

char cidade[30] = "Chicago";


cidade = "Nova York"; // Copia Nova York para cidade. Errado!

Para copiar "Nova York" para cidade, você deve usar

strcpy(cidade, "Nova York");

forte A função strncpy funciona como strcpy, exceto que leva um terceiro argumento especificando o
número de caracteres a serem copiados. Por exemplo, o código a seguir copia os três primeiros
caracteres “Novo” para cidade.

cidade de char [9];


strncpy(cidade, "Nova York", 3);

Há um problema com este código. A função strncpy não anexa um terminador nulo à string de
destino se o número especificado de caracteres for menor ou igual ao comprimento da string de origem.
Se o número especificado de caracteres for maior que o comprimento da sequência de origem, a
sequência de origem será copiada para o destino preenchida com terminadores nulos até o final da
sequência de destino. Tanto strcpy quanto strncpy podem potencialmente substituir os limites de um
array. Para garantir uma cópia segura, verifique os limites antes de usar essas funções.
Machine Translated by Google

7.11 Cordas Dó 315

7.11.4 Concatenando Strings Usando strcat e strncat


A função strcat pode ser usada para anexar a string do segundo argumento à string do primeiro argumento. estragado

Para que a função funcione, a primeira string já deve ter recebido memória suficiente. Por exemplo, o código a
seguir funciona bem para anexar s2 a s1.

char s1[7] = "abc";


char s2[4] = "def";
strcat(s1, s2); cout
<< s1 << endl; //A impressão é abcdef

No entanto, o código a seguir não funciona porque não há espaço para adicionar s2 em s1.

char s1[4] = "abc";


char s2[4] = "def";
strcat(s1, s2);

A função strncat funciona como strcat, exceto que leva um terceiro argumento especificando o número de strncat
caracteres a serem concatenados da string de destino com a string de origem.
Por exemplo, o código a seguir concatena os três primeiros caracteres "ABC" com s:

char s[9] = "abc";


strncat(s, "ABCDEF", 3);
cout << s << endl; //A impressão é abcABC

Tanto strcat quanto strncat podem potencialmente substituir os limites de um array. Para garantir segurança
concatenando, verifique os limites antes de usar essas funções.

7.11.5 Comparando Strings Usando strcmp


A função strcmp pode ser usada para comparar duas strings. Como você compara duas strings? Você compara strcmp
seus caracteres correspondentes de acordo com seus códigos numéricos. A maioria dos compiladores usa o
código ASCII para caracteres. A função retorna o valor 0 se s1 for igual a s2, um valor menor que 0 se s1 for
menor que s2 e um valor maior que 0 se s1 for maior que s2. Por exemplo, suponha que s1 seja "abc" e s2
seja "abg" e strcmp(s1, s2) retorne um valor negativo. Os dois primeiros caracteres (a vs. a) de s1 e s2 são
comparados. Por serem iguais, os dois segundos caracteres (b vs. b) são comparados. Como também são
iguais, os terceiros dois caracteres (c vs. g) são comparados. Como o caractere c é 4 menor que g, a
comparação retorna um valor negativo. Exatamente qual valor é retornado depende do compilador. Os
compiladores Visual C++ e GNU retornam -1, mas o compilador Borland C++ retorna -4 , pois o caractere c é
4 menor que g.
Aqui está um exemplo de uso da função strcmp :

char s1[] = "Bom dia";


char s2[] = "Boa tarde";
se (strcmp(s1, s2) > 0)
cout << "s1 é maior que s2" << endl; senão se
(strcmp(s1, s2) == 0)
cout << "s1 é igual a s2" << endl; senão cout
<<
"s1 é menor que s2" << endl;

Ele exibe que s1 é maior que s2.


A função strncmp funciona como strcmp, exceto que leva um terceiro argumento especificando o número strncmp
de caracteres a serem comparados. Por exemplo, o código a seguir compara os primeiros quatro caracteres
nas duas strings.
Machine Translated by Google

316 Capítulo 7 Matrizes unidimensionais e strings C


char s1[] = "Bom dia";
char s2[] = "Boa tarde";
cout << strncmp(s1, s2, 4) << endl;

Ele exibe 0.

7.11.6 Conversão entre Strings e Números


reboque A função atoi pode ser usada para converter uma string C em um inteiro do tipo int e a função atol pode ser
atol usada para converter uma string C em um inteiro do tipo longo . Por exemplo, o código a seguir converte as
strings numéricas s1 e s2 em números inteiros:

char s1[] = "65";


char s2[] = "4";
cout << atoi(s1) + atoi(s2) << endl;

Ele exibe 69.


atof A função atof pode ser usada para converter uma string C em um número de ponto flutuante. Para a prova-
Por exemplo, o código a seguir converte as strings numéricas s1 e s2 em números de ponto flutuante:

char s1[] = "65,5";


char s2[] = "4.4";
cout << atof(s1) + atof(s2) << endl;

Ele exibe 69,9.


afogado A função itoa pode ser usada para converter um número inteiro em uma string C com base em uma raiz especificada.
Por exemplo, o seguinte código

caractere s1[15];
caractere s2[15];
caractere s3[15];
isto(100, s1, 16);
itoa(100, s2, 2);
itoa(100, s3, 10);
cout << "O número hexadecimal para 100 é " << s1 << final;
cout << "O número binário para 100 é " cout << << s2 << final;
"s3 é " << s3 << endl;

exibições

O número hexadecimal para 100 é 64


O número binário para 100 é 1100100
s3 é 100

Observe que alguns compiladores C++ podem não suportar a função itoa .

7.18 Quais são as diferenças entre os seguintes arrays?


ÿVerificação de ponto
char s1[] = {'a', 'b', 'c'};
char s2[] = "abc";

7.19 Suponha que s1 e s2 sejam definidos da seguinte forma:

char s1[] = "abc";


char s2[] = "efg";

As seguintes expressões/afirmações estão corretas?

a. s1 = "bom"
b. s1 <s2
Machine Translated by Google

Resumo do Capítulo 317

c.s1 [0]

d. s1[0] <s2[0]
e. strcpy(s1, s2)

f. strcmp(s1, s2)

g. strlen(s1)

Termos chave
matriz 286 String C 312
tamanho da matriz declaratório 287 índice 287
índice de matriz 288 pesquisa linear 306
inicializador de matriz 289 terminador nulo ('\0') 312
pesquisa binária 306 classificação de seleção 310

matriz const 300

Resumo do capítulo
1. Um array armazena uma lista de valores do mesmo tipo.

2. Um array é declarado usando a sintaxe

elementType arrayName[tamanho]

3. Cada elemento do array é representado usando a sintaxe arrayName[index].

4. Um índice deve ser um número inteiro ou uma expressão inteira.

5. O índice do array é baseado em 0, o que significa que o índice do primeiro elemento é 0.

6. Os programadores muitas vezes referenciam erroneamente o primeiro elemento em uma matriz com o índice 1 em vez de
do que 0. Isso causa o erro de índice off-by-one.

7. Acessar elementos da matriz usando índices além dos limites causa limites fora dos limites
erro.

8. Fora dos limites é um erro grave, mas não é verificado automaticamente pelo compilador C++.

9. C++ tem uma notação abreviada, conhecida como inicializador de array, que declara e inicializa
iza um array em uma única instrução usando a sintaxe:

elementType arrayName[] = {valor0, valor1, ..., valork};

10. Quando um array é passado para uma função, o endereço inicial do array é passado para o
parâmetro de matriz na função.

11. Quando você passa um argumento de array para uma função, muitas vezes você também deve passar o tamanho
outro argumento, para que a função saiba quantos elementos existem no array.

12. Você pode especificar parâmetros de array const para evitar que arrays sejam acidentalmente
modificado.
Machine Translated by Google

318 Capítulo 7 Matrizes unidimensionais e strings C

13. Uma matriz de caracteres que termina com um terminador nulo é chamada de string C.

14. Uma string literal é uma string C.

15. C++ fornece diversas funções para processamento de strings C.

16. Você pode obter o comprimento da string C usando a função strlen .

17. Você pode copiar uma string C para outra string C usando a função strcpy .

18. Você pode comparar duas strings C usando a função strcmp .

19. Você pode usar a função itoa para converter um número inteiro em uma string C e usar atoi para
converter uma string em um número inteiro.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 7.2–7.4
*7.1 (Atribuir notas) Escreva um programa que leia as pontuações dos alunos, obtenha a melhor pontuação e,
em seguida, atribua notas com base no seguinte esquema:

A nota é A se a pontuação for 7 = melhor - 10;


A nota é B se a pontuação for 7 = melhor - 20;
A nota é C se a pontuação for 7 = melhor - 30;
A nota é D se a pontuação for 7 = melhor - 40;
Caso contrário, a nota é F.

O programa solicita que o usuário insira o número total de alunos, depois solicita que o usuário insira
todas as notas e conclui exibindo as notas. Aqui está um exemplo de execução:

Digite o número de alunos: 4


Insira 4 pontuações: 40 55 70 58
A pontuação do aluno 0 é 40 e a nota é C
A pontuação do aluno 1 é 55 e a nota é B
A pontuação do aluno 2 é 70 e a nota é A
A pontuação do aluno 3 é 58 e a nota é B

7.2 (Inteiros maiores e menores) Escreva um programa que leia 6 inteiros e exiba o maior e o menor entre
esses inteiros.

*7.3 (Contar ocorrências de números) Escreva um programa que leia no máximo 100 inteiros entre 1 e 100 e
conte a ocorrência de cada número. Suponha que a entrada termine com 0. Aqui está um exemplo de
execução do programa:
Machine Translated by Google

Exercícios de Programação 319

Insira os números inteiros entre 1 e 100: 2 5 6 5 4 3 23 43 2 0 2 ocorre 2 vezes

3 ocorre 1 vez
4 ocorre 1 vez
5 ocorre 2 vezes
6 ocorre 1 vez
23 ocorre 1 vez
43 ocorre 1 vez

Observe que se um número ocorrer mais de uma vez, a palavra plural “vezes” será usada
na saída.
7.4 (Conte o número de vogais e consoantes) Escreva um programa que leia um número não
especificado de alfabetos maiúsculos ou minúsculos e determine quantos deles são vogais
e quantos são consoantes. Insira zero para indicar o fim da entrada.

**7.5 (Imprimir números distintos) Escreva um programa que leia 10 números e exiba números distintos
(ou seja, se um número aparecer múltiplas vezes, ele será exibido apenas uma vez). (Dica:
leia um número e armazene-o em uma matriz, se for novo. Se o número já estiver na matriz,
descarte-o. Após a entrada, a matriz contém os números distintos.) Aqui está um exemplo
de execução do programa:

Digite dez números: 1 2 3 2 1 6 3 4 5 2


Os números distintos são: 1 2 3 6 4 5

*7.6 (Revisar Listagem 5.17, PrimeNumber.cpp) A Listagem 5.17 determina se um número n é primo
.,
verificando se 2, 3, 4, 5, 6, . . n/2 é um divisor. Se um divisor for encontrado, n não é primo.
Uma abordagem mais eficiente para determinar se n é primo é verificar se algum dos
números primos é menor ou igual a 2n
pode dividir n uniformemente. Caso contrário, n é primo. Reescreva a Listagem 5.17 para exibir os primeiros 50
números primos usando esta abordagem. Você precisa usar um array para armazenar os
números primos e depois usá-los para verificar se são possíveis divisores para n.
*7.7 (Soma de números aleatórios pares) Escreva um programa que gere vinte e cinco números
inteiros aleatórios entre 0 e 25 e exiba a soma de números inteiros pares. (Dica: use rand()
% 25 para gerar um número inteiro aleatório entre 0 e 25. Use uma matriz de 25 números
inteiros, digamos num, para armazenar os números inteiros aleatórios gerados entre 0 e 25.)

Seções 7.5–7.7
7.8 (Produto de um array) Escreva duas funções sobrecarregadas que retornem o produto dos
elementos de um array com os seguintes cabeçalhos:

produto interno (const int array[], tamanho interno );


produto duplo (const double array [], tamanho interno );

Escreva um programa de teste que solicite ao usuário que insira 3 valores duplos , invoque
esta função e exiba o produto desses valores.
7.9 (Encontre o menor elemento) Escreva uma função que encontre o menor elemento em uma
matriz de valores duplos usando o seguinte cabeçalho:

double min( array duplo[], tamanho interno )


Machine Translated by Google

320 Capítulo 7 Matrizes unidimensionais e strings C

Escreva um programa de teste que solicite ao usuário que insira 10 números, invoque
esta função e exiba o valor mínimo. Aqui está um exemplo de execução do programa:

Insira dez números: 1,9 2,5 3,7 2 1,5 6 3 4 5 2 O número mínimo é 1,5

7.10 (Encontre o índice do maior elemento) Escreva uma função que retorne o índice do
maior elemento em um array de inteiros. Se houver mais desses elementos do que
um, retorne o maior índice. Use o seguinte cabeçalho:

int indexOfLargestElement( matriz dupla[], tamanho interno )

Escreva um programa de teste que solicite ao usuário que insira 15 números,


invoque esta função para retornar o índice do maior elemento e exiba o índice.
*7.11 (Estatísticas: cálculo do desvio) O Exercício de Programação 5.47 calcula o desvio
padrão dos números. Este exercício utiliza uma fórmula diferente, mas equivalente,
Nota de vídeo
Encontre o desvio padrão
para calcular o desvio padrão de n números.
n n

a XI (xi - média) 2
= x1 + x2 + + xn
c
eu=1 eu=1
média =
n n desvio = H uma n-1

Para calcular o desvio com esta fórmula, você deve armazenar os números individuais
usando uma matriz, para que possam ser usados após a obtenção da média.
Seu programa deve conter as seguintes funções:

// Calcula a média de um array de valores duplos double mean(const double


x[], int size)

// Calcula o desvio dos valores duplos double deviation(const


double x[], int size)

Escreva um programa de teste que solicite ao usuário que insira 10 números e exiba
a média e o desvio, conforme mostrado no exemplo a seguir:

Insira dez números: 1,9 2,5 3,7 2 1 6 3 4 5 2 A média é 3,11

O desvio padrão é 1,55738

Seções 7.8–7.9
7.12 (Tempo de execução) Escreva um programa que gere aleatoriamente um array de 100.000
inteiros e uma chave. Estime o tempo de execução para invocar o linearSearch
função na Listagem 7.9. Classifique o array e estime o tempo de execução para
invocar a função binarySearch na Listagem 7.10. Você pode usar o seguinte modelo
de código para obter o tempo de execução:

startTime longo = tempo(0);


executar a tarefa;
endTime longo = tempo(0);
tempo de execução longo = endTime - startTime;
Machine Translated by Google

Exercícios de Programação 321

7.13 (Jogo: Lançar um dado) Um dado tem seis faces representando os valores 1, 2, . . . , 6,
respectivamente. Escreva um programa que lance um dado 10.000 vezes e exiba o número de
ocorrências de cada valor.

**7.14 (Classificação por bolha) Escreva uma função de classificação que use o algoritmo de classificação
por bolha. O algoritmo faz várias passagens pelo array. Em cada passagem, pares vizinhos
sucessivos são comparados. Se um par estiver em ordem decrescente, seus valores serão
trocados; caso contrário, os valores permanecem inalterados. A técnica é chamada de
classificação por bolha ou classificação por afundamento porque os valores menores
gradualmente “borbulham” até o topo e os valores maiores afundam.

O algoritmo pode ser descrito da seguinte forma:

bool alterado = verdadeiro;


fazer

{
alterado = falso;
for (int j = 0; j <listSize - 1; j++)
if (lista[j] > lista[j + 1]) {

trocar lista[j] por lista[j + 1];


alterado = verdadeiro;
}
} enquanto (alterado);

Claramente, a lista está em ordem crescente quando o loop termina. É fácil mostrar que o loop
do executa no máximo listSize - 1 vezes.

Escreva um programa de teste que leia um array de dez números duplos, invoque a função e
exiba os números ordenados.

*7.15 (Jogo: quebra-cabeça do armário) Uma escola tem 100 armários e 100 alunos. Todos os armários
estão fechados no primeiro dia de aula. À medida que os alunos entram, o primeiro aluno,
denominado S1, abre todos os armários. Em seguida, o segundo aluno, S2, começa com o
segundo armário, denominado L2, e fecha todos os outros armários. O aluno S3 começa com o
terceiro armário e troca a cada três armários (fecha se estiver aberto e abre se estiver fechado).
O aluno S4 começa com o armário L4 e troca a cada quatro armários. O aluno S5 começa com
L5 e troca a cada cinco armários, e assim sucessivamente, até que o aluno S100 troque de
L100.

Depois que todos os alunos passaram pelo prédio e trocaram os armários, quais armários ficam
abertos? Escreva um programa para encontrar sua resposta e exibir todos os números de
armários abertos separados por exatamente um espaço.

(Dica: use uma matriz de 100 elementos bool, cada um indicando se um armário está aberto
(verdadeiro) ou fechado (falso). Inicialmente, todos os armários estão fechados.)

7.16 (Classificação reversa por bolha) No Exercício de programação 7.14, você usou a classificação por
bolha para classificar um array. A função de classificação por bolha compara repetidamente os
pares vizinhos sucessivos na matriz e os troca se estiverem em ordem decrescente. Modifique
este programa trocando pares vizinhos sucessivos se estiverem em ordem crescente. Escreva
um programa de teste que leia um array de 15 inteiros, invoque a função e exiba os números
classificados em ordem decrescente.

***7.17 (Jogo: máquina de feijão) A máquina de feijão, também conhecida como quincunce ou caixa de Galton,
é um dispositivo para experimentos estatísticos que leva o nome do cientista inglês Sir Francis
Galton. Consiste em uma placa vertical com pregos (ou estacas) uniformemente espaçados em
formato triangular, conforme mostrado na Figura 7.10.
Machine Translated by Google

322 Capítulo 7 Matrizes unidimensionais e strings C

(a) (b) (c)

Figura 7.10 Cada bola segue um caminho aleatório e cai em uma fenda.

As bolas são lançadas pela abertura do tabuleiro. Cada vez que uma bola atinge um prego, ela
tem 50% de chance de cair para a esquerda ou para a direita. As pilhas de bolas são acumuladas
nas ranhuras na parte inferior do tabuleiro.

Escreva um programa que simule a máquina de feijão. Seu programa deverá solicitar ao usuário
que insira o número de bolas e o número de slots (máximo 50) na máquina. Simule a queda de
cada bola imprimindo sua trajetória. Por exemplo, o caminho da bola na Figura 7.10b é LLRRLLR
e o caminho da bola na Figura 7.10c é RLRRLRR. Exiba o acúmulo final das bolas nos slots em
um histograma. Aqui está um exemplo de execução do programa:

Digite o número de bolas a serem largadas: 5


Insira o número de slots na máquina de feijão: 8

LRLRLRR
RRLLLRR
LLRLLRR
RRRLLLL
LRLRRLR

OOO

(Dica: crie uma matriz chamada slots. Cada elemento nos slots armazena o número de bolas em
um slot. Cada bola cai em um slot através de um caminho. O número de Rs em um caminho é a
posição do slot onde a bola cai. Por exemplo, para o caminho LRLRLRR, a bola cai nos slots[4],
e para o caminho RRLLLLL, a bola cai nos slots[2].)

***7.18 (Jogo: Oito Rainhas) O quebra-cabeça clássico das Oito Rainhas consiste em colocar oito rainhas em um
tabuleiro de xadrez de modo que duas não possam atacar uma à outra (ou seja, não há duas
rainhas na mesma linha, na mesma coluna ou na mesma diagonal) . Existem muitas soluções possíveis.
Escreva um programa que exiba uma dessas soluções. Um exemplo de saída é mostrado abaixo:

|Q| | | | | | | |

||| | |Q| | | |
|||||| | |Q|
|||| | |Q| | |
| | |Q| | | | | |
||||| | |Q| |
| |Q| | | | | | |
|| | |Q| | | | |
Machine Translated by Google

Exercícios de Programação 323

***7.19 (Jogo: múltiplas soluções das Oito Rainhas) O Exercício de Programação 7.18 encontra uma solução para o
problema das Oito Rainhas. Escreva um programa para contar todas as soluções possíveis para o
problema das Oito Rainhas e exibir todas as soluções.

7.20 (Matrizes estritamente idênticas) Duas matrizes lista1 e lista2 são estritamente idênticas se tiverem o
mesmo comprimento e lista1[i] for igual a lista2[i] para cada i. Escreva uma função que retorne
verdadeiro se lista1 e lista2 forem estritamente idênticas usando o seguinte cabeçalho:

bool estritamenteEqual(const int lista1[], const int lista2[],


tamanho interno )

Escreva um programa de teste que solicite ao usuário que insira duas listas de inteiros e mostre se
as duas são estritamente idênticas. Seguem as execuções de amostra. Observe que o primeiro
número da entrada indica o número de elementos da lista. Este número não faz parte da lista.
Suponha que o tamanho da lista seja no máximo 20.

Digite a lista1: 5 2 5 6 1 6
Digite a lista2: 5 2 5 6 1 6
Duas listas são estritamente idênticas

Digite a lista1: 5 2 5 6 6 1
Digite a lista2: 5 2 5 6 1 6
Duas listas não são estritamente idênticas

**7.21 (Simulação: problema do coletor de cupons) O coletor de cupons é um problema estatístico clássico com
muitas aplicações práticas. O problema é selecionar repetidamente objetos de um conjunto de objetos
e determinar quantas coletas são necessárias para que todos os objetos sejam selecionados pelo
menos uma vez. Uma variação do problema é escolher repetidamente cartas de um baralho
embaralhado de 52 cartas e descobrir quantas escolhas são necessárias antes de ver uma de cada
naipe. Suponha que uma carta escolhida seja colocada de volta no baralho antes de escolher outra.
Escreva um programa para simular o número de escolhas necessárias para obter quatro cartas de
cada naipe e exibir as quatro cartas escolhidas (é possível que uma carta seja escolhida duas vezes).
Aqui está um exemplo de execução do programa:

rainha de Espadas
5 de Clubes
rainha dos corações
4 de Ouros
Número de escolhas: 12

7.22 (Matemática: Combinações) Escreva um programa que solicite ao usuário que insira um número inteiro e
exiba todas as combinações para obter esse número inteiro como a soma de dois dados.

7.23 (Matrizes idênticas) Duas matrizes list1 e list2 são idênticas se tiverem o mesmo conteúdo. Escreva uma
função que retorne verdadeiro se lista1 e lista2 forem idênticas usando o seguinte cabeçalho:

bool isEqual(const int lista1[], const int lista2[], tamanho interno )

Escreva um programa de teste que solicite ao usuário que insira duas listas de inteiros e mostre se
as duas são idênticas. Aqui estão os exemplos de execução. Observe que o primeiro número da
entrada indica o número de elementos da lista. Este número não faz parte da lista. Suponha que o
tamanho da lista seja no máximo 20.
Machine Translated by Google

324 Capítulo 7 Matrizes unidimensionais e strings C

Digite a lista1: 5 2 5 6 6 1
Digite a lista2: 5 5 2 6 1 6
Duas listas são idênticas

Digite a lista1: 5 5 5 6 6 1
Digite a lista2: 5 2 5 6 1 6
Duas listas não são idênticas

*7.24 (Reconhecimento de padrão: quatro números iguais consecutivos) Escreva a seguinte função que testa se
o array tem quatro números consecutivos com o mesmo valor.

bool isConsecutiveFour (const int valores [], tamanho interno )

Escreva um programa de teste que solicite ao usuário que insira uma série de números inteiros e
mostre se a série contém quatro números consecutivos com o mesmo valor. Seu programa deve
primeiro solicitar que o usuário insira o tamanho da entrada – ou seja, o número de valores na série.
Suponha que o número máximo de valores seja 80. Aqui estão alguns exemplos de execução:

Insira o número de valores: 8


Insira os valores: 3 4 5 5 5 5 4 5
A lista tem quatros consecutivos

Insira o número de valores: 9


Insira os valores: 3 4 5 5 6 5 5 4 5
A lista não tem quatros consecutivos

7.25 (Jogo: Soma das faces pares ou ímpares) Escreva um programa que lança três dados e exibe a soma
das faces dos três dados se todas as faces tiverem um número par ou se todas as faces tiverem um
número ímpar. Caso contrário, o programa exibe o maior número entre as faces dos três dados.

**7.26 (Mesclar duas listas classificadas) Escreva a seguinte função que mescla duas listas classificadas em uma
nova lista classificada:

mesclagem nula (const int lista1[], int tamanho1, const int lista2[], int tamanho2, int lista3[])

Implemente a função de uma forma que faça comparações size1 + size2 . Escreva um programa de
teste que solicite ao usuário que insira duas listas ordenadas e exiba a lista mesclada. Aqui está um
exemplo de execução. Observe que o primeiro número da entrada indica o número de elementos da
lista. Este número não faz parte da lista. Suponha que o tamanho máximo da lista seja 80.

Digite a lista1: 5 1 5 16 61 111


Digite a lista2: 4 2 4 5 6
A lista mesclada é 1 2 4 5 5 6 16 61 111
Machine Translated by Google

Exercícios de Programação 325

**7.27 (Classificado?) Escreva a seguinte função que retorna verdadeiro se a lista já estiver classificada
em ordem crescente:

bool isSorted(const int list[], int size)

Escreva um programa de teste que solicite ao usuário que insira uma lista e mostre se a lista está
ordenada ou não. Aqui está um exemplo de execução. Observe que o primeiro número da entrada
indica o número de elementos da lista. Este número não faz parte da lista.
Suponha que o tamanho máximo da lista seja 80.

Entrar na lista: 8 10 1 5 16 61 9 11 1
A lista não está ordenada

Insira a lista: 10 1 1 3 4 4 5 7 9 11 21
A lista já está ordenada

**7.28 (Partição de uma lista) Escreva a seguinte função que particiona a lista usando o
primeiro elemento, chamado pivô:

partição int ( lista int[], tamanho int )

Após a partição, os elementos da lista são reorganizados de modo que todos os elementos antes
do pivô sejam menores ou iguais ao pivô e o elemento após o pivô seja maior que o pivô. A função
também retorna o índice onde o pivô está localizado na nova lista. Por exemplo, suponha que a
lista seja {5, 2, 9, 3, 6, 8}. Após a partição, a lista passa a ser {3, 2, 5, 9, 6, 8}. Implemente a função
de uma forma que leve o tamanho do número de comparações.

Escreva um programa de teste que solicite ao usuário que insira uma lista e exiba a lista após a
partição. Aqui está um exemplo de execução. Observe que o primeiro número da entrada indica o
número de elementos da lista. Este número não faz parte da lista. Suponha que o tamanho máximo
da lista seja 80.

Entrar na lista: 8 10 1 5 16 61 9 11 1
Após a partição, a lista é 9 1 5 1 10 61 11 16

*7.29 (Área de um polígono) Escreva um programa que solicite ao usuário que insira os pontos de um polígono
convexo e exiba sua área. Suponha que o polígono tenha seis pontos finais e que os pontos sejam
inseridos no sentido horário. Para a definição de um polígono convexo, consulte
www.mathopenref.com/polygonconvex.html. Dica: A área total de um polígono é a soma das áreas
dos pequenos triângulos mostrados na Figura 7.11.

p1

p2
p0

pág.3

p5
pág.4

Figura 7.11 Um polígono convexo pode ser dividido em pequenos triângulos não sobrepostos.
Machine Translated by Google

326 Capítulo 7 Matrizes unidimensionais e strings C

Aqui está um exemplo de execução do programa:

Insira as coordenadas de seis pontos: -8,5 10 0 11,4 5,5


7,8 6 -5,5 0 -7 -3,5 -3,5
A área total é 183,95

*7.30 (Substituir: espaço por sublinhado) Escreva um programa que solicite ao usuário que insira uma string e substitua
todos os espaços nela contidos por sublinhados.

**7.31 (Elementos comuns) Escreva um programa que solicite ao usuário que insira dois arrays de 10 inteiros e exiba os
elementos comuns que aparecem em ambos os arrays. Aqui está um exemplo de execução.

Digite a lista1: 8 5 10 1 6 16 61 9 11 2
Digite a lista2: 4 2 3 10 3 34 35 67 3 1
Os elementos comuns são 10 1 2

Seção 7.11
*7.32 (Exibir a string mais longa) Reescreva a função mais longa no Exercício de Programação 6.42 para encontrar a mais
longa entre as duas strings usando strings C com o seguinte cabeçalho:

string mais longa(const char s1[], const char s2[],


char string mais longa[])

Escreva um programa de teste que solicite ao usuário que insira duas strings C e exiba a mais longa. Por
exemplo, se duas strings forem Bem-vindo e Programação, a saída será Programação.

*7.33 (Sequência de palíndromo) Modifique o Exercício de Programação 4.17 usando a seguinte função para descobrir se a
string C s é um palíndromo ou não. A função retorna o comprimento da string se for um palíndromo. Caso
contrário, retorna -1.

int isPalindrome(const char s[])

Escreva um programa de teste que leia uma string C, invoque a função e verifique se a string é um palíndromo
ou não. O programa retorna o comprimento da string se for um palíndromo.

*7.34 (Ocorrências de um caractere especificado) Reescreva a função de contagem no Exercício de programação 6.44
para encontrar o número de ocorrências de um caractere especificado em uma string C usando o seguinte
cabeçalho:

contagem int (const char s[], char a)

Escreva um programa de teste que leia uma string e um caractere e exiba o número de ocorrências do
caractere na string. A execução de amostra é a mesma do Exercício de Programação 6.44.

*7.35 (Conte as letras em uma string) Escreva uma função que conte o número de letras em
uma string C usando o seguinte cabeçalho:

int contagemLetras(const char s[])

Escreva um programa de teste que leia uma string C e exiba o número de letras da string. Aqui está um
exemplo de execução do programa:

Digite uma string: 2010 está chegando


O número de cartas em 2010 que está chegando é 8
Machine Translated by Google

Exercícios de Programação 327

**7.36 (Swap case) Reescreva a função swapCase no Exercício de Programação 6.46 para obter uma nova
string s2 na qual as letras maiúsculas são alteradas para minúsculas e as minúsculas para
maiúsculas em s1 usando o seguinte cabeçalho de função:

void swapCase(const char s1[], char s2[])

Escreva um programa de teste que solicite ao usuário que insira uma string, invoque essa função
e exiba a nova string. A execução de amostra é a mesma do Exercício de Programação 6.46.

*7.37 (Contar a ocorrência de cada letra em uma string) Escreva uma função que conte a
ocorrência de cada letra na string usando o seguinte cabeçalho:

contagem nula (const char s[], int conta[])

. .conta
onde contagens é uma matriz de 26 inteiros. contagens[0], contagens[1], . e counts[25] ,
a ocorrência de a, b, . . . e z, respectivamente. As letras não diferenciam maiúsculas de minúsculas,
ou seja, as letras A e a são contadas da mesma forma que a.

Escreva um programa de teste que leia uma string, invoque a função de contagem e dispare
reproduz as contagens diferentes de zero. Aqui está um exemplo de execução do programa:

Insira uma string: Bem-vindo a Nova York! c: 1


vez
e.: 3 vezes
k: 1 vez
eu: 1 vez
m: 1 vez
n: 1 vez
ó: 3 vezes
r: 1 vez
t: 1 vez
v: 2 vezes
sim: 1 vez

* 7.38 (Converter float em string) Escreva uma função que converte um número de ponto flutuante em uma C-
String usando o seguinte cabeçalho:

void ftoa(duplo f, char s[])

Escreva um programa de teste que solicite ao usuário que insira um número de ponto flutuante e
exiba cada dígito e o ponto decimal separados por um espaço. Aqui está um exemplo de execução:

Digite um número: 232,45 O número


é232.45

*7.39 (Negócios: verifique ISBN-13) Reescreva o Exercício de Programação 5.51 usando uma string C em vez
de uma string para armazenar os números ISBN. Escreva a seguinte função que obtém a soma de
verificação dos primeiros 12 dígitos:

int getChecksum(const char s[])

Seu programa deve ler a entrada como uma string C. As execuções de amostra são as mesmas
do Exercício de Programação 5.51.
Machine Translated by Google

328 Capítulo 7 Matrizes unidimensionais e strings C

*7.40 (Decimal para Binário) Use a função itoa descrita na Tabela 7.1 para converter um número decimal
em um número binário usando strings C com o seguinte cabeçalho:

void itoaUse(int valor, char s[], int raiz)

Escreva um programa de teste que solicite ao usuário que insira um número decimal e exiba
o valor binário correspondente como uma string.
*7.41 (Decimal para Octal ou Hex) Use a função itoa descrita na Tabela 7.1 para converter um número
decimal em um número octal ou hexadecimal usando strings C com o seguinte cabeçalho:

void itoaUse(int valor, char s[], int raiz)

Escreva um programa de teste que solicite ao usuário que insira um número decimal e uma
base e exiba o número octal ou valor hexadecimal correspondente.
**7.42 (Binário para Octal) Reescreva a função bin2Octal no Exercício de Programação 6.40 para
converter uma string binária em um número octal com o seguinte cabeçalho:

void bin2Octal(char binárioString[], int octalNumber)

Escreva um programa de teste que solicite ao usuário que insira uma string binária e exiba
seu valor octal equivalente.
**7.43 (Octal para binário) Reescreva a função octal2Binary no Exercício de Programação 6.41 para
converter um número octal em uma string binária com o seguinte cabeçalho:

void octal2Binary(int octalNumber, char binárioString[])

Escreva um programa de teste que solicite ao usuário que insira um número octal e exiba
sua string binária equivalente.
Machine Translated by Google

CAPÍTULO

8
Multidimensional
Matrizes

Objetivos
n Para dar exemplos de representação de dados usando matrizes bidimensionais
(§8.1).
n Para declarar arrays bidimensionais e acessar elementos de array em um array
bidimensional usando índices de linha e coluna (§8.2).

n Programar operações comuns para matrizes bidimensionais (exibição de matrizes,


soma de todos os elementos, localização de elementos mínimos e máximos e
embaralhamento aleatório) (§8.3).

n Para passar arrays bidimensionais para funções (§8.4).

n Escrever um programa para avaliar questões de múltipla escolha usando matrizes


bidimensionais (§8.5).

n Resolver o problema do par mais próximo usando matrizes bidimensionais (§8.6).

n Para verificar uma solução de Sudoku usando matrizes bidimensionais (§8.7).

n Declarar e usar matrizes multidimensionais (§8.8).


Machine Translated by Google

330 Capítulo 8 Matrizes Multidimensionais

8.1 Introdução
Os dados em uma tabela ou matriz podem ser representados usando uma matriz bidimensional.
Chave
Apontar
O Capítulo 7 introduziu como usar arrays unidimensionais para armazenar coleções lineares de elementos. Você
pode usar uma matriz bidimensional para armazenar uma matriz ou tabela. Por exemplo, a tabela a seguir que
descreve as distâncias entre as cidades pode ser armazenada usando uma matriz bidimensional.

Tabela de distância (em milhas)

Chicago Boston Nova York Atlanta Miami-Dallas Houston

Chicago 0 983 787 714 1375 967 1087


Boston 983 0 214 1102 1763 1723 1842
Nova Iorque 787 214 0 888 1549 1548 1627
Atlanta 714 1102 888 0 661 781 810
Miami 1375 1763 1549 661 0 1426 1187
Dallas 967 1723 1548 781 1426 0 239
Houston 1087 1842 1627 810 1187 239 0

8.2 Declarando Matrizes Bidimensionais


Um elemento em uma matriz bidimensional é acessado por meio de um índice de linha e coluna.
Chave
Apontar
A sintaxe para declarar um array bidimensional é

elementType nomeArray[ROW_SIZE][COLUMN_SIZE];

Como exemplo, aqui está como declarar uma matriz bidimensional de valores int :

matriz interna [5][5];

Dois subscritos são usados em uma matriz bidimensional, um para a linha e outro para a coluna. Como em
um array unidimensional, o índice para cada subscrito é do tipo int e começa em 0, conforme mostrado na Figura
8.1a.

[0] [1] [2] [3] [4] [0] [1] [2] [3] [4] [0] [1] [2]

[0] [0] [0] 123

[1] [1] [1] 456

[2] [2] 7 [2] 789

[3] [3] [3] 10 11 12

[4] [4] int m[4][3] = {{1, 2,


3}, {4, 5, 6}, {7,
matriz interna[5][5]; matriz[2][1] = 7; 8, 9}, {10, 11,
12} };

(a) (b) (c)

Figura 8.1 O índice de cada subscrito de um array bidimensional é um valor int começando em 0.
Machine Translated by Google

8.3 Processando Matrizes Bidimensionais 331

Para atribuir o valor 7 a um elemento específico na linha 2 e na coluna 1, conforme mostrado na Figura 8.1b, você pode
usar o seguinte:

matriz[2][1] = 7;

Cuidado
É um erro comum usar array[2, 1] para acessar o elemento na linha 2 e na coluna 1. Em C+
+, cada subscrito deve ser colocado entre colchetes.

Você também pode usar um inicializador de array para declarar e inicializar um array bidimensional. Por exemplo, o
código em (a) abaixo declara um array com os valores iniciais especificados, conforme mostrado na Figura 8.1c. Isso é
equivalente ao código em (b).

int m[4][3] = { {1,


intm [4][3]; m[0]
2, 3}, {4, 5, 6},
[0] = 1; m[0][1] = 2; m[0][2] = 3; m[1][0] = 4; m[1][1]
{7, 8, 9}, {10, Equivalente = 5; m[1][2] = 6; m[2][0] = 7; m[2][1] = 8; m[2][2] =
11, 12}
9; m[3][0] = 10; m[3][1] = 11; m[3][2] = 12;
};

(a) (b)

8.1 Declarar e criar uma matriz int 4 × 5 .

ÿVerificação de ponto
8.2 Qual é a saída do código a seguir?

intm [5][6];
int x[] = {1, 2};
m[0][1] = x[1];
cout << "m[0][1] é " << m[0][1];

8.3 Quais das seguintes afirmações são declarações de array válidas?

int r[2];
intx [];
int y[3][];

8.3 Processando matrizes bidimensionais


Loops for aninhados são frequentemente usados para processar uma matriz bidimensional.
Chave
Apontar
Suponha que uma matriz array seja declarada da seguinte forma:

const int ROW_SIZE = 10;


const int COLUMN_SIZE = 10;
matriz interna [ROW_SIZE][COLUMN_SIZE];

Aqui estão alguns exemplos de processamento de matrizes bidimensionais:

1. (Inicializando arrays com valores de entrada) O loop a seguir inicializa o array com o usuário Nota de vídeo
valores de entrada:
Processar matrizes 2-D

cout << "Entrar" " linhas e "


<< ROW_SIZE <<
<< COLUMN_SIZE << "colunas:" << endl;
for (int i = 0; i < ROW_SIZE; i++)
for (int j = 0; j <COLUMN_SIZE; j++)
cin >> matriz[i][j];
Machine Translated by Google

332 Capítulo 8 Matrizes Multidimensionais

2. (Inicializando arrays com valores aleatórios) O loop a seguir inicializa o array com
valores aleatórios entre 0 e 99:

for (int linha = 0; linha < ROW_SIZE; linha++)


{
for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) {

matriz[linha][coluna] = rand() % 100;


}
}

3. (Exibindo matrizes) Para exibir uma matriz bidimensional, você deve exibir cada
elemento na matriz usando um loop como o seguinte:

for (int linha = 0; linha < ROW_SIZE; linha++) {

for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) {

cout << matriz[linha][coluna] << " ";


}

cout << endl;


}

4. (Somando todos os elementos) Use uma variável chamada total para armazenar a soma. Inicialmente total
é 0. Adicione cada elemento da matriz ao total usando um loop como este:

total interno = 0;
for (int linha = 0; linha < ROW_SIZE; linha++) {

for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) {

total += matriz[linha][coluna];
}
}

5. (Somando elementos por coluna) Para cada coluna, use uma variável chamada total para armazenar sua
soma. Adicione cada elemento da coluna ao total usando um loop como este:

for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) {

total interno = 0;
for (int linha = 0; linha < ROW_SIZE; linha++)
total += matriz[linha][coluna];
cout << "Soma da coluna" << coluna << "é" << total << final;
}

6. (Qual linha tem a maior soma?) Use as variáveis maxRow e indexOfMaxRow para rastrear a maior soma
e índice da linha. Para cada linha, calcule sua soma e atualize maxRow
e indexOfMaxRow se a nova soma for maior.

int maxLinha = 0;
int indexOfMaxRow = 0;

// Obtém a soma da primeira linha em maxRow


for (int coluna = 0; coluna <COLUMN_SIZE; coluna++)
maxRow += matriz[0][coluna];

for (int linha = 1; linha < ROW_SIZE; linha++)


Machine Translated by Google

8.3 Processando Matrizes Bidimensionais 333

{
int totalOfThisRow = 0;
for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) totalOfThisRow
+= matriz[linha][coluna];

if (totalOfThisRow > maxRow) {

maxRow = totalOfThisRow;
indexOfMaxRow = linha;
}
}

cout << "Linha" << indexOfMaxRow


<< "tem a soma máxima de" << maxRow << endl;

7. (Embaralhamento aleatório) O embaralhamento dos elementos em um array unidimensional


foi introduzido na Seção 7.2.4, “Processando arrays”. Como você embaralha todos os
elementos em uma matriz bidimensional? Para fazer isso, para cada elemento matriz[i][j],
gere aleatoriamente os índices i1 e j1 e troque a matriz[i][j] pela matriz[i1][j1], da seguinte forma:

srand(tempo(0));

for (int i = 0; i < ROW_SIZE; i++) {

for (int j = 0; j < TAMANHO_COLUNA; j++) {

int i1 = rand()% ROW_SIZE;


int j1 = rand()% COLUMN_SIZE;

// Troca matriz[i][j] com matriz[i1][j1]


temperatura dupla = matriz[i][j];
matriz[i][j] = matriz[i1][j1]; matriz[i1][j1] =
temp;
}
}

8.4 Qual é a saída do código a seguir?


ÿVerificação de ponto
#include <iostream>
usando namespace std;

int principal()
{
matriz interna [4][4] =
{{1, 2, 3, 4},
{4, 5, 6, 7},
{8, 9, 10, 11},
{12, 13, 14, 15}};

soma interna = 0;

para (int i = 0; i < 4; i++)


soma += matriz[i][i];

cout << soma << endl;

retornar 0;
}
Machine Translated by Google

334 Capítulo 8 Matrizes Multidimensionais

8.5 Qual é a saída do código a seguir?


#include <iostream>
usando namespace std;

int principal()
{
matriz interna [4][4] =
{{1, 2, 3, 4},
{4, 5, 6, 7},
{8, 9, 10, 11},
{12, 13, 14, 15}};

soma interna = 0;

para (int i = 0; i < 4; i++)


cout << matriz[i][1] << " ";

retornar 0;
}

8.4 Passando arrays bidimensionais para funções


Ao passar um array bidimensional para uma função, C++ exige que o tamanho da coluna
Chave
Apontar seja especificado na declaração do tipo de parâmetro da função.

A Listagem 8.1 dá um exemplo de uma função que retorna a soma de todos os elementos de uma
matriz.
Nota de vídeo

Passar argumentos de array 2D


Listagem 8.1 PassTwoDimensionalArray.cpp
1 #include <iostream>
2 usando namespace std;

3 4 const int COLUMN_SIZE = 4;

tamanho de coluna fixo 5 6 soma int (const int a[][COLUMN_SIZE], int rowSize)
7{
total interno = 0;
for (int linha = 0; linha < tamanho da linha; linha++)
8 {
9 for (int coluna = 0; coluna <COLUMN_SIZE; coluna++)
10 {
11 total += a[linha][coluna];
12 }
13 }
14
15 retorno total;
16 17 18 }
19
20 int principal()
21 {
22 const int ROW_SIZE = 3;
23 int m[ROW_SIZE][COLUMN_SIZE];
"
24 cout << "Entrar" << ROW_SIZE << linhas e "
25 << COLUMN_SIZE << " colunas: " << endl;
26 for (int i = 0; i < ROW_SIZE; i++)
27 for (int j = 0; j <COLUMN_SIZE; j++)
28 cin >> m[i][j];
Machine Translated by Google

8.5 Problema: Avaliando um Teste de Múltipla Escolha 335

29
30 cout << "\nA soma de todos os elementos é " << soma(m, ROW_SIZE) << endl; matriz de passagem

31
retornar 0;
32 33 }

Insira 3 linhas e 4 colunas:


1234
5678
9 10 11 12
A soma de todos os elementos é 78

A função soma (linha 6) possui dois argumentos. O primeiro especifica uma matriz bidimensional
com um tamanho de coluna fixo. A segunda especifica o tamanho da linha da matriz bidimensional.

8.6 Quais das seguintes declarações de função estão erradas?


ÿVerificação de ponto
int f(int[][] a, int rowSize, int columnSize); int f(int a[][], int
rowSize, int columnSize);
int f(int a[][3], int rowSize);

8.5 Problema: Avaliando um Teste de Múltipla Escolha


O problema é escrever um programa que avalie testes de múltipla escolha.
Chave
Apontar
Suponha que haja 8 alunos e 10 perguntas e que as respostas sejam armazenadas em uma matriz
bidimensional. Cada linha registra as respostas de um aluno às perguntas. Por exemplo, a matriz a seguir
armazena o teste.

Respostas dos alunos às perguntas:


0 1234 56789

Aluno 0 ABACCDEEAD

Aluno 1 DBABCAEEAD

Aluno 2 EDDACBEAD

Aluno 3 CBAEDCEEAD

Aluno 4 ABDCCDEEAD

Aluno 5 BBECCDEEAD

Aluno 6 BBACCDEEAD

Aluno 7 EBECCDEEAAD

A chave é armazenada em um array unidimensional, como segue:

Chave para as perguntas:


0 1234 56789

chave DBDCCDAEAD
Machine Translated by Google

336 Capítulo 8 Matrizes Multidimensionais

Seu programa avalia o teste e exibe o resultado. O programa compara as respostas de


cada aluno com a chave, conta o número de respostas corretas e exibe. A Listagem 8.2
fornece o programa.

Listagem 8.2 GradeExam.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
const int NUMBER_OF_STUDENTS = 8;
6 7 const int NUMBER_OF_QUESTIONS = 10;

// Respostas dos alunos às perguntas


matriz bidimensional 8 respostas de caracteres [NUMBER_OF_STUDENTS][NUMBER_OF_QUESTIONS] =
9 10 11 {
12 {'A', 'B', 'A', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
13 {'D', 'B', 'A', 'B', 'C', 'A', 'E', 'E', 'A', 'D'},
14 {'E', 'D', 'D', 'A', 'C', 'B', 'E', 'E', 'A', 'D'},
15 {'C', 'B', 'A', 'E', 'D', 'C', 'E', 'E', 'A', 'D'},
16 {'A', 'B', 'D', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
17 {'B', 'B', 'E', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
18 {'B', 'B', 'A', 'C', 'C', 'D', 'E', 'E', 'A', 'D'},
19 {'E', 'B', 'E', 'C', 'C', 'D', 'E', 'E', 'A', 'D'}
20 };
21
22 // Chave para as perguntas
variedade 23 teclas char [] = {'D', 'B', 'D', 'C', 'C', 'D', 'A', 'E', 'A', 'D'};
24
25 //Classifica todas as respostas
26 for (int i = 0; i < NUMBER_OF_STUDENTS; i++)
27 {
28 // Aluno da primeira série
29 int contagem correta = 0;
30 for (int j = 0; j < NÚMERO_DE_QUESTIONS; j++)
31 {
32 if (respostas[i][j] == chaves[j])
33 Contagem correta++;
34 }
35
36 cout << "Aluno " correctCount A contagem correta de << i << "é" <<
37 << endl;
38 }
39
40 retornar 0;
41 }

A contagem correta do aluno 0 é 7


A contagem correta do aluno 1 é 6
A contagem correta do aluno 2 é 5
A contagem correta do aluno 3 é 4
A contagem correta do aluno 4 é 8
A contagem correta do aluno 5 é 7
A contagem correta do aluno 6 é 7
A contagem correta do aluno 7 é 7
Machine Translated by Google

8.6 Problema: Encontrando um Par Mais Próximo 337

A instrução nas linhas 10 a 20 declara e inicializa um array bidimensional de caracteres.


A instrução na linha 23 declara e inicializa um array de valores char .
Cada linha na matriz Answers armazena a resposta de um aluno, que é avaliada comparando-a com a
chave nas chaves da matriz. Imediatamente após a nota de um aluno, o resultado do aluno é exibido.

8.6 Problema: Encontrando um Par Mais Próximo


Esta seção apresenta um problema geométrico para encontrar o par de pontos mais próximo.
Chave
Apontar
Dado um conjunto de pontos, o problema do par mais próximo consiste em encontrar os dois pontos mais
próximos um do outro. Na Figura 8.2, por exemplo, os pontos (1, 1) e (2, 0,5) estão mais próximos um do
outro. Existem várias maneiras de resolver esse problema. Uma abordagem intuitiva é calcular as distâncias
entre todos os pares de pontos e encontrar o par com a distância mínima, conforme implementado na
Listagem 8.3.

(1, 3) (3, 3) Animação do par mais próximo no


xy Site complementar
(4, 2)
0 1 3

1 1 1
(1, 1)
2 1 1
(2, 0,5)
3 2 0,5

4 2
(4, 0,5)
5 3 1
(1, 1) (2, 1)
6 4 3
7 4 2 0,5

Figura 8.2 Os pontos podem ser representados em uma matriz bidimensional.

Listagem 8.3 FindNearestPoints.cpp


1 #include <iostream> Nota de vídeo
2 #incluir <cmath> Pontos mais próximos
3 usando namespace std;
4
5 // Calcula a distância entre dois pontos (x1, y1) e (x2, y2) 6 double getDistance(double x1,
double y1, double x2, double y2) distância entre dois pontos
7{
retornar sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
8 9}
10
11 int principal()
12 {
13 const int NUMBER_OF_POINTS = 8;
14
15 // Cada linha em pontos representa um ponto
16 pontos duplos [NUMBER_OF_POINTS][2]; Matriz 2-D
17
18 cout << "Enter " for << NUMBER_OF_POINTS << " pontos: ";
19 (int i = 0; i < NUMBER_OF_POINTS; i++) leia todos os pontos
20 cin >> pontos[i][0] >> pontos[i][1];
21
22 // p1 e p2 são os índices no array de pontos
23 int p1 = 0, p2 = 1; //Dois pontos iniciais
24 double menorDistance = getDistance(pontos[p1][0], pontos[p1][1], rastrear menor distância
25 pontos[p2][0], pontos[p2][1]); // Inicializa distância mais curta
Machine Translated by Google

338 Capítulo 8 Matrizes Multidimensionais

26
27 //Calcula a distância para cada dois pontos
para cada ponto eu 28 para (int i = 0; i < NUMBER_OF_POINTS; i++)
29 {
para cada ponto j 30 para (int j = i + 1; j < NUMBER_OF_POINTS; j++)
31 {
distância entre i e j 32 distância dupla = getDistance(pontos[i][0], pontos[i][1], pontos[j][0], pontos[j][1]); //
33 Encontre a distância
34
atualizar menor distância 35 if (menorDistância > distância)
36 {
37 p1 = eu; //Atualiza p1
38 p2 = j; //Atualiza p2
39 distância mais curta = distância; //Atualiza a distância mais curta
40 }
41 }
42 }
43
44 //Exibir resultado
"
45 cout << "Os dois pontos mais próximos são "(" <<
46 << pontos[p1][0] << ", " << pontos[p1][1] << ") e (" <<
47 pontos[p2][0] << ", " << pontos[p2][1] << ")" << endl;
48
49 retornar 0;
50 }

Insira 8 pontos: -1 3 -1 -1 1 1 2 0,5 2 -1 3 3 4 2 4 -0,5 Os dois pontos mais próximos são


(1, 1) e (2, 0,5)

Os pontos são lidos no console e armazenados em uma matriz bidimensional chamada points
(linhas 19–20). O programa usa a variável shortDistance (linha 24) para armazenar a distância entre dois
pontos mais próximos, e os índices desses dois pontos na matriz de pontos são armazenados em p1 e p2
(linha 23).
Para cada ponto no índice i, o programa calcula a distância entre os pontos[i] e os pontos[j] para todos j
> i (linhas 28–42). Sempre que uma distância menor é encontrada, as variáveisshortDistance , p1 e p2 são
atualizadas (linhas 37–39).
A distância entre dois pontos (x1, y1) e (x2, y2) pode ser calculada usando o + (y2 - y1)
2 2
fórmula 2(x2 - x1) na função getDistance (linhas 6–9).
O programa assume que a planície tem pelo menos dois pontos. Você pode modificar facilmente o
programa para lidar com o caso se a planície tiver um ponto ou nenhum.
vários pares mais próximos Observe que pode haver mais de um par de pontos mais próximos com a mesma distância mínima. O
programa encontra um desses pares. Você pode modificar o programa para encontrar todos os pares mais
próximos no Exercício de Programação 8.10.

Dica
Arquivo de entrada É complicado inserir todos os pontos pelo teclado. Você pode armazenar a entrada em um
arquivo, digamos FindNearestPoints.txt, e compilar e executar o programa usando o seguinte
comando:

g++ FindNearestPoints.cpp –o FindNearestPoints.exe


FindNearestPoints.exe <FindNearestPoints.txt
Machine Translated by Google

8.7 Problema: Sudoku 339

8.7 Problema: Sudoku


O problema é verificar se uma determinada solução do Sudoku está correta.
Chave
Apontar
Este livro ensina como programar usando uma ampla variedade de problemas com vários níveis de dificuldade.
Usamos exemplos simples, curtos e estimulantes para apresentar técnicas de programação e resolução de
problemas e usamos exemplos interessantes e desafiadores para motivar os alunos.
Esta seção apresenta um problema interessante, do tipo que aparece nos jornais todos os dias. É um quebra-
cabeça de colocação de números, comumente conhecido como Sudoku. Este é um problema muito desafiador.
Para torná-lo acessível aos novatos, esta seção apresenta uma solução para uma versão simplificada do
problema do Sudoku, que consiste em verificar se a solução está correta. Como encontrar uma solução para o
problema do Sudoku é fornecido no Suplemento VI.A.
Sudoku é uma grade 9 x 9 dividida em caixas menores de 3 x 3 (também chamadas de regiões ou blocos),
conforme mostrado na Figura 8.3a. Algumas células, chamadas células fixas, são preenchidas com números de células fixas

1 a 9. O objetivo é preencher as células vazias, também chamadas de células livres, com números de 1 a 9 para células livres

que cada linha, cada coluna e cada caixa 3 * 3 contenha os números de 1 a 9, conforme mostrado na Figura
8.3b.

5 3 7 534678912
6 195 672195348
98 6 198342567
8 6 3 859761423
4 8 3 1 Solução 426853791
7 2 6 713924856
6 961537284
419 5 287419635
8 79 345286179

(um) quebra-cabeça (b) solução

A Figura 8.3 (b) é a solução do quebra-cabeça Sudoku em (a).

Por conveniência, usamos o valor 0 para indicar uma célula livre, conforme mostrado na Figura 8.4a. A grade
pode ser representado naturalmente usando uma matriz bidimensional, como mostrado na Figura 8.4b. representando uma grade

530070000 grade interna [9][9] =


600195000 {{5, 3, 0, 0 , 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9,
098000060 5, 0, 0, 0 } , {0, 9, 8, 0, 0, 0, 0, 6, 0},
{8, 0, 0 , 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0,
800060003 3, 0, 0, 1} , {7, 0, 0 , 0, 2, 0, 0, 0, 6},
400803001 {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1,
9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}, };
700020006
060000000
000419005
000080079

(a) (b)

Figura 8.4 Uma grade pode ser representada usando uma matriz bidimensional.
Machine Translated by Google

340 Capítulo 8 Matrizes Multidimensionais

Encontrar uma solução para o quebra-cabeça é substituir 0 na grade por números apropriados entre 1 e 9. Para a solução
da Figura 8.3b, a grade deve ser como mostrada na Figura 8.5.

Uma grade de solução


é {{5, 3, 4, 6, 7, 8, 9, 1, 2}, {6, 7, 2, 1,
9, 5, 3, 4, 8}, {1, 9, 8, 3, 4, 2, 5, 6,
7}, {8, 5, 9, 7, 6, 1, 4, 2, 3}, {4, 2 , 6 ,
8, 5, 3, 7, 9, 1}, {7, 1, 3, 9, 2, 4, 8, 5,
6}, {9, 6, 1, 5, 3, 7 , 2 , 8, 4}, {2, 8, 7,
4, 1, 9, 6, 3, 5}, {3, 4, 5, 2, 8, 6, 1, 7,
9} };

Figura 8.5 Uma solução é armazenada em grade.

Suponha que uma solução para um quebra-cabeça Sudoku seja encontrada, como você verifica se a solução é correta?
correto? Aqui estão duas abordagens:

n Uma maneira de verificar isso é ver se cada linha tem números de 1 a 9, se cada coluna tem
números de 1 a 9, e cada caixa pequena tem números de 1 a 9.

n A outra maneira é verificar cada célula. Cada célula deve ser um número de 1 a 9 e o
a célula é única em cada linha, cada coluna e cada caixa pequena.

A Listagem 8.4 fornece um programa que solicita ao usuário que insira uma solução e o programa informa verdadeiro se
a solução for válida. Usamos a segunda abordagem do programa para verificar se a solução está correta.

Listagem 8.4 CheckSudokuSolution.cpp


1 #include <iostream>
2 usando namespace std;

3 4 void readASolution(int grid[][9]);


5 bool isValid(const int grid[][9]);
6 bool isValid(int i, int j, const int grid[][9]);

7 8 int principal()
9{
10 // Leia um quebra-cabeça de Sudoku
11 grade interna [9][9];
ler entrada 12 readASolution(grade);
13
solução válida? 14 cout << (isValid(grid) ? "Solução válida" : "Solução inválida");
15
16 retornar 0;
17 }
18
19 // Leia um quebra-cabeça Sudoku no teclado
leia a solução 20 void readASolution(int grid[][9])
21 {
cout << "Insira um quebra-cabeça Sudoku:" << endl;
para (int i = 0; i < 9; i++)
22 para (int j = 0; j < 9; j++)
cin >> grade[i][j];
23 24 25 26 }
Machine Translated by Google

8.7 Problema: Sudoku 341

27
28 // Verifica se as células fixas são válidas na grade 29 bool isValid(const int grid[][9]) 30 {
verifique a solução

31 para (int i = 0; i < 9; i++)


32 para (int j = 0; j < 9; j++)
33 if (grade[i][j] < 1 || grade[i][j] > 9 ||
34 !isValid(i, j, grade)) retorna falso;
35
36
37 retornar verdadeiro; //As células fixas são válidas
38 }
39
40 // Verifica se grid[i][j] é válido na grade 41 bool isValid(int i, int j, const int grid[][9])

42 {
43 // Verifica se grid[i][j] é válido na linha do i
44 for (int coluna = 0; coluna < 9; coluna++) verificar linhas
45 if (coluna! = j && grade[i][coluna] == grade[i][j])
46 retorna falso;
47
48 // Verifica se grid[i][j] é válido na coluna j
49 for (int linha = 0; linha < 9; linha++) verificar colunas
50 if (linha! = i && grade[linha][j] == grade[i][j])
51 retorna falso;
52
53 // Verifica se grid[i][j] é válido na caixa 3 por 3
54 for (int linha = (i/ 3) * 3; linha < (i/ 3) * 3 + 3; linha++) marque pequenas caixas
55 para (int col = (j/ 3) * 3; col < (j/ 3) * 3 + 3; col++)
56 if (linha! = i && col! = j && grade[linha][col] == grade[i][j])
57 retorna falso;
58
59 retornar verdadeiro; // O valor atual em grid[i][j] é válido
60 }

Insira uma solução de quebra-cabeça Sudoku:


963174258
178325649
254689731
821437596
496852317
735961824
589713462
317246985
642598173
Solução válida

O programa invoca a função readASolution(grid) (linha 12) para ler uma solução
Sudoku em uma matriz bidimensional representando uma grade Sudoku.
A função isValid(grid) verifica se os valores na grade são válidos. Ele verifica função isValid

se cada valor está entre 1 e 9 e se cada valor é válido na grade (linhas 31–35).
A função isValid(i, j, grid) verifica se o valor em grid[i][j] é válido. função isValid sobrecarregada

Ele verifica se grid[i][j] aparece mais de uma vez na linha i (linhas 44–46), na coluna j
(linhas 49–51) e na caixa 3 * 3 (linhas 54–57).
Machine Translated by Google

342 Capítulo 8 Matrizes Multidimensionais

Como você localiza todas as células na mesma caixa? Para qualquer grade[i][j], a célula inicial da caixa 3 * 3
que a contém é grade[(i / 3) * 3][(j / 3) * 3], conforme ilustrado na Figura 8.6.

grade[0][0] grade[0][6]

Para qualquer grade[i][j] nesta caixa 3 3, sua célula inicial


é grade[3*(i/3)][3*(j/3)] (ou seja, grade[0][6]). Por
exemplo, para grid[2][8], i 2 e j 8, 3*(j/3) 0 e 3*(j/3) 6.
grade[6][3]

Para qualquer grade[i][j] nesta caixa 3 3,


sua célula inicial é grade[3*(i/3)][3*(j/3)]
(ou seja, grade[6][3]). Por exemplo,
para grid[8][5], i 8 e j 5, 3*(i/3) 6 e 3*(j/3) 3.

Figura 8.6 A localização da primeira célula em uma caixa 3 * 3 determina a localização das outras células na caixa.

Com esta observação, você pode identificar facilmente todas as células da caixa. Suponha que grade[r]
[c] é a célula inicial de uma caixa 3 * 3, as células na caixa podem ser percorridas em um loop aninhado da
seguinte forma:

// Obtém todas as células em uma caixa 3 por 3 começando em grid[r][c]


for (int linha = r; linha < r + 3; linha++)
for (int col = c; col < c + 3; col++)
// grid[row][col] está na caixa

Arquivo de entrada É complicado inserir 81 números no teclado. Você pode armazenar a entrada em um arquivo, digamos
CheckSudokuSolution.txt (consulte www.cs.armstrong.edu/liang/data/CheckSudokuSolution
.txt) e compile e execute o programa usando o seguinte comando:

g++ CheckSudokuSolution.cpp –o CheckSudokuSolution.exe


CheckSudokuSolution.exe <CheckSudokuSolution.txt

8.8 Matrizes Multidimensionais


Você pode criar uma matriz de qualquer dimensão em C++.
Chave
Apontar
Na seção anterior, você usou uma matriz bidimensional para representar uma matriz ou tabela.
Ocasionalmente, você precisará representar estruturas de dados ndimensionais. Em C++, você pode criar matrizes
n-dimensionais para qualquer número inteiro n.
A declaração de uma matriz bidimensional pode ser generalizada para a declaração de uma matriz n-
dimensional para n 7 = 3. Por exemplo, você pode usar uma matriz tridimensional para armazenar notas de exames
para uma turma de seis alunos com cinco exames e cada exame tem duas partes (múltipla escolha e redação).
A sintaxe a seguir declara uma variável de matriz tridimensional pontuações.

pontuações duplas [6][5][2];

Você também pode usar a notação abreviada para criar e inicializar o array da seguinte maneira:

pontuações duplas [6][5][2] = {


{{7,5, 20,5}, {9,0, 22,5}, {15, 33,5}, {13, 21,5}, {15, 2,5}},
{{4,5, 21,5}, {9,0, 22,5}, {15, 34,5}, {12, 20,5}, {14, 9,5}},
Machine Translated by Google

8.8 Matrizes Multidimensionais 343

{{6,5, 30,5}, {9,4, 10,5}, {11, 33,5}, {11, 23,5}, {10, 2,5}},
{{6,5, 23,5}, {9,4, 32,5}, {13, 34,5}, {11, 20,5}, {16, 7,5}},
{{8,5, 26,5}, {9,4, 52,5}, {13, 36,5}, {13, 24,5}, {16, 2,5}},
{{9,5, 20,5}, {9,4, 42,5}, {13, 31,5}, {12, 20,5}, {16, 6,5}}};

pontuações[0][1][0] refere-se à pontuação de múltipla escolha do segundo exame do


primeiro aluno, que é 9,0. pontuações[0][1][1] refere-se à nota da redação do segundo exame
do primeiro aluno, que é 22,5. Isso está representado na figura a seguir:

Qual aluno Qual exame Múltipla escolha ou redação

pontuações[ eu ] [ j ] [ k ]

8.8.1 Problema: Temperatura e Umidade Diárias


Suponha que uma estação meteorológica registre a temperatura e a umidade a cada hora de cada dia e
armazene os dados dos últimos 10 dias em um arquivo de texto chamado weather.txt (consulte www.cs.armstrong
.edu/liang/data/Weather.txt). Cada linha do arquivo consiste em quatro números que indicam dia, hora,
temperatura e umidade. O conteúdo do arquivo pode aparecer como em (a):

1 1 76,4 0,92 10 24 98,7 0,74

1 2 77,7 0,93 1 2 77,7 0,93

... ...

10 23 97,7 0,71 10 23 97,7 0,71

10 24 98,7 0,74 1 1 76,4 0,92

(a) (b)

Observe que as linhas do arquivo não são necessárias em ordem. Por exemplo, o arquivo pode aparecer
como mostrado em (b).
Sua tarefa é escrever um programa que calcule a temperatura e a umidade médias diárias durante os 10
dias. Você pode usar o redirecionamento de entrada para ler os dados do arquivo e armazená-los em uma matriz
tridimensional chamada dados. O primeiro índice de dados na faixa de 0 a 9 representa 10 dias, o segundo
índice de 0 a 23 representa 24 horas e o terceiro índice de 0 a 1 representa temperatura e umidade,
respectivamente. Observe que os dias são numerados de 1 a 10 e as horas são numeradas de 1 a 24 no arquivo.
Como o índice da matriz começa em 0, data[0][0][0] armazena a temperatura do dia 1 na hora 1 e data[9][23]

[1] armazena a umidade do dia 10 às 24 horas.


O programa é fornecido na Listagem 8.5.

Listagem 8.5 Weather.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
Machine Translated by Google

344 Capítulo 8 Matrizes Multidimensionais

const int NUMBER_OF_DAYS = 10;


const int NUMBER_OF_HOURS = 24;
dados duplos [NUMBER_OF_DAYS][NUMBER_OF_HOURS][2];

6 // Lê a entrada usando o redirecionamento de entrada de um arquivo


7 int dia, hora;
matriz tridimensional 8 temperatura dupla , umidade;
9 for (int k = 0; k < NUMBER_OF_DAYS * NUMBER_OF_HOURS; k++)
10 {
11 cin >> dia >> hora >> temperatura >> umidade;
12 dados[dia - 1][hora - 1][0] = temperatura;
13 dados[dia - 1][hora - 1][1] = umidade;
14 }
15
16 // Encontre a temperatura e umidade média diária
17 for (int i = 0; i < NUMBER_OF_DAYS; i++)
18 {
19 double dailyTemperatureTotal = 0, dailyHumidityTotal = 0;
20 for (int j = 0; j <NÚMERO_DE_HORAS; j++)
21 {
22 dailyTemperatureTotal += dados[i][j][0];
23 dailyHumidityTotal += dados[i][j][1];
24 }
25
26 //Exibir resultado
27 cout << "Dia " << i << "A temperatura média de" é "
28 << dailyTemperatureTotal / NUMBER_OF_HOURS << endl;
29 cout << "Dia " << i << "A umidade média de" é "
30 << dailyHumidityTotal / NUMBER_OF_HOURS << endl;
31 }
32
33 retornar 0;
34 35 36 37 38 }

A temperatura média do dia 0 é 77,7708


A umidade média do dia 0 é 0,929583
A temperatura média do dia 1 é 77,3125
A umidade média do dia 1 é 0,929583
...
A temperatura média do dia 9 é 79,3542
A umidade média do dia 9 é 0,9125

Você pode usar o seguinte comando para compilar o programa:

g++ Tempo.cpp –o Tempo

Use o seguinte comando para executar o programa:

Weather.exe < Weather.txt

Os dados de um array tridimensional são declarados na linha 8. O loop nas linhas 13 a 18 lê a


entrada do array. Você poderia inserir a entrada do teclado, mas seria estranho. Por conveniência,
armazenamos os dados em um arquivo e usamos o redirecionamento de entrada para ler os dados do
arquivo. O loop nas linhas 24–28 adiciona todas as temperaturas de cada hora do dia a dailyTemperatureTotal
e toda a umidade de cada hora para dailyHumidityTotal. A temperatura e a umidade médias diárias
são exibidas nas linhas 31–34.
Machine Translated by Google

8.8 Matrizes Multidimensionais 345

8.8.2 Problema: Adivinhar aniversários


A Listagem 4.4, GuessBirthday.cpp, fornece um programa que adivinha uma data de aniversário. O programa
pode ser simplificado armazenando os números em cinco conjuntos em uma matriz tridimensional e solicitando as
respostas ao usuário usando um loop, conforme mostrado na Listagem 8.6.

Listagem 8.6 GuessBirthdayUsingArray.cpp


1 #include <iostream>
2 #include <iomanip>
3 usando namespace std;

4 5 int principal()
6{
7 dia interno = 0; // Dia a definir
8 resposta de caractere ;

9 10 int datas[5][4][4] = { matriz tridimensional


11 {{ 1, 3, 5, 7},
12 { 9, 11, 13, 15},
13 {17, 19, 21, 23},
14 {25, 27, 29, 31}},
15 {{ 2, 3, 6, 7},
16 {10, 11, 14, 15},
17 {18, 19, 22, 23},
18 {26, 27, 30, 31}},
19 {{ 4, 5, 6, 7},
20 {12, 13, 14, 15},
21 {20, 21, 22, 23},
22 {28, 29, 30, 31}},
23 {{ 8, 9, 10, 11},
24 {12, 13, 14, 15},
25 {24, 25, 26, 27},
26 {28, 29, 30, 31}},
27 {{16, 17, 18, 19},
28 {20, 21, 22, 23},
29 {24, 25, 26, 27},
30 {28, 29, 30, 31}}};
31
32 for (int i = 0; i < 5; i++) {
33
34 cout << "Seu aniversário está em Set" << (i + 1) << "?" << fim; Conjunto 1, 2, 3, 4, 5?
35 for (int j = 0; j < 4; j++) {
36
37 para (int k = 0; k < 4; k++)
38 cout << setw(3) << datas[i][j][k] << " ";
39 cout << endl;
40 }
41 cout << "\nDigite N/n para Não e S/y para Sim: ";
42 cin >> resposta;
43 if (resposta == 'S' || resposta == 's')
44 dia += datas[i][0][0];
45 }
46
47 cout << "Seu aniversário é " << dia << fim;
48
49 retornar 0;
50}

Uma matriz tridimensional de datas é criada nas linhas 10–30. Esta matriz armazena cinco conjuntos de
números. Cada conjunto é uma matriz bidimensional 4 por 4.
Machine Translated by Google

346 Capítulo 8 Matrizes Multidimensionais

O loop que começa na linha 32 exibe os números de cada conjunto e solicita ao usuário que responda se o dia
está no conjunto (linhas 37–38). Se for, o primeiro número (datas[i][0]
[0]) no conjunto é adicionado à variável dia (linha 44).

8.7 Declare e crie um array int 4 × 6 × 5 .

ÿVerificação de ponto

Resumo do capítulo
1. Um array bidimensional pode ser usado para armazenar uma tabela.

2. Um array bidimensional pode ser criado usando a sintaxe: elementType arrayName


[ROW_SIZE][COLUMN_SIZE].

3. Cada elemento em uma matriz bidimensional é representado usando a sintaxe: arrayName


[rowIndex][columnIndex].

4. Você pode criar e inicializar uma matriz bidimensional usando um inicializador de matriz com a sintaxe:
elementType arrayName[][COLUMN_SIZE] = {{valores de linha}, ..., {valores de linha}}.

5. Você pode passar um array bidimensional para uma função; no entanto, C++ exige que o
o tamanho da coluna seja especificado na declaração da função.

6. Você pode usar matrizes de matrizes para formar matrizes multidimensionais. Por exemplo, um array
tridimensional é declarado como um array de arrays usando a sintaxe elementType arrayName[size1]
[size2][size3].

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 8.2–8.5

*8.1 (Soma de elementos coluna por colunas) Escreva uma função que retorne a soma de todos os
elementos em uma coluna especificada em uma matriz usando o seguinte cabeçalho:

const int TAMANHO = 4;


double sumColumn(const double m[][SIZE], int rowSize, int columnIndex);

Escreva um programa de teste que leia uma matriz 3 por 4 e exiba a soma de cada coluna. Aqui está
um exemplo de execução:

Insira uma matriz 3 por 4 linha por linha: 1,5 2 3


4
5,5 6 7 8
9,5 1 3 1
A soma dos elementos na coluna 0 é 16,5
A soma dos elementos na coluna 1 é 9
A soma dos elementos na coluna 2 é 13
A soma dos elementos na coluna 3 é 13
Machine Translated by Google

Exercícios de Programação 347

*8.2 (Soma da diagonal maior em uma matriz) Escreva uma função que some todos os valores duplos na
diagonal maior em uma matriz n * n de valores duplos usando o seguinte cabeçalho:

const int TAMANHO =


4; double sumMajorDiagonal(const double m[][SIZE]);

Escreva um programa de teste que leia uma matriz 4 por 4 e exiba a soma de todos os seus
elementos na diagonal maior. Aqui está um exemplo de execução:

Insira uma matriz 4 por 4 linha por linha: 1 2


34567
8 9 10
11 12 13 14
15 16
A soma dos elementos da diagonal maior é 34

*8.3 (Classificar e exibir os alunos) Reescreva a Listagem 8.2, GradeExam.cpp, para classificar os alunos
na ordem inversa e exibir apenas aqueles com mais de seis respostas corretas.

*8.4 (Calcular notas totais de cada aluno) Suponha que as notas obtidas por todos os alunos sejam
armazenadas em uma matriz bidimensional. Cada linha registra as notas de um aluno em cinco
testes. Por exemplo, a matriz a seguir armazena as notas dos testes de oito alunos.
Escreva um programa que exiba os alunos e o total de notas obtidas em cinco testes, em ordem
decrescente do total de notas.

T1 T2 T3 T4 T5
Aluno 0 12 14 13 14 15
Aluno 1 17 13 14 13 13
Aluno 2 13 13 14 13 13
Aluno 3 19 13 14 17 13
Aluno 4 13 15 14 13 16
Aluno 5 13 14 14 16 13
Aluno 6 13 17 14 18 13
Aluno 7 16 13 15 19 12

8.5 (Álgebra: adicionar duas matrizes) Escreva uma função para adicionar duas matrizes aeb e salve
o resultado em c.

a21 a22 a23 b21 b22 a23


a21 + b21 a22 + b22 a23 + b23
£ a31
a11 a32
a12 a33
a13 ÿ + £ b11
b31b12
b32b13
b33 ÿ = £ a11
a31+ +b11
b31a12
a32+ +b12
b32a13
a33+ +b13
b33 ÿ

O cabeçalho da função é

const int N = 3;

void addMatrix(const double a[][N],


const duplo b[][N], duplo c[][N]);
Machine Translated by Google

348 Capítulo 8 Matrizes Multidimensionais

Cada elemento cij é aij + bij . Escreva um programa de teste que solicite ao usuário que insira duas matrizes 3
* 3 e exiba sua adição. Aqui está um exemplo de execução:

Insira a matriz1: 1 2 3 4 5 6 7 8 9
Insira a matriz2: 0 2 4 1 4,5 2,2 1,1 4,3 5,2
A adição das matrizes é 1 4 7
1234 024
5 6 + 1 4,5 2,2 7 8 9 = 5 9,5 8,2
1,1 4,3 5,2 8,1 12,3 14,2

**8.6 (Aplicação financeira: imposto computacional) Reescreva a Listagem 3.3, ComputeTax.cpp, usando arrays. Para cada
status de depósito, existem seis alíquotas de imposto. Cada taxa é aplicada a um determinado valor do lucro
tributável. Por exemplo, do rendimento tributável de $ 400.000 para um único arquivador, $ 8.350 são tributados
a 10%, (33.950–8.350) a 15%, (82.250–33.950) a 25%, (171.550–82.550) a 28%, (372.550 –82.250) em 33%
e (400.000–372.950) em 36%. As seis taxas são iguais para todos os status de depósito, que podem ser
representados na seguinte matriz:

taxas duplas [] = {0,10, 0,15, 0,25, 0,28, 0,33, 0,36};

Os colchetes de cada taxa para todos os status de depósito podem ser representados em uma matriz
bidimensional da seguinte forma:

colchetes internos [4][5] = {

{8350, 33950, 82250, 171550, 372950}, // Arquivador único


{16700, 67900, 137050, 20885, 372950}, // Casado em conjunto
// ou qualificado // viúvo(a)

{8350, 33950, 68525, 104425, 186475}, // Casado separadamente


{11950, 45500, 117450, 190200, 372950} // Chefe da família
};

Suponha que o lucro tributável seja de $ 400.000 para arquivadores únicos. O imposto pode ser calculado da
seguinte forma:

imposto = colchetes[0][0] * taxas[0] + (colchetes[0][1]


– colchetes[0][0]) * taxas[1] + (colchetes[0][2] – colchetes[0 ][1]) * taxas[2]
+ (colchetes[0][3] – colchetes[0][2]) * taxas[3] + (colchetes[0][4] – colchetes[0]
[3] ) * taxas[4] + (400000 – colchetes[0][4]) * taxas[5]

**8.7 (Explorar matriz) Escreva um programa que preencha aleatoriamente 0s e 1s em uma matriz quadrada 4 por 4,
imprima a matriz e encontre as linhas, colunas e diagonais com todos 0s ou 1s. Aqui está um exemplo de
execução do programa:

0111
0000
0100
1111
Todos os 0 na linha 1
Todos os 1 estão na linha 3
Não há números iguais em uma coluna
Não há números iguais na diagonal maior
Não há números iguais na subdiagonal
Machine Translated by Google

Exercícios de Programação 349

***8.8 (Embaralhar colunas) Escreva uma função que embaralha as colunas em um array int bidimensional
usando o seguinte cabeçalho:

void shuffle(int m[3][columnSize]);

Escreva um programa de teste que embaralhe a seguinte matriz:

int m[3][5] = {{1, 2, 3, 4, 5}, {3, 4, 5, 6, 7}, {5, 6, 7, 8, 9}};

**8.9 ( Álgebra : multiplicar duas matrizes) Escreva uma função para multiplicar duas matrizes aeb e salve o
resultado em c .

a21 a22 a23 b21 b22 b23 c21 c22 c23


£ a31
a11 a32
a12 a33
a13 ÿ * £ b11
b31b12
b32b13
b33 ÿ = £ c11
c31c12
c32c13
c33 ÿ

O cabeçalho da função é

const int N = 3;
void multiplicarMatriz(const duplo a[][N], const duplo b[][N],
duplo c[][N]);

Cada elemento cij é ai1 * b1j + ai2 * b2j + ai3 * b3j.


Escreva um programa de teste que solicite ao usuário que insira duas matrizes 3 * 3 e exiba seu
produto. Aqui está um exemplo de execução:

Insira a matriz1: 1 2 3 4 5 6 7 8 9
Insira a matriz2: 0 2 4 1 4,5 2,2 1,1 4,3 5,2
A multiplicação das matrizes é 5,3 23,9 24
123 0 2,0 4,0
4 5 6 * 1 4,5 2,2 7 8 9 = 11,6 56,3 58,2
1,1 4,3 5,2 17,9 88,7 92,4

Seção 8.6

**8.10 (Todos os pares mais próximos) A Listagem 8.3, FindNearestPoints.cpp, encontra um par mais próximo.
Revise o programa para exibir todos os pares mais próximos com a mesma distância mínima.
Aqui está um exemplo de execução:

Digite o número de pontos: 8


Insira 8 pontos: 0 0 1 1 -1 -1 2 2 -2 -2 -3 -3 -4 -4 5 5 Os dois pontos mais próximos
são (0,0, 0,0) e (1,0, 1,0)
Os dois pontos mais próximos são (0,0, 0,0) e (-1,0, -1,0)
Os dois pontos mais próximos são (1,0, 1,0) e (2,0, 2,0)
Os dois pontos mais próximos são (-1,0, -1,0) e (-2,0, -2,0)
Os dois pontos mais próximos são (-2,0, -2,0) e (-3,0, -3,0)
Os dois pontos mais próximos são (-3,0, -3,0) e (-4,0, -4,0)
A distância deles é 1,4142135623730951

**8.11 (Jogo: nove caras e coroas) Nove moedas são colocadas em uma matriz 3 * 3 com algumas voltadas
para cima e outras voltadas para baixo. Você pode representar o estado das moedas usando um 3 * 3
matriz com valores 0 (cabeça) e 1 (cauda). aqui estão alguns exemplos:

0000 1010 1101 1011 1001


1000 0110 0000 1010 1111
0 0 1 0 0
Machine Translated by Google

350 Capítulo 8 Matrizes Multidimensionais

Cada estado também pode ser representado usando um número binário. Por exemplo, as
matrizes anteriores correspondem aos números

000010000 101001100 110100001 101110100 100111110

O número total de possibilidades é 512. Portanto, você pode usar números decimais 0, 1, 2,
3,. . . e 511 para representar todos os estados da matriz. Escreva um programa que solicite ao
usuário que insira um número entre 0 e 511 e exiba a matriz correspondente com os caracteres
H e T. Aqui está um exemplo de execução:

Digite um número entre 0 e 511: 7


HHH
HHH
TTT

O usuário digitou 7, que corresponde a 000000111. Como 0 significa H e 1


para T, a saída está correta.
*8.12 (Pontos mais próximos um do outro) A Listagem 8.3, FindNearestPoints.cpp, é um programa que
encontra dois pontos em um espaço bidimensional mais próximos um do outro. Revise o
Nota de vídeo
programa para que ele encontre dois pontos em um espaço tridimensional mais próximo um
Pontos mais próximos 3-D
do outro. Use uma matriz bidimensional para representar os pontos. Teste o programa usando
os seguintes pontos:

pontos duplos [][3] = {{-1, 0, 3}, {-1, -1, -1}, {4, 1, 1}, {2, 0,5, 9}, {3,5, 2, -1}, {3, 1,5, 3}, {-1,5,
4, 2}, {5,5, 4, -0,5}};

A fórmula para calcular a distância entre dois pontos (x1, y1, z1) e (x2, y2, z2) é 2(x2 - x1)
2 2 2.
+ (y2 - y1) + (z2 - z1)
*8.13 (Classificar array bidimensional em ordem inversa) Escreva uma função para classificar um array
bidimensional em ordem inversa usando o seguinte cabeçalho:

void reverseSort(int m[][2], int numberOfRows)

A função executa uma classificação principalmente no primeiro elemento nas linhas e, em


seguida, no segundo elemento nas linhas, se os primeiros elementos forem iguais. Por
exemplo, a matriz {{9, 7}, {6, 12}, {9, 10}, {6, 7}, {6, 6}, {9, 6}} será classificada como {{ 9, 10},
{9, 7}, {9, 6}, {6, 12}, {6, 7}, {6, 6}}. Escreva um programa de teste que solicite ao usuário que
insira 12 pontos, invoque esta função e exiba os pontos classificados.
*8.14 (Menor linha e coluna) Escreva um programa que preencha aleatoriamente 0s e 1s em uma matriz 6
* 6, imprima a matriz e encontre a primeira linha e a primeira coluna com menos 1s. Aqui está
um exemplo de execução do programa:

110101
110111
100010
111110
110101
100100
O menor índice da linha: 2
O menor índice da coluna: 2
Machine Translated by Google

Exercícios de Programação 351

*8.15 (Álgebra: matriz inversa 2 * 2 ) O inverso de uma matriz quadrada A é denotado


A-1, tal que A × A-1 = I, onde I é a matriz identidade com todos os 1s na diagonal e

0 em todas as outras células. Por exemplo, o inverso da matriz3 J41R2é, J -2 1 R1,5


, J-0,5
1234R
ou seja,

* J1,5
-2 -0,5
1 R=J1001R

O inverso de uma matriz A 2 * 2 pode ser obtido usando a seguinte fórmula


se ad – bc != 0:

1
A = J abcd
A-1R= ad - bc J d -b -ca R

Implemente a seguinte função para obter uma inversa da matriz:

void inverse(const double A[][2], double inverseOfA[][2])

Escreva um programa de teste que solicite ao usuário que insira a, b, c, d para uma matriz e
exiba sua matriz inversa. Aqui está um exemplo de execução:

Insira a, b, c, d: 1 2 3 4 -2,0 1,0


1,5 -0,5

Insira a, b, c, d: 0,5 2 1,5 4,5 -6,0


2,6666666666666665 2,0
-0,6666666666666666

*8.16 (Geometria: mesma reta?) O Exercício de Programação 6.20 fornece uma função para
testar se três pontos estão na mesma reta. Escreva a seguinte função para testar
se todos os pontos na matriz de pontos estão na mesma linha.

const int TAMANHO =


2; bool sameLine(const double points[][SIZE], int numberOfPoints)

Escreva um programa que solicite ao usuário que insira cinco pontos e mostre se eles
estão na mesma linha. Aqui estão exemplos de execuções:

Insira cinco pontos: 3,4 2 6,5 9,5 2,3 2,3 5,5 5 -5 4 Os cinco pontos
não estão na mesma linha

Insira cinco pontos: 1 1 2 2 3 3 4 4 5 5


Os cinco pontos estão na mesma linha
Machine Translated by Google

352 Capítulo 8 Matrizes Multidimensionais


Seções 8.7–8.8
***8.17 (Localize o maior elemento) Escreva a seguinte função que encontra a localização
do maior elemento em uma matriz bidimensional.

void localizarLargest(const double a[][4], int location[])

O local é armazenado em um local de matriz unidimensional que contém dois


elementos. Esses dois elementos indicam os índices de linha e coluna do maior
elemento da matriz bidimensional. Escreva um programa de teste que solicite ao
usuário que insira uma matriz bidimensional 3 * 4 e exiba a localização do maior
elemento da matriz. Aqui está um exemplo de execução:

Insira a matriz: 23,5


35 2 10 4,5 3
45 3,5 35 44
5,5 9,6 A
localização do maior elemento está em (1, 2)

*8.18 (Álgebra: inversa da matriz 3 * 3 ) A inversa de uma matriz quadrada A é denotada


A-1, tal que A × A-1 = I, onde I é a matriz identidade com todos os 1s na diagonal

231
0,5
e 0 em todas as outras células. Por exemplo, o inverso da matriz4 C
5132
S 1é
1 0,5 -0,5
C -2
-1,5
0,50,5
1 S , ou seja, C 1

0,5
231 1 0,5 -0,5 010
4
21 5 3 S * C -2 0,50,5
1 -1,5
S = C 1 0 0 a31 a32 0a33
0 1SS

a21 a22 a23


O inverso de uma matriz 3 * 3 A = C pode sera13
a11 a12 obtido usando o

seguinte fórmula se A 0:

1
A-1 = a23a31 - a21a33 a11a33 - a13a31 a13a21 - a11a23
A C a22a33
a21a32 -- a23a32
a22a31 a13a32
a12a31 - a12a33 a12a23 -- a12a21
a11a32 a11a22 a13a22 S

a21 a22 a23 a11a22a33 + a31a12a23 + a13a21a32


UMA = a13
3 a11
a31
a12
a32 a33 3 =

- a13a22a31 - a11a23a32 - a33a21a12.

Implemente a seguinte função para obter uma inversa da matriz:

void inverse(const double A[][3], double inverseOfA[][3])


Machine Translated by Google

Exercícios de Programação 353

Escreva um programa de teste que solicite ao usuário que digite a11, a12, a13, a21, a21,
a23, a31, a32, a33, para uma matriz, e exibe sua matriz inversa. Aqui está um exemplo de execução:

Digite a11, a12, a13, a21, a22, a23, a31, a32, a33: 1 2 1 2 3 1 4 5 3
-2 0,5 0,5 1
0,5 -0,5 1 -1,5
0,5

Digite a11, a12, a13, a21, a22, a23, a31, a32, a33: 1 4 2 2 5 8 2 1 8
2,0 -1,875 1,375 0,0
0,25 -0,25 -0,5
0,4375 -0,1875

***8.19 (tsunami financeiro) Os bancos emprestam dinheiro uns aos outros. Em tempos económicos
difíceis, se um banco falir, poderá não conseguir pagar o empréstimo. O ativo total de um
banco é o seu saldo circulante mais os empréstimos a outros bancos. A Figura 8.7 é um
diagrama que mostra cinco bancos. Os saldos atuais dos bancos são 25, 125, 175, 75 e 181
milhões de dólares, respectivamente. A aresta direcionada do nó 1 para o nó 2 indica que o banco 1
empresta 40 milhões de dólares ao banco 2.

125
1
100,5 85
25
75
0
125 3

40
125
320,5 75

125 2 175
181 4

Figura 8.7 Os bancos emprestam dinheiro uns aos outros.

Se o ativo total de um banco estiver abaixo de um determinado limite, o banco não está seguro. Se
um banco não for seguro, o dinheiro que pediu emprestado não pode ser devolvido ao credor, e o
credor não pode contabilizar o empréstimo no seu activo total. Consequentemente, o credor também
pode estar inseguro, se o seu activo total estiver abaixo do limite. Escreva um programa para
encontrar todos os bancos inseguros. Seu programa lê a entrada da seguinte maneira. Primeiro ele
lê dois inteiros n e limite, onde n indica o número de bancos e limite é o ativo mínimo para manter
um banco seguro. Em seguida, ele lê n linhas que descrevem as informações para n
bancos com id de 0 a n-1. O primeiro número da linha é o saldo do banco, o segundo número indica
o número de bancos que pediram dinheiro emprestado ao banco e o restante são pares de dois
números. Cada par descreve um mutuário. O primeiro número do par é a identificação do mutuário
e o segundo é o valor emprestado. Suponha que o número máximo de bancos seja 100. Por
exemplo, a entrada para os cinco bancos na Figura 8.7 é a seguinte (o limite é 201):

5 201
25 2 1 100,5 4 320,5
125 2 2 40 3 85
175 2 0 125 3 75
75 1 0 125 181
1 2 125
Machine Translated by Google

354 Capítulo 8 Matrizes Multidimensionais

O ativo total do banco 3 é (75 + 125), que é inferior a 201. Portanto, o banco 3 não é seguro.
Depois que o banco 3 se torna inseguro, o ativo total do banco 1 passa a ser 125 + 40. Portanto, o
banco 1 também é inseguro. A saída do programa deve ser

Bancos inseguros são 3 1

(Dica: use uma matriz bidimensional mutuários para representar empréstimos. empréstimo[i]
[j] indica o empréstimo que o banco i empresta ao banco j. Quando o banco j se tornar inseguro, o
empréstimo[i][j] deverá ser definido como 0.)

***8.20 (jogo TicTacToe) Em um jogo de TicTacToe, dois jogadores se revezam marcando uma célula disponível em
uma grade 3 * 3 com seus respectivos tokens (X ou O). Quando um jogador coloca três fichas em
uma linha horizontal, vertical ou diagonal na grade, o jogo termina e esse jogador vence. Um empate
(sem vencedor) ocorre quando todas as células da grelha foram preenchidas com fichas e nenhum
jogador conseguiu uma vitória. Crie um programa para jogar TicTacToe. O programa solicita que o
primeiro jogador insira um token X e, em seguida, solicita que o segundo jogador insira um token O.

Sempre que um token é inserido, o programa exibe novamente o tabuleiro no console e determina o
status do jogo (vitória, empate ou inacabado). Aqui está um exemplo de execução:

-------------

| | | |
-------------

| | | |
-------------

| | | |
-------------

Insira uma linha (0, 1 ou 2) para o jogador X: 1


Insira uma coluna (0, 1 ou 2) para o jogador X: 1

-------------

| | | |
-------------

| |X| |
-------------

| | | |
-------------

Insira uma linha (0, 1 ou 2) para o jogador O: 1


Insira uma coluna (0, 1 ou 2) para o jogador O: 2

-------------

| | | |
-------------

| |X|Ó|
-------------

| | | |
-------------

Insira uma linha (0, 1 ou 2) para o jogador X:

...

-------------

|X| | |
-------------

| Ó | X| Ó |
-------------

| | |X|
-------------

X jogador venceu
Machine Translated by Google

Exercícios de Programação 355

**8.21 (Reconhecimento de padrão: quatro números iguais consecutivos) Escreva a seguinte função que testa
se uma matriz bidimensional tem quatro números consecutivos do mesmo valor, seja
horizontalmente, verticalmente ou diagonalmente.

bool éConsecutiveFour(int valores[][7])

Escreva um programa de teste que solicite ao usuário que insira o número de linhas e colunas de
uma matriz bidimensional e, em seguida, os valores da matriz e exiba verdadeiro se a matriz
contiver quatro números consecutivos com o mesmo valor. Caso contrário, exiba falso. Aqui estão
alguns exemplos de casos verdadeiros:

0103161 0103161 0103161 0103161


0168601 0168601 0168601 0168601
5621829 5521829 5621629 9621829
6561191 6561191 6566191 6961191
1361407 1561407 1361407 1391407
3333407 3533407 3633407 3339407

***8.22 (Jogo: conectar quatro) Conectar quatro é um jogo de tabuleiro para dois jogadores no qual os jogadores
colocam alternadamente discos coloridos em uma grade suspensa verticalmente de sete colunas
e seis linhas, conforme mostrado abaixo.

O objetivo do jogo é conectar quatro discos da mesma cor em uma linha, coluna ou diagonal
antes que seu oponente possa fazer o mesmo. O programa solicita que dois jogadores deixem
cair um disco VERMELHO (mostrado em azul escuro) ou AMARELO (mostrado em azul claro)
alternadamente. Sempre que um disco é descartado, o programa exibe novamente o tabuleiro no
console e determina o status do jogo (ganhar, empatar ou continuar). Aqui está um exemplo de
execução:

| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
----------------------

Solte um disco vermelho na coluna (0–6): 0


Machine Translated by Google

356 Capítulo 8 Matrizes Multidimensionais

||||||||||||||| | | | | |
| | | | |
| | | | |
| | | | |
| | | | |
|R| | | | | | |
----------------------

Coloque um disco amarelo na coluna (0–6): 3

|||||||||||||||||||||||||||||| | |
| |
| |
| |
| |
|R| | |S| | | |

...
...
...

Coloque um disco amarelo na coluna (0–6): 6


||||||||

||||||||

|| | |R| | | |
|| | |S|R|S| |
| | |R|S|S|S|S|
|R|S|R|S|R|R|R|
----------------------

O jogador amarelo venceu

*8.23 (Cidade central) Dado um conjunto de cidades, o ponto central é a cidade que tem a menor
distância total a todas as outras cidades. Escreva um programa que solicite ao usuário
que insira o número de cidades e a localização das cidades (coordenadas) e encontre a
cidade central e sua distância total para todas as outras cidades. Suponha que o número
máximo de cidades seja 20.

Insira o número de cidades: 5


Insira as coordenadas das cidades: 2,5 5 5,1 3 1 9 5,4 54 5,5 2,1
A cidade central está em (2,5, 5,0)
A distância total para todas as outras cidades é 60,81

*8.24 (Transposição de uma matriz) Escreva um programa que insira uma matriz e exiba a
transposta dessa matriz. A transposta de uma matriz é obtida convertendo todas as
linhas de uma determinada matriz em colunas e vice-versa.
*8.25 (matriz de Markov) Uma matriz n * n é chamada de matriz de Markov positiva, se cada
elemento for positivo e a soma dos elementos em cada coluna for 1. Escreva a seguinte
função para verificar se uma matriz é uma matriz de Markov:

const int TAMANHO = 3;


bool isMarkovMatrix(const double m[][SIZE]);
Machine Translated by Google

Exercícios de Programação 357

Escreva um programa de teste que solicite ao usuário que insira uma matriz 3 * 3 de valores duplos
e teste se é uma matriz de Markov. Aqui estão exemplos de execuções:

Insira uma matriz 3 por 3 linha por linha: 0,15 0,875


0,375
0,55 0,005 0,225
0,30 0,12 0,4
É uma matriz de Markov

Insira uma matriz 3 por 3 linha por linha: 0,95 -0,875


0,375
0,65 0,005 0,225
0,30 0,22 -0,4
Não é uma matriz de Markov

*8.26 (Classificação de linhas) Implemente a seguinte função para classificar as linhas em uma matriz
bidimensional. Uma nova matriz é retornada. A matriz original está intacta.

const int TAMANHO = 3;


void sortRows(const double m[][SIZE], double resultado[][SIZE]);

Escreva um programa de teste que solicite ao usuário que insira uma matriz 3 * 3 de valores duplos
e exiba uma nova matriz ordenada por linhas. Aqui está um exemplo de execução:

Insira uma matriz 3 por 3 linha por linha: 0,15 0,875


0,375
0,55 0,005 0,225
0,30 0,12 0,4
A matriz classificada por linha é 0,15
0,375 0,875 0,005 0,225
0,55 0,12 0,30 0,4

*8.27 (Classificação de colunas) Implemente a seguinte função para classificar as colunas em uma matriz
bidimensional. Uma nova matriz é retornada. A matriz original está intacta.

const int TAMANHO = 3;


void sortColumns(const double m[][SIZE], double resultado[][SIZE]);

Escreva um programa de teste que solicite ao usuário que insira uma matriz 3 * 3 de valores duplos
e exiba uma nova matriz ordenada por colunas. Aqui está um exemplo de execução:

Insira uma matriz 3 por 3 linha por linha: 0,15 0,875


0,375
0,55 0,005 0,225
0,30 0,12 0,4
A matriz classificada por coluna é 0,15
0,0050 0,225 0,3 0,12
0,375 0,55 0,875 0,4
Machine Translated by Google

358 Capítulo 8 Matrizes Multidimensionais

8.28 (Matrizes estritamente idênticas) Duas matrizes bidimensionais m1 e m2 são estritamente


idênticas se seus elementos correspondentes forem iguais. Escreva uma função que retorne
verdadeiro se m1 e m2 forem estritamente idênticos, usando o seguinte cabeçalho:

const int TAMANHO = 3;


bool equals(const int m1[][SIZE], const int m2[][SIZE]);

Escreva um programa de teste que solicite ao usuário que insira duas matrizes 3 * 3 de
inteiros e mostre se as duas são estritamente idênticas. Aqui estão os exemplos de execução.

Digite m1: 51 22 25 6 1 4 24 54 6
Insira m2: 51 22 25 6 1 4 24 54 6
Duas matrizes são estritamente idênticas

Digite m1: 51 25 22 6 1 4 24 54 6
Insira m2: 51 22 25 6 1 4 24 54 6
Duas matrizes não são estritamente idênticas

8.29 (Matrizes idênticas) Duas matrizes bidimensionais m1 e m2 são idênticas se tiverem o mesmo
conteúdo. Escreva uma função que retorne verdadeiro se m1 e m2 forem idênticos, usando
o seguinte cabeçalho:

const int TAMANHO = 3;


bool equals(const int m1[][SIZE], const int m2[][SIZE]);

Escreva um programa de teste que solicite ao usuário que insira duas matrizes 3 * 3 de
inteiros e mostre se as duas são idênticas. Aqui estão os exemplos de execução.

Digite m1: 51 25 22 6 1 4 24 54 6
Insira m2: 51 22 25 6 1 4 24 54 6
Duas matrizes são idênticas

Insira m1: 51 5 22 6 1 4 24 54 6
Insira m2: 51 22 25 6 1 4 24 54 6
Duas matrizes não são idênticas

*8.30 (Álgebra: resolver equações lineares) Escreva uma função que resolva o seguinte
Sistema 2 * 2 de equação linear:

a00x + a01y = b0 b0a11 - b1a01 = b1a00 - b0a10


x= e
a10x + a11y = b1 a00a11 - a01a10 a00a11 - a01a10

O cabeçalho da função é

const int TAMANHO = 2;


bool linearEquation(const double a[][SIZE], const double b[], double resultado[]);

A função retorna falso se a00a11 - a01a10 for 0; caso contrário, retorna verdadeiro.
Escreva um programa de teste que solicite ao usuário que insira a00, a01, a10, a11, b0, b1 e
exiba o resultado. Se a00a11 - a01a10 for 0, informe que "A equação não tem solução".
Um exemplo de execução é semelhante ao Exercício de Programação 3.3.
Machine Translated by Google

Exercícios de Programação 359

*8.31 (Geometria: ponto de interseção) Escreva uma função que retorne o ponto de interseção das
duas retas. O ponto de intersecção das duas linhas pode ser encontrado usando a fórmula
mostrada no Exercício de Programação 3.22. Suponha que (x1, y1) e (x2, y2) sejam os
dois pontos na linha 1 e (x3, y3) e (x4, y4) na linha 2. Se a equação não tiver soluções,
as duas linhas são paralelas. O cabeçalho da função é

const int TAMANHO = 2;


bool getIntersectingPoint(const double points[][SIZE], double result[]);

Os pontos são armazenados em uma matriz bidimensional de 4 * 2 pontos com (pontos[0]


[0], pontos[0][1]) para (x1, y1). A função retorna o ponto de intersecção e verdadeiro,
se as duas linhas forem paralelas. Escreva um programa que solicite ao usuário que
insira quatro pontos e exiba o ponto de intersecção. Veja o Exercício de Programação
3.22 para um exemplo de execução.
*8.32 (Geometria: área de um triângulo) Escreva uma função que retorne a área de um triângulo usando o seguinte
cabeçalho:

const int TAMANHO = 2;


double getTriangleArea(const double points[][SIZE]);

Os pontos são armazenados em uma matriz bidimensional 3 * 2 pontos com (pontos[0]


[0], pontos[0][1]) para (x1, y1). A área do triângulo pode ser calculada usando a fórmula do Exercício de
Programação 2.19. A função retorna 0, se os três pontos estiverem na mesma linha. Escreva um programa
que solicite ao usuário que insira duas linhas e exiba o ponto de intersecção. Aqui está um exemplo de
execução do programa:

Insira x1, y1, x2, y2, x3, y3: 2,5 2 5 -1,0 4,0 2,0
A área do triângulo é 2,25

Insira x1, y1, x2, y2, x3, y3: 2 2 4,5 4,5 6 6


Os três pontos estão na mesma linha

*8.33 (Geometria: subáreas poligonais) Um polígono convexo de 4 vértices é dividido em quatro


triângulos, conforme mostrado na Figura 8.8.

v2 (x2, y2)

v1 (x1, y1)
v3 (x3, y3)

v4 (x4, y4)

Figura 8.8 Um polígono de 4 vértices é definido por quatro vértices.

Escreva um programa que solicite ao usuário que insira as coordenadas dos quatro vértices e exiba as
áreas dos quatro triângulos em ordem crescente. Aqui está um exemplo de execução:

Insira x1, y1, x2, y2, x3, y3, x4, y4: -2,5 2 4 4 3 -2 -2 -3,5
As áreas são 1.390 1.517 8.082 8.333
Machine Translated by Google

360 Capítulo 8 Matrizes Multidimensionais

*8.34 (Geometria: ponto mais baixo à direita) Em geometria computacional, muitas vezes você precisa
encontrar o ponto mais baixo à direita em um conjunto de pontos. Escreva a seguinte função
que retorna o ponto mais baixo à direita em um conjunto de pontos.

const int TAMANHO = 2;


void getRightmostLowestPoint(const pontos duplos [][TAMANHO],
int numberOfPoints, double rightMostPoint[]);

Escreva um programa de teste que solicite ao usuário que insira as coordenadas de seis pontos
e exiba o ponto mais baixo à direita. Aqui está um exemplo de execução:

Insira 6 pontos: 1,5 2,5 -3 4,5 5,6 -7 6,5 -7 8 1 10 2,5


O ponto mais baixo à direita é (6,5, -7,0)

*8.35 (Jogo: encontre a célula invertida) Suponha que você receba uma matriz 6 * 6 preenchida com 0
e 1. Todas as linhas e todas as colunas têm o número par de 1s. Deixe o usuário inverter uma
célula (ou seja, mudar de 1 para 0 ou de 0 para 1) e escrever um programa para descobrir qual
célula foi invertida. Seu programa deve solicitar ao usuário que insira um array 6 * 6 com 0
e 1 e encontre a primeira linha r e a primeira coluna c onde a paridade é violada (ou seja, o
número de 1 não é par). A célula invertida está em (r, c). Aqui está um exemplo de execução:

Insira uma matriz 6 por 6 linha por linha: 1


1101111
1100010
1111111
1101111
0100001

A primeira linha e coluna onde a paridade é violada está em (0, 1)

*8.36 (Verificação de paridade) Escreva um programa que gere uma matriz bidimensional 9 * 9 preenchida
com 0s e 1s, exiba a matriz e verifique se cada linha e cada coluna possuem o número ímpar
de 1s.
Machine Translated by Google

CAPÍTULO

9
Objetos e Classes

Objetivos
n Descrever objetos e classes e usar classes para modelar objetos
(§9.2).

n Usar notações gráficas UML para descrever classes e objetos (§9.2).

n Demonstrar a definição de classes e a criação de objetos (§9.3).

n Para criar objetos usando construtores (§9.4).

n Para acessar campos de dados e invocar funções usando o membro do objeto


operador de acesso (.) (§9.5).

n Para separar uma definição de classe de uma implementação de classe (§9.6).

n Para evitar múltiplas inclusões de arquivos de cabeçalho usando a diretiva de


guarda de inclusão #ifndef (§9.7).

n Saber o que são funções inline em uma classe (§9.8).

n Declarar campos de dados privados com funções get e set apropriadas para
encapsulamento de campos de dados e tornar as classes fáceis de manter (§9.9).

n Compreender o âmbito dos campos de dados (§9.10).

n Aplicar abstração de classes para desenvolver software (§9.11).


Machine Translated by Google

362 Capítulo 9 Objetos e Classes

9.1 Introdução
A programação orientada a objetos permite desenvolver software em grande escala de maneira eficaz.
Chave
Apontar
Tendo aprendido o material dos capítulos anteriores, você será capaz de resolver muitos problemas de
por que POO? programação usando seleções, loops, funções e arrays. Entretanto, esses recursos não são suficientes para
o desenvolvimento de sistemas de software em larga escala. Este capítulo inicia a introdução da programação
orientada a objetos, que permitirá desenvolver sistemas de software em larga escala de maneira eficaz.

9.2 Definindo Classes para Objetos


Uma classe define as propriedades e comportamentos dos objetos.
Chave
Apontar
A programação orientada a objetos (OOP) envolve programação usando objetos. Um objeto representa uma
programação orientada a objetos entidade no mundo real que pode ser distintamente identificada. Por exemplo, um aluno, uma carteira, um
objeto círculo, um botão e até mesmo um empréstimo podem ser vistos como objetos. Um objeto tem identidade,
estado e comportamento únicos.

estado n O estado de um objeto (também conhecido como propriedades ou atributos) é representado por
campo de campos de dados com seus valores atuais. Um objeto círculo, por exemplo, possui um campo de
dados de propriedade dados, raio, que é a propriedade que caracteriza um círculo. Um objeto retângulo, por exemplo,
possui campos de dados, largura e altura, que são as propriedades que caracterizam um retângulo.

comportamento n O comportamento de um objeto (também conhecido como ações) é definido por funções. Invocar uma
função em um objeto é pedir ao objeto que execute uma ação. Por exemplo, você pode definir uma
função chamada getArea() para objetos circulares. Um objeto círculo pode invocar getArea() para
retornar sua área.

aula Objetos do mesmo tipo são definidos usando uma classe comum. Uma classe é um modelo, projeto ou
contrato contrato que define quais serão os campos de dados e funções de um objeto. Um objeto é uma instância de
uma classe. Você pode criar muitas instâncias de uma classe. A criação de uma instância é chamada de
instanciação instanciação. Os termos objeto e instância são frequentemente intercambiáveis. A relação entre classes e
objeto objetos é análoga à relação entre receitas de torta de maçã e tortas de maçã. Você pode fazer quantas tortas
instância de maçã quiser com uma única receita. A Figura 9.1 mostra uma classe chamada Circle e seus três objetos.

Nome da Classe: Círculo Um modelo de classe

Campos de dados:
raio é

Funções:
Getaria

Objeto Círculo 1 Objeto Círculo 2 Objeto Círculo 3 Três objetos da


classe Circle
Campos de dados: Campos de dados: Campos de dados:
raio é 1,0 raio é 25 raio é 125

Figura 9.1 Uma classe é um modelo para a criação de objetos.

aula Uma classe C++ usa variáveis para definir campos de dados e funções para definir comportamentos. Além
campo de dados disso, uma classe fornece funções de um tipo especial, conhecidas como construtores, que são invocadas
função quando um novo objeto é criado. Um construtor é um tipo especial de função. Os construtores podem
Machine Translated by Google

9.2 Definindo Classes para Objetos 363

executam qualquer ação, mas são projetados para executar ações de inicialização, como inicializar os campos de construtor
dados dos objetos. A Figura 9.2 mostra um exemplo da classe para objetos Circle .

classe Círculo

{ público:
// O raio deste círculo double radius;
Campo de dados

//Constrói um objeto circular


Círculo() {

raio = 1;
}
Construtores
//Constrói um objeto circular
Círculo(duplo novoRaio) {

raio = novoRaio;
}

//Retorna a área deste círculo


double getArea() { Função

raio de retorno * raio * 3,14159;

} };

Figura 9.2 Uma classe é um modelo que define objetos do mesmo tipo.

A ilustração de classes e objetos na Figura 9.1 pode ser padronizada usando a notação UML (Unified Modeling
Language), conforme mostrado na Figura 9.3. Isso é chamado de diagrama de classes UML ou simplesmente Diagrama de classes UML

diagrama de classes. O campo de dados é denotado como

dataFieldName: dataFieldType

O construtor é denotado como

ClassName(nomedoparâmetro: tipo de parâmetro)

A função é denotada como

functionName(nomedoparâmetro: tipoparâmetro): returnType

Diagrama de classes UML Círculo Nome da classe

+ raio: duplo Campos de dados

Construtores e
O símbolo + significa público +Círculo()
funções
+Círculo(newRadius: duplo) +getArea():
duplo

Notação UML
círculo1: Círculo círculo2: Círculo círculo3: Círculo
para objetos
raio 1,0 raio 25 raio 125

Figura 9.3 Classes e objetos podem ser representados usando notações UML.
Machine Translated by Google

364 Capítulo 9 Objetos e Classes

9.3 Exemplo: Definindo Classes e Criando Objetos


Classes são definições para objetos e objetos são criados a partir de classes.
Chave
Apontar
A Listagem 9.1 é um programa que demonstra classes e objetos. Ele constrói três objetos circulares
com raios 1,0, 25 e 125 e exibe o raio e a área de cada um. Altere o raio do segundo objeto para
100 e exiba seu novo raio e área.

Listagem 9.1 TestCircle.cpp


Nota de vídeo 1 #include <iostream>
Usar aulas 2 usando namespace std;

definir classe 3 4 classe Círculo


{
5 6 público:
// O raio deste círculo
campo de dados 7 8 raio duplo ;

9 //Constrói um objeto círculo padrão


construtor sem argumento 10 Círculo()
11 12 {
13 raio = 1;
14 }
15
16 //Constrói um objeto circular
segundo construtor 17 Círculo(duplo newRadius)
18 {
19 raio = novoRaio;
20 }
21
22 //Retorna a área deste círculo
função 23 getArea duplo ()
24 {
raio de retorno * raio * 3,14159;
25 }
não omita 26 27}; //Deve colocar ponto e vírgula aqui

função principal 28 29 int principal()


30 {
criando objeto 31 Círculo círculo1(1,0);
criando objeto 32 Círculo círculo2(25);
criando objeto 33 Círculo círculo3(125);
34
35 cout << "A área do círculo de raio"
raio de acesso 36 << círculo1.radius << cout "é" << círculo1.getArea() << endl;
invocando getArea 37 << "A área do círculo de raio"
38 << círculo2.radius << cout "é" << círculo2.getArea() << endl;
39 << "A área do círculo de raio"
40 << círculo3.radius << "é" << círculo3.getArea() << endl;
41
42 // Modifica o raio do círculo
modificar raio 43 círculo2.raio = 100;
44 cout << "A área do círculo de raio"
<< círculo2.radius << "é" << círculo2.getArea() << endl;
45
46 retornar 0;
47 48}
Machine Translated by Google

9.3 Exemplo: Definindo Classes e Criando Objetos 365

A área do círculo de raio 1 é 3,14159


A área do círculo de raio 25 é 1963,49
A área do círculo de raio 125 é 49.087,3
A área do círculo de raio 100 é 31415,9

A classe é definida nas linhas 4–27. Não esqueça que o ponto e vírgula (;) na linha 27 é obrigatório. finalizando a definição da classe

A palavra-chave public na linha 6 indica que todos os campos de dados, construtores e funções podem público
ser acessados a partir dos objetos da classe. Se você não usar a palavra-chave pública , a visibilidade
será privada por padrão. A visibilidade privada será introduzida na Seção 9.8. privado por padrão
A função principal cria três objetos chamados círculo1, círculo2 e círculo3 com raio 1,0,
25 e 125, respectivamente (linhas 31–33). Esses objetos têm raios diferentes, mas as
mesmas funções. Portanto, você pode calcular suas respectivas áreas usando a função getArea() .
Os campos de dados podem ser acessados através do objeto usando círculo1.radius,
círculo2.radius e círculo3.radius, respectivamente. As funções são invocadas usando
Circle1.getArea(), Circle2.getArea() e Circle3.getArea(), respectivamente.
Esses três objetos são independentes. O raio do círculo2 é alterado para 100 na linha 43.
O novo raio e área do objeto são exibidos nas linhas 44–45.
Como outro exemplo, considere os aparelhos de TV. Cada TV é um objeto com estado (canal atual,
nível de volume atual, ligado ou desligado) e comportamentos (mudar de canal, ajustar volume, ligar/
desligar). Você pode usar uma classe para modelar aparelhos de TV. O diagrama UML para a classe é
mostrado na Figura 9.4.

televisão

canal: interno O canal atual (1 a 120) desta TV.


nível de volume: int O nível de volume atual (1 a 7) desta TV.
ligado: booleano Indica se esta TV está ligada/desligada.

+TV() Constrói um objeto TV padrão.


+ativar(): vazio Liga esta TV.

+desligar(): vazio Desliga esta TV.

+setChannel(newChannel: int): void Define um novo canal para esta TV.

+setVolume(novoNívelVolume: int): void Define um novo nível de volume para esta TV.

+canalUp(): vazio Aumenta o número do canal em 1.


+canalDown(): vazio Diminui o número do canal em 1.
+aumentarvolume(): vazio Aumenta o nível do volume em 1.
+diminuir volume(): vazio Diminui o nível do volume em 1.

Figura 9.4 A classe TV modela aparelhos de TV.

A Listagem 9.2 fornece um programa que define a classe TV e utiliza a classe TV para criar dois objetos.

Listagem 9.2 TV.cpp


1 #include <iostream>
2 usando namespace std;

TV de 3 4 classes definir uma classe


5{
6 público:
Machine Translated by Google

366 Capítulo 9 Objetos e Classes

campos de dados canal interno ;


int nível de volume; // O nível de volume padrão é 1
bool ativado; // Por padrão a TV está desligada
7
construtor 8 TELEVISÃO()

9 {
10 canal = 1; // O canal padrão é 1
11 nível de volume = 1; // O nível de volume padrão é 1
12 ativado = falso; // Por padrão a TV está desligada
13 }
14
ligar a TV 15 void turnOn()
16 {
17 ativado = verdadeiro;
18 }
19
desligue a televisão 20 anular desligar ()
21 {
22 ativado = falso;
23 }
definir um novo canal 24
25 void setChannel(int novoCanal)
26 {
27 if (em && newChannel >= 1 && newChannel <= 120)
28 canal = novoCanal;
29 }
30
definir um novo volume 31 void setVolume(int novoVolumeLevel)
32 {
33 if (on && novoVolumeLevel >= 1 && novoVolumeLevel <= 7)
34 nível de volume = novoNível de volume;
35 }
36
aumentar canal 37 vazio canalUp()
38 39 40 41{
42 if (no && canal < 120)
43 canal++;
44 }
45
diminuir canal 46 vazio canalDown()
47 {
48 if (no && canal > 1)
49 canal--;
50 }
51
aumentar o volume 52 void volumeUp()
53 {
54 if (em && nível de volume < 7)
55 nível de volume++;
56 }
57
diminuir o volume 58 void volumeDown()
59 {
60 if (em && nível de volume > 1)
61 } nível de volume--;
62
63};
64
função principal 65 int principal()
66 {
Machine Translated by Google

9.4 Construtores 367


67 televisão tv1; criar uma televisão

68 tv1.turnOn(); ligar

69 tv1.setChannel(30); definir um novo canal


70 tv1.setVolume(3); definir um novo volume
71
72 televisão tv2; criar uma televisão

73 tv2.turnOn(); ligar
74 tv2.canalUp(); aumentar canal
75 tv2.canalUp();
76 tv2.volumeUp(); aumentar o volume
77
78 cout << "o canal da tv1 é " << tv1.canal estado de exibição
79 << "e o nível de volume é" << tv1.volumeLevel << endl;
80 cout << "o canal da tv2 é " << tv2.canal
81 << "e o nível do volume é" << tv2.volumeLevel << endl;
82
83 retornar 0;
84 }

o canal da tv1 é 30 e o nível de volume é 3


o canal da tv2 é 3 e o nível de volume é 2

Observe que o canal e o nível de volume não serão alterados se a TV não estiver ligada. Antes de alterar um
canal ou nível de volume, os valores atuais são verificados para garantir que o canal e o nível de volume estejam
dentro da faixa correta.
O programa cria dois objetos nas linhas 67 e 72 e invoca as funções nos objetos para executar ações para
definir canais e níveis de volume e para aumentar canais e volumes. O programa exibe o estado dos objetos nas
linhas 78–81. As funções são invocadas usando uma sintaxe como tv1.turnOn() (linha 68). Os campos de dados
são acessados usando uma sintaxe como tv1.channel (linha 78).

Esses exemplos deram uma ideia de classes e objetos. Você pode ter muitas dúvidas sobre construtores e
objetos, acessar campos de dados e invocar funções de objetos. As seções a seguir discutem essas questões
em detalhes.

9.4 Construtores
Um construtor é invocado para criar um objeto.
Chave
Apontar
Os construtores são um tipo especial de função, com três peculiaridades:

n Os construtores devem ter o mesmo nome da própria classe. nome do construtor

n Construtores não possuem um tipo de retorno – nem mesmo void. nenhum tipo de retorno

n Construtores são invocados quando um objeto é criado. Os construtores desempenham o papel de invocar construtor
inicializar objetos.

O construtor tem exatamente o mesmo nome da classe definidora. Assim como as funções regulares, os
construtores podem ser sobrecarregados (ou seja, vários construtores com o mesmo nome, mas assinaturas sobrecarga do construtor
diferentes), facilitando a construção de objetos com diferentes conjuntos de valores de dados.
É um erro comum colocar a palavra-chave void na frente de um construtor. Por exemplo, sem vazio

círculo vazio () {

}
Machine Translated by Google

368 Capítulo 9 Objetos e Classes

A maioria dos compiladores C++ relatará um erro, mas alguns tratarão isso como uma função regular, não
como construtor.
inicializar campo de dados Os construtores servem para inicializar campos de dados. O campo de dados radius não possui valor inicial, portanto
deve ser inicializado no construtor (linhas 13 e 19 da Listagem 9.1). Observe que uma variável (local ou global) pode ser
declarada e inicializada em uma instrução, mas como membro de uma classe, um campo de dados não pode ser inicializado
quando é declarado. Por exemplo, seria errado substituir a linha 8 na Listagem 9.1 por

raio duplo = 5; // Errado para declaração do campo de dados

Uma classe normalmente fornece um construtor sem argumentos (por exemplo, Circle()). Tal con-
construtor sem argumento O construtor é chamado de construtor sem argumento ou sem argumento.
Uma classe pode ser definida sem construtores. Nesse caso, um construtor sem argumentos com corpo vazio é definido
construtor padrão implicitamente na classe. Chamado de construtor padrão, ele é fornecido automaticamente somente se nenhum construtor
estiver definido explicitamente na classe.
lista de inicializadores de construtores Os campos de dados podem ser inicializados no construtor usando uma lista de inicializadores na seguinte sintaxe:

ClassName (lista de parâmetros)


: datafield1(value1), datafield2(value2) // Lista de inicializadores
{
//Declarações adicionais se necessário
}

A lista de inicializadores inicializa datafield1 com valor1 e datafield2 com valor2.

Por exemplo,

Círculo::Círculo() : Círculo::Círculo() {
raio(1) igual a
raio = 1;
{} }

(a) (b)

O construtor em (b), que não usa uma lista de inicializadores, é na verdade mais intuitivo que aquele em (a). No entanto,
o uso de uma lista de inicializadores é necessário para inicializar campos de dados de objetos que não possuem um construtor
sem argumentos. Este é um tópico avançado abordado no Suplemento IV.E no Site Complementar.

9.5 Construindo e Utilizando Objetos


Os dados e funções de um objeto podem ser acessados através do operador ponto (.) através do nome do objeto.
Chave
Apontar

construir objetos Um construtor é invocado quando um objeto é criado. A sintaxe para criar um objeto usando o construtor no-arg é

invocar construtor sem argumento NomeDaClasse nomeDoobjeto;

Por exemplo, a declaração a seguir cria um objeto chamado círculo1 invocando o construtor sem argumentos da classe
Circle .

Círculo círculo1;

A sintaxe para criar um objeto usando um construtor com argumentos é

construir com argumentos NomeDaClasse nomeDoobjeto(argumentos);


Machine Translated by Google

9.5 Construindo e Utilizando Objetos 369

Por exemplo, a declaração a seguir cria um objeto chamado círculo2 invocando o método
Construtor da classe Circle com raio especificado 5.5.

Círculo círculo2(5,5);

No termo OOP, o membro de um objeto refere-se aos seus campos de dados e funções. Os objetos recém-
criados são alocados na memória. Depois que um objeto é criado, seus dados podem ser acessados e suas operador ponto

funções invocadas usando o operador ponto (.), também conhecido como operador de acesso ao membro do objeto:operador de acesso de membro

n objectName.dataField faz referência a um campo de dados no objeto.

n objectName.function(arguments) invoca uma função no objeto.

Por exemplo, círculo1.radius faz referência ao raio em círculo1 e círculo1.getArea()


invoca a função getArea em círculo1. Funções são invocadas como operações em objetos.
O raio do campo de dados é conhecido como variável de membro de instância ou simplesmente variável de variável de instância

instância, porque depende de uma instância específica. Pelo mesmo motivo, a função getArea é chamada de função de membro

função membro de instância ou função de instância, porque você pode invocá-la somente em uma instância função de instância

específica. O objeto no qual uma função de instância é invocada é chamado de objeto de chamada.
objeto de chamada

Observação

Ao definir uma classe personalizada, coloque a primeira letra de cada palavra em maiúscula no nome
de uma classe — por exemplo, os nomes de classe Círculo, Retângulo e Mesa. Os nomes das convenção de nomenclatura de classe

classes na biblioteca C++ são nomeados em letras minúsculas. Os objetos são nomeados como variáveis. convenção de nomenclatura de objetos

Os seguintes pontos sobre classes e objetos são dignos de nota:

n Você pode usar tipos de dados primitivos para definir variáveis. Você também pode usar nomes de classes para
declarar nomes de objetos. Nesse sentido, uma classe também é um tipo de dados. classe é um tipo

n Em C++, você pode usar o operador de atribuição = para copiar o conteúdo de um objeto para outro. cópia para membros
Por padrão, cada campo de dados de um objeto é copiado para sua contraparte no outro objeto. Por
exemplo,

círculo2 = círculo1;

copia o raio em círculo1 para círculo2. Após a cópia, círculo1 e círculo2


ainda são dois objetos diferentes, mas têm o mesmo raio.

n Os nomes dos objetos são como nomes de arrays. Depois que um nome de objeto é declarado, ele
representa um objeto. Não pode ser reatribuído para representar outro objeto. Nesse sentido, o nome
de um objeto é uma constante, embora o conteúdo do objeto possa mudar. A cópia membro pode nome do objeto constante
alterar o conteúdo de um objeto, mas não seu nome.

n Um objeto contém dados e pode invocar funções. Isso pode levar você a pensar que um objeto é muito
grande. Mas não é. Os dados são armazenados fisicamente em um objeto, mas as funções não.
Como as funções são compartilhadas por todos os objetos da mesma classe, o compilador cria
apenas uma cópia para compartilhamento. Você pode descobrir o tamanho real de um objeto usando
a função sizeof . Por exemplo, o código a seguir exibe o tamanho dos objetos círculo1 e círculo2. tamanho do objeto

Seu tamanho é 8, pois o raio do campo de dados é duplo, o que ocupa 8 bytes.

Círculo círculo1;
Círculo círculo2(5,0);

cout << sizeof(circle1) << endl;


cout << sizeof(circle2) << endl;
Machine Translated by Google

370 Capítulo 9 Objetos e Classes

Normalmente você cria um objeto nomeado e posteriormente acessa seus membros através de seu nome.
Ocasionalmente você pode criar um objeto e usá-lo apenas uma vez. Nesse caso, você não precisa nomeá-lo.
objetos anônimos Esses objetos são chamados de objetos anônimos.
A sintaxe para criar um objeto anônimo usando o construtor no-arg é

Nome da classe()

A sintaxe para criar um objeto anônimo usando o construtor com argumentos é

NomeDaClasse(argumentos)

Por exemplo,

círculo1 = Círculo();

cria um objeto Circle usando o construtor no-arg e copia seu conteúdo para Circle1.

círculo1 = Círculo(5);

cria um objeto Circle com raio 5 e copia seu conteúdo para círculo1.
Por exemplo, o código a seguir cria objetos Circle e invoca seu getArea()
função.

cout << "A área é " cout << Círculo().getArea() << endl; <<
<< "A área é " Círculo(5).getArea() << endl;

Como você pode ver nesses exemplos, você pode criar um objeto anônimo se ele não for referenciado
posteriormente.

Cuidado
construtor sem argumento Observe que em C++, para criar um objeto anônimo usando o construtor no-arg, você deve
adicionar parênteses após o nome do construtor (por exemplo, Circle()). Para criar um
objeto nomeado usando o construtor sem argumento, você não pode usar os parênteses
após o nome do construtor (por exemplo, você usa Círculo círculo1 em vez de Círculo círculo1()).
Esta é a sintaxe necessária, que você apenas precisa aceitar.

9.1 Descreva o relacionamento entre um objeto e sua classe definidora. Como você define uma classe?
ÿVerificação de ponto Como você declara e cria um objeto?

9.2 Quais são as diferenças entre construtores e funções?

9.3 Como você cria um objeto usando um construtor sem argumentos? Como você cria um
objeto usando um construtor com argumentos?

9.4 Uma vez declarado um nome de objeto, ele pode ser reatribuído para referenciar outro
objeto?

9.5 Supondo que a classe Circle esteja definida como na Listagem 9.1, mostre a impressão do seguinte
código:

Círculo c1(5);
Círculo c2(6);
c1=c2;
""
cout << c1.radius << << c2.radius << endl;

9.6 O que há de errado no código a seguir? (Use a classe Circle definida na Listagem 9.1, TestCircle.cpp.)
Machine Translated by Google

9.6 Separando Definição de Classe da Implementação 371

int principal() int principal()


{ {
Círculo c1(); Círculo c1(5);
cout << c1.getRadius() << endl; Círculo c1(6);
retornar 0; retornar 0;
} }

(a) (b)

9.7 O que há de errado no código a seguir?


classe Círculo
{
público:
Círculo()
{
}
raio duplo = 1;
};

9.8 Qual das seguintes afirmações está correta?

Círculo c;

Círculo c();

9.9 Suponha que as duas seguintes sejam declarações independentes. Eles estão corretos?

Círculo c;

Círculo c = Círculo();

Nota de vídeo

9.6 Separando Definição de Classe da Implementação Definição de classe separada

Separar a definição de classe da implementação de classe torna a classe fácil de manter.


Chave
Apontar

C++ permite separar a definição de classe da implementação. A definição da classe descreve o contrato da
classe e a implementação da classe executa o contrato. A definição da classe simplesmente lista todos os
campos de dados, protótipos de construtores e protótipos de funções.
A implementação da classe implementa os construtores e funções. A definição e implementação da classe
podem estar em dois arquivos separados. Ambos os arquivos devem ter o mesmo nome, mas nomes de
extensão diferentes. O arquivo de definição de classe tem um nome de extensão .h (h significa cabeçalho) e
o arquivo de implementação de classe tem um nome de extensão .cpp.
As Listagens 9.3 e 9.4 apresentam a definição e implementação da classe Circle .

Listagem 9.3 Círculo.h


1 classe Círculo
2{
3 público:
4 // O raio deste círculo
5 raio duplo ; campo de dados

6
7 //Constrói um objeto círculo padrão
8 Círculo(); construtor sem argumento
Machine Translated by Google

372 Capítulo 9 Objetos e Classes

9 //Constrói um objeto circular


segundo construtor 10 Círculo(duplo);
11
12 13 //Retorna a área deste círculo
protótipo de função 14 double getArea();
ponto e vírgula obrigatório 15};

Cuidado
não omita ponto e vírgula É um erro comum omitir o ponto e vírgula (;) no final da definição da classe.

Listagem 9.4 Circle.cpp


incluir definição de classe 1 #include "Círculo.h"

2 3 // Construa um objeto circular padrão


implementar construtor 4 Círculo::Círculo()
{
raio = 1;
5 6 7}

8 9 // Construa um objeto circular


implementar construtor 10 Círculo::Círculo(double newRadius)
11 {
12 raio = novoRaio;
13}
14
15 // Retorna a área deste círculo
implementar função 16 círculo duplo ::getArea()
17 {
18 raio de retorno * raio * 3,14159;
19}

operador de resolução de O símbolo :: , conhecido como operador de resolução de escopo binário, especifica o escopo de um
escopo binário membro da classe em uma classe.

Aqui, Circle:: precedendo cada construtor e função na classe Circle informa ao


compilador que esses construtores e funções são definidos na classe Circle .
A Listagem 9.5 é um programa que utiliza a classe Circle . Tal programa que usa a classe é
cliente muitas vezes referido como um cliente da classe.

Listagem 9.5 TestCircleWithHeader.cpp


1 #include <iostream>
incluir definição de classe 2 #include "Círculo.h"
3 usando namespace std;

4 5 int principal()
6{
construir círculo Círculo círculo1;
construir círculo Círculo círculo2(5,0);
7
8 cout << "A área do círculo de raio"
9 << círculo1.radius << cout "é" << círculo1.getArea() << endl;
10 << "A área do círculo de raio"
11 << círculo2.radius << "é" << círculo2.getArea() << endl;
12
13 // Modifica o raio do círculo
definir um novo raio 14 15 16 círculo2.raio = 100;
Machine Translated by Google

9.6 Separando Definição de Classe da Implementação 373

17 cout << "A área do círculo de raio"


18 << círculo2.radius << "é" << círculo2.getArea() << endl;
19
retornar 0;
20 21 }

A área do círculo de raio 1 é 3,14159


A área do círculo de raio 5 é 78,5397
A área do círculo de raio 100 é 31415,9

Existem pelo menos dois benefícios em separar uma definição de classe da implementação. por que separação?

1. Oculta a implementação da definição. Você pode ficar à vontade para alterar a implementação.
O programa cliente que utiliza a classe não precisa ser alterado, desde que a definição não seja alterada.

2. Como fornecedor de software, você pode simplesmente fornecer ao cliente o arquivo de cabeçalho e o código-objeto
da classe, sem revelar o código-fonte para implementação da classe. Isso protege a propriedade intelectual do
fornecedor do software.

Observação

Para compilar um programa principal a partir da linha de comando, você precisa adicionar todos os compilar a partir da linha de comando
seus arquivos de suporte no comando. Por exemplo, para compilar TestCircleWithDefinition.cpp
usando um compilador GNU C++, o comando é

g++ Circle.h Circle.cpp TestCircleWithHeader.cpp –o Principal

Observação

Se o programa principal usar outros programas, todos esses arquivos de origem do programa
deverão estar presentes no painel do projeto no IDE. Caso contrário, você poderá obter erros de compilar do IDE
vinculação. Por exemplo, para executar TestCircleWithHeader.cpp, você precisa colocar
TestCircleWithHeader.cpp, Circle.cpp e Circle.h no painel do projeto em Visual C++, conforme mostrado na Figura 9.5.

Adicionar arquivos .h

aqui

Adicionar .cpp
arquivos aqui

Figura 9.5 Para que o programa seja executado, você precisa colocar todos os arquivos dependentes no painel do projeto.
Machine Translated by Google

374 Capítulo 9 Objetos e Classes

9.10 Como você separa a definição de classe da implementação?


ÿVerificação de ponto
9.11 Qual é a saída do código a seguir? (Use a classe Circle definida em
Listagem 9.3, Círculo.h.)

int principal() int principal()


{ {
Círculo c1; cout << Círculo(8).getArea() << endl;
Círculo c2(6);
c1=c2;
cout << c1.getArea() << endl; retornar 0;
}
retornar 0;
}

(a) (b)

9.7 Prevenindo Múltiplas Inclusões


Chave
A proteção de inclusão evita que os arquivos de cabeçalho sejam incluídos várias vezes.
Apontar
É um erro comum incluir, inadvertidamente, o mesmo arquivo de cabeçalho em um programa várias vezes. Suponha que
Head.h inclua Circle.h e TestHead.cpp inclua Head.h e Circle.h, conforme mostrado nas Listagens 9.6 e 9.7.

Listagem 9.6 Head.h


incluir Círculo.h 1 #include "Círculo.h"
2 // Outro código em Head.h omitido

Listagem 9.7 TestHead.cpp


incluir Círculo.h 1 #include "Círculo.h"
incluir Head.h 2 #include "Cabeça.h"

3 4 int principal()
5{
6 // Outro código em TestHead.cpp omitido
7}

Se você compilar TestHead.cpp, receberá um erro de compilação indicando que existem várias definições para
Circle. O que há de errado aqui? Lembre-se de que o pré-processador C++ insere o conteúdo do arquivo de cabeçalho na
posição onde o cabeçalho está incluído. Circle.h está incluído na linha 1. Como o arquivo de cabeçalho para Circle
também está incluído em Head.h (veja a linha 1 na Listagem 9.6), o pré-processador adicionará a definição para a classe
Circle outra vez como resultado da inclusão de Head . .h em TestHead.cpp, o que causa erros de inclusão múltipla.

A diretiva C++ #ifndef juntamente com a diretiva #define podem ser usadas para evitar que um arquivo de cabeçalho
guarda de inclusão seja incluído várias vezes. Isso é conhecido como guarda de inclusão. Para fazer isso funcionar, você deve adicionar três
linhas ao arquivo de cabeçalho. As três linhas estão destacadas na Listagem 9.8.

Listagem 9.8 CircleWithInclusionGuard.h


o símbolo está definido? 1 #ifndef CÍRCULO_H
definir símbolo 2 #define CÍRCULO_H

3 4 classe Círculo
5{
6 público:
Machine Translated by Google

9.8 Funções Inline nas Classes 375

// O raio deste círculo


raio duplo ;
7
8 //Constrói um objeto círculo padrão
9 Círculo();
10
11 //Constrói um objeto circular
12 Círculo(duplo);
13 14 15
16 //Retorna a área deste círculo
17 double getArea();
18};
19
20 #endif fim de #ifndef

Lembre-se de que as instruções precedidas pelo sinal de cerquilha (#) são diretivas de pré-processador.
Eles são interpretados pelo pré-processador C++. A diretiva do pré-processador #ifndef significa “se não
definido”. A linha 1 testa se o símbolo CIRCLE_H já está definido. Caso contrário, defina o símbolo na linha 2
usando a diretiva #define e o restante do arquivo de cabeçalho será incluído; caso contrário, o restante do
arquivo de cabeçalho será ignorado. A diretiva #endif é necessária para indicar o fim do arquivo de cabeçalho.
Para evitar erros de inclusão múltipla, defina uma classe usando o seguinte modelo e convenção
para nomear o símbolo:

#ifndef ClassName_H
#defineNomedaClasse_H _

Um cabeçalho de classe para a classe chamada ClassName

#fim se

Se você substituir Circle.h por CircleWithInclusionGuard.h nas Listagens 9.6 e 9.7, o programa não terá o erro
de inclusão múltipla.

9.12 O que pode causar erros de inclusão múltipla? Como você evita múltiplas inclusões de arquivos
de cabeçalho? ÿVerificação de ponto

9.13 Para que serve a diretiva #define ?

9.8 Funções Inline em Classes


Você pode definir funções curtas como funções embutidas para melhorar o desempenho.
Chave
Apontar
A Seção 6.10, “Funções Inline”, apresentou como melhorar a eficiência da função usando funções inline.
Quando uma função é implementada dentro de uma definição de classe, ela se torna automaticamente uma
função embutida. Isso também é conhecido como definição embutida. Por exemplo, na definição a seguir para definição embutida

a classe A, o construtor e a função f1 são automaticamente funções embutidas, mas a função f2 não é.

classe A
{
público:
A()
{
// Faça alguma coisa;
}

duplo f1()
{
//Retorna um número
}
Machine Translated by Google

376 Capítulo 9 Objetos e Classes

duplo f2();
};

Existe outra maneira de definir funções embutidas para classes. Você pode definir funções embutidas no arquivo
de implementação da classe. Por exemplo, para definir a função f2 como uma função inline, preceda a palavra-chave
inline no cabeçalho da função da seguinte forma:

//Implementa a função como inline


duplo embutido A::f2()
{
//Retorna um número
}

Conforme observado na Seção 6.10, funções curtas são boas candidatas para funções inline, mas funções longas
funções não são.

9.14 Como você implementa todas as funções inline na Listagem 9.4, Circle.cpp?
ÿVerificação de ponto

9.9 Encapsulamento de Campo de Dados


Tornar os campos de dados privados protege os dados e facilita a manutenção da classe.
Chave
Apontar
O raio dos campos de dados na classe Circle da Listagem 9.1 pode ser modificado diretamente (por exemplo,
círculo1.radius = 5). Esta não é uma boa prática – por dois motivos:

n Primeiro, os dados podem ser adulterados.

n Em segundo lugar, torna a classe difícil de manter e vulnerável a bugs. Suponha que você queira modificar
a classe Circle para garantir que o raio seja não negativo depois que outros programas já tiverem usado
a classe. Você precisa alterar não apenas a classe Circle , mas também os programas que usam a classe
Circle . Isto ocorre porque os clientes podem ter modificado o raio diretamente (por exemplo,
myCircle.radius = -5).

Para evitar modificações diretas nas propriedades, você deve declarar o campo de dados como privado, usando
encapsulamento de campo de dados a palavra-chave private . Isso é conhecido como encapsulamento de campo de dados. Tornando o campo de dados
privado do raio privado na classe Circle , você pode definir a classe da seguinte forma:

classe Círculo
{
público:
Círculo();
Círculo(duplo);
double getArea();

privado:
raio duplo ;
};

Um campo de dados privado não pode ser acessado por um objeto através de uma referência direta fora da
classe que define o campo privado. Mas muitas vezes um cliente precisa recuperar e/ou modificar um campo de
dados. Para tornar um campo de dados privado acessível, forneça uma função get para retornar o valor do campo.
Para permitir que um campo de dados privados seja atualizado, forneça uma função set para definir um novo valor.

Observação

acessador Coloquialmente, uma função get é chamada de acessador e uma função set é chamada
mutador de modificador.
Machine Translated by Google

9.9 Encapsulamento de Campo de Dados 377

Uma função get possui a seguinte assinatura:

returnType getPropertyName()

Se returnType for bool, por convenção a função get deverá ser definida da seguinte forma: acessador bool

bool éPropertyName()

Uma função set tem a seguinte assinatura:

void setPropertyName(dataType propertyValue)

Vamos criar uma nova classe de círculo com um raio de campo de dados privado e suas funções de acesso e
modificador associadas. O diagrama de classes é mostrado na Figura 9.6. A nova classe círculo está definida na
Listagem 9.9.

Círculo

O sinal - indica
-raio: duplo O raio deste círculo (padrão: 1,0).
modificador privado

+Círculo() Constrói um objeto circular padrão.


+Círculo(raio: duplo) Constrói um objeto circular com o raio especificado.

+getRadius(): duplo Retorna o raio deste círculo.

+setRadius(raio: duplo): vazio Define um novo raio para este círculo.

+getArea(): duplo Retorna a área deste círculo.

Figura 9.6 A classe Circle encapsula propriedades do círculo e fornece get/set e outras funções.

Listagem 9.9 CircleWithPrivateDataFields.h


1 #ifndef CÍRCULO_H
2 #define CÍRCULO_H

3 4 classe Círculo
{
5 6 público: público
7 Círculo();
8 Círculo(duplo);
double getArea();
9 10 double getRadius(); função de acesso
11 void setRadius(duplo); função mutadora
12
13 privado: privado
14 raio duplo ;
15};
16
17 #endif

A Listagem 9.10 implementa o contrato de classe especificado no arquivo de cabeçalho da Listagem 9.9.

Listagem 9.10 CircleWithPrivateDataFields.cpp


1 #include "CircleWithPrivateDataFields.h" incluir arquivo de cabeçalho
2
3 // Construa um objeto círculo padrão
4 Círculo::Círculo() construtor
Machine Translated by Google

378 Capítulo 9 Objetos e Classes


5{
raio = 1;
6 7}
8
9 // Construa um objeto circular
construtor 10 Círculo::Círculo(double newRadius)
11 {
12 raio = novoRaio;
13}
14
15 // Retorna a área deste círculo
obter área 16 círculo duplo ::getArea()
17 {
18 raio de retorno * raio * 3,14159;
19}
20
21 // Retorna o raio deste círculo
obter raio 22 círculo duplo ::getRadius()
23 {
24 raio de retorno ;
25}
26
27 // Defina um novo raio
definir raio 28 círculo vazio ::setRadius(double newRadius)
29 {
raio = (novoRaio >= 0) ? novoRaio: 0;
30 31}

A função getRadius() (linhas 22–25) retorna o raio, e a função setRadius (newRadius)


(linha 28–31) define um novo raio no objeto. Se o novo raio for negativo, 0 será definido
como o raio do objeto. Como essas funções são as únicas maneiras de ler e modificar o
raio, você tem controle total sobre como a propriedade radius é acessada. Se for
necessário alterar a implementação das funções, não será necessário alterar os programas
clientes. Isso torna a classe fácil de manter.
A Listagem 9.11 é um programa cliente que usa a classe Circle para criar um objeto
Circle e modifica o raio usando a função setRadius .

Listagem 9.11 TestCircleWithPrivateDataFields.cpp


1 #include <iostream>
incluir arquivo de cabeçalho 2 #include "CircleWithPrivateDataFields.h"
3 usando namespace std;

4 5 int principal()
6{
construir objeto Círculo círculo1;
construir objeto 7 8 Círculo círculo2(5,0);

9 cout << "A área do círculo de raio"


obter raio 10 << círculo1.getRadius() << "is" << círculo1.getArea() << endl;
11 cout << "A área do círculo de raio"
12 << círculo2.getRadius() << "is" << círculo2.getArea() << endl;
13
14 // Modifica o raio do círculo
definir raio 15 círculo2.setRadius(100);
16 cout << "A área do círculo de raio"
17 << círculo2.getRadius() << "é" << círculo2.getArea() << endl;
18
19 retornar 0;
20 21 }
Machine Translated by Google

9.10 O Escopo das Variáveis 379

A área do círculo de raio 1 é 3,14159


A área do círculo de raio 5 é 78,5397
A área do círculo de raio 100 é 31415,9

O raio do campo de dados é declarado privado. Os dados privados podem ser acessados apenas dentro de
sua classe definidora. Você não pode usar Circle1.radius no programa cliente. Ocorreria um erro de compilação
se você tentasse acessar dados privados de um cliente.

Dica
Para evitar que os dados sejam adulterados e para facilitar a manutenção da classe, os
campos de dados neste livro serão privados.

9.15 O que há de errado no código a seguir? (Use a classe Circle definida na Listagem 9.9,
CircleWithPrivateDataFields.h.) ÿVerificação de ponto

Círculo c;
cout << c.radius << endl;
9.16 O que é uma função de acessador? O que é uma função mutadora? Quais são os nomes
convenções para tais funções?

9.17 Quais são os benefícios do encapsulamento de campos de dados?

9.10 O Escopo das Variáveis


O escopo das variáveis de instância e estáticas é a classe inteira, independentemente de onde as
Chave
variáveis são declaradas. Apontar

O Capítulo 6 discutiu o escopo de variáveis globais, variáveis locais e variáveis locais estáticas.
Variáveis globais são declaradas fora de todas as funções e são acessíveis a todas as funções em seu escopo. O
escopo de uma variável global começa na sua declaração e continua até o final do programa. Variáveis locais são
definidas dentro de funções. O escopo de uma variável local começa na sua declaração e continua até o final do
bloco que contém a variável. Variáveis locais estáticas são armazenadas permanentemente no programa para que
possam ser usadas na próxima chamada da função.

Os campos de dados são declarados como variáveis e são acessíveis a todos os construtores e funções
na aula. Os campos de dados e funções podem estar em qualquer ordem em uma classe. Por exemplo, todas as
declarações a seguir são iguais:

classe Círculo classe Círculo classe Círculo

{ público: { público: { privado:


Círculo(); Círculo(); raio duplo ;
Círculo(duplo); Círculo(duplo);
double getArea(); público:
double getRadius(); void privado: double getArea();
setRadius(duplo); raio duplo ; double getRadius(); void
setRadius(duplo);
privado: público:
raio duplo ; double getArea(); público:
}; double getRadius(); void Círculo();
setRadius(duplo); }; Círculo(duplo); };

(a) (b) (c)


Machine Translated by Google

380 Capítulo 9 Objetos e Classes

Dica
público primeiro Embora os membros da classe possam estar em qualquer ordem, o estilo comum em C++ é colocar
primeiro os membros públicos e depois os membros privados.

Esta seção discute as regras de escopo de todas as variáveis no contexto de uma classe.
Você pode declarar uma variável para um campo de dados apenas uma vez, mas pode declarar o mesmo
nome de variável em uma função muitas vezes em funções diferentes.
Variáveis locais são declaradas e usadas localmente dentro de uma função. Se uma variável local tiver o
mesmo nome de um campo de dados, a variável local terá precedência e o campo de dados com o mesmo nome
ficará oculto. Por exemplo, no programa da Listagem 9.12, x é definido como um campo de dados e como uma
variável local na função.

Listagem 9.12 HideDataField.cpp


1 #include <iostream>
2 usando namespace std;

3 4 classe Foo
5{
6 público:
campo de dados x 7int x ; // Campo de dados
campo de dados y 8 inteiro ; // Campo de dados

construtor sem argumento 9 Foo()


10 {
11 x = 10;
12 y = 10;
13 }
14
15 vazio p()
16 {
variável local 17 interno x = 20; //variável local
18 cout << "x é" << x << endl;
19 cout << "y é" << y << endl;
20 21 }
22};

23 24 int principal()
25 {
criar objeto 26 Foo foo;
invocar função 27 foo.p();
28
29 retornar 0;
30 }

x é 20
você tem 10 anos

Por que a impressão é 20 para x e 10 para y? Aqui está o porquê:

n x é declarado como um campo de dados na classe Foo , mas também é definido como uma variável local na função p()

com um valor inicial de 20. O último x é exibido no console na linha 19.

n y é declarado como um campo de dados, portanto é acessível dentro da função p().


Machine Translated by Google

9.11 Abstração e Encapsulamento de Classe 381

Dica
Conforme demonstrado no exemplo, é fácil cometer erros. Para evitar confusão, não declare o mesmo nome de variável duas vezes

em uma classe, exceto para parâmetros de função.

9.18 Os campos de dados e funções podem ser colocados em qualquer ordem em uma classe? ÿVerificação de ponto

9.11 Abstração e encapsulamento de classe Nota de vídeo


A classe de empréstimo

Abstração de classe é a separação entre implementação de classe e uso de uma classe. Os detalhes da
implementação são encapsulados e ocultos do usuário. Isso é conhecido como encapsulamento de classe. Chave
Apontar

No Capítulo 6 você aprendeu sobre abstração de funções e a utilizou no desenvolvimento de programas passo a
passo. C++ fornece muitos níveis de abstração. Abstração de classe é a separação entre implementação de classe abstração de classe

e uso de uma classe. O criador de uma classe fornece uma descrição da classe e informa ao usuário como ela
pode ser usada. A coleção de funções e campos acessíveis de fora da classe, juntamente com a descrição de
como se espera que esses membros se comportem, serve como contrato da classe. Conforme mostrado na Figura
9.7, o usuário da classe não precisa saber como a classe é implementada. Os detalhes da implementação são contrato de classe

encapsulados e ocultos do usuário. Isso é conhecido como encapsulamento de classe. Por exemplo, você pode
criar um objeto Círculo e encontrar a área do círculo sem saber como a área é calculada. encapsulamento de classe

A implementação da classe Contrato de classe


é como uma caixa Os clientes usam o
Aula (protótipos
preta escondida dos clientes classe através do
de função e contrato da classe
constantes públicas)

Figura 9.7 A abstração de classe separa a implementação da classe do uso da classe.

Abstração e encapsulamento de classe são duas faces da mesma moeda. Muitos exemplos da vida real
ilustram o conceito de abstração de classe. Considere, por exemplo, construir um sistema de computador. Seu
computador pessoal é composto de muitos componentes, como CPU, CD-ROM, disquete, placa-mãe, ventilador e
assim por diante. Cada componente pode ser visto como um objeto que possui propriedades e funções. Para fazer
com que os componentes funcionem juntos, tudo que você precisa saber é como cada componente é usado e como
ele interage com os outros. Você não precisa saber como funciona internamente. A implementação interna é
encapsulada e oculta para você. Você pode construir um computador sem saber como um componente é
implementado.
A analogia do sistema computacional reflete precisamente a abordagem orientada a objetos. Cada componente
pode ser visto como um objeto da classe do componente. Por exemplo, você pode ter uma classe que modela
todos os tipos de ventiladores para uso em um computador, com propriedades como tamanho e velocidade do
ventilador, funções como iniciar, parar e assim por diante. Um ventilador específico é uma instância desta classe
com valores de propriedade específicos.
Como outro exemplo, considere obter um empréstimo. Um empréstimo específico pode ser visto como um
objeto de uma classe Empréstimo . A taxa de juros, o valor do empréstimo e o período do empréstimo são suas
propriedades de dados, e calcular o pagamento mensal e o pagamento total são suas funções. Quando você
compra um carro, um objeto de empréstimo é criado instanciando a classe com a taxa de juros do empréstimo, o
valor do empréstimo e o período do empréstimo. Você pode então usar as funções para encontrar o pagamento
mensal e o pagamento total do seu empréstimo. Como usuário da classe Loan , você não precisa saber como
essas funções são implementadas.
Machine Translated by Google

382 Capítulo 9 Objetos e Classes

Vamos usar a classe Loan como exemplo para demonstrar a criação e uso de classes.
Loan tem os campos de dados AnnualInterestRate, numberOfYears e LoanAmount,
e as funções getAnnualInterestRate, getNumberOfYears, getLoanAmount,
setAnnualInterestRate, setNumberOfYears, setLoanAmount, getMonthlyPayment
e getTotalPayment, conforme mostrado na Figura 9.8.

Empréstimo

-annualInterestRate: double -numberOfYears: int A taxa de juros anual do empréstimo (padrão: 2,5).
-loanAmount: double O número de anos do empréstimo (padrão: 1)
O valor do empréstimo (padrão: 1.000).

+Empréstimo() Constrói um objeto de empréstimo padrão.


+Empréstimo(taxa: duplo, anos: int, Constrói um empréstimo com taxa de juros, anos e valor do
quantidade: duplo) empréstimo especificados.

+getAnnualInterestRate(): double +getNumberOfYears(): Retorna a taxa de juros anual deste empréstimo.


int +getLoanAmount(): double Retorna o número de anos deste empréstimo.
+setAnnualInterestRate( taxa: double): void Retorna o valor deste empréstimo.
Define uma nova taxa de juros anual para este empréstimo.

+setNumberOfYears( anos: int): Define um novo número de anos para este empréstimo.
void +setLoanAmount( valor:
double): void Define um novo valor para este empréstimo.

+getMonthlyPayment(): double
+getTotalPayment(): double Retorna o pagamento mensal deste empréstimo.
Retorna o pagamento total deste empréstimo.

Figura 9.8 A classe Empréstimo modela as propriedades e comportamentos dos empréstimos.

O diagrama UML da Figura 9.8 serve como contrato para a classe Loan . Ao longo do livro, você desempenhará
o papel de usuário e desenvolvedor de classe. O usuário pode usar a classe sem saber como ela é implementada.
Suponha que a classe Loan esteja disponível, com o arquivo de cabeçalho, conforme mostrado na Listagem 9.13.
Vamos começar escrevendo um programa de teste que utiliza a classe Loan , na Listagem 9.14.

Listagem 9.13 Empréstimo.h


1 #ifndef LOAN_H
2 #define LOAN_H

Empréstimo de 3 4 classes

{
funções públicas 5 6 público:
7 Empréstimo();
8 Empréstimo( taxa dupla, anos int , valor duplo );
double getAnnualInterestRate();
9 10 int getNumberOfYears();
11 double getLoanAmount();
12 void setAnnualInterestRate( taxa dupla);
13 void setNumberOfYears(int anos);
14 void setLoanAmount( valor duplo);
double getMonthlyPayment();
15 double getTotalPayment();
16
campos privados 17 18 privado:
19 taxa de juros anual dupla ;
Machine Translated by Google

9.11 Abstração e Encapsulamento de Classe 383

20 número interno de anos;


21 valor do empréstimo duplo ;
22};
23
24 #endif

Listagem 9.14 TestLoanClass.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include "Empréstimo.h" incluir cabeçalho do empréstimo

4 usando namespace std;

5 6 int principal()
7{
//Insira a taxa de juros anual
cout << "Insira a taxa de juros anual, por exemplo 8,25: ";
8 taxa de juros anual dupla ;
9 cin >> taxa de juros anual;
10
11 //Insira o número de anos
12 cout << "Insira o número de anos como um número inteiro, por exemplo 5: ";
13 número interno de anos;
14 cin >> númeroDeAnos; número de entrada de anos
15
16 //Insira o valor do empréstimo
17 cout << "Insira o valor do empréstimo, por exemplo 120.000,95: ";
18 valor do empréstimo duplo ;
19 cin >> valor do empréstimo; valor do empréstimo de entrada
20
21 // Cria o objeto Empréstimo
22 23 24 Empréstimo de empréstimo (taxa de juros anual, número de anos, valor do empréstimo); criar objeto de empréstimo
25
26 //Exibir resultados
27 cout << fixo << setprecision(2); cout << "O
28 pagamento mensal é "
29 << empréstimo.getMonthlyPayment() << endl; pagamento mensal
30 cout << "O pagamento total é " << lend.getTotalPayment() << endl; pagamento total
31
32 retornar 0;
33 }
34

A função principal lê a taxa de juros, o período de pagamento (em anos) e o valor do empréstimo
(linhas 8 a 21), cria um objeto Empréstimo (linha 24) e, em seguida, obtém o pagamento mensal (linha
29) e o pagamento total (linha 30) usando as funções de instância na classe Loan .
A classe Loan pode ser implementada como na Listagem 9.15.

Listagem 9.15 Loan.cpp


1 #include "Empréstimo.h"
2 #incluir <cmath>
3 usando namespace std;
4

5 Empréstimo::Empréstimo() construtor sem argumento


6{
taxa de juros anual = 9,5;
númeroDeAnos = 30;
valor do empréstimo = 100.000;
7 8 9 10}
Machine Translated by Google

384 Capítulo 9 Objetos e Classes


11
construtor 12 Empréstimo::Empréstimo( taxa dupla, anos int , valor duplo )
13 {
14 taxa de juros anual = taxa;
15 númeroDeAnos = anos;
16 valor do empréstimo = valor;
17}
18
função de acessador 19 Empréstimo duplo ::getAnnualInterestRate()
20 {
21 retornar taxa de juros anual;
22}
23
função de acessador 24 int Empréstimo::getNumberOfYears()
25 {
26 retornar númeroDeAnos;
27}
28
função de acessador 29 empréstimo duplo ::getLoanAmount()
30 {
31 valor do empréstimo de retorno ;
32}
33
função mutadora 34 anular Empréstimo::setAnnualInterestRate( taxa dupla)
35 {
36 taxa de juros anual = taxa;
37}
38
função mutadora 39 anulado Empréstimo::setNumberOfYears(int anos)
40 {
41 númeroDeAnos = anos;
42}
43
função mutadora 44 void Loan::setLoanAmount( valor duplo)
45 {
46 valor do empréstimo = valor;
47}
48
receber pagamento mensal 49 Empréstimo duplo ::getMonthlyPayment()
50 {
51 double taxa de juros mensal = taxa de juros anual / 1200;
52 valor do empréstimo de retorno * taxa de juros mensal / (1 -
53 (pow(1 / (1 + taxa de juros mensal), número de anos * 12)));
54 }
55
receber o pagamento total 56 Empréstimo duplo ::getTotalPayment()
57 {
58 return getMonthlyPayment() * númeroDeAnos * 12;
59}

Do ponto de vista de um desenvolvedor de classe, uma classe é projetada para ser usada por muitos
clientes diferentes. Para ser útil em uma ampla gama de aplicações, uma classe deve fornecer diversas
maneiras de personalização por meio de construtores, propriedades e funções.
A classe Loan contém dois construtores, três funções get , três funções set e as funções para
encontrar o pagamento mensal e o pagamento total. Você pode construir um empréstimo
objeto usando o construtor no-arg ou aquele com três parâmetros: taxa de juros anual, número de anos
e valor do empréstimo. As três funções get , getAnnualInterest, getNumberOfYears e getLoanAmount,
retornam a taxa de juros anual, os anos de pagamento e o valor do empréstimo, respectivamente.
Machine Translated by Google

Termos-chave 385

Dica Pedagógica Importante O


diagrama UML para a classe Loan é mostrado na Figura 9.8. Os alunos devem começar
escrevendo um programa de teste que use a classe Loan mesmo que não saibam como a
classe Loan é implementada. Isso tem três benefícios:

n Demonstra que desenvolver uma classe e usar uma classe são duas tarefas distintas. n
Permite pular a implementação complexa de certas classes sem interromper a sequência do
livro.
n É mais fácil aprender como implementar uma classe se você estiver familiarizado com ela
através de seu uso.

Para todos os exemplos de agora em diante, você pode primeiro criar um objeto da classe e
tentar usar suas funções antes de voltar sua atenção para sua implementação.

9.19 Qual é a saída do código a seguir? (Use a classe Loan definida na Listagem 9.13, Loan.h.)
ÿVerificação de ponto

#include <iostream>
#include "Loan.h"
usando namespace std;

classe A

{ público:
Empréstimo;
int eu; };

int principal()
{
Uma;
cout << a.loan.getLoanAmount() << endl; cout << ai
<< endl;

retornar 0;
}

Termos chave
acessador 376 definição inline 375
objeto anônimo 370 operador instância 362
de resolução de escopo binário (::) 372 objeto de função de instância 369
chamada 369 classe variável de instância 369
362 abstração instanciação 362
de classe 381 função de membro 369
encapsulamento de classe operador de acesso de membro
381 cliente 369 mutador
372 construtor 362 376 construtor sem
lista de inicializadores de construtor argumento
382 contrato 362 368 objeto 362 programação orientada a objetos
campo de dados (OOP) 362
362 encapsulamento de campo de propriedade 362
dados 376 construtor padrão privado 376
368 operador de ponto (.) público 365 estado 362
369 proteção de inclusão 374 Diagrama de classes UML 363
Machine Translated by Google

386 Capítulo 9 Objetos e Classes

Resumo do capítulo

1. Uma classe é um modelo para objetos.

2. Uma classe define os campos de dados para armazenar as propriedades dos objetos e fornece
construtores para criar objetos e funções para manipulá-los.

3. Os construtores devem ter o mesmo nome da própria classe.

4. Um construtor não arg é um construtor que não possui argumentos.

5. Uma classe também é um tipo de dados. Você pode usá-lo para declarar e criar objetos.

6. Um objeto é uma instância de uma classe. Você usa o operador ponto (.) para acessar membros de
esse objeto através de seu nome.

7. O estado de um objeto é representado por campos de dados (também conhecidos como propriedades) com seus
valores atuais.

8. O comportamento de um objeto é definido por um conjunto de funções.

9. Os campos de dados não possuem valores iniciais. Eles devem ser inicializados em construtores.

10. Você pode separar a definição de classe da implementação de classe definindo a classe em um arquivo de
cabeçalho e a implementação da classe em um arquivo separado.

11. A diretiva C++ #ifndef , chamada de proteção de inclusão, pode ser usada para evitar que um arquivo de cabeçalho
seja incluído várias vezes.

12. Quando uma função é implementada dentro de uma definição de classe, ela automaticamente se torna um
função embutida.

13. Palavras-chave de visibilidade especificam como a classe, a função e os dados são acessados.

14. Uma função ou dados públicos são acessíveis a todos os clientes.

15. Uma função ou dado privado só pode ser acessado dentro da classe.

16. Você pode fornecer uma função get ou uma função set para permitir que os clientes vejam ou modifiquem os dados.

17. Coloquialmente, uma função get é chamada de getter (ou acessador), e uma função set é chamada de setter (ou
modificador).

18. Uma função get possui a assinatura

returnType getPropertyName()

19. Se o returnType for bool, a função get deve ser definida como

bool éPropertyName().

20. Uma função definida tem a assinatura

void setPropertyName(dataType propertyValue)


Machine Translated by Google

Exercícios de Programação 387

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Nota Pedagógica
Os exercícios atingem três objetivos: três objetivos
1. Projetar e desenhar UML para aulas;

2. Implementar classes da UML;

3. Utilize classes para desenvolver aplicações.

As soluções para os diagramas UML dos exercícios pares podem ser baixadas do site Companion
e todas as outras do site do Instructor.

Seções 9.2–9.11

9.1 (A classe Rectangle ) Projete uma classe chamada Rectangle para representar um retângulo.
A classe contém:

n Dois campos de dados duplos denominados largura e altura que especificam a largura e a altura
do retângulo. n Um
construtor sem argumentos que cria um retângulo com largura 1 e altura 1.
n Um construtor que cria um retângulo padrão com a largura especificada e
altura.
n As funções de acessador e modificador para todos os campos de dados.
n Uma função chamada getArea() que retorna a área deste retângulo.
n Uma função chamada getPerimeter() que retorna o perímetro.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie dois objetos Rectangle . Atribua largura 4 e altura 40 ao primeiro objeto e largura 3,5 e altura
35,9 ao segundo. Exiba as propriedades de ambos os objetos e encontre suas áreas e perímetros.

9.2 (A classe Fan ) Projete uma classe chamada Fan para representar um fã. A classe contém:

n Um campo de dados interno chamado speed que especifica a velocidade do ventilador. Um fã tem Nota de vídeo
A aula de fãs
três velocidades indicadas com um valor 1, 2 ou 3.
n Um campo de dados bool chamado on que especifica se o ventilador está ligado.
n Um campo de dados duplo chamado radius que especifica o raio do ventilador.
n Um construtor sem argumentos que cria um ventilador padrão com velocidade 1, em falso, e
raio 5.
n As funções de acessador e modificador para todos os campos de dados.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie dois objetos Fan . Atribua velocidade 3, raio 10 e ative-o no primeiro objeto. Atribua velocidade
2, raio 5 e desligue-o para o segundo objeto. Invoque suas funções de acesso para exibir as
propriedades do ventilador.

9.3 (A classe Account ) Projete uma classe chamada Account que contenha:

n Um campo de dados int denominado id para a conta.


n Um campo de dados duplo denominado saldo da conta.
n Um campo de dados duplo denominado AnnualInterestRate que armazena o valor atual
taxa de juro.
Machine Translated by Google

388 Capítulo 9 Objetos e Classes

n Um construtor sem argumentos que cria uma conta padrão com id 0, saldo 0 e
taxa de juros anual 0.
n As funções acessador e modificador para id, balance e
taxa de juros anual.
n Uma função chamada getMonthlyInterestRate() que retorna o valor mensal
taxa de juro.
n Uma função chamada retirar(quantia) que retira uma quantia especificada da conta.

n Uma função chamada deposit(amount) que deposita uma quantia especificada no


conta.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie um objeto Conta com um ID de conta 1122, um saldo de 20.000 e uma taxa de juros anual de
4,5%. Use a função de saque para sacar $ 2.500, use a função de depósito para depositar $
3.000 e imprima o saldo, os juros mensais.

9.4 (A classe MyPoint ) Projete uma classe chamada MyPoint para representar um ponto com
coordenadas x e y. A classe contém:

n Dois campos de dados x e y que representam as coordenadas.


n Um construtor sem argumentos que cria um ponto (0, 0).
n Um construtor que constrói um ponto com coordenadas especificadas.
n Duas funções get para os campos de dados x e y, respectivamente.
n Uma função chamada distância que retorna a distância deste ponto até outro
ponto do tipo MyPoint .

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie dois pontos (0, 0) e (10, 30,5) e exiba a distância entre eles.

*9.5 (A classe Time ) Projete uma classe chamada Time. A classe contém:

n Campos de dados hora, minuto e segundo que representam uma hora.


n Um construtor sem argumentos que cria um objeto Time para a hora atual. n Um
construtor que constrói um objeto Time com um tempo decorrido especificado desde
no meio da noite, 1º de janeiro de 1970, em segundos.
n Um construtor que constrói um objeto Time com a hora, minuto e hora especificados.
segundo.
n Três funções get para os campos de dados hora, minuto e segundo.
n Uma função chamada setTime(int elapseTime) que define um novo horário para o
objeto usando o tempo decorrido.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie dois objetos Time , um usando um construtor sem argumentos e outro usando Time(555550),
e exiba suas horas, minutos e segundos.
(Dica: os dois primeiros construtores extrairão hora, minuto e segundo do tempo decorrido. Por
exemplo, se o tempo decorrido for 555550 segundos, a hora será 10, o minuto será 19 e o segundo
será 9. Para o não -arg construtor, a hora atual pode ser obtida usando time(0), conforme mostrado
na Listagem 2.9, ShowCurrentTime.cpp.)

*9.6 (Álgebra: equações quadráticas) Projete uma classe chamada QuadraticEquation para uma equação
quadrática ax2 + bx + x = 0. A classe contém:

n Campos de dados a , bec que representam três coeficientes. n Um


construtor para os argumentos para a, b e c.
n Três funções get para a, b e c.
n Uma função chamada getDiscriminant() que retorna o discriminante, que é
b2 - 4ac.
Machine Translated by Google

Exercícios de Programação 389

n As funções denominadas getRoot1() e getRoot2() para retornar duas raízes de


a equação:

-b + 2b2 - 4ac -b - 2b2 - 4ac


= =
r1 e r2
2a 2a

Estas funções são úteis apenas se o discriminante for não negativo. Deixe que essas
funções retornem 0 se o discriminante for negativo.
Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de
teste que solicite ao usuário que insira valores para a, b e c e exiba o resultado com base no
discriminante. Se o discriminante for positivo, exiba as duas raízes. Se o discriminante for 0,
exiba a raiz única. Caso contrário, exiba "A equação não tem raízes reais".

*9.7 (Cronômetro) Projete uma classe chamada StopWatch. A classe contém:

n Campos de dados privados startTime e endTime com funções get .


n Um construtor sem argumentos que inicializa startTime com a hora atual.
n Uma função chamada start() que redefine startTime para a hora atual.
n Uma função chamada stop() que define endTime para a hora atual.
n Uma função chamada getElapsedTime() que retorna o tempo decorrido para o
cronômetro em milissegundos.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de


teste que meça o tempo de execução da classificação de 100.000 números usando
classificação por seleção.

*9.8 (A classe Date ) Projete uma classe chamada Date. A classe contém:

n Campos de dados ano, mês e dia que representam uma data.


n Um construtor sem argumentos que cria um objeto Date para a data atual. n
Um construtor que constrói um objeto Date com um tempo decorrido especificado desde
no meio da noite, 1º de janeiro de 1970, em segundos.
n Um construtor que constrói um objeto Date com o ano, mês e data especificados.
dia.
n Três funções get para os campos de dados ano, mês e dia.
n Uma função chamada setDate(int elapseTime) que define uma nova data para o
objeto usando o tempo decorrido.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de


teste que crie dois objetos Date , um usando um construtor sem argumentos e outro usando
Date(555550), e exiba seu ano, mês e dia.
(Dica: os dois primeiros construtores extrairão ano, mês e dia do tempo decorrido. Por
exemplo, se o tempo decorrido for 561555550 segundos, o ano será 1987, o mês será 10 e
o dia será 17. Para o não -arg construtor, a data atual pode ser obtida usando time(0),
conforme mostrado na Listagem 2.9, ShowCurrentTime.cpp.)
*9.9 (Álgebra: 2 * 2 equações lineares) Projete uma classe chamada LinearEquation para um
Sistema 2 * 2 de equações lineares:

machado + por = e ed - namorado


= de - ec
x= e
cx + dy = f anúncio - BC anúncio - BC

A classe contém:

n Campos de dados privados a, b, c, d, e e f.


n Um construtor com os argumentos para a, b, c, d, e e f.
Machine Translated by Google

390 Capítulo 9 Objetos e Classes

n Seis obtêm funções para a, b, c, d, e e f.


n Uma função chamada isSolvable() que retorna verdadeiro se ad - bc não for 0.
n Funções getX() e getY() que retornam a solução da equação.

Desenhe o diagrama UML para a classe e depois implemente a classe. Escreva um programa
de teste que solicite ao usuário que digite a, b, c, d, e e f e exiba o resultado.
Se ad - bc for 0, informe que “A equação não tem solução”. Veja o Exercício de Programação
3.3 para exemplos de execução.
**9.10 (Geometria: intersecção) Suponha que dois segmentos de linha se cruzam. Os dois pontos finais
para o primeiro segmento de linha são (x1, y1) e (x2, y2) e para o segundo segmento de
linha são (x3, y3) e (x4, y4). Escreva um programa que solicite ao usuário que insira esses
quatro pontos finais e exiba o ponto de interseção. Use a classe LinearEquation do
Exercício 9.9 para encontrar o ponto interessante. Veja o Exercício de Programação 3.22
para exemplos de execução.

**9.11 (A classe EvenNumber ) Defina a classe EvenNumber para representar um número par
número. A classe contém:

n Um valor de campo de dados do tipo int que representa o valor inteiro armazenado no
objeto.
n Um construtor sem argumento que cria um objeto EvenNumber para o valor 0.
n Um construtor que constrói um objeto EvenNumber com o valor especificado.
n Uma função chamada getValue() para retornar um valor int para este objeto.
n Uma função chamada getNext() para retornar um objeto EvenNumber que representa o
próximo número par após o número par atual neste objeto.
n Uma função chamada getPrevious() para retornar um objeto EvenNumber que representa
o número par anterior antes do número par atual neste objeto.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de


teste que crie um objeto EvenNumber para o valor 16 e invoque as funções getNext() e
getPrevious() para obter e exibir esses números.
Machine Translated by Google

CAPÍTULO

10
Orientado a Objeto
Pensamento

Objetivos
n Para processar strings usando a classe string (§10.2).
n Desenvolver funções com argumentos de objeto (§10.3).

n Para armazenar e processar objetos em arrays (§10.4).

n Para distinguir entre variáveis e funções de instância e estáticas


(§10.5).

n Definir funções constantes para evitar que os campos de dados sejam


modificados acidentalmente (§10.6).

n Explorar as diferenças entre o paradigma processual e o paradigma orientado a objetos


(§10.7).

n Criar uma classe para índice de massa corporal (§10.7).

n Desenvolver classes para modelar relações de composição (§10.8).

n Para projetar uma classe para uma pilha (§10.9).

n Projetar classes que sigam as diretrizes de design de classes (§10.10).


Machine Translated by Google

392 Capítulo 10 Pensamento Orientado a Objetos

10.1 Introdução
O foco deste capítulo está no design de classes e explora as diferenças entre programação
Chave
Apontar processual e programação orientada a objetos.

O Capítulo 9 introduziu o importante conceito de objetos e classes. Você aprendeu como definir classes, criar
objetos e usar objetos. A abordagem deste livro é ensinar a resolução de problemas e técnicas fundamentais de
programação antes da programação orientada a objetos. Este capítulo aborda a transição da programação
processual para a orientada a objetos. Os alunos verão os benefícios da programação orientada a objetos e a
utilizarão de forma eficaz.
Nosso foco aqui é no design de classes. Usaremos vários exemplos para ilustrar as vantagens da abordagem
orientada a objetos. O primeiro exemplo é a classe string fornecida na biblioteca C++. Os outros exemplos
envolvem projetar novas classes e usá-las em aplicativos. Também apresentaremos alguns recursos de linguagem
que suportam esses exemplos.

10.2 A classe de string


Nota de vídeo A classe string define o tipo de string em C++. Ele contém muitas funções úteis para manipulação de
Chave
A classe de cordas Apontar strings.

Em C++ existem duas maneiras de processar strings. Uma maneira é tratá-los como arrays de caracteres que
terminam com o terminador nulo ('\0'), conforme discutido na Seção 7.11, “C-Strings”. Eles são conhecidos como
strings C. O terminador nulo indica o fim da string, o que é importante para o funcionamento das funções da string
C. A outra maneira é processar strings usando a classe string .
Você pode usar as funções de string C para manipular e processar strings, mas a classe de string é mais fácil. O
processamento de strings C exige que o programador saiba como os caracteres são armazenados no array. A
classe string oculta o armazenamento de baixo nível do programador. O programador está livre de detalhes de
implementação.
A Seção 4.8, “O tipo string ”, introduziu brevemente o tipo string. Você aprendeu como recuperar um caractere
de string usando a função at(index) e o operador de subscrito [] e usar as funções size() e length() para retornar o
número de caracteres em uma string. Esta seção fornece uma discussão mais detalhada sobre o uso de objetos
string.

10.2.1 Construindo uma String


Você criou uma string usando uma sintaxe como esta:

strings = "Bem-vindo ao C++";

Esta afirmação não é eficiente porque leva duas etapas. Ele primeiro cria um objeto string usando uma string literal
e depois copia o objeto para s.
A melhor maneira de criar uma string é usar o construtor de string assim:

strings ("Bem-vindo ao C++");

string vazia Você pode criar uma string vazia usando o construtor no-arg da string . Por exemplo, a instrução a seguir cria
uma string vazia:

cordas;

String C para string Você também pode criar uma string a partir de uma string C usando o construtor de string , conforme mostrado
no código a seguir:

char s1[] = "Bom dia";


cadeia s(s1);

Aqui s1 é uma string C e s é um objeto string.


Machine Translated by Google

10.2 A string Classe 393

10.2.2 Anexando a uma String


Você pode usar diversas funções sobrecarregadas para adicionar novos conteúdos a uma string, conforme mostrado
na Figura 10.1.

corda

+acrescentar(s: string): string Acrescenta string s a este objeto string.

+append(s: string, índice: int, n: int): string Acrescenta n número de caracteres em s começando na posição
índice para esta string.

+ anexar (s: string, n: int): string Acrescenta o primeiro n número de caracteres em s a esta string.

+ anexar (n: int, ch: char): string Acrescenta n cópias do caractere ch a esta string.

Figura 10.1 A classe string fornece as funções para anexar uma string.

Por exemplo:

string s1("Bem vindo");


s1.append(" para C++"); // Acrescenta "to C++" a s1
cout << s1 << endl; // s1 agora se torna Bem-vindo ao C++

string s2("Bem vindo");


s2.append(" para C e C++", 0, 5); // Acrescenta "to C" a s2
cout << s2 << endl; // s2 agora se torna Bem-vindo ao C

string s3("Bem-vindo");
s3.append(" para C e C++", 5); // Acrescenta "to C" a s3
cout << s3 << endl; // s3 agora se torna Bem-vindo ao C

string s4("Bem-vindo");
s4.append(4, 'G'); // Acrescenta "GGGG" a s4
cout << s4 << endl; // s4 agora se torna WelcomeGGGG

10.2.3 Atribuindo uma String


Você pode usar diversas funções sobrecarregadas para atribuir novos conteúdos a uma string, conforme mostrado
na Figura 10.2.

corda

+assign(s[]: char): string +assign(s: Atribui uma matriz de caracteres ou uma string s a esta string.

string): string +assign(s: string, índice: Atribui string s a esta string.


int, n: int): string Atribui n número de caracteres em s começando na posição
índice para esta string.

+assign(s: string, n: int): string +assign(n: int, ch: Atribui o primeiro n número de caracteres em s a esta string.
char): string Atribui n cópias do caractere ch a esta string.

Figura 10.2 A classe string fornece as funções para atribuir uma string.

Por exemplo:

string s1("Bem vindo");


s1.assign("Dallas"); // Atribui "Dallas" a s1
cout << s1 << endl; // s1 agora se torna Dallas
Machine Translated by Google

394 Capítulo 10 Pensamento Orientado a Objetos

string s2("Bem vindo");


s2.assign("Dallas, Texas", 0, 5); // Atribui "Dalla" a s2 cout << s2 << endl; //
s2 agora se torna Dalla

string s3("Bem-vindo");
s3.assign("Dallas, Texas", 5); // Atribui "Dalla" a s3 cout << s3 << endl; //
s3 agora se torna Dalla

string s4("Bem-vindo");
s4.assign(4, 'G'); // Atribui "GGGG" a s4 cout << s4 <<
endl; // s4 agora se torna GGGG

10.2.4 Funções at, clear, erase e empty Você pode usar a função
at(index) para recuperar um caractere em um índice especificado, clear() para limpar a
string, erase(index, n) para excluir parte da string e vazio() para testar se uma string está
vazia, conforme mostrado na Figura 10.3.

corda

+at(índice: int): char Retorna o caractere no índice de posição desta string.

+claro(): vazio Remove todos os caracteres desta string.

+ apagar (índice: int, n: int): string Remove n caracteres desta string começando no índice de posição.

+vazio(): bool Retorna verdadeiro se esta string estiver vazia.

Figura 10.3 A classe string fornece as funções para recuperar um caractere, limpar e apagar uma string e
verificar se uma string está vazia.

Por exemplo:

string s1("Bem vindo");


cout << s1.at(3) << endl; // s1.at(3) retorna c cout <<
s1.erase(2, 3) << endl; // s1 agora é Weme s1.clear(); // s1 agora
está vazio cout << s1.empty() << endl; //
s1.empty retorna 1 (significa verdadeiro)

10.2.5 Funções comprimento, tamanho, capacidade e c_str()


Você pode usar as funções length(), size() ecapacidade () para obter o comprimento,
tamanho e capacidade de uma string e c_str() para retornar uma string C, conforme
mostrado na Figura 10.4. As funções length() e size() são apelidos. As funções c_str() e
data() são as mesmas no novo C++ 11. A função capacidade() retorna o tamanho do buffer
interno que é sempre maior ou igual ao tamanho real da string.

corda

+comprimento(): int Retorna o número de caracteres nesta string.

+tamanho(): int O mesmo que comprimento().

+capacidade(): int Retorna o tamanho do armazenamento alocado para esta string.

+c_str(): char[] Retorna uma string C para esta string.

+dados(): char[] O mesmo que c_str().

Figura 10.4 A classe string fornece as funções para obter o comprimento, a capacidade e a string C da string.
Machine Translated by Google

10.2 A string Classe 395

Por exemplo, veja o seguinte código:

1 string s1("Bem-vindo"); 2 cout criar string


<< s1.length() << endl; // O comprimento é 7 3 cout << s1.size()
<< endl; // O tamanho é 7 4 cout << s1.capacity() << endl; //
A capacidade é 15 5 6 s1.erase(1, 2); 7 cout << s1.length() << endl; // O

comprimento agora é 5 apagar dois caracteres


8 cout << s1.size() << endl; // O tamanho agora é 5 9 cout <<
s1.capacity() << endl; //A capacidade ainda é 15

Nota A

capacidade é definida como 15 quando a string s1 é criada na linha 1. Depois que dois caracteres são capacidade?
apagados na linha 6, a capacidade ainda é 15, mas o comprimento e o tamanho tornam-se 5.

10.2.6 Comparando Strings Muitas


vezes, em um programa, você precisa comparar o conteúdo de duas strings. Você pode usar a
função comparar . Esta função retorna um valor int maior que 0, 0 ou menor que 0 se esta
string for maior, igual ou menor que a outra string, conforme mostrado na Figura 10.5.

corda

+comparar(s: string): int Retorna um valor maior que 0, 0 ou menor que 0 se esta string
for maior que, igual ou menor que s.

+compare(índice: int, n: int, s: string): int Compara esta string com a substring s(index,…, index n1).

Figura 10.5 A classe string fornece as funções para comparar strings.

Por exemplo:

string s1("Bem vindo");


string s2("Bem-vindo");
cout << s1.compare(s2) << endl; // Retorna –1 cout <<
s2.compare(s1) << endl; // Retorna 1 cout << s1.compare("Bem-
vindo") << endl; //Retorna 0

10.2.7 Obtendo Substrings Você pode


obter um único caractere de uma string usando a função at . Você também pode obter uma
substring de uma string usando a função substr , conforme mostrado na Figura 10.6.

corda

+substr(índice: int, n: int): string Retorna uma substring de n caracteres desta string começando no
índice de posição.

+substr(índice: int): string Retorna uma substring desta string começando no índice de posição.

Figura 10.6 A classe string fornece as funções para obter substrings.

Por exemplo:

string s1("Bem vindo"); cout


<< s1.substr(0, 1) << endl; // Retorna W
Machine Translated by Google

396 Capítulo 10 Pensamento Orientado a Objetos

cout << s1.substr(3) << endl; // Retorna como cout <<


s1.substr(3, 3) << endl; //Retorna com

10.2.8 Pesquisando em uma string Você


pode usar a função find para pesquisar uma substring ou um caractere em uma string, conforme mostrado
na Figura 10.7. A função retorna string::npos (não uma posição) se nenhuma correspondência for
encontrada. npos é uma constante definida na classe string .

corda

+ encontrar (ch: char): não assinado Retorna a posição do primeiro caractere correspondente para ch.

+ encontrar (ch: char, índice: int): não assinado Retorna a posição do primeiro caractere correspondente para ch em
ou do índice de posição.

+encontrar(s: string): não assinado Retorna a posição da primeira substring s correspondente.

+ encontrar (s: string, índice: int): não assinado Retorna a posição da primeira substring s correspondente começando em
ou a partir do índice de posição.

Figura 10.7 A classe string fornece as funções para encontrar substrings.

Por exemplo:

string s1("Bem-vindo ao HTML"); cout


<< s1.find("co") << endl; // Retorna 3 cout << s1.find("co",
6) << endl; // Retorna string::npos cout << s1.find('o') << endl; // Retorna 4 cout
<< s1.find('o', 6) << endl; // Retorna 9

10.2.9 Inserindo e substituindo strings Você pode usar as


funções insert e replace para inserir uma substring e substituir uma substring em uma string, conforme
mostrado na Figura 10.8.

Corda

+inserir(índice: int, s: string): string Insere a string s nesta string na posição index.

+inserir(índice: int, n: int, ch: char): string Insere o caractere ch n vezes nesta string na posição index.

+substituir(índice: int, n: int, s: string): string Substitui os n caracteres começando na posição index nesta string pela
string s.

Figura 10.8 A classe string fornece as funções para inserir e substituir substrings.

Aqui estão alguns exemplos de uso das funções inserir e substituir :

string s1("Bem-vindo ao HTML");


s1.insert(11, "C++ e "); cout << s1
<< endl; // s1 torna-se Bem-vindo ao C++ e HTML

strings2 ("AA");
s2.inserir(1, 4, 'B'); cout <<
s2 << endl; // s2 se torna ABBBBA

string s3("Bem-vindo ao HTML");


s3.replace(11, 4, "C++"); cout <<
s3 << endl; // s3 torna-se Bem-vindo ao C++
Machine Translated by Google

10.2 A string Classe 397


Observação

Um objeto string invoca anexar, atribuir, apagar, substituir e inserir


funções para alterar o conteúdo do objeto string . Essas funções também retornam a nova string. string de retorno
Por exemplo, no código a seguir, s1 invoca a função insert em s1 e a nova string é retornada e
atribuída a s2. insira "C++ e"

string s1("Bem-vindo ao HTML");


string s2 = s1.insert(11, "C++ e ");
cout << s1 << endl; // s1 torna-se Bem-vindo ao C++ e HTML
cout << s2 << endl; // s2 torna-se Bem-vindo ao C++ e HTML

Observação

Na maioria dos compiladores, a capacidade é automaticamente aumentada para


acomodar mais caracteres para as funções anexar, atribuir, inserir e substituir. Se a capacidade muito pequena?
capacidade for fixa e muito pequena, a função copiará tantos caracteres quanto possível.

10.2.10 Operadores de String


C++ oferece suporte a operadores para simplificar operações de string. A Tabela 10.1 lista os operadores de string.

Tabela 10.1 Operadores de String

Operador Descrição

[] Acessa caracteres usando o operador de subscrito de array.


= Copia o conteúdo de uma string para a outra.
+ Concatena duas strings em uma nova string.
+= Acrescenta o conteúdo de uma string à outra.
<< Insere uma string em um stream
>> Extrai caracteres de um fluxo para uma string delimitada por um espaço em branco ou
pelo caractere terminador nulo.

==, !=, <, Seis operadores relacionais para comparação de strings.

<=, >, >=

Aqui estão os exemplos para usar esses operadores:

string s1 = "ABC"; //O operador = =


cadeia s2 = s1; //O operador =
para (int i = s2.size() – 1; i >= 0; i--)
cout << s2[i]; // O operador []

string s3 = s1 + "DEFG"; //O operador + +


cout << s3 << endl; // s3 se torna ABCDEFG <<

s1+= "ABC"; +=
cout << s1 << endl; // s1 se torna ABCABC

s1 = “ABC”;
s2 = “ABE”;
cout << (s1 == s2) << endl; // Exibe 0 (significa falso) ==
cout << (s1 != s2) << endl; // Exibe 1 (significa verdadeiro) !=
cout << (s1 > s2) << endl; // Exibe 0 (significa falso) >
cout << (s1 >= s2) << endl; // Exibe 0 (significa falso) >=
cout << (s1 <s2) << endl; // Exibe 1 (significa verdadeiro) <
cout << (s1 <= s2) << endl; // Exibe 1 (significa verdadeiro) <=
Machine Translated by Google

398 Capítulo 10 Pensamento Orientado a Objetos

10.2.11 Convertendo Números em Strings


A Seção 7.11.6, “Conversão entre Strings e Números”, introduziu como converter uma string em um inteiro e um
número de ponto flutuante usando as funções atoi e atof. Você também pode usar a função itoa para converter um
número inteiro em uma string. Às vezes você precisa converter um número de ponto flutuante em uma string. Você
pode escrever uma função para realizar a conversão. Entretanto, uma abordagem simples é usar a classe
stringstream no cabeçalho <sstream> . string-stream fornece uma interface para manipular strings como se
fossem fluxos de entrada/saída. Uma aplicação do stringstream é converter números em strings. Aqui está um
exemplo:

1 stringstream ss; 2ss <<


número para stringstream 3,1415; 3 strings =
stringstream para string ss.str();

10.2.12 Dividindo Strings


Muitas vezes você precisa extrair as palavras de uma string. Suponha que as palavras estejam separadas por
espaços em branco. Você pode usar a classe stringstream discutida na seção anterior para realizar esta tarefa. A
Listagem 10.1 dá um exemplo que extrai as palavras de uma string e as exibe em linhas separadas.

Listagem 10.1 ExtractWords.cpp


1 #include <iostream>
incluir cabeçalho de stream 2 #include <sstream>
incluir cabeçalho de string 3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
string text("Programar é divertido");
criar stringstream stringstream ss(texto);
8
"
9 cout << "As palavras no texto são string word; << fim;
10
fim do fluxo 11 enquanto (!ss.eof())
12 {
obter dados do fluxo 13 ss >> palavra;
14 cout << palavra << endl;
15 }
16
17 retornar 0;
18 19 20 }

As palavras do texto são


Programação
é
diversão

O programa cria um objeto stringstream para a string de texto (linha 9) e este objeto pode ser usado como um
fluxo de entrada para leitura de dados do console. Ele envia dados do fluxo de string para uma palavra do objeto
string (linha 15). A função eof() no stringstream
class retorna verdadeiro quando todos os itens no string stream são lidos (linha 13).
Machine Translated by Google

10.2 A string Classe 399

10.2.13 Estudo de caso: Substituição de strings


Neste estudo de caso, você escreverá a seguinte função que substitui a ocorrência de uma substring
oldSubStr por uma nova substring newSubStr na string s.

bool replaceString(string& s, const string& oldSubStr, const string& newSubStr)

A função retorna verdadeiro se a string s for alterada e, caso contrário, retorna falso.
A Listagem 10.2 fornece o programa.

Listagem 10.2 ReplaceString.cpp


1 #include <iostream>
2 #incluir <string> incluir cabeçalho de string
3 usando namespace std;
4
5 // Substitua oldSubStr em s por newSubStr
6 bool replaceString(string& s, const string& oldSubStr, 7 8 9 int main() função substituirString
const string& newSubStr);

10 {
11 // Solicita ao usuário que insira s, oldSubStr e newSubStr
12 cout << "Insira string s, oldSubStr e newSubStr: ";
13 string s, oldSubStr, newSubStr;
14 cin >> s >> oldSubStr >> newSubStr;
15
16 bool isReplaced = replaceString(s, oldSubStr, newSubStr); invocar replaceString
17
18 if (é substituído)
19 cout << "A string substituída é " else << s << endl;
20
21 cout << "Sem correspondências" << endl;

retornar 0;
22 23 24 }
25
26 bool replaceString(string& s, const string& oldSubStr, 27 const string& newSubStr)

28 {
29 bool éReplaced = falso; É substituído
30 int posição atual = 0;
31 while (posiçãoatual <s.comprimento())
32 {
33 posição int = s.find(oldSubStr, currentPosition); substring de pesquisa
34 if (position == string::npos) // Não há mais correspondências
35 return é substituído; outro retornar é substituído
36
37 {
38 s.replace(posição, oldSubStr.length(), newSubStr); substituir substring
39 posiçãoatual = posição + newSubStr.length();
40 éSubstituído = verdadeiro; // Pelo menos uma correspondência
41 }
42 }
43
44 return é substituído;
45 }
Machine Translated by Google

400 Capítulo 10 Pensamento Orientado a Objetos

Insira a string s, oldSubStr e newSubStr: abcdabab ab AAA


A string substituída é AAAcdAAAAAA

Insira a string s, oldSubStr e newSubStr: abcdabab gb AAA


Sem combinações

O programa solicita que o usuário insira uma string, uma substring antiga e uma nova substring (linha
14). O programa invoca a função repalceString para substituir todas as ocorrências da substring antiga
pela nova substring (linha 16) e exibe uma mensagem indicando se a string foi substituída (linhas 18–21).

A função replaceString procura oldSubStr em string s começando em currentPosition começando


em 0 (linha 30). A função find na classe string é usada para encontrar uma substring em uma string (linha
33). Ele retorna string::npos se não for encontrado. Neste caso, a busca termina e a função retorna
isReplaced (linha 35). isReplaced é um bool
variável e inicialmente definida como falsa (linha 29). Sempre que uma correspondência para uma substring é encontrada, ela é
definida como verdadeira (linha 40).
A função encontra repetidamente uma substring e a substitui por uma nova substring usando a função
replace (linha 38) e redefine a posição de pesquisa atual (linha 39) para procurar uma nova
correspondência no restante da string.

10.1 Para criar uma string "Bem-vindo ao C++", você pode usar uma instrução como esta:
ÿVerificação de ponto
string s1("Bem-vindo ao C++");

ou isto:

string s1 = "Bem-vindo ao C++";

Qual é o melhor? Por que?

10.2 Suponha que s1 e s2 sejam duas strings, dadas a seguir:

string s1("Eu tenho um sonho");


string s2("Programação de Computador");

Suponha que cada expressão seja independente. Quais são os resultados das seguintes
expressões?

(1) s1.append(s2) (13) s1.apagar(1, 2)


(2) s1.append(s2, 9, 7) (14) s1.comparar(s3) (15)
(3) s1.append("NOVO", 3) (4) s1.comparar(0, 10, s3)
s1.append(3, 'N') (5) (16)s1.c_str()
s1.assign(3, 'N') (6) (17)s1.substr(4, 8)
s1.assign(s2, 9, 7) (7) (18)s1.substr(4)
s1.assign("NOVONOVO", 3) (19)s1.find('A')
(8) s1.atribuir(3, 'N') (20) s1.find('a', 9)
(9) s1.at(0) (21) s1.replace(2, 4, "NOVO")
(10) s1.comprimento() (22) s1.inserir(4, "NOVO")
(11) s1.tamanho() (23)s1.inserir(6, 8, 'N')
(12) s1.capacidade() (24)s1.vazio()

10.3 Suponha que s1 e s2 sejam dados da seguinte forma:

string s1("Eu tenho um sonho");


string s2("Programação de Computador");
char s3[] = "ABCDEFGHIJKLMN";
Machine Translated by Google

10.3 Passando Objetos para Funções 401

Suponha que cada expressão seja independente. Quais são os resultados de s1, s2 e s3
após cada uma das seguintes afirmações?

(1)s1.claro()
(2) s1.cópia(s3, 5, 2)
(3) s1.comparar(s2)

10.4 Suponha que s1 e s2 sejam dados da seguinte forma:

string s1("Eu tenho um sonho");


string s2("Programação de Computador");

Suponha que cada expressão seja independente. Quais são os resultados das seguintes
expressões?

(1)s1[0] (6) s1 >= s2 (7)


(2) s1 = s2 s1 < s2
(3)s1 = "C++" +s2 (4)s2 += (8)s1 <=s2
"C++" (9) s1 == s2
(5) s1 > s2 (10)s1!=s2

10.5 Suponha que você tenha entrado em Nova York ao executar os programas a seguir. O que
seria a impressão?

#include <iostream> #include <iostream>


#incluir <string> #incluir <string>
usando namespace std; usando namespace std;

int principal() int principal()


{ {
cout << "Insira a cidade: "; cout << "Insira a cidade: ";
cidade de cordas; cidade de cordas;
cin >> cidade; getline(cin,cidade);

cout << cidade << endl; cout << cidade << endl;

retornar 0; retornar 0;
} }

(a) (b)

10.6 Mostre a saída do código a seguir (a função replaceString é definida em


Listagem 10.2).

string s("abcdabab"), oldSubStr("ab"), newSubStr("AAA");


substituaString(s, oldSubStr, newSubStr);
cout << s << endl;

10.7 Se a função replaceString for retornada da linha 44 da Listagem 10.2, o valor retornado é
sempre falso?

10.3 Passando Objetos para Funções


Os objetos podem ser passados para uma função por valor ou por referência, mas é mais eficiente
Chave
passar objetos por referência. Apontar

Até agora, você aprendeu como passar argumentos de tipos primitivos, tipos de array e tipos de string para
funções. Você pode passar qualquer tipo de objeto para funções. Você pode passar objetos por valor ou
por referência. A Listagem 10.3 dá um exemplo que passa um objeto por valor.
Machine Translated by Google

402 Capítulo 10 Pensamento Orientado a Objetos

Listagem 10.3 PassObjectByValue.cpp


1 #include <iostream>
2 // CircleWithPrivateDataFields.h é definido na Listagem 9.9
incluir arquivo de cabeçalho 3 #include "CircleWithPrivateDataFields.h"
4 usando namespace std;

parâmetro de objeto 5 6 void printCircle(Círculo c)


7{
8 cout << "A área do círculo de "
círculo de acesso << c.getRadius() << "é" << c.getArea() << endl;
9 10}
11
12 int principal()
13 {
criar círculo 14 Círculo meuCírculo(5.0);
passar objeto 15 printCircle(meuCírculo);
16
17 retornar 0;
18}

A área do círculo de 5 é 78,5397

A classe Circle definida CircleWithPrivateDataFields.h na Listagem 9.9 está incluída na linha 3. O


parâmetro para a função printCircle é definido como Circle (linha 6). O principal
A função cria um objeto Circle myCircle (linha 14) e o passa para printCircle
função por valor (linha 15). Passar um argumento de objeto por valor é copiar o objeto para o parâmetro
da função. Portanto, o objeto c na função printCircle é independente do objeto myCircle na função
main, conforme mostrado na Figura 10.9a.

c é um alias para myCircle

meuCírculo: Círculo c: Círculo meuCírculo: Círculo

raio 5,0 Copie meuCírculo para c raio 5,0 raio 5,0

(a) (b)

Figura 10.9 Você pode passar um objeto para uma função (a) por valor ou (b) por referência.

A Listagem 10.4 dá um exemplo que passa um objeto por referência.

Listagem 10.4 PassObjectByReference.cpp


1 #include <iostream>
incluir arquivo de cabeçalho 2 #include "CircleWithPrivateDataFields.h"
3 usando namespace std;

parâmetro de referência 4 5 void printCircle(Círculo& c)


6{
cout << "A área do círculo de "
círculo de acesso << c.getRadius() << "é" << c.getArea() << endl;
789}
10
11 int principal()
12 {
criar círculo 13 Círculo meuCírculo(5.0);
Machine Translated by Google

10.3 Passando Objetos para Funções 403

14 printCircle(meuCírculo); referência de passagem


15
16 retornar 0;
17}

A área do círculo de 5 é 78,5397

Um parâmetro de referência do tipo Circle é declarado na função printCircle (linha 5). A função main
cria um objeto Circle myCircle (linha 13) e passa a referência do objeto para a função printCircle (linha
14). Portanto, o objeto c na função printCircle é essencialmente um alias do objeto myCircle na função
main, conforme mostrado na Figura 10.9b.

Embora você possa passar um objeto para uma função por valor ou por referência, passar por referência passar objeto por referência
é preferido porque leva tempo e espaço de memória adicional para passar por valor.

10.8 Por que a passagem por referência é preferida para passar um objeto para uma função?
ÿVerificação de ponto
10.9 Qual é a impressão do código a seguir?

#include <iostream>
usando namespace std;

contagem de classe
{
público:
contagem interna ;

Contagem (int c)
{
contagem = c;
}

Contar()
{
contagem = 0;
}
};

incremento vazio (contagem c, int vezes)


{
c.contagem++;
vezes++;
}

int principal()
{
Conte minha contagem;
int vezes = 0;

para (int i = 0; i < 100; i++)


incremento(minhaContagem, vezes);

cout << "minhaContagem.contagem é" << minhaContagem.contagem;


conta << "vezes é" <<vezes;

retornar 0;
}
Machine Translated by Google

404 Capítulo 10 Pensamento Orientado a Objetos

10.10 Se o código destacado no Ponto de Verificação 10.9 for alterado para

incremento vazio (contagem e c, int vezes)

qual será a impressão?

10.11 Se o código destacado no Ponto de Verificação 10.9 for alterado para

incremento vazio (contagem& c, int& tempos)

qual será a impressão?


10.12 Você pode alterar o código destacado no Ponto de Verificação 10.9 para o seguinte?

incremento vazio (const Count& c, int times)

10.4 Matriz de Objetos


Você pode criar uma matriz de qualquer objeto, assim como uma matriz de valores primitivos ou strings.
Chave
Apontar
No Capítulo 7, foram criados arrays de elementos de tipo primitivo e strings. Você pode criar matrizes de
qualquer objeto. Por exemplo, a instrução a seguir declara um array de 10 objetos Circle :

Círculo círculoArray[10]; // Declara um array de dez objetos Circle

O nome do array é circleArray, e o construtor sem argumento é chamado para inicializar cada elemento do
array. Portanto, circleArray[0].getRadius() retorna 1, porque o construtor sem argumento atribui 1 ao raio.

Você também pode usar o inicializador de array para declarar e inicializar um array usando um construtor
com argumentos. Por exemplo,

Círculo círculoArray[3] = {Círculo(3), Círculo(4), Círculo(5)};

A Listagem 10.5 dá um exemplo que demonstra como usar um array de objetos. O programa resume as
áreas de uma série de círculos. Ele cria CircleArray, um array composto por 10 objetos Circle ; em seguida,
define os raios do círculo com raio 1, 2, 3, 4,. . . e 10 e exibe a área total dos círculos na matriz.

Listagem 10.5 TotalArea.cpp

1 #include <iostream>
2 #include <iomanip>
incluir arquivo de cabeçalho 3 #include "CircleWithPrivateDataFields.h"
4 usando namespace std;

5 6 // Adicionar áreas circulares


matriz de objetos 7 soma dupla (Círculo círculoArray[], tamanho interno )
8{
// Inicializa a soma
9 soma dupla = 0;
10 11
12 //Adiciona áreas para somar
13 for (int i = 0; i <tamanho; i++)
obter área 14 soma += círculoArray[i].getArea();
15
16 soma de retorno ;
17 }
18
19 // Imprime um array de círculos e sua área total
matriz de objetos 20 void printCircleArray(Círculo círculoArray[], tamanho interno )
Machine Translated by Google

10.4 Matriz de Objetos 405

21 {
22 cout << setw(35) << esquerda << "Raio" << setw(8) << "Área" << endl;
23 for (int i = 0; i <tamanho; i++)
24 {
25 cout << setw(35) << esquerda << círculoArray[i].getRadius() << setw(8) <<
26 círculoArray[i].getArea() << endl;
27 }
28
29 cout << "----------------------------------------" << endl;
30
31 // Calcula e exibe o resultado
32 cout << setw(35) << left << "A área total dos círculos é"
33 << setw(8) << soma(circleArray, tamanho) << endl;
34 }
35
36 int principal()
37 {
38 const int TAMANHO = 10;
39
40 // Cria um objeto Círculo com raio 1
41 Círculo círculoArray[TAMANHO]; criar matriz
42
43 for (int i = 0; i < TAMANHO; i++)
44 {
45 círculoArray[i].setRadius(i + 1); novo raio
46 }
47
48 printCircleArray(circleArray, TAMANHO); matriz de passagem

49
50 retornar 0;
51}

Raio Área
1 3.14159
2 12.5664
3 28.2743
4 50.2654
5 78.5397
6 113.097
7 153.938
8 201.062
9 254.469
10 314.159
--------------------------------------------

A área total dos círculos é 1209.51

O programa cria um array de dez objetos Circle (linha 41). Duas classes Circle foram
introduzidas no Capítulo 9. Este exemplo usa a classe Circle definida na Listagem 9.9 (linha 3).
Cada elemento de objeto no array é criado usando o construtor no-arg do Círculo . Um
novo raio para cada círculo é definido nas linhas 43–46. CircleArray[i] refere-se a um objeto
Circle na matriz. CircleArray[i].setRadius(i + 1) define um novo raio no objeto Circle (linha
45). A matriz é passada para a função printCircleArray , que exibe o raio e a área de cada
círculo e a área total dos círculos (linha 48).
A soma das áreas do círculo é calculada usando a função soma (linha 33), que leva
a matriz de objetos Circle como argumento e retorna um valor duplo para a área total.
Machine Translated by Google

406 Capítulo 10 Pensamento Orientado a Objetos

10.13 Como você declara um array de 10 objetos string ?

ÿVerificação de ponto
10.14 Qual é a saída no código a seguir?
1int principal ( )
2{
string cidades[] = {"Atlanta", "Dallas", "Savannah"};
34 cout << cidades[0] << endl;
cout << cidades[1] << endl;
56

retornar 0;
7 8}

Nota de vídeo

estático versus instância 10.5 Instâncias e Membros Estáticos


Uma variável estática é compartilhada por todos os objetos da classe. Uma função estática não pode
Chave
Apontar acessar membros de instância da classe.

campo de dados de instância Os campos de dados usados nas classes até agora são conhecidos como campos de dados de instância ou variáveis
variáveis de instância de instância. Uma variável de instância está vinculada a uma instância específica da classe; não é compartilhado entre
objetos da mesma classe. Por exemplo, suponha que você crie os seguintes objetos usando a classe Circle na
Listagem 9.9, CircleWithPrivateDataFields.h:

Círculo círculo1;
Círculo círculo2(5);

O raio em círculo1 é independente do raio em círculo2 e é armazenado em um local de memória


diferente. As alterações feitas no raio do círculo1 não afetam o raio do círculo2 e vice-versa.

variável estática Se quiser que todas as instâncias de uma classe compartilhem dados, use variáveis estáticas, também conhecidas
como variáveis de classe. Variáveis estáticas armazenam valores para as variáveis em um local de memória comum.
Conseqüentemente, todos os objetos da mesma classe serão afetados se um objeto alterar o valor de uma variável
função estática estática. C++ suporta funções estáticas, bem como variáveis estáticas. Funções estáticas podem ser chamadas sem
função de instância criar uma instância da classe. Lembre-se de que as funções de instância só podem ser chamadas a partir de uma
instância específica.
Vamos modificar a classe Circle adicionando uma variável estática numberOfObjects para contar o número de
objetos circulares criados. Quando o primeiro objeto desta classe é criado, numberOfObjects é 1. Quando o segundo
objeto é criado, numberOfObjects torna-se 2. A UML da nova classe círculo é mostrada na Figura 10.10. A classe
Circle define a variável de instância radius e a variável estática numberOfObjects, as funções de instância
getRadius, setRadius e getArea e a função estática getNumberOfObjects.

(Observe que variáveis e funções estáticas estão sublinhadas no diagrama UML.)

Notação UML:
+: variáveis ou funções públicas
–: variáveis ou funções privadas instanciar
sublinhado: variáveis ou funções estáticas círculo1: Círculo Memória
Círculo raio = 1 1 raio
númeroDeObjetos = 2
-raio: duplo
-númeroDeObjetos: int
2 número de objetos
instanciar
+getNumberOfObjects(): int círculo2: Círculo
+getRadius(): duplo
+setArea(raio: duplo): vazio raio = 5 5 raio
+getArea(): duplo númeroDeObjetos = 2

Figura 10.10 As variáveis de instância, que pertencem às instâncias, possuem armazenamento em memória independente umas das outras.
As variáveis estáticas são compartilhadas por todas as instâncias da mesma classe.
Machine Translated by Google

10.5 Instância e Membros Estáticos 407

Para declarar uma variável estática ou uma função estática, coloque o modificador estático na
declaração da variável ou função. Portanto, a variável estática numberOfObjects e a função estática
getNumberOfObjects() podem ser declaradas da seguinte forma:

estático int númeroOfObjects; declarar variável estática


static int getNumberOfObjects(); definir função estática

A nova classe círculo é definida na Listagem 10.6

Listagem 10.6 CircleWithStaticDataFields.h


1 #ifndef CÍRCULO_H
2 #define CÍRCULO_H

3 4 classe Círculo
5{
6 público:
7 Círculo();
Círculo(duplo);
double getArea();
8 double getRadius();
9 10 11 void setRadius(duplo);
12 static int getNumberOfObjects(); função estática
13
14 privado:
15 raio duplo ;
16 estático int númeroOfObjects; variável estática
17};
18
19 #endif

Uma função estática getNumberOfObjects é declarada na linha 12 e uma variável estática


numberOfObjects é declarado na linha 16 como um campo de dados privado na classe.
A Listagem 10.7 apresenta a implementação da classe Circle :

Listagem 10.7 CircleWithStaticDataFields.cpp


1 #include "CircleWithStaticDataFields.h" incluir cabeçalho
2
3 int Circle::numberOfObjects = 0; inicializar variável estática
4
5 // Construa um objeto circular
6 Círculo::Círculo()
7{
raio = 1;
númeroDeObjetos++; incrementar
8 9 10} número de objetos
11
12 // Construa um objeto circular
13 Círculo::Círculo(double newRadius)
14 {
15 raio = novoRaio;
16 númeroDeObjetos++; incrementar
17} número de objetos
18
19 // Retorna a área deste círculo
20 círculo duplo ::getArea()
21 {
22 raio de retorno * raio * 3,14159;
23}
Machine Translated by Google

408 Capítulo 10 Pensamento Orientado a Objetos

24
25 // Retorna o raio deste círculo
26 círculo duplo ::getRadius()
27 {
raio de retorno ;
28 29}
30
31 // Defina um novo raio
32 círculo vazio ::setRadius(double newRadius)
33 {
raio = (novoRaio >= 0) ? novoRaio: 0;
34 35}
36
37 // Retorna o número de objetos circulares
38 int Círculo::getNumberOfObjects()
39 {
retornar número de objetos 40 retornar númeroDeObjetos;
41}

O campo de dados estáticos numberOfObjects é inicializado na linha 3. Quando um objeto Circle é


criado, numberOfObjects é incrementado (linhas 9, 16).
Funções de instância (por exemplo, getArea()) e campos de dados de instância (por exemplo, radius)
pertencem a instâncias e podem ser usados somente após a criação das instâncias. Eles são acessados a partir
de uma instância específica. Funções estáticas (por exemplo, getNumberOfObjects()) e campos de dados
estáticos (por exemplo, numberOfObjects) podem ser acessados a partir de qualquer instância da classe, bem
como a partir do nome da classe.
O programa na Listagem 10.8 demonstra como usar variáveis e funções de instância e estáticas e ilustra os
efeitos de usá-las.

Listagem 10.8 TestCircleWithStaticDataFields.cpp


1 #include <iostream>
incluir cabeçalho 2 #include "CircleWithStaticDataFields.h"
3 usando namespace std;

4 5 int principal()
6{
cout << "Número de objetos circulares criados: "
<< Circle::getNumberOfObjects() << endl;

7 Círculo círculo1;
8 cout << "A área do círculo de raio"
invocar função de instância 9 << círculo1.getRadius() << "is" << círculo1.getArea() << endl;
10 cout << "Número de objetos circulares criados: "
invocar função estática 11 << Circle::getNumberOfObjects() << endl;
12
13 Círculo círculo2(5,0);
14 cout << "A área do círculo de raio"
15 << círculo2.getRadius() << "is" << círculo2.getArea() << endl;
16 cout << "Número de objetos circulares criados: "
invocar função estática 17 << Circle::getNumberOfObjects() << endl;
18
modificar raio 19 círculo1.setRadius(3.3);
20 cout << "A área do círculo de raio"
21 << círculo1.getRadius() << "is" << círculo1.getArea() << endl;
22
"
23 cout << "circle1.getNumberOfObjects() retorna <<
invocar função estática 24 25 26 27 círculo1.getNumberOfObjects() << endl;
Machine Translated by Google

10.5 Instância e Membros Estáticos 409


"
28 cout << "circle2.getNumberOfObjects() retorna <<
29 círculo2.getNumberOfObjects() << endl; invocar função estática
30
31 retornar 0;
32 }

Número de objetos circulares criados: 0


A área do círculo de raio 1 é 3,14159
Número de objetos circulares criados: 1
A área do círculo de raio 5 é 78,5397
Número de objetos circulares criados: 2
A área do círculo de raio 3,3 é 34,2119
círculo1.getNumberOfObjects() retorna 2
círculo2.getNumberOfObjects() retorna 2

Variáveis e funções estáticas podem ser acessadas sem criar objetos. A linha 8 exibe o número de objetos, que
é 0, pois nenhum objeto foi criado.
A função principal cria dois círculos, círculo1 e círculo2 (linhas 10, 16). A variável de instância radius em
círculo1 é modificada para se tornar 3,3 (linha 22). Esta alteração não afeta a variável de instância radius em
círculo2, pois essas duas variáveis de instância são independentes.
A variável estática numberOfObjects torna-se 1 após a criação do círculo1 (linha 10) e torna-se 2 após a criação
do círculo2 (linha 16).
Você pode acessar campos de dados estáticos e funções a partir das instâncias da classe — por exemplo,
Circle1.getNumberOfObjects() na linha 27 e Circle2.getNumberOfObjects() na linha 29. Mas é melhor acessá-los
a partir do nome da classe — por exemplo, Circle ::. Observe que nas linhas 27 e 29 Circle1.getNumberOfObjects()
e Circle2.getNumberOfObjects()
poderia ser substituído por Circle::getNumberOfObjects(). Isso melhora a legibilidade, porque o leitor pode
reconhecer facilmente a função estática getNumberOfObjects().

Dica
Use ClassName::functionName(arguments) para invocar uma função estática e usar nome de classe

ClassName::staticVariable para acessar variáveis estáticas. Isso melhora a legibilidade,


porque o usuário pode reconhecer facilmente a função estática e os dados na classe.

Dica
Como você decide se uma variável ou função deve ser instância ou estática? Uma variável instância ou estática?
ou função que depende de uma instância específica da classe deve ser uma variável ou
função de instância. Uma variável ou função que não depende de uma instância específica
da classe deve ser uma variável ou função estática. Por exemplo, cada círculo tem seu
próprio raio. O raio depende de um círculo específico. Portanto, radius é uma variável
de instância da classe Circle . Como a função getArea depende de um círculo específico,
é uma função de instância. Como numberOfObjects não depende de nenhuma instância
específica, ele deve ser declarado estático.

10.15 Um campo de dados e uma função podem ser declarados como instância ou estáticos. Quais são os critérios
para decidir? ÿVerificação de ponto

10.16 Onde você inicializa um campo de dados estáticos?

10.17 Suponha que a função f() seja estática definida na classe C e c seja um objeto da classe C.
Você pode invocar cf(), C::f() ou c::f()?
Machine Translated by Google

410 Capítulo 10 Pensamento Orientado a Objetos

10.6 Funções de Membros Constantes


C++ também permite especificar uma função de membro constante para informar ao compilador
Chave
Apontar que a função não deve alterar o valor de nenhum campo de dados no objeto.

Você usou a palavra-chave const para especificar um parâmetro constante para informar ao compilador
que o parâmetro não deve ser alterado na função. Você também pode usar a palavra-chave const para
função constante especificar uma função de membro constante (ou simplesmente uma função constante) para informar ao
compilador que a função não altera os campos de dados no objeto. Para fazer isso, coloque a palavra-
chave const no final do cabeçalho da função. Por exemplo, você pode redefinir a classe Circle na Listagem
10.6 conforme mostrado na Listagem 10.9, e o arquivo de cabeçalho é implementado na Listagem 10.10.

Listagem 10.9 CircleWithConstantMemberFunctions.h


1 #ifndef CÍRCULO_H
2 #define CÍRCULO_H

3 4 classe Círculo
{
5 6 público:
7 Círculo();
8 Círculo(duplo);
função constante double getArea() const;
função constante 9 10 double getRadius() const;
11 void setRadius(duplo);
12 static int getNumberOfObjects();
13
14 privado:
15 raio duplo ;
16 static int numberOfObjects;
17};
18
19 #endif

Listagem 10.10 CircleWithConstantMemberFunctions.cpp


1 #include "CircleWithConstantMemberFunctions.h"

2 3 int Circle::numberOfObjects = 0;

4 5 // Construa um objeto circular


6 Círculo::Círculo()
7{
raio = 1;
númeroDeObjetos++;
8 9 10}
11
12 // Construa um objeto circular
13 Círculo::Círculo(double newRadius)
14 {
15 raio = novoRaio;
16 númeroDeObjetos++;
17}
18
19 // Retorna a área deste círculo
função constante 20 círculo duplo ::getArea() const
21 {
22 raio de retorno * raio * 3,14159;
23}
Machine Translated by Google

10.6 Funções de Membros Constantes 411

24
25 // Retorna o raio deste círculo
26 círculo duplo ::getRadius() const função constante
27 {
raio de retorno ;
28 29}
30
31 // Defina um novo raio
32 círculo vazio ::setRadius(double newRadius)
33 {
raio = (novoRaio >= 0) ? novoRaio: 0;
34 35}
36
37 // Retorna o número de objetos circulares
38 int Círculo::getNumberOfObjects()
39 {
40 retornar númeroDeObjetos;
41}

Somente funções-membro de instância podem ser definidas como funções constantes. Assim como os parâmetros programação defensiva
constantes, as funções constantes são para programação defensiva. Se a sua função alterar por engano o valor dos
campos de dados em uma função, um erro de compilação será relatado. Observe que você pode definir apenas
funções instantâneas constantes, não funções estáticas. Uma função get de instância sempre deve ser definida como
uma função membro constante, porque não altera o conteúdo do objeto.
Se uma função não altera o objeto que está sendo passado, você deve definir o parâmetro
constante usando a palavra-chave const assim: parâmetro constante

void printCircle(const Círculo& c)


{
cout << "A área do círculo de "
<< c.getRadius() << "é" << c.getArea() << endl;
}

Observe que este código não será compilado se a função getRadius() ou getArea() não estiver definida como const.
Se você usar a classe Circle definida na Listagem 9.9, a função anterior não será compilada, porque getRadius() e
getArea() não são const definidos. Entretanto, se você usar a classe Circle definida na Listagem 10.9, a função
anterior será compilada, porque getRadius() e getArea() são definidos como const.

Dica
Você pode usar o modificador const para especificar um parâmetro de referência constante ou use const consistentemente
uma função de membro constante. Você deve usar o modificador const consistentemente sempre
que apropriado.

10.18 Verdadeiro ou falso? Somente funções-membro de instância podem ser definidas como constantes
funções. ÿVerificação de ponto

10.19 O que há de errado na seguinte definição de classe?

contagem de classe
{
público:
contagem interna ;

Contagem (int c)
{
contagem = c;
}
Machine Translated by Google

412 Capítulo 10 Pensamento Orientado a Objetos

Contar()
{
contagem = 0;
}

int getCount() const {

contagem de retorno ;
}

void incrementCount() const {

contar++;

} };

10.20 O que há de errado no código a seguir?

#include <iostream>
usando namespace std;

classe A

{ público:
A();
double getNúmero();

privado:
número duplo ; };

R::A() {

número = 1;
}

double A::getNumber()

{ número de retorno ;
}

void printA(const A& a) {

cout << "O número é " << a.getNumber() << endl;


}

int principal()
{
Um meuObject;
printA(meuObjeto);

retornar 0;
}

Nota de vídeo

Pensando em objetos 10.7 Pensando em Objetos


O paradigma processual concentra-se no design de funções. O paradigma
Chave
Apontar orientado a objetos une dados e funções em objetos. O design de software usando o
paradigma orientado a objetos concentra-se em objetos e em operações sobre objetos.
Machine Translated by Google

10.7 Pensando em Objetos 413

Este livro introduziu técnicas fundamentais de programação para resolução de problemas usando loops, funções
e arrays. O estudo dessas técnicas estabelece uma base sólida para a programação orientada a objetos. As
classes fornecem mais flexibilidade e modularidade para a construção de software reutilizável. Esta seção usa a
abordagem orientada a objetos para melhorar a solução de um problema apresentado no Capítulo 3. Observando
as melhorias, você obterá insights sobre as diferenças entre a programação processual e a programação orientada
a objetos e verá os benefícios de desenvolver código reutilizável usando objetos. e aulas.

A Listagem 3.2, ComputeAndInterpreteBMI.cpp, apresentou um programa para calcular o índice de massa


corporal. O programa não pode ser reutilizado em outros programas. Para tornar o código reutilizável, defina uma
função para calcular o índice de massa corporal da seguinte forma:

getBMI duplo ( peso duplo, altura dupla )

Esta função é útil para calcular o índice de massa corporal para um peso e altura específicos.
No entanto, tem limitações. Suponha que você precise associar o peso e a altura ao nome e à data de nascimento
de uma pessoa. Você pode declarar variáveis separadas para armazenar esses valores. Mas esses valores não
estão fortemente acoplados. A forma ideal de acoplá-los é criar um objeto que os contenha. Como esses valores
estão vinculados a objetos individuais, eles devem ser armazenados em campos de dados de instância. Você
pode definir uma classe chamada IMC, conforme mostrado na Figura 10.11.

As funções get para esses campos de dados são


fornecidas na classe, mas omitidas no diagrama
UML por questões de brevidade.

IMC

-nome: string O nome da pessoa.

-idade: int A idade da pessoa.

-peso: duplo O peso da pessoa em libras.

-altura: duplo A altura da pessoa em polegadas.

+IMC(nome: string, idade: int, peso: duplo, altura: Cria um objeto de IMC com nome, idade,
duplo) peso e altura especificados.

+IMC(nome: string, peso: duplo, altura: duplo) Cria um objeto IMC com o especificado
nome, peso, altura e idade padrão de 20 anos.

+getIMC(): const dupla Retorna o IMC.

+getStatus(): string const Retorna o status do IMC (por exemplo,


normal, sobrepeso, etc.)

Figura 10.11 A classe IMC encapsula informações de IMC.

A classe de IMC pode ser definida como na Listagem 10.11.

Listagem 10.11 IMC.h


1 #ifndef IMC_H
2 #define IMC_H

3 4 #incluir <string>
5 usando namespace std;

6 7 classe IMC
8{
9 público:
Machine Translated by Google

414 Capítulo 10 Pensamento Orientado a Objetos

construtores 10 IMC (const string& newName, int newAge, double


11 newWeight, double newHeight);
12 IMC (const string& newName, double newWeight, double newHeight);
funções 13 double getBMI() const;
14 string getStatus() const;
15 string getNome() const;
16 int getIdade() const;
17 double getPeso() const;
18 double getHeight() const;
19
20 privado:
21 nome da sequência;
22 idade interna ;
23 peso duplo ;
24 altura dupla ;
25};
26
27 #endif

Dica
O parâmetro string newName é definido como passagem por referência usando a sintaxe string&
newName. Isso melhora o desempenho, evitando que o compilador faça uma cópia do objeto que
está sendo passado para a função. Além disso, a referência é definida como const para evitar que
newName seja modificado acidentalmente. Você deve sempre passar um parâmetro de objeto por
referência. Se o objeto não mudar na função, defina-o como um parâmetro de referência const .
parâmetro de referência const

Dica
Se uma função membro não alterar os campos de dados, defina-a como uma função const . Todas as
função constante funções-membro da classe BMI são funções const .

Suponha que a classe IMC tenha sido implementada. A Listagem 10.12 é um programa de teste que
utiliza esta classe.

Listagem 10.12 UseBMIClass.cpp


1 #include <iostream>
2 #include "IMC.h"
3 usando namespace std;

4 5 int principal()
6{
criar objeto IMC bmi1("John Doe", 18, 145, 70);
invocar função de instância cout << "O IMC para " << << bmi1.getName() << "é"
""
bmi1.getBMI() << << bmi1.getStatus() << endl;
7
criar objeto 8 IMC imc2("Susan King", 215, 70);
invocar função de instância 9 cout << "O IMC para " << << bmi2.getName() << "é"
""
10 bmi2.getBMI() << + bmi2.getStatus() << endl;
11
12 retornar 0;
13 14 15 16 }

O IMC para John Doe é 20,8051 Normal


O IMC de Susan King é 30,849 obesa
Machine Translated by Google

10.7 Pensando em Objetos 415

A linha 7 cria um objeto bmi1 para John Doe e a linha 11 cria um objeto bmi2 para Susan King. Você
pode usar as funções de instância getName(), getBMI() e getStatus() para retornar as informações de IMC
em um objeto de IMC .
A classe IMC pode ser implementada como na Listagem 10.13.

Listagem 10.13 IMC.cpp


1 #include <iostream>
2 #include "IMC.h"
3 usando namespace std;

4 5 IMC::IMC(const string& newName, int newAge, double newWeight, construtor


double newHeight)
67{
nome = novoNome;
idade = novaIdade;
8 peso = novoPeso;
9 altura = novaAltura;
10 11 12 }
13
14 IMC::IMC(const string& newName, double newWeight, double newHeight) construtor
15 {
16 nome = novoNome;
17 idade = 20;
18 peso = novoPeso;
19 altura = novaAltura;
20}
21
22 IMC duplo ::getBMI() const obter IMC
23 {
24 const duplo QUILOGRAMAS_PER_POUND = 0,45359237;
25 const duplo METERS_PER_INCH = 0,0254;
26 IMC duplo = peso * QUILOGRAMAS_PER_POUND /
27 ((altura * METERS_PER_INCH) * (altura * METERS_PER_INCH));
28 retornar IMC;
29}
30
31 string IMC::getStatus() const obterStatus
32 {
33 duplo IMC = getIMC();
34 se (IMC < 18,5)
35 retornar "Abaixo do peso";
36 senão se (IMC < 25)
37 retornar "Normal";
38 senão se (IMC < 30)
39 retornar "Excesso de peso";
40 outro
41 retornar "Obeso";
42 }
43
44 string IMC::getName() const
45 {
46 nome de retorno ;
47}
48
49 int IMC::getAge() const
50 {
51 idade de retorno ;
52}
Machine Translated by Google

416 Capítulo 10 Pensamento Orientado a Objetos

53 54 IMC duplo ::getPeso() const


55 {
peso de retorno ;
56 57}

58 59 IMC duplo ::getHeight() const


60 {
61 altura de retorno ;
62}

A fórmula matemática para calcular o IMC usando peso e altura é fornecida na Seção 3.7, “Estudo de
caso: Calculando o Índice de Massa Corporal”. A função de instância getBMI()
retorna o IMC. Como o peso e a altura são campos de dados de instância no objeto, a função getBMI()
pode usar essas propriedades para calcular o IMC do objeto.
A função de instância getStatus() retorna uma string que interpreta o IMC. A interpretação também é
dada na Seção 3.7.
Este exemplo demonstra as vantagens de usar o paradigma orientado a objetos em relação ao
paradigma processual. O paradigma processual concentra-se no design de funções. O paradigma
Processual versus Objeto orientado a objetos une dados e funções em objetos. O design de software usando o paradigma orientado
Paradigmas Orientados a objetos concentra-se em objetos e em operações sobre objetos. A abordagem orientada a objetos
combina o poder do paradigma processual com uma dimensão adicional que integra dados com operações
em objetos.
Na programação processual, os dados e as operações nos dados são separados, e esta metodologia
requer o envio de dados para funções. A programação orientada a objetos coloca os dados e as operações
que lhes pertencem em uma única entidade chamada objeto ; esta abordagem resolve muitos dos
problemas inerentes à programação processual. A abordagem de programação orientada a objetos
organiza os programas de uma forma que reflete o mundo real, no qual todos os objetos estão associados
a atributos e atividades. O uso de objetos melhora a reutilização do software e torna os programas mais
fáceis de desenvolver e manter.

10.21 Qual é a saída do código a seguir?


ÿVerificação de ponto
#include <iostream>
#incluir <string>
#include "IMC.h"
usando namespace std;

int principal()
{
string nome("John Doe");
IMC imc1(nome, 18, 145, 70);
nome[0] = 'P';

cout << "nome de bmi1.getName() é " << bmi1.getName() <<


fim;
cout << "nome é" << nome << endl;

retornar 0;
}

10.22 No código a seguir, qual será a saída de as e bk no main


função?

#include <iostream>
#incluir <string>
usando namespace std;
Machine Translated by Google

10.7 Pensando em Objetos 417

classe A

{ público:
A()
{
s = "João";
}

cordas;
}

classe B

{ público:
B(){

k = 4; };

intk ; };

int principal()
{
Uma;
cout << as << endl;

Bb;
cout << bk << endl;

retornar 0;
}

10.23 O que há de errado no código a seguir?

#include <iostream>
#include <string>
usando namespace std;

classe A

{ público:
A() { };
strings ("abc"); };

int principal()
{
Uma;
cout << as << endl;

retornar 0;
}

10.24 O que há de errado no código a seguir?

#include <iostream>
#include <string>
usando namespace std;
classe A
Machine Translated by Google

418 Capítulo 10 Pensamento Orientado a Objetos

{
público:
A() { };

privado:
cordas;
};

int principal()
{
Uma;
cout << as << endl;

retornar 0;
}

10.8 Composição de Objetos


Um objeto pode conter outro objeto. A relação entre os dois é chamada de composição.
Chave
Apontar

Na Listagem 10.11, você definiu a classe IMC para conter um campo de dados de string . A relação entre o IMC
e o barbante é a composição.
agregação A composição é na verdade um caso especial do relacionamento de agregação . Os modelos de agregação
tem um relacionamento possuem relacionamentos e representam um relacionamento de propriedade entre dois objetos. O objeto
proprietário é chamado de objeto agregador e sua classe de classe agregadora. O objeto sujeito é chamado de
objeto agregado e sua classe de classe agregada.
Um objeto pode pertencer a vários outros objetos agregadores. Se um objeto pertence exclusivamente a um
objeto agregador, o relacionamento entre o objeto e seu objeto agregador é denominado composição. Por
composição exemplo, “um aluno tem um nome” é uma relação de composição entre a classe Aluno e a classe Nome ,
enquanto “um aluno tem um endereço” é uma relação de agregação entre a classe Aluno e a classe Endereço ,
uma vez que um endereço pode ser compartilhado por vários alunos. Na UML, um losango preenchido é anexado
a uma classe agregadora (por exemplo, Aluno) para denotar o relacionamento da composição com uma classe
agregada (por exemplo, Nome), e um losango vazio é anexado a uma classe agregadora (por exemplo, Aluno)
para denotam o relacionamento de agregação com uma classe agregada (por exemplo, Endereço), conforme
mostrado na Figura 10.12.

Composição Agregação

1 1 1 1..3
Nome Estudante Endereço

Figura 10.12 Um aluno tem um nome e um endereço.

multiplicidade Cada classe envolvida em um relacionamento pode especificar uma multiplicidade. Uma multiplicidade pode
ser um número ou intervalo que especifica quantos objetos da classe estão envolvidos no relacionamento. O
caractere * significa um número ilimitado de objetos, e o intervalo m..n significa que o número de objetos deve
estar entre m e n, inclusive. Na Figura 10.12, cada aluno possui apenas um endereço, e cada endereço pode ser
compartilhado por até três alunos. Cada aluno tem um nome e um nome é exclusivo para cada aluno.
Machine Translated by Google

10.8 Composição de Objeto 419

Um relacionamento de agregação geralmente é representado como um campo de dados na classe de agregação.


Por exemplo, o relacionamento na Figura 10.12 pode ser representado da seguinte forma:

nome da classe classe Aluno endereço da classe


{ { {
... privado: ...
} Nome nome; }
Endereço de endereço;

...
}

Classe agregada Agregando classe Classe agregada

Pode existir agregação entre objetos da mesma classe. Por exemplo, uma pessoa pode ter um supervisor. Isso é
ilustrado na Figura 10.13.

1
Pessoa

1 Supervisor

Figura 10.13 Uma pessoa pode ter um supervisor.

Na relação “uma pessoa tem um supervisor”, conforme mostrado na Figura 10.13, um supervisor pode
ser representado como um campo de dados na classe Person , como segue:

classe Pessoa

{ privado:
Supervisor de pessoa; // O tipo dos dados é a própria classe

...
}

Se uma pessoa puder ter vários supervisores, como mostrado na Figura 10.14, você poderá usar um array para
armazenar supervisores (por exemplo, 10 supervisores).

classe Pessoa
1 {
Pessoa ...
Supervisor privado:
eu
Supervisores pessoais[10];
}

Figura 10.14 Uma pessoa pode ter vários supervisores.

Observação

Como os relacionamentos de agregação e composição são representados usando classes de maneiras agregação ou composição
semelhantes, não os diferenciaremos e chamaremos ambas de composições.

10.25 O que é composição de objetos?


ÿVerificação de ponto
10.26 Qual é a diferença entre agregação e composição?

10.27 O que é a notação UML de agregação e composição?

10.28 Por que tanto a agregação quanto a composição são chamadas juntas de composição?
Machine Translated by Google

420 Capítulo 10 Pensamento Orientado a Objetos

10.9 Estudo de caso: a classe StackOfIntegers


Esta seção projeta uma classe para modelar pilhas.
Chave
Apontar
Lembre-se de que uma pilha é uma estrutura de dados que contém dados do tipo último a entrar, primeiro a sair, conforme
mostrado na Figura 10.15.

Dados1 Dados2 Dados3

Dados3
Dados2 Dados2
Dados1 Dados1 Dados1

Dados3 Dados2 Dados1

Dados2
Dados1 Dados1

Figura 10.15 Uma pilha contém dados do tipo último a entrar, primeiro a sair.

pilha As pilhas têm muitas aplicações. Por exemplo, o compilador usa uma pilha para processar invocações de função. Quando
uma função é invocada, seus parâmetros e variáveis locais são colocados em um registro de ativação que é colocado em uma
pilha. Quando uma função chama outra função, os parâmetros e variáveis locais da nova função são colocados em um novo
registro de ativação que é colocado na pilha. Quando uma função termina seu trabalho e retorna ao seu chamador, seu registro de
ativação é liberado da pilha.

Você pode definir uma classe para modelar pilhas. Para simplificar, suponha que a pilha contenha os valores int . Portanto,
nomeie a classe de pilha como StackOfIntegers. O diagrama UML para a classe é mostrado na Figura 10.16.

StackOfIntegers

-elementos[100]: int Uma matriz para armazenar inteiros na pilha.


-tamanho: int O número de inteiros na pilha.

+StackOfIntegers() Constrói uma pilha vazia.


+isEmpty(): bool const Retorna verdadeiro se a pilha estiver vazia
+ espiar(): int const Retorna o número inteiro no topo da pilha sem
removendo-o da pilha.
+push(valor: int): vazio Armazena um número inteiro no topo da pilha.
+pop(): int Remove o número inteiro no topo da pilha e o retorna.
+getSize():int const Retorna o número de elementos na pilha.

Figura 10.16 A classe StackOfIntegers encapsula o armazenamento da pilha e fornece as operações para manipular a pilha.

Suponha que a classe esteja disponível, conforme definido na Listagem 10.14. Vamos escrever um programa de teste na
Listagem 10.15 que usa a classe para criar uma pilha (linha 7), armazena dez inteiros 0, 1, 2, . . . e 9 (linhas 9–10) e os exibe na
ordem inversa (linhas 12–13).

Listagem 10.14 StackOfIntegers.h


1 #ifndef STACK_H
2 #define STACK_H
3
Machine Translated by Google

10.9 Estudo de caso: A classe StackOfIntegers 421

4 classes StackOfIntegers
{
5 6 público: membros públicos
StackOfIntegers();
7 8 bool isEmpty() const;
int espiar() const;
9 10 void push( valor int);
11 intpop ();
12 int getSize() const;
13
14 privado: membros privados
15 elementos internos [100]; matriz de elementos
16 tamanho interno ;
17};
18
19 #endif

Listagem 10.15 TestStackOfIntegers.cpp


1 #include <iostream>
2 #include "StackOfIntegers.h" Cabeçalho StackOfIntegers
3 usando namespace std;

4 5 int principal()
6{
Pilha StackOfIntegers; criar uma pilha

para (int i = 0; i < 10; i++)


7 pilha.push(i); empurre para empilhar
8
9 enquanto (!stack.isEmpty()) pilha vazia?
10 cout << stack.pop() << " "; sair da pilha
11
12 retornar 0;
13 14 15 16 }

9876543210

Como você implementa a classe StackOfIntegers ? Os elementos da pilha são


armazenados em um array denominado elementos. Quando você cria uma pilha, o array
também é criado. O construtor no-arg inicializa size como 0. A variável size conta o número
de elementos na pilha e size – 1 é o índice do elemento no topo da pilha, conforme mostrado
na Figura 10.17. Para uma pilha vazia, o tamanho é 0.

elementos[capacidade - 1]
.
.
.

elementos[tamanho – 1] principal

. capacidade 100

.
tamanho

elementos[1]
elementos[0] fundo

Figura 10.17 A classe StackOfIntegers encapsula o armazenamento da pilha e fornece


as operações para manipular a pilha.
Machine Translated by Google

422 Capítulo 10 Pensamento Orientado a Objetos

A classe StackOfIntegers é implementada na Listagem 10.16.

Listagem 10.16 StackOfIntegers.cpp


Cabeçalho StackOfIntegers 1 #include "StackOfIntegers.h"

construtor 2 3 StackOfIntegers::StackOfIntegers()
4{
inicializar tamanho tamanho = 0;
5 6}
7
8 bool StackOfIntegers::isEmpty() const
9{
10 tamanho de retorno == 0;
11}
12
13 int StackOfIntegers::peek() const
14 {
15 retornar elementos[tamanho - 1];
16}
17
18 void StackOfIntegers::push( valor int)
19 {
20 elementos[tamanho++] = valor;
21}
22
23 int StackOfIntegers::pop()
24 {
25 retornar elementos[--tamanho];
26}
27
28 int StackOfIntegers::getSize() const
29 {
30 tamanho de retorno ;
31}

10.29 Quando uma pilha é criada, quais são os valores iniciais do array de elementos ?
ÿVerificação de ponto
10.30 Quando uma pilha é criada, qual é o valor do tamanho da variável?

10.10 Diretrizes de Design de Classe


As diretrizes de design de classe são úteis para projetar classes de som.
Chave
Apontar
Este capítulo se preocupa principalmente com o design orientado a objetos. Embora existam muitas
metodologias orientadas a objetos, a UML tornou-se a notação padrão da indústria para modelagem
orientada a objetos e por si só leva a uma metodologia. O processo de projetar classes exige a
identificação das classes e a descoberta dos relacionamentos entre elas.
Você aprendeu como projetar classes a partir dos exemplos deste capítulo e de
muitos outros exemplos nos capítulos anteriores. Aqui estão algumas diretrizes.

10.10.1 Coesão
Uma classe deve descrever uma única entidade e todas as operações da classe devem se encaixar
propósito coerente logicamente para apoiar um propósito coerente. Você pode usar uma turma para alunos, por exemplo, mas
não deve combinar alunos e funcionários na mesma turma, pois alunos e funcionários são entidades diferentes.
separando responsabilidades Uma única entidade com muitas responsabilidades pode ser dividida em várias classes para separar
responsabilidades.
Machine Translated by Google

10.10 Diretrizes de Design de Classe 423

10.10.2 Consistência
Siga o estilo de programação padrão e as convenções de nomenclatura. Escolha nomes informativos para classes, convenções de nomenclatura

campos de dados e funções. Um estilo popular em C++ é colocar a declaração de dados após as funções e colocar
os construtores antes das funções.
Escolha nomes de forma consistente. É uma boa prática escolher os mesmos nomes para nomes semelhantes. consistência de nomenclatura

operações usando sobrecarga de função.


Em geral, você deve fornecer consistentemente um construtor público sem argumentos para construir uma construtor sem argumento
instância padrão. Se uma classe não suportar um construtor sem argumentos, documente o motivo. Se nenhum
construtor for definido explicitamente, um construtor público padrão sem argumentos com corpo vazio será assumido.

10.10.3 Encapsulamento
Uma classe deve usar o modificador private para ocultar seus dados do acesso direto dos clientes. Isso torna a encapsulando campos de dados
classe fácil de manter.
Forneça uma função get somente se desejar que o campo seja legível e forneça uma função set somente se
desejar que o campo seja atualizável. Uma classe também deve ocultar funções não destinadas ao uso do cliente.
Tais funções devem ser definidas como privadas.

10.10.4 Clareza
Coesão, consistência e encapsulamento são boas diretrizes para alcançar clareza no design.
Além disso, uma aula deve ter um contrato claro, fácil de explicar e entender. fácil de explicar
Os usuários podem incorporar classes em muitas combinações, ordens e ambientes diferentes. funções independentes
Portanto, você deve projetar uma classe que não imponha restrições sobre o que o usuário pode fazer com ela ou
quando, projetar as propriedades de uma forma que permita ao usuário defini-las em qualquer ordem e com
qualquer combinação de valores, e projetar funções de forma independente. da sua ordem de ocorrência. Por
exemplo, a classe Loan na Listagem 9.13 contém as funções setLoanAmount, setNumberOfYears e
setAnnualInterestRate. Os valores dessas propriedades podem ser definidos em qualquer ordem.
Você não deve declarar um campo de dados que possa ser derivado de outros campos de dados. Por exemplo, propriedades independentes
a seguinte classe Person possui dois campos de dados: data de nascimento e idade. Como a idade pode ser
derivada da data de nascimento, a idade não deve ser declarada como um campo de dados.

classe Pessoa {

público:
...

privado:
Data nascimentoData;
idade interna ;
}

10.10.5 Completude
As aulas são projetadas para serem usadas por muitos clientes diferentes. Para ser útil em uma ampla gama de
aplicações, uma classe deve fornecer diversas maneiras de personalização por meio de propriedades e funções.
Por exemplo, a classe string contém mais de 20 funções que são úteis para diversas aplicações.

10.10.6 Instância vs. Estática


Uma variável ou função que depende de uma instância específica da classe deve ser uma variável ou função de
instância. Uma variável compartilhada por todas as instâncias de uma classe deve ser declarada estática. Por
exemplo, a variável numberOfObjects em Circle na Listagem 10.9 é
Machine Translated by Google

424 Capítulo 10 Pensamento Orientado a Objetos

compartilhado por todos os objetos da classe Circle e, portanto, é declarado estático. Uma função que não depende de
uma instância específica deve ser definida como uma função estática. Por exemplo, a função getNumberOfObjects em
Circle não está vinculada a nenhuma instância específica e, portanto, é definida como uma função estática.

Sempre faça referência a variáveis e funções estáticas de um nome de classe (em vez de um objeto)
para melhorar a legibilidade e evitar erros.
Um construtor é sempre uma instância, porque é usado para criar uma instância específica. Uma variável ou função
estática pode ser invocada a partir de uma função de instância, mas uma variável ou função de instância não pode ser
invocada a partir de uma função estática.

10.31 Descreva as diretrizes de design de classe.


ÿVerificação de ponto

Termos chave

agregação 418 função de instância 406


composição 418 variável de instância 406
função constante 414 multiplicidade 418
tem um relacionamento 418 função estática 406
campo de dados de instância 406 variável estática 406

Resumo do capítulo

1. A classe de string C++ encapsula uma matriz de caracteres e fornece muitas funções
para processar strings, como anexar, atribuir, at, limpar , apagar , esvaziar ,
comprimento, c_str, comparar, substr, localizar, inserir e substituir.

2. C++ suporta operadores ([], =, +, +=, <<, >>, ==, !=, <, <=, >, >=) para simplificar string
operações.

3. Você pode usar cin para ler uma string terminando com um caractere de espaço em branco e usar getline(cin, s,
delimiterCharacter) para ler uma string terminando com o caractere delimitador especificado.

4. Você pode passar um objeto para uma função por valor ou por referência. Para desempenho, é preferível passar
por referência.

5. Se a função não alterar o objeto que está sendo passado, defina o parâmetro do objeto como um parâmetro de
referência constante para evitar que os dados do objeto sejam modificados acidentalmente.

6. Uma variável ou função de instância pertence a uma instância de uma classe. Seu uso está associado a instâncias
individuais.

7. Uma variável estática é uma variável compartilhada por todas as instâncias da mesma classe.

8. Uma função estática é uma função que pode ser invocada sem o uso de instâncias.

9. Cada instância de uma classe pode acessar variáveis e funções estáticas da classe.
Para maior clareza, entretanto, é melhor invocar variáveis e funções estáticas usando ClassName::staticVariable
e ClassName::functionName(arguments).

10. Se uma função não altera os campos de dados de um objeto, defina a constante da função para
evitar erros.
Machine Translated by Google

Exercícios de Programação 425

11. Uma função constante não altera os valores de nenhum campo de dados.

12. Você pode especificar uma função membro como constante colocando o modificador const no
final da declaração da função.

13. A abordagem orientada a objetos combina o poder do paradigma processual com uma dimensão
adicional que integra dados com operações em objetos.

14. O paradigma processual centra-se na concepção de funções. O paradigma orientado a objetos


une dados e funções em objetos.

15. O design de software usando o paradigma orientado a objetos concentra-se em objetos e operações
em objetos.

16. Um objeto pode conter outro objeto. A relação entre os dois é chamada
composição.

17. Algumas diretrizes para o design de classes são coesão, consistência, encapsulamento, clareza e
completude.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 10.2–10.6

*10.1 (Anagramas) Escreva uma função que verifique se duas palavras são anagramas. Duas palavras são
anagramas se contiverem as mesmas letras em qualquer ordem. Por exemplo, “silencioso” e
“ouvir” são anagramas. O cabeçalho da função é o seguinte:

bool éAnagrama(const string& s1, const string& s2)

Escreva um programa de teste que solicite ao usuário que insira duas strings e verifique se são
anagramas. Aqui estão exemplos de execuções:

Insira uma string s1: silencioso


Insira uma string s2: listen
silencioso e listen são anagramas

Insira uma string s1: divisão


Insira uma string s2: lisp split
e lisp não são anagramas

*10.2 (Caracteres comuns) Escreva uma função que retorne os caracteres comuns de dois
strings usando o seguinte cabeçalho:

string commonChars(const string& s1, const string& s2)

Escreva um programa de teste que solicite ao usuário que insira duas strings e exiba seus
caracteres comuns. Aqui estão alguns exemplos de execução:
Machine Translated by Google

426 Capítulo 10 Pensamento Orientado a Objetos

Insira uma string s1: abcd


Insira uma string s2: aecaten
Os caracteres comuns são ac

Insira uma string s1: abcd


Insira uma string s2: efg
Nenhum personagem comum

**10.3 (Bioinformática: encontrar genes) Os biólogos usam uma sequência de letras A, C, T e G para modelar um genoma. Um gene é uma

substring de um genoma que começa após um tripleto ATG

e termina antes de um trio TAG, TAA ou TGA. Além disso, o comprimento de uma sequência
genética é um múltiplo de 3 e o gene não contém nenhum dos trigêmeos ATG, TAG, TAA e
TGA. Escreva um programa que solicite ao usuário que insira um genoma e exiba todos os
genes do genoma. Se nenhum gene for encontrado na sequência de entrada, não exibirá nenhum gene.
Aqui estão os exemplos de execução:

Insira uma sequência de genoma: TTATGTTTTAAGGATGGGGCGTTAGTT


TTT
GGGCGT

Insira uma sequência de genoma: TGTGTGTATAT


nenhum gene foi encontrado

10.4 (Classificar caracteres em uma string) Escreva uma função que retorne uma string classificada usando o seguinte cabeçalho:

classificação de string(string& s)

Escreva um programa de teste que solicite ao usuário que insira uma string e exiba a nova string classificada. Aqui está um

exemplo de execução do programa:

Digite uma string s: silencioso


A string classificada é eilnst

*10.5 (Verificar palíndromo) Escreva a seguinte função para verificar se uma string é um palíndromo, assumindo que as letras não

diferenciam maiúsculas de minúsculas:

bool éPalindrome(const string& s)

Escreva um programa de teste que leia uma string e mostre se ela é um palíndromo.

Aqui estão alguns exemplos de execução:

Digite uma string s: ABa


Aba é um palíndromo

Digite uma string s: AcBa


Acba não é um palíndromo
Machine Translated by Google

Exercícios de Programação 427

*10.6 (Contar as vogais em uma string) Escreva a função countVowels que conta o número de vogais usando
a classe string da seguinte maneira:

int contagemVogais(const string& s)

Escreva um programa de teste que solicite ao usuário que insira uma string e invoque o método countVowels
função e exibe o número total de vogais na string.

*10.7 (Contar ocorrências de cada dígito em um número inteiro) Escreva a função countDigits
usando o seguinte cabeçalho para contar as ocorrências de cada dígito:

void countDigits(const int& número, int dArray[], tamanho int )

onde tamanho é o tamanho da matriz dArray . Neste caso, é 10 para armazenar a contagem de
dez dígitos, ou seja, de 0 a 9.

Escreva um programa de teste que solicite ao usuário que insira um número inteiro, invoque a
função countDigits e exiba as contagens de cada dígito no número inteiro fornecido.

*10.8 (Aplicação financeira: unidades monetárias) Reescreva a Listagem 2.12, ComputeChange.


cpp, para corrigir a possível perda de precisão ao converter um valor flutuante em um valor inteiro.
Insira a entrada como uma string como "11.56". Seu programa deve extrair o valor em dólares antes
da vírgula decimal e os centavos após o valor decimal.

**10.9 (Adivinhar letras maiúsculas) Escreva um programa que solicite repetidamente ao usuário que digite uma
letra maiúscula para um estado. Ao receber a entrada do usuário, o programa informa se a resposta
está correta. Um exemplo de execução é mostrado abaixo:

Qual é a capital do Alabama? Montgomery Sua resposta


está correta.
Qual é a capital do Alasca? Anchorage A capital do
Alasca é Juneau

Suponha que cinquenta estados e suas capitais estejam armazenados em uma matriz bidimensional,
conforme mostrado na Figura 10.18. O programa solicita que o usuário insira as capitais de dez
estados e exibe a contagem total correta.

Alabama Montgomery
Alasca Juneau
Arizona Fénix
... ...

Figura 10.18 Um array bidimensional armazena estados e suas capitais.

Seção 10.7
10.10 (A classe MyInteger ) Projete uma classe chamada MyInteger. A classe contém o Nota de vídeo
seguindo: A classe MyInteger
n Um campo de dados int denominado valor que armazena o valor int representado por este objeto.
n Um construtor que cria um objeto MyInteger para o valor int especificado .
n Uma função get constante que retorna o valor int .
n Funções constantes isEven(), isOdd(), isPrime() que retornam verdadeiro se o
o valor é par, ímpar ou primo, respectivamente.
n Funções estáticas isEven(int), isOdd(int), isPrime(int) que retornam verdadeiro
se o valor especificado for par, ímpar ou primo, respectivamente.
Machine Translated by Google

428 Capítulo 10 Pensamento Orientado a Objetos

n Funções estáticas isEven(const MyInteger&), isOdd(const MyInteger&),


isPrime(const MyInteger&) que retornam verdadeiro se o valor especificado
for par, ímpar ou primo, respectivamente.
n Funções constantes equals(int) e equals(const MyInteger&) que retornam verdadeiro se o valor
no objeto for igual ao valor especificado.
n Uma função estática parseInt(const string&) que converte uma string em um int
valor.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa cliente que
teste todas as funções da classe.

10.11 (Modifique a classe Loan ) Reescreva a classe Loan na Listagem 9.13 para adicionar duas funções
estáticas para calcular o pagamento mensal e o pagamento total, como segue:

double getMonthlyPayment(double AnnualInterestRate, int


numberOfYears, double lendAmount)

double getTotalPayment(double AnnualInterestRate, int


numberOfYears, double lendAmount)

Escreva um programa cliente para testar essas duas funções.

Seções 10.8–10.11

10.12 (A classe Stock ) Projete uma classe chamada Stock que contenha o seguinte:

n Um campo de dados de string denominado símbolo para o símbolo da ação.


n Um campo de dados de string denominado nome para o nome do estoque.
n Um campo de dados duplo chamado previousClosingPrice que armazena o estoque
preço do dia anterior.
n Um campo de dados duplo chamado currentPrice que armazena o preço da ação para o
hora atual.

n Um construtor que cria um estoque com símbolo e nome especificados.


n As funções de acessador constante para todos os campos de dados.
n As funções modificadoras para previousClosingPrice e currentPrice.
n Uma função constante chamada getChangePercent() que retorna a porcentagem
alterado de previousClosingPrice para currentPrice.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um programa de teste que
crie um objeto Stock com o símbolo de ação MSFT, o nome Microsoft Corporation e o preço de
fechamento anterior de 27,5. Defina um novo preço atual para 27,6
e exibir a porcentagem de alteração de preço.

10.13 (Geometria: polígono regular de n lados) Um polígono regular de n lados tem n lados do mesmo
comprimento e todos os seus ângulos têm o mesmo grau (ou seja, o polígono é equilátero e
equiângulo). Projete uma classe chamada RegularPolygon que contenha o seguinte:

n Um campo de dados int privado denominado n que define o número de lados do polígono.
n Um campo de dados duplo privado denominado lado que armazena o comprimento do lado.
n Um campo de dados duplo privado denominado x que define a coordenada x do centro
do polígono.
n Um campo de dados duplo privado denominado y que define a coordenada y do centro
do polígono.
n Um construtor sem argumentos que cria um polígono regular com n 3, lado 1, x 0 e y 0. n Um
construtor que cria um polígono regular com o número especificado de lados
e comprimento do lado e centralizado em (0, 0).
n Um construtor que cria um polígono regular com o número especificado de lados,
comprimento do lado e coordenadas x e y.
Machine Translated by Google

Exercícios de Programação 429

n As funções de acesso constante e funções modificadoras para todos os campos de dados.

n A função constante getPerimeter() que retorna o perímetro do


polígono.
n A função constante getArea() que retorna a área do polígono. A
fórmula para calcular a área de um polígono regular é
2
n*s
Área = .

4 * seu p nb

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva


um programa de teste que crie três objetos RegularPolygon , usando o
construtor no-arg, usando RegularPolygon(6, 4) e usando
RegularPolygon(10, 4, 5.6, 7.8). Para cada objeto, exiba seu perímetro e área.
*10.14 (Exibir os números não primos) Escreva um programa que exiba todos os números não
primos menores que 100 em ordem decrescente. Use a classe StackOfIntegers para
armazenar os números não primos (por exemplo, 4, 6, 8,...) e recuperá-los e exibi-los
na ordem inversa.

***10.15 (Jogo: carrasco) Escreva um jogo da forca que gere aleatoriamente uma palavra e solicite ao
usuário que adivinhe uma letra por vez, conforme mostrado no exemplo.
Cada letra da palavra é exibida em um asterisco. Quando o usuário dá um palpite
correto, a letra real é exibida. Quando o usuário terminar uma palavra, exiba o
número de erros e pergunte ao usuário se deseja continuar para outra palavra.
Declare um array para armazenar palavras, como segue:

// Use as palavras que desejar


string palavras[] = {"escrever", "aquilo", ...};

(Adivinha) Digite uma letra na palavra *******> p


(Adivinha) Digite uma letra na palavra p******> r
(Adivinha) Digite uma letra na palavra pr**r** > p
p já está na palavra
(Adivinhar) Insira uma letra na palavra pr**r** > o (Adivinhar)
Insira uma letra na palavra pro*r** > g (Adivinhar) Insira
uma letra na palavra progr** > nn não está na palavra

(Adivinhar) Insira uma letra no programa de palavras** >


m (Adivinhar) Insira uma letra no programa de palavras**
> a A palavra é programa. Você errou 1 vez

Você quer adivinhar outra palavra? Digite s ou n>

*10.16 (Exibir os múltiplos) Escreva um programa que receba um número inteiro e exiba seus
primeiros dez múltiplos em ordem decrescente. Por exemplo, se o número inteiro
for 5, os primeiros dez múltiplos serão exibidos como 50, 45, 40, 35, 30 25, 20, 15,
10, 5. Use a classe StackOfIntegers para armazenar os múltiplos (por exemplo, 5,
10, ..., 50) e recupere e exiba os múltiplos na ordem inversa.

**10.17 (A classe Location ) Projete uma classe chamada Location para localizar um valor máximo
e sua localização em um array bidimensional. A classe contém campos de dados
públicos linha, coluna e maxValue que armazenam o valor máximo e seus índices
em uma matriz bidimensional com linha e coluna como tipo int e maxValue como
tipo duplo .
Machine Translated by Google

430 Capítulo 10 Pensamento Orientado a Objetos

Escreva a seguinte função que retorna a localização do maior elemento em uma matriz
bidimensional. Suponha que o tamanho da coluna seja fixo.

const int ROW_SIZE = 3;


const int COLUMN_SIZE = 4;
Localização localizarLargest(const double a[][COLUMN_SIZE]);

O valor de retorno é uma instância de Location. Escreva um programa de teste que


solicite ao usuário que insira um array bidimensional e exiba a localização do maior
elemento do array. Aqui está um exemplo de execução:

Insira uma matriz bidimensional 3 por 4: 23,5 35


2 10 4,5 3 45
3,5 35 44 5,5
9,6 A localização
do maior elemento é 45 em (1, 2)
Machine Translated by Google

CAPÍTULO

11
Ponteiros e
Memória Dinâmica
Gerenciamento

Objetivos
n Descrever o que é um ponteiro (§11.1).

n Para aprender como declarar um ponteiro e atribuir um endereço de memória a ele


(§11.2).

n Para acessar valores através de ponteiros (§11.2).

n Para definir tipos sinônimos usando a palavra-chave typedef (§11.3).

n Para declarar ponteiros constantes e dados constantes (§11.4).

n Explorar a relação entre arrays e ponteiros e acesso


elementos da matriz usando ponteiros (§11.5).

n Para passar argumentos de ponteiro para uma função (§11.6).

n Para aprender como retornar um ponteiro de uma função (§11.7).

n Para usar funções de array com ponteiros (§11.8).

n Para usar o operador new para criar matrizes dinâmicas (§11.9).

n Para criar objetos dinamicamente e acessar objetos via ponteiros (§11.10).

n Para referenciar o objeto de chamada usando este ponteiro (§11.11).

n Para implementar o destruidor para realizar operações personalizadas


(§11.12).

n Criar uma turma para alunos matriculando cursos (§11.13).

n Para criar um objeto usando o construtor de cópia que copia dados de


outro objeto do mesmo tipo (§11.14).

n Para personalizar o construtor de cópia para executar uma cópia profunda


(§11.15).
Machine Translated by Google

432 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

11.1 Introdução
Variáveis de ponteiro também são conhecidas como ponteiros. Você pode usar um ponteiro para referenciar o endereço
Chave
Apontar de uma matriz, um objeto ou qualquer variável.

O ponteiro é um dos recursos mais poderosos do C++. É o coração e a alma da linguagem de programação C++. Muitos dos
recursos e bibliotecas da linguagem C++ são construídos usando ponteiros.
por que ponteiros? Para ver por que os ponteiros são necessários, consideremos escrever um programa que processe um número não especificado
de inteiros. Você usaria um array para armazenar os inteiros. Mas como você cria o array se não sabe seu tamanho? O tamanho
pode mudar conforme você adiciona ou remove números inteiros. Para lidar com isso, seu programa precisa ser capaz de alocar e
liberar memória para os números inteiros em tempo de execução. Isso pode ser feito usando ponteiros.

11.2 Noções básicas sobre ponteiro

Nota de vídeo Uma variável de ponteiro contém o endereço da memória. Através do ponteiro, você pode usar o operador de
Chave
Noções básicas de ponteiro Apontar
desreferência * para acessar o valor real em um local específico da memória.

Variáveis de ponteiro, chamadas simplesmente de ponteiros, são declaradas para conter endereços de memória como seus valores.
Normalmente, uma variável contém um valor de dados – por exemplo, um número inteiro, um valor de ponto flutuante e um
caractere. No entanto, um ponteiro contém o endereço de memória de uma variável que, por sua vez, contém um valor de dados.
Conforme mostrado na Figura 11.1, o ponteiro pCount contém o endereço de memória para contagem de variáveis.
Cada byte de memória possui um endereço único. O endereço de uma variável é o endereço do primeiro byte alocado para
essa variável. Suponha que três variáveis contagem, status e letra sejam declaradas da seguinte forma:

contagem interna = 5;
status curto = 2;
letra char = 'A';
strings ("ABC");

Conforme mostrado na Figura 11.1, a variável count é declarada como um tipo int que contém quatro bytes, a variável status
é declarada como um tipo short que contém dois bytes e a variável letter é declarada como um tipo char que contém um byte.
Observe que o código ASCII para 'A' é hexadecimal 55. A variável s é declarada como um tipo de string cujo tamanho de memória
pode mudar, dependendo do número de caracteres na string, mas o endereço de memória da string é fixo, uma vez que a string é
declarada.
declarar ponteiro Como qualquer outra variável, os ponteiros devem ser declarados antes de serem usados. Para declarar um ponteiro, use a
seguinte sintaxe:

tipo de dados* pVarName;

Cada variável declarada como ponteiro deve ser precedida de um asterisco (*). Por exemplo, as instruções a seguir declaram
ponteiros chamados pCount, pStatus e pLetter, que podem apontar para uma variável int , uma variável curta , uma variável char
e uma string, respectivamente.

int* pContagem;
curto* pStatus;
char* pLetra;
string*pString;

Agora você pode atribuir o endereço de uma variável a um ponteiro. Por exemplo, o código a seguir atribui o endereço da
atribuir endereço variável count a pCount:

pCont = &contagem;

operador de endereço O símbolo e comercial (&) é chamado de operador de endereço quando colocado na frente de uma variável.
É um operador unário que retorna o endereço da variável. Portanto, você pode pronunciar &count como o endereço da contagem.
Machine Translated by Google

11.2 Noções básicas sobre ponteiro 433

.
contagem
.
interna = 5; status
0013FF60 curto = 2; letra char
0013FF61 = 'A'; strings = "ABC";
contagem (tipo int, 4 bytes)
0013FF62 int* pCont = &contagem;
0013FF63 05 curto* pStatus = &status; char*
pLetra = &letra; string* pString
0013FF64 = &s;
status (tipo curto, 2 bytes)
0013FF65 02
pCont = &contagem;
0013FF66 55 letra (tipo char, 1 byte)
&: operador de endereço
0013FF67 Conteúdo para &count significa o endereço da contagem
objetos de string.
.
*: operador de desreferenciação
.
*pCount significa que o valor apontado por pCount é
.
atribuído a v.

Contagem: 00

13
pCount é 0013FF60
FF

60

pStatus: 00

13
pStatus é 0013FF64
FF

64

cartas: 00

13
pLetra é 0013FF66
FF

66

pString: 00

13
pString é 0013FF67
FF

67

.
.

A Figura 11.1 pCount contém o endereço de memória da variável count.

A Listagem 11.1 fornece um exemplo completo que demonstra o uso de ponteiros.

Listagem 11.1 TestPointer.cpp


1 #include <iostream>
2 usando namespace std; 3 4 int

principal() 5 { 6 7

contagem interna = declarar variável


5; int* pCont = &contagem; declarar ponteiro
Machine Translated by Google

434 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

contagem de acessos cout << "O valor de contagem é " << contagem << endl;
acessando &contar 8 cout << "O endereço da contagem é " cout << "O << &count << endl;
acessando pCount 9 endereço da contagem é " cout << "O valor da << Contagem << endl;
acessando *pCount 10 contagem é " << *pCount << endl;
11
12 retornar 0;
13 14 15 }

O valor da contagem é 5
O endereço da contagem é 0013FF60
O endereço da contagem é 0013FF60
O valor da contagem é 5

A linha 6 declara uma variável chamada count com valor inicial 5. A linha 7 declara uma variável ponteiro
chamada pCount e inicializada com o endereço da variável count. A Figura 11.1 mostra a relação entre contagem
e pCount.
Um ponteiro pode ser inicializado quando é declarado ou usando uma instrução de atribuição. Como-
sempre, se você atribuir um endereço a um ponteiro, a sintaxe será

pCont = &contagem; // Correto

em vez de

*pCont = &contagem; // Errado

A linha 10 exibe o endereço de contagem usando &count. A linha 11 exibe o valor armazenado em pCount,
que é igual a &count. O valor armazenado em count é recuperado diretamente de count na linha 9 e indiretamente
por meio de uma variável de ponteiro usando *pCount na linha 12.
referência indireta Referenciar um valor por meio de um ponteiro costuma ser chamado de indireção. A sintaxe para referência
obter um valor de um ponteiro é

*ponteiro

Por exemplo, você pode aumentar a contagem usando

contar++; // Referência direta

ou

(*pContagem)++; //Referência indireta

operador indireto O asterisco (*) usado na instrução anterior é conhecido como operador indireto ou operador de desreferência
operador de desreferência (desreferência significa referência indireta). Quando um ponteiro é desreferenciado, o valor no endereço armazenado
desreferenciado no ponteiro é recuperado. Você pode pronunciar *pCount como o valor apontado indiretamente por pCount ou
simplesmente apontado por pCount.
Os seguintes pontos sobre ponteiros são dignos de nota:

*em três formas n O asterisco (*) pode ser usado de três maneiras diferentes em C++:

n Como um operador de multiplicação, como

área dupla = raio * raio * 3,14159;

n Para declarar uma variável de ponteiro, como

int* pCont = &contagem;


Machine Translated by Google

11.2 Noções básicas sobre ponteiro 435

n Como o operador de desreferência, como

(*pContagem)++;

Não se preocupe. O compilador pode dizer para que o símbolo * é usado em um programa.

n Uma variável de ponteiro é declarada com um tipo como int ou double. Você deve atribuir o endereço da tipo de ponteiro

variável do mesmo tipo. É um erro de sintaxe se o tipo da variável não corresponder ao tipo do ponteiro.
Por exemplo, o código a seguir está errado:

área interna = 1;
double* pArea = &area; // Errado

Você pode atribuir um ponteiro a outro ponteiro do mesmo tipo, mas não pode atribuir um ponteiro a
uma variável que não seja ponteiro. Por exemplo, o código a seguir está errado:

área interna = 1;
int* pArea = &area; int i =
área; // Errado

n Ponteiros são variáveis. Portanto, as convenções de nomenclatura para variáveis são aplicadas a nomeando ponteiros
ponteiros. Até agora, nomeamos ponteiros com prefixo p, como pCount e pArea. No entanto, é
impossível fazer cumprir esta convenção. Logo você perceberá que o nome de um array é na verdade
um ponteiro.

n Como uma variável local, um ponteiro local recebe um valor arbitrário se você não o inicializar. Um ponteiro
pode ser inicializado com 0, que é um valor especial para indicar que o ponteiro aponta para nada. Para
evitar erros, você deve sempre inicializar ponteiros. Desreferenciar um ponteiro que não foi inicializado
pode causar um erro fatal de tempo de execução ou pode modificar acidentalmente dados importantes.
Várias bibliotecas C++, incluindo <iostream>, definem NULL como uma constante com valor 0. É mais
descritivo usar NULL do que 0. NULO

Suponha que pX e pY sejam duas variáveis ponteiro para as variáveis x e y, conforme mostrado na Figura 11.2.
Para entender as relações entre as variáveis e seus ponteiros, vamos examinar o efeito de atribuir pY a pX e *pY a
*pX. efeito da atribuição =
A instrução pX = pY atribui o conteúdo de pY a pX. O conteúdo de pY é o endereço da variável y. Assim, após
esta atribuição, pX e pY contêm o mesmo conteúdo, conforme ilustrado na Figura 11.2a.

Agora considere *pX = *pY. Com o símbolo de asterisco na frente de pX e pY, você está lidando com as
variáveis apontadas por pX e pY. *pX refere-se ao conteúdo em x e *pY refere-se ao conteúdo em y. Portanto, a
instrução *pX = *pY atribui 6 a *pX, conforme ilustrado na Figura 11.2b.
Você pode declarar um ponteiro int usando a sintaxe

int* p;

ou

interno *p;

ou

interno * p; int* p, int *p ou int * p

Todos estes são equivalentes. Qual é o melhor é uma questão de preferência pessoal. Este livro usa o estilo int* p
para declarar um ponteiro por dois motivos:

1. int* p separa claramente o tipo int* do identificador p. p é do tipo int*, não


do tipo int.
Machine Translated by Google

436 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

px x

Endereço de x Endereço de x 5

pY e

Endereço de você Endereço de você 6

pX = pY; *pX = *pY;

px x px x

Endereço de x Endereço de x 5 Endereço de x Endereço de x 6

pY e pY e

Endereço de você Endereço de você 6 Endereço de você Endereço de você 6

(a) (b)

Figura 11.2 (a) pY é atribuído a pX; (b) *pY é atribuído a *pX.

2. Mais adiante neste livro, você verá que uma função pode retornar um ponteiro. É mais intuitivo
escreva o cabeçalho da função como

typeName* functionName(parameterList);

em vez de

typeName *functionName(parameterList);

Uma desvantagem de usar a sintaxe do estilo int* p é que ela pode levar a um erro como este:

int* p1, p2;

Esta linha parece declarar dois ponteiros, mas na verdade é igual a

int *p1, p2;

Recomendamos que você sempre declare uma variável de ponteiro em uma única linha como esta:

int* p1;
int* p2;

11.1 Como você declara uma variável de ponteiro? Uma variável de ponteiro local tem um padrão
ÿVerificação de ponto valor?

11.2 Como você atribui o endereço de uma variável a uma variável de ponteiro? O que há de errado no
código a seguir?
Machine Translated by Google

11.3 Definindo tipos sinônimos usando a palavra-chave typedef 437

interno x =
30; int* pX =
x; cout << "x é " << x << endl; <<
cout << "x é " pX << endl;

11.3 Mostre a impressão do seguinte código:

interno x =
30; int* p = &x;
cout << *p << endl;

int y = 40; p
= &y;
cout << *p << endl;

11.4 Mostre a impressão do seguinte código:

duplo x = 3,5;
duplo* p1 = &x;

duplo y = 4,5;
duplo* p2 = &y;

cout << *p1 + *p2 << endl;

11.5 Mostre a impressão do seguinte código:

strings = "ABCD";
string* p = &s;

cout << p << endl; cout


<< *p << endl; cout <<
(*p)[0] << endl;

11.6 O que há de errado no código a seguir?

duplo x = 3,0; int*


pX = &x;

11.7 Ambas as variáveis p1 e p2 são ponteiros se p1 e p2 forem definidas da seguinte forma:

duplo* p1, p2;

11.3 Definindo tipos sinônimos usando a


palavra-chave typedef
Um tipo sinônimo pode ser definido usando a palavra-chave typedef . Chave
Apontar
Lembre-se de que o tipo unsigned é sinônimo de unsigned int. C++ permite definir tipos sinônimos
personalizados usando a palavra-chave typedef . Tipos sinônimos podem ser usados para simplificar a
codificação e evitar possíveis erros.
A sintaxe para definir um novo sinônimo para um tipo de dados existente é a seguinte:

typedef existenteType novoType;

Por exemplo, a instrução a seguir define inteiro como sinônimo de int:

typedef int inteiro;


Machine Translated by Google

438 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

Então agora você pode declarar uma variável int usando

valor inteiro = 40;

A declaração typedef não cria novos tipos de dados. Apenas cria um sinônimo para um tipo de dados. Este recurso é
útil para definir um nome de tipo de ponteiro para facilitar a leitura do programa. Por exemplo, você pode definir um tipo
chamado intPointer para int* da seguinte maneira:

typedef int* intPointer;

Uma variável de ponteiro inteiro agora pode ser declarada da seguinte forma:

intPointerp;

que é o mesmo que

int* p;

Uma vantagem de usar um nome de tipo de ponteiro é evitar erros envolvendo falta de asteriscos.
iscos. Se você pretende declarar duas variáveis de ponteiro, a seguinte declaração está errada:

int* p1, p2;

Uma boa maneira de evitar esse erro é usar o tipo sinônimo intPointer da seguinte forma:

intPointer p1, p2;

Com esta sintaxe, p1 e p2 são declarados como variáveis do tipo intPointer .

11.8 Como você define um novo tipo chamado doublePointer que é sinônimo de
ÿVerificação de ponto dobro*?

11.4 Usando const com ponteiros


Chave
Um ponteiro constante aponta para um local de memória constante, mas o valor real no local de memória pode
Apontar ser alterado.

Você aprendeu como declarar uma constante usando a palavra-chave const . Uma vez declarada, uma constante não
ponteiro constante pode ser alterada. Você pode declarar um ponteiro constante. Por exemplo:

raio duplo = 5;
duplo* const p = &raio;

Aqui p é um ponteiro constante. Deve ser declarado e inicializado na mesma instrução. Você não pode atribuir um novo
endereço a p posteriormente. Embora p seja uma constante, os dados apontados por p não são constantes. Você pode
mudar isso. Por exemplo, a seguinte instrução altera o raio para 10:

*p = 10;

dados constantes Você pode declarar que os dados desreferenciados são constantes? Sim. Você pode adicionar a palavra-chave const
na frente do tipo de dados, da seguinte maneira:

Dados constantes Ponteiro constante

const double* const pValue = &radius;

Neste caso, o ponteiro é uma constante e os dados apontados pelo ponteiro também são uma constante.
Machine Translated by Google

11.5 Matrizes e Ponteiros 439

Se você declarar o ponteiro como

const duplo* p = &radius;

então o ponteiro não é uma constante, mas os dados apontados pelo ponteiro são uma constante.
Por exemplo:

raio duplo = 5;
duplo* const p = &raio;
comprimento duplo = 5;
*p = 6; // OK
p = &comprimento; // Errado porque p é um ponteiro constante

const duplo* p1 = &raio;


*p1 = 6; // Errado porque p1 aponta para um dado constante
p1 = &comprimento; // OK

const duplo* const p2 = &radius;


*p2 = 6; // Errado porque p2 aponta para um dado constante
p2 = &comprimento; // Errado porque p2 é um ponteiro constante

11.9 O que há de errado no código a seguir?


ÿVerificação de ponto
interno x;
int* const p = &x;
interno ;
p = &y;

11.10 O que há de errado no código a seguir?

interno x;
const int* p = &x;
interno ;
p = &y;
*p = 5;

11.5 Matrizes e Ponteiros


Um nome de array C++ é na verdade um ponteiro constante para o primeiro elemento do array.
Chave
Apontar
Uma matriz sem colchetes e subscrito representa, na verdade, o endereço inicial da matriz.
Nesse sentido, um array é essencialmente um ponteiro. Suponha que você declare uma matriz de valores int da
seguinte forma:

lista interna [6] = {11, 12, 13, 14, 15, 16};

A instrução a seguir exibe o endereço inicial do array:

cout << "O endereço inicial do array é " << list << endl;

A Figura 11.3 mostra o array na memória. C++ permite acessar os elementos do


array usando o operador de desreferência. Para acessar o primeiro elemento, use *list.
Outros elementos podem ser acessados usando *(lista + 1), *(lista + 2), *(lista + 3),
*(lista + 4) e *(lista + 5).
Um número inteiro pode ser adicionado ou subtraído de um ponteiro. O ponteiro é incrementado ou aritmética de ponteiro
decrementado por esse número inteiro vezes o tamanho do elemento para o qual o ponteiro aponta.
A lista de array aponta para o endereço inicial do array. Suponha que este endereço seja 1000. A lista + 1 será
1001? Não. É 1000 + sizeof(int). Por que? Como a lista é declarada como um
Machine Translated by Google

440 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

lista lista+1 lista+2 lista+3 lista+4 lista+5

lista[0] lista[1] lista[2] lista[3] lista[4] lista[5]

11 12 13 14 15 16 11 12 13 14 15 16

*lista *(lista+1) *(lista+2)*(lista+3) *(lista+4) *(lista+5)

Figura 11.3 A lista de array aponta para o primeiro elemento do array.

array de elementos int , C++ calcula automaticamente o endereço do próximo elemento adicionando sizeof(int). Lembre-
se de que a função sizeof(type) retorna o tamanho de um tipo de dados (consulte a Seção 2.8, “Tipos de dados
numéricos e operações”). O tamanho de cada tipo de dados depende da máquina. No Windows, o tamanho do tipo int
geralmente é 4. Portanto, não importa o tamanho de cada elemento da lista, list + 1 aponta para o segundo elemento da
lista, e list + 3 aponta para o terceiro, e assim por diante .

Observação

por que índice baseado em 0? Agora você vê por que um índice de array começa com 0. Um array é na verdade um ponteiro. lista + 0
aponta para o primeiro elemento da matriz e list[0] refere-se ao primeiro elemento da matriz.

A Listagem 11.2 fornece um programa completo que utiliza ponteiros para acessar elementos de um array.

Listagem 11.2 ArrayPointer.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
declarar matriz 56 lista interna [6] = {11, 12, 13, 14, 15, 16};
7
8 para (int i = 0; i < 6; i++)
endereço incremental cout << "endereço: " << (lista + i) << " valor: " " valor: "
""
operador de desreferência 9 << *(lista + i) << << lista[i] <<
variável indexada de array 10 << endl;
11
12 retornar 0;
13 14 }

endereço: 0013FF4C valor: 11 valor: 11


endereço: 0013FF50 valor: 12 valor: 12
endereço: 0013FF54 valor: 13 valor: 13
endereço: 0013FF58 valor: 14 valor: 14
endereço: 0013FF5C valor: 15 valor: 15
endereço: 0013FF60 valor: 16 valor: 16

Conforme mostrado no exemplo de saída, o endereço da lista de array é 0013FF4C. Portanto, (lista + 1) é na
verdade 0013FF4C + 4 e (lista + 2) é 0013FF4C + 2 * 4 (linha 9). Os elementos do array são acessados usando a
desreferência do ponteiro *(lista + 1) (linha 10). A linha 11 acessa os elementos via índice usando list[i], que equivale a
*(list + i).

Cuidado
operador precedente *(lista + 1) é diferente de *lista + 1. O operador de desreferência (*) tem precedência sobre +.
Portanto, *list + 1 adiciona 1 ao valor do primeiro elemento do array, enquanto *(list + 1) desreferencia
o elemento no endereço (list + 1) do array.
Machine Translated by Google

11.5 Matrizes e Ponteiros 441

Observação

Os ponteiros podem ser comparados usando operadores relacionais (==, !=, <, <=, >, >=) para determinar comparar ponteiros
sua ordem.

Matrizes e ponteiros formam um relacionamento próximo. Um ponteiro para um array pode ser usado da mesma forma
uma matriz. Você pode até usar ponteiro com índice. A Listagem 11.3 dá um exemplo desse tipo.

Listagem 11.3 PointerWithIndex.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
lista interna [6] = {11, 12, 13, 14, 15, 16}; declarar matriz
int* p = lista; declarar ponteiro

para (int i = 0; i < 6; i++)


6 cout << "endereço: " << (lista + i) << endereço incremental
""
7 " valor: " " << *(lista + i) << << lista[i] << operador de desreferência
""
8 valor: " " valor: << << *(p + i) << << << matriz com índice
""
9 " " valor: " p[i] << endl; << operador de desreferência
10 ponteiro com índice
11
12 retornar 0;
13 14 15 16 17 }

endereço: 0013FF4C valor: 11 valor: 11 valor: 11 valor: 11


endereço: 0013FF50 valor: 12 valor: 12 valor: 12 valor: 12
endereço: 0013FF54 valor: 13 valor: 13 valor: 13 valor: 13
endereço: 0013FF58 valor: 14 valor: 14 valor: 14 valor: 14
endereço: 0013FF5C valor: 15 valor: 15 valor: 15 valor: 15
endereço: 0013FF60 valor: 16 valor: 16 valor: 16 valor: 16

A linha 7 declara um ponteiro int p atribuído ao endereço do array.

int* p = lista;

Observe que não usamos o operador de endereço (&) para atribuir o endereço do array ao ponteiro, porque o nome
do array já é o endereço inicial do array. Esta linha é equivalente a

int* p = &lista[0];

Aqui, &list[0] representa o endereço de list[0].


Como visto neste exemplo, para lista de array, você pode acessar um elemento usando a sintaxe de array list[i] bem
como a sintaxe de ponteiro *(list + i). Quando um ponteiro como p aponta para um array, você pode usar a sintaxe do
ponteiro ou a sintaxe do array para acessar um elemento do array – ou seja, *(p + i) ou p[i]. Você pode usar sintaxe de
array ou sintaxe de ponteiro para acessar arrays, o que for mais conveniente. No entanto, há uma diferença entre arrays e
ponteiros. Depois que um array é declarado, você não pode alterar seu endereço. Por exemplo, a seguinte declaração é
ilegal:

int lista1[10], lista2[10];


lista1 = lista2; // Errado

Na verdade, um nome de array é tratado como um ponteiro constante em C++. ponteiro constante
Machine Translated by Google

442 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

strings C baseadas em ponteiro Strings C são frequentemente chamadas de strings baseadas em ponteiros, porque podem ser convenientemente
acessado usando ponteiros. Por exemplo, as duas declarações a seguir são adequadas:

char cidade[7] = "Dallas"; // Opção 1 char* pCity


= "Dallas"; // Opção 2

Cada declaração cria uma sequência que contém os caracteres 'D', 'a', 'l', 'l', 'a', 's' e '\0'.

sintaxe de array Você pode acessar city ou pCity usando a sintaxe de array ou a sintaxe de ponteiro. Por exemplo,
sintaxe de ponteiro cada um dos seguintes

cout << cidade[1] << endl; cout


<< *(cidade + 1) << endl; cout <<
pCidade[1] << endl; cout <<
*(pCidade + 1) << endl;

exibe o caractere a (o segundo elemento da string).

11.11 Suponha que você declarou int* p e que o valor atual de p é 100. O que é p + 1?
ÿVerificação de ponto
11.12 Suponha que você declarou int* p. Quais são as diferenças entre p++, *p++ e
(*p)++?
11.13 Suponha que você declarou int p[4] = {1, 2, 3, 4}. O que são *p, *(p+1), p[0]
ep [1]?

11.14 O que há de errado no código a seguir?

caractere*
p; cin >> p;

11.15 Qual é a impressão das seguintes afirmações?

char* const pCidade = "Dallas"; cout


<< cidade << endl; cout <<
*pCidade << endl; cout <<
*(pCidade + 1) << endl; cout <<
*(pCidade + 2) << endl; cout <<
*(pCidade + 3) << endl;

11.16 Qual é a saída do código a seguir?

char* cidade = "Dallas"; cout


<< cidade[0] << endl;

char* cidades[] = {"Dallas", "Atlanta", "Houston"}; cout <<


cidades[0] << endl; cout <<
cidades[0][0] << endl;

11.6 Passando argumentos de ponteiro em uma chamada de função


Uma função C++ pode ter parâmetros de ponteiro.
Chave
Apontar
Você aprendeu duas maneiras de passar argumentos para uma função em C++: passagem por valor e passagem
por referência. Você também pode passar argumentos de ponteiro em uma chamada de função. Um argumento
de ponteiro pode ser passado por valor ou por referência. Por exemplo, você pode definir uma função da seguinte
maneira:

vazio f(int* p1, int* &p2)


Machine Translated by Google

11.6 Passando argumentos de ponteiro em uma chamada de função 443

que é equivalente a

typedef int* intPointer;


void f(intPointer p1, intPointer& p2)

Considere invocar a função f(q1, q2) com dois ponteiros q1 e q2:

n O ponteiro q1 é passado para p1 por valor. Portanto, *p1 e *q1 apontam para o mesmo conteúdo.
Se a função f alterar *p1 (por exemplo, *p1 = 20), *q1 também será alterado. Entretanto, se a
função f altera p1 (por exemplo, p1 = somePointerVariable), q1 não é alterado.

n O ponteiro q2 é passado para p2 por referência. Portanto, q2 e p2 agora são apelidos. Eles são
essencialmente os mesmos. Se a função f alterar *p2 (por exemplo, *p2 = 20), *q2 também será alterado.
Se a função f alterar p2 (por exemplo, p2 = somePointerVariable), q2 também será alterado.

A Listagem 6.14, SwapByValue.cpp, demonstrou o efeito da passagem por valor. A Listagem 6.17,
SwapByReference.cpp, demonstrou o efeito da passagem por referência com variáveis de referência.
Ambos os exemplos usaram a função swap para demonstrar o efeito. Damos agora um exemplo de passagem
de ponteiros na Listagem 11.4.

Listagem 11.4 TestPointerArgument.cpp


1 #include <iostream> Nota de vídeo
2 usando namespace std; Passar argumentos de ponteiro
3
4 // Troca duas variáveis usando passagem por valor
5 troca nula1 (int n1, int n2) passagem por valor
6{
7 temperatura interna = n1;
n1 = n2;
n2 = temperatura;
8 9 10}
11
12 // Troca duas variáveis usando passagem por referência
13 troca nula2 (int& n1, int& n2) passagem por referência
14 {
15 temperatura interna = n1;
16 n1 = n2;
17 n2 = temperatura;
18 }
19
20 // Passa dois ponteiros por valor
21 troca nula3 (int* p1, int* p2) passar um ponteiro por valor
22 {
23 temperatura interna = *p1;
24 *p1 = *p2;
25 *p2 = temperatura;
26 }
27
28 // Passa dois ponteiros por referência
29 void swap4(int* &p1, int* &p2) passar um ponteiro por referência
30 {
31 int* temperatura = p1;
p1 =p2;
32 p2 = temperatura;
33 34 }

35 36 int principal()
Machine Translated by Google

444 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

37 {
//Declara e inicializa variáveis
38 39 int num1 = 1;
40 int num2 = 2;
41
42 cout << "Antes de invocar a função swap, num1 é "
43 << num1 << " e num2 é " << num2 << endl;
44
45 // Invoca a função swap para tentar trocar duas variáveis
46 troca1(num1, num2);
47
48 cout << "Depois de invocar a função swap, num1 é " " e num2 é " << num1 <<
49 << num2 << endl;
50
51 cout << "Antes de invocar a função swap, num1 é "
52 << num1 << " e num2 é " << num2 << endl;
53
54 // Invoca a função swap para tentar trocar duas variáveis
55 troca2(num1, num2);
56
57 cout << "Depois de invocar a função swap, num1 é " " e num2 é " << num1 <<
58 << num2 << endl;
59
60 cout << "Antes de invocar a função swap, num1 é "
61 << num1 << " e num2 é " << num2 << endl;
62
63 // Invoca a função swap para tentar trocar duas variáveis
64 troca3(&num1, &num2);
65
66 cout << "Depois de invocar a função swap, num1 é " " e num2 é " << num1 <<
67 << num2 << endl;
68
69 int* p1 = &num1;
70 int* p2 = &num2;
71 cout << "Antes de invocar a função swap, p1 é "
72 << p1 << " e p2 é " << p2 << endl;
73
74 // Invoca a função swap para tentar trocar duas variáveis
75 trocar4(p1, p2);
76
77 cout << "Depois de invocar a função swap, p1 é " << p1 <<
" e p2 é " << p2 << endl;

retornar 0;
78 79 80 81}

Antes de invocar a função de troca, num1 é 1 e num2 é 2


Depois de invocar a função swap, num1 é 1 e num2 é 2
Antes de invocar a função de troca, num1 é 1 e num2 é 2
Depois de invocar a função swap, num1 é 2 e num2 é 1
Antes de invocar a função de troca, num1 é 2 e num2 é 1
Depois de invocar a função swap, num1 é 1 e num2 é 2
Antes de invocar a função swap, p1 é 0028FB84 e p2 é 0028FB78
Depois de invocar a função swap, p1 é 0028FB78 e p2 é 0028FB84

Quatro funções swap1, swap2, swap3 e swap4 são definidas nas linhas 5–34. A
função swap1 é invocada passando o valor de num1 para n1 e o valor de num2 para n2 (linha 46).
Machine Translated by Google

11.6 Passando argumentos de ponteiro em uma chamada de função 445

A função swap1 troca os valores em n1 e n2. n1, num1, n2, num2 são variáveis
independentes. Após invocar a função, os valores nas variáveis num1 e num2 não são alterados.
A função swap2 possui dois parâmetros de referência, int& n1 e int& n2 (linha 13). As referências de num1 e
num2 são passadas para n1 e n2 (linha 55), então n1 e num1 são aliases e n2 e num2 são aliases. n1 e n2 são
trocados em swap2. Após o retorno da função, os valores nas variáveis num1 e num2 também são trocados.

A função swap3 possui dois parâmetros de ponteiro, p1 e p2 (linha 21). Os endereços num1 e num2 são
passados para p1 e p2 (linha 64), então p1 e &num1 referem-se ao mesmo local de memória e p2 e &num2
referem-se ao mesmo local de memória. *p1 e *p2 são trocados em swap3. Após o retorno da função, os valores
nas variáveis num1 e num2 também são trocados.
A função swap4 possui dois parâmetros de ponteiro, p1 e p2, passados por referência (linha 29).
Invocar esta função troca p1 por p2 (linha 75).
Um parâmetro de array em uma função sempre pode ser substituído usando um parâmetro de ponteiro. Por parâmetro de array ou parâmetro
exemplo, de ponteiro

void m(int lista[], tamanho interno ) pode ser substituído por void m(int* lista, tamanho interno )

vazio m(char c_string[]) pode ser substituído por vazio m(char* c_string)

Lembre-se de que uma string C é uma matriz de caracteres que termina com um terminador nulo. O tamanho
de uma string C pode ser detectado a partir da própria string C.
Se um valor não mudar, você deve declará-lo const para evitar que seja modificado acidentalmente. A parâmetro const
Listagem 11.5 dá um exemplo.

Listagem 11.5 ConstParameter.cpp


1 #include <iostream>
2 usando namespace std;

3 4 void printArray(const int*, const int); protótipo de função

5 6 int principal()
7{
lista interna [6] = {11, 12, 13, 14, 15, 16}; declarar matriz
printArray(lista, 6); invocar printArray
8
9 retornar 0;
10 11 12 }
13
14 void printArray(const int* lista, const int tamanho)
15 {
16 for (int i = 0; i <tamanho; i++)
17 cout << lista[i] << " ";
18}

19 11 12 13 14 15 16

A função printArray declara um parâmetro de array com dados constantes (linha 4). Isso garante que o
conteúdo do array não será alterado. Observe que o parâmetro size também é declarado const. Isso geralmente
não é necessário, pois um parâmetro int é passado por valor.
Mesmo que o tamanho seja modificado na função, isso não afeta o valor do tamanho original fora desta função.
Machine Translated by Google

446 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

11.17 Qual é a saída do código a seguir?


ÿVerificação de ponto
#include <iostream>
usando namespace std;

vazio f1(int x, int& y, int* z)


{
x++;
y++;
(*z)++;
}

int principal()
{
int i = 1, j = 1, k = 1;
f1(i, j, &k);

cout << "i é" cout << eu << endl;


<< "j é" cout << "k << j << endl;
é" << k << final;

retornar 0;
}

11.7 Retornando um Ponteiro de Funções


Uma função C++ pode retornar um ponteiro.
Chave
Apontar
Você pode usar ponteiros como parâmetros em uma função. Você pode retornar um ponteiro de uma função?
Sim você pode.
Suponha que você queira escrever uma função que passe um argumento de array, inverta-o e retorne
a matriz. Você pode definir uma função reversa e implementá-la conforme mostrado na Listagem 11.6.

Listagem 11.6 ReverseArrayUsingPointer.cpp


1 #include <iostream>
2 usando namespace std;

função reversa 3 4 int* reverso(int* lista, tamanho interno )


5{
for (int i = 0, j = tamanho - 1; i < j; i++, j--)
{
// Troca lista[i] por lista[j]
trocar temperatura interna = lista[j];
6 lista[j] = lista[i];
7 lista[i] = temp;
8 }
9
lista de devolução 10 lista de retorno ;
11 12 13 14 15 }
16
matriz de impressão 17 void printArray(const int* lista, tamanho interno )
18 {
19 for (int i = 0; i <tamanho; i++)
20 cout << lista[i] << " ";
21}

22 23 int principal()
24 {
25 lista interna [] = {1, 2, 3, 4, 5, 6};
invocar reverso 26 int* p = reverso(lista, 6);
Machine Translated by Google

11.8 Funções úteis de array 447

printArray(p, 6); matriz de impressão

retornar 0;
27 28 29 30 }

654321

O protótipo da função reversa é especificado assim:

int* reverso(int* lista, tamanho interno )

O tipo de valor de retorno é um ponteiro int . Ele troca o primeiro elemento pelo último, segundo elemento
mento com o penúltimo, . . . e assim por diante na lista, conforme mostrado no diagrama a seguir:

lista

A função retorna lista como um ponteiro na linha 14.

11.18 Mostre a saída do código a seguir.


ÿVerificação de ponto
#include <iostream>
usando namespace std;

int* f(int lista1[], const int lista2[], tamanho interno )


{
for (int i = 0; i <tamanho; i++)
lista1[i]+ = lista2[i];
lista de retorno1 ;
}

int principal()
{
int lista1[] = {1, 2, 3, 4};
int lista2[] = {1, 2, 3, 4};
int* p = f(lista1, lista2, 4);
cout << p[0] << endl;
cout << p[1] << endl;

retornar 0;
}

11.8 Funções úteis de array


As funções min_element, max_element, sort, random_shuffle e find podem
Chave
ser usadas para arrays. Apontar

C++ fornece diversas funções para manipulação de arrays. Você pode usar as funções min_element e
max_element para retornar o ponteiro para o elemento mínimo e máximo em uma matriz, a função sort para
classificar uma matriz, a função random_shuffle para embaralhar aleatoriamente uma matriz e a função find
para encontrar um elemento em uma matriz . Todas essas funções usam ponteiros nos argumentos e no valor
de retorno. A Listagem 11.7 dá um exemplo do uso dessas funções.

Listagem 11.7 UsefulArrayFunctions.cpp


1 #include <iostream>
2 #incluir <algoritmo>
3 usando namespace std;

4 5 void printArray(const int* lista, tamanho interno ) matriz de impressão


Machine Translated by Google

448 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

6{
for (int i = 0; i <tamanho; i++)
cout << lista[i] << " ";
cout << endl;
7 8 9 10}
11
12 int principal()
13 {
declarar uma matriz 14 lista interna [] = {4, 2, 3, 6, 5, 1};
15 printArray(lista, 6);
16
elemento_min 17 int* min = min_element(lista, lista + 6);
max_element 18 int* max = max_element(lista, lista + 6);
"
19 cout << "O valor mínimo é " << *min << << (min - no índice "
20 lista) << endl;
"
21 cout << "O valor máximo é " << << *máx.<< no índice "
22 (max - lista) << endl;
23
random_shuffle 24 random_shuffle(lista, lista + 6);
25 printArray(lista, 6);
26
organizar 27 ordenar(lista, lista + 6);
28 printArray(lista, 6);
29
30 chave interna = 4;
encontrar 31 int* p = encontrar(lista, lista + 6, chave);
32 se (p! = lista + 6)
33 cout << "O valor " << *p << << (p - lista) "é encontrado na posição"
34 << endl;
35 outro
36 cout << "O valor" << *p << "não foi encontrado" << endl;
37
38 retornar 0;
39 }

423651
O valor mínimo é 1 no índice 5
O valor máximo é 6 no índice 3
526341
123456
O valor 4 é encontrado na posição 3

elemento_min Invocar min_element(list, list + 6) (linha 17) retorna o ponteiro para o menor elemento na
matriz de list[0] a list[5]. Neste caso, ele retorna lista + 5 pois o valor 1 é o menor elemento
do array e o ponteiro para este elemento é lista + 5. Observe que os dois argumentos
passados para a função são os ponteiros que especificam um intervalo e o segundo ponteiro
aponta o final do intervalo especificado.
random_shuffle Invocar random_shuffle(list, list + 6) (linha 24) reorganiza aleatoriamente os elementos
na matriz de list[0] para list[5].
organizar Invocar sort(list, list + 6) (linha 27) classifica os elementos do array de list[0]
listar [5].
encontrar Invocar find(list, list + 6, key) (linha 31) encontra a chave na matriz de list[0] a list[5]. A
função retorna o ponteiro que aponta o elemento correspondente no array se o elemento for
encontrado; caso contrário, retorna o ponteiro que aponta para a posição após o último
elemento do array (ou seja, lista + 6 neste caso).
Machine Translated by Google

11.9 Alocação Dinâmica de Memória Persistente 449

11.19 Mostre a saída do seguinte código:


ÿVerificação de ponto
lista interna [] = {3, 4, 2, 5, 6, 1};
cout << *min_element(lista, lista + 2) << endl;
cout << *max_element(lista, lista + 2) << endl;
cout << *encontrar(lista, lista + 6, 2) << endl;
cout << encontrar(lista, lista + 6, 20) << endl;
ordenar(lista, lista + 6);
cout << lista[5] << endl;

11.9 Alocação Dinâmica de Memória Persistente


O operador new pode ser usado para criar memória persistente em tempo de execução para valores, matrizes
Chave
e objetos de tipos primitivos. Apontar

A Listagem 11.6 escreve uma função que passa um argumento de array, inverte-o e retorna o array. novo operador
Suponha que você não queira alterar o array original. Você pode reescrever a função que passa um argumento de array e
retorna um novo array que é a reversão do argumento de array.
Um algoritmo para a função pode ser descrito da seguinte forma:

1. Deixe o array original ser uma lista.

2. Declare um novo array chamado result que tenha o mesmo tamanho do array original.

3. Escreva um loop para copiar o primeiro elemento, o segundo,. . . , e assim por diante na matriz original até o último
elemento, penúltimo, . . . , na nova matriz, conforme mostrado no diagrama a seguir:

lista

resultado

4. Retorne o resultado como um ponteiro.

O protótipo da função pode ser especificado assim:

int* reverso(const int* lista, tamanho interno )

O tipo de valor de retorno é um ponteiro int . Como você declara um novo array na Etapa 2? Você pode tentar declará-lo
como

resultado interno [tamanho];

Mas C++ não permite que o tamanho seja uma variável. Para evitar essa limitação, vamos supor que o tamanho do array
seja 6. Portanto, você pode declará-lo como

resultado interno [6];

Agora você pode implementar o código da Listagem 11.8, mas logo descobrirá que ele não está funcionando corretamente.

Listagem 11.8 WrongReverse.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int* reverso(const int* lista, tamanho interno ) função reversa


5{
Machine Translated by Google

450 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

declarar matriz de resultados resultado interno [6];

reverter para resultado for (int i = 0, j = tamanho - 1; i < tamanho; i++, j--)
{
6 resultado[j] = lista[i];
7 }
8
resultado de retorno 9 resultado de retorno ;
10 11 12 13 14 }
15
matriz de impressão 16 void printArray(const int* lista, tamanho interno )
17 {
18 for (int i = 0; i <tamanho; i++)
19 cout << lista[i] << " ";
20}
21
22 int principal()
23 {
24 lista interna [] = {1, 2, 3, 4, 5, 6};
invocar reverso 25 int* p = reverso(lista, 6);
matriz de impressão 26 printArray(p, 6);
27
28 retornar 0;
29 }

6 4462476 4419772 1245016 4199126 4462476

A saída de amostra está incorreta. Por que? A razão é que o resultado da matriz é armazenado no
registro de ativação na pilha de chamadas. A memória na pilha de chamadas não persiste; quando a
função retorna, o registro de ativação usado pela função na pilha de chamadas é descartado da pilha
de chamadas. A tentativa de acessar o array através do ponteiro resultará em valores errados e
imprevisíveis. Para corrigir esse problema, você deve alocar armazenamento persistente para a matriz
de resultados para que ela possa ser acessada após o retorno da função. Discutiremos a correção a seguir.
alocação dinâmica de memória C++ suporta alocação dinâmica de memória, o que permite alocar armazenamento persistente
dinamicamente. A memória é criada usando o operador new . Por exemplo,

int* p = novo int(4);

Aqui, new int diz ao computador para alocar espaço de memória para uma variável int inicializada
com 4 em tempo de execução, e o endereço da variável é atribuído ao ponteiro p. Então você pode
acessar a memória através do ponteiro.
Você pode criar um array dinamicamente. Por exemplo,

cout << "Digite o tamanho do array: ";


tamanho interno ;
cin >> tamanho;
int* lista = new int[tamanho];

Aqui, new int[size] diz ao computador para alocar espaço de memória para um array int com o número
especificado de elementos, e o endereço do array é atribuído à lista. A matriz criada usando o operador
matriz dinâmica new também é conhecida como matriz dinâmica. Observe que quando você cria um array regular, seu
tamanho deve ser conhecido em tempo de compilação. Não pode ser uma variável. Deve ser uma
constante. Por exemplo,

números internos [40]; // 40 é um valor constante


Machine Translated by Google

11.9 Alocação Dinâmica de Memória Persistente 451

Ao criar um array dinâmico, seu tamanho é determinado em tempo de execução. Pode ser um número inteiro
variável. Por exemplo,

int* lista = new int[tamanho]; // tamanho é uma variável

A memória alocada usando o operador new é persistente e existe até que seja explicitamente excluída ou o
programa seja encerrado. Agora você pode corrigir o problema do exemplo anterior criando um novo array
dinamicamente na função reversa . Este array pode ser acessado após o retorno da função. A Listagem 11.9
apresenta o novo programa.

Listagem 11.9 CorrectReverse.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int* reverso(const int* lista, tamanho interno ) função reversa


{
5 int* resultado = novo int[tamanho]; criar matriz
6
7 for (int i = 0, j = tamanho - 1; i < tamanho; i++, j--) reverter para resultado
8 {
9 resultado[j] = lista[i];
10 }
11 12
13 resultado de retorno ; resultado de retorno

14}
15
16 void printArray(const int* lista, tamanho interno ) matriz de impressão

17 {
18 for (int i = 0; i <tamanho; i++)
19 cout << lista[i] << " ";
20}
21
22 int principal()
23 {
24 lista interna [] = {1, 2, 3, 4, 5, 6};
int* p = reverso(lista, 6); invocar reverso
25 printArray(p, 6); matriz de impressão

26
27 retornar 0;
28 29 }

654321

A Listagem 11.9 é quase idêntica à Listagem 11.6, exceto que o novo array de resultados é criado usando
o operador new dinamicamente. O tamanho pode ser uma variável ao criar um array usando o operador new .

C++ aloca variáveis locais na pilha, mas a memória alocada pelo novo operador está em uma área de
memória chamada freestore ou heap. A memória heap permanece disponível até que você a libere explicitamente freestore
ou o programa termine. Se você alocar memória heap para uma variável enquanto estiver em uma função, a amontoar

memória ainda estará disponível após o retorno da função.


A matriz de resultados é criada na função (linha 6). Depois que a função retorna na linha 25, a matriz de
resultados fica intacta. Assim, você pode acessá-lo na linha 26 para imprimir todos os elementos do array
resultante .
Machine Translated by Google

452 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

excluir operador Para liberar explicitamente a memória criada pelo operador new , use o operador delete para o ponteiro. Por
exemplo,

excluir p;

excluir uma matriz dinâmica A palavra delete é uma palavra-chave em C++. Se a memória for alocada para um array, o []
o símbolo deve ser colocado entre a palavra-chave delete e o ponteiro para a matriz para liberar a memória
corretamente. Por exemplo,

excluir [] lista;

Depois que a memória apontada por um ponteiro é liberada, o valor do ponteiro torna-se indefinido.
Além disso, se algum outro ponteiro apontar para a mesma memória que foi liberada, esse outro ponteiro também será

ponteiros pendurados indefinido. Esses ponteiros indefinidos são chamados de ponteiros pendentes. Não aplique o ponteiro pendente. Fazer
operador de desreferência * isso causaria erros graves.

Cuidado
excluir memória dinâmica
Use a palavra-chave delete apenas com o ponteiro que aponta para a memória criada
pelo novo operador. Caso contrário, poderá causar problemas inesperados. Por exemplo,
o código a seguir está errado, porque p não aponta para uma memória criada usando new.
interno x = 10;
int* p = &x;
excluir p; // Isto está errado

Você pode reatribuir inadvertidamente um ponteiro antes de excluir a memória para a qual ele aponta.
Considere o seguinte código:

1 int* p = novo int;


2 *p = 45;
3p = novo int;

A linha 1 declara um ponteiro atribuído com um endereço de memória para um valor int , conforme mostrado na
Figura 11.4a. A linha 2 atribui 45 à variável apontada por p, conforme mostrado na Figura 11.4b. A linha 3 atribui um
novo endereço de memória a p, conforme mostrado na Figura 11.4c. O espaço de memória original que contém o valor
45 não é acessível porque não é apontado por nenhum ponteiro. Esta memória não pode ser acessada e não pode ser

vazamento de memória
excluída. Este é um vazamento de memória.

p novo interno;

endereço, por exemplo, 0013FF60 0013FF60 ainda não inicializado

(a) int *p = novo int; aloca memória para um valor int e atribui um endereço
a p.

p
0013FF60 45
endereço, por exemplo, 0013FF60

(b) *p = 45; atribui 45 ao local de memória apontado por p.

0013FF60 45

A memória em 0013FF60 não é referenciada por nenhum ponteiro. É um vazamento.

p novo interno;

endereço, por exemplo, 0013FF64 0013FF64 ainda não inicializado

(c) p = novo int; atribui um novo endereço a p.

Figura 11.4 Espaço de memória não referenciado causa vazamento de memória.


Machine Translated by Google

11.10 Criando e Acessando Objetos Dinâmicos 453

A alocação dinâmica de memória é um recurso poderoso, mas você deve usá-lo com cuidado para evitar
vazamentos de memória e outros erros. Como uma boa prática de programação, cada chamada para new deve ser
acompanhada por uma chamada para delete.

11.20 Como você cria espaço de memória para um valor duplo ? Como você acessa esse valor duplo ? Como
você libera essa memória? ÿVerificação de ponto

11.21 A memória dinâmica é destruída quando o programa é encerrado?

11.22 Explique o vazamento de memória.

11.23 Suponha que você crie um array dinâmico e posteriormente precise liberá-lo. Identifique dois erros no
código a seguir:

duplo x[] = novo duplo[30];


...
excluir x;

11.24 O que há de errado no código a seguir:

duplo d = 5,4;
duplo* p1 = d;

11.25 O que há de errado no código a seguir:

duplo d = 5,4;
duplo* p1 = &d;
excluir p1;

11.26 O que há de errado no código a seguir:

duplo* p1;
p1* = 5,4;

11.27 O que há de errado no código a seguir:

duplo* p1 = novo duplo;


duplo* p2 = p1;
*p2 = 5,4;
excluir p1;
cout << *p2 << endl;

11.10 Criando e Acessando Objetos Dinâmicos


Para criar um objeto dinamicamente, invoque o construtor do objeto usando a sintaxe new
Chave
ClassName(argumentos). Apontar

Você também pode criar objetos dinamicamente no heap usando a sintaxe mostrada abaixo. criar objeto dinâmico

NomeClasse* pObject = new NomeClasse(); ou


NomeClasse* pObject = novoNomeClasse ;

cria um objeto usando o construtor no-arg e atribui o endereço do objeto ao ponteiro.

ClassName* pObject = new ClassName(argumentos);

cria um objeto usando o construtor com argumentos e atribui o endereço do objeto ao ponteiro.
Machine Translated by Google

454 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

Por exemplo,

// Cria um objeto usando o construtor sem argumento string* p = new


string(); // ou string* p = nova string;

// Cria um objeto usando o construtor com argumentos


string* p = nova string("abcdefg");

Para acessar membros do objeto por meio de um ponteiro, você deve desreferenciar o ponteiro e usar o
operador ponto (.) para os membros do objeto. Por exemplo,

string* p = nova string("abcdefg"); cout << "Os


"
invocar substr() três primeiros caracteres da string são
<< (*p).substr(0, 3) << endl;
invocar comprimento() cout << "O comprimento da string é " << (*p).length() << endl;

C++ também fornece um operador abreviado de seleção de membros para acessar membros de objetos a partir
operador de seta de um ponteiro: operador de seta (->), que é um traço (-) imediatamente seguido pelo símbolo de maior que (>) .
Por exemplo,
"
invocar substr() cout << "Os três primeiros caracteres da string são
<< p->substr(0, 3) << endl;
invocar comprimento() cout << "O comprimento da string é " << p->length() << endl;

Os objetos são destruídos quando o programa é encerrado. Para destruir explicitamente um objeto, invoque

excluir objeto dinâmico excluir p;

11.28 Os seguintes programas estão corretos? Se não, corrija-os.


ÿVerificação de ponto

int principal() int principal() int principal()


{ { {
cadeia s1; string* p = nova string; string*p = nova string("ab");
string* p = s1; string* p1 = nova string();
retornar 0;
retornar 0; retornar 0; }
} }

(a) (b) (c)

11.29 Como você cria um objeto dinamicamente? Como você exclui um objeto? Por que é
o código em (a) está errado e em (b) está correto?

int principal() int principal()


{ {
cadeia s1; string*p = nova string();
string* p = &s1; excluir p;
excluir p;
retornar 0; retornar 0;
} }

(a) (b)
Machine Translated by Google

11.11 Este ponteiro 455

11.30 No código a seguir, as linhas 7 e 8 criam um objeto anônimo e imprimem a área


do círculo. Por que a linha 8 é ruim?
1 #include <iostream>
2 #include "Círculo.h"
3 usando namespace std;

4 5 int principal()
6{
7 cout << Círculo(5).getArea() << endl;
cout << (new Circle(5))->getArea() << endl;

8 retornar 0;
9 10 11}

11.11 Este ponteiro


O ponteiro this aponta para o próprio objeto de chamada.
Chave
Apontar
Às vezes você precisa fazer referência ao campo de dados oculto de uma classe em uma função. Por exemplo, um
nome de campo de dados é frequentemente usado como nome de parâmetro em uma função de conjunto para o variável oculta
campo de dados. Nesse caso, você precisa fazer referência ao nome do campo de dados oculto na função para
definir um novo valor para ele. Um campo de dados oculto pode ser acessado usando a palavra-chave this , que é esta palavra-chave

um ponteiro especial integrado que faz referência ao objeto de chamada. Você pode reescrever a classe Circle
definida em CircleWithPrivateDataFields.h na Listagem 9.9 usando o ponteiro this , conforme mostrado na Listagem 11.10.

Listagem 11.10 CircleWithThisPointer.cpp


1 #include "CircleWithPrivateDataFields.h" // Definido na Listagem 9.9 incluir arquivo de cabeçalho

2 3 // Construa um objeto circular padrão


4 Círculo::Círculo()
5{
raio = 1;
6 7}

8 9 // Construa um objeto circular


10 Círculo::Círculo( raio duplo)
11 {
12 isto->raio = raio; // ou (*this).radius = raio; este ponteiro
13}
14
15 // Retorna a área deste círculo
16 círculo duplo ::getArea()
17 {
18 raio de retorno * raio * 3,14159;
19}
20
21 // Retorna o raio deste círculo
22 círculo duplo ::getRadius()
23 {
24 raio de retorno ;
25}
26
27 // Defina um novo raio
28 círculo vazio ::setRadius( raio duplo)
29 {
isto->raio = (raio >= 0) ? raio: 0; este ponteiro
30 31}
Machine Translated by Google

456 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

O nome do parâmetro radius no construtor (linha 10) é uma variável local. Para referenciar o raio do
campo de dados no objeto, você deve usar this->radius (linha 12). O nome do parâmetro radius na função
setRadius (linha 28) é uma variável local. Para referenciar o raio do campo de dados no objeto, você deve
usar this->radius (linha 30).

11.31 O que há de errado no código a seguir? Como isso pode ser consertado?
ÿVerificação de ponto
//Constrói um objeto circular
Círculo::Círculo( raio duplo)
{
raio = raio;
}
Nota de vídeo

Destruidor e cópia
construtor 11.12 Destruidores
Cada classe possui um destruidor, que é chamado automaticamente quando um objeto é excluído.
Chave
Apontar
Os destruidores são o oposto dos construtores. Um construtor é invocado quando um objeto é criado e um
destruidor destruidor é invocado automaticamente quando o objeto é destruído. Cada classe possui um destruidor
padrão se o destruidor não for definido explicitamente. Às vezes, é desejável implementar destruidores para
realizar operações customizadas. Os destruidores têm o mesmo nome dos construtores, mas você deve
colocar um caractere til (~) na frente. A Listagem 11.11 mostra um Círculo
classe com um destruidor definido.

Listagem 11.11 CircleWithDestructor.h


1 #ifndef CÍRCULO_H
2 #define CÍRCULO_H

3 4 classe Círculo
{
5 6 público:
7 Círculo();
8 Círculo(duplo);
9 ~Círculo(); // Destruidor
10 duplo getArea() const;
11 duplo getRadius() const;
12 void setRadius(duplo);
13 static int getNumberOfObjects();
14
15 privado:
16 raio duplo ;
17 static int numberOfObjects;
18};
19
20 #endif

Um destruidor para a classe Circle é definido na linha 9. Os destruidores não têm tipo de retorno nem
argumentos.
A Listagem 11.12 fornece a implementação da classe Circle definida em CircleWith-Destructor.h.

Listagem 11.12 CircleWithDestructor.cpp


incluir cabeçalho 1 #include "CircleWithDestructor.h"
2
3 int Circle::numberOfObjects = 0;
4
5 // Construa um objeto círculo padrão
Machine Translated by Google

11.12 Destruidores 457

6 Círculo::Círculo()
7{
raio = 1;
númeroDeObjetos++;
8 9 10}
11
12 // Construa um objeto circular
13 Círculo::Círculo( raio duplo)
14 {
15 isto->raio = raio;
16 númeroDeObjetos++;
17}
18
19 // Retorna a área deste círculo
20 círculo duplo ::getArea() const
21 {
22 raio de retorno * raio * 3,14159;
23}
24
25 // Retorna o raio deste círculo
26 círculo duplo ::getRadius() const
27 {
28 raio de retorno ;
29}
30
31 // Defina um novo raio
32 círculo vazio ::setRadius( raio duplo)
33 {
34 isto->raio = (raio >= 0) ? raio: 0;
35}
36
37 // Retorna o número de objetos circulares
38 int Círculo::getNumberOfObjects()
39 {
40 retornar númeroDeObjetos;
41}
42
43 // Destrua um objeto circular
44 Círculo::~Círculo() implementar destruidor
45 {
46 númeroDeObjetos--;
47}

A implementação é idêntica a CircleWithStaticDataFields.cpp na Listagem 10.7, exceto


que o destruidor é implementado para diminuir numberOfObjects nas linhas 44–47.
O programa na Listagem 11.13 demonstra os efeitos dos destruidores.

Listagem 11.13 TestCircleWithDestructor.cpp


1 #include <iostream>
2 #include "CircleWithDestructor.h" incluir cabeçalho
3 usando namespace std;

4 5 int principal()
6{
Círculo* pCírculo1 = novo Círculo(); criar pCircle1
7 8 Círculo* pCírculo2 = novo Círculo(); criar pCircle2
Círculo* pCírculo3 = novo Círculo(); criar pCircle3
9
10 11 cout << "Número de objetos circulares criados: "
Machine Translated by Google

458 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

exibir número de objetos 12 << Circle::getNumberOfObjects() << endl;


13
destruir pCircle1 14 excluir pCircle1;
15
16 cout << "Número de objetos circulares criados: "
exibir número de objetos 17 << Circle::getNumberOfObjects() << endl;
18
19 retornar 0;
20 }

Número de objetos circulares criados: 3


Número de objetos circulares criados: 2

O programa cria três objetos Circle usando o operador new nas linhas 7–9. Depois disso,
numberOfObjects passa a ser 3. O programa exclui um objeto Circle na linha 14. Depois disso,
numberOfObjects passa a ser 2.
Os destruidores são úteis para excluir memória e outros recursos alocados dinamicamente por
objeto, conforme mostrado no estudo de caso da próxima seção.

11.32 Toda classe possui um destruidor? Como é nomeado um destruidor? Pode estar
ÿVerificação de ponto sobrecarregado? Você pode redefinir um destruidor? Você pode invocar um destruidor explicitamente?

11.33 Qual é a saída do código a seguir?

#include <iostream>
usando namespace std;

classe Funcionário
{
público:
Funcionário ( id interno)
{
isto->id = id;
}

~Funcionário()
{
cout << "objeto com id " } << identificação << "é destruído" << endl;

privado:
você mão;
};

int principal()
{
Funcionário* e1 = novo Funcionário(1);
Funcionário* e2 = novo Funcionário(2);
Funcionário* e3 = novo Funcionário(3);

excluir e3;
excluir e2;
excluir e1;

retornar 0;
}
Machine Translated by Google

11.13 Estudo de Caso: A Classe do Curso 459

11.34 Por que a classe a seguir precisa de um destruidor? Adicione um.

classe Pessoa

{ público:
Pessoa()

{ númeroDeCrianças = 0; filhos
= nova string[20]; }

void addAChild(string nome)

{ filhos[númeroDeFilhos++] = nome;
}

string* getCrianças() {

devolver os filhos;
}

int getNumberOfChildren() {

retornar númeroDeCrianças;
}

privado:
string* filhos; número
interno de crianças; };

11.13 Estudo de caso: A aula do curso


Esta seção projeta uma classe para cursos de modelagem.
Chave
Apontar
Suponha que você precise processar informações do curso. Cada curso tem um nome e um número de alunos que fazem
o curso. Você deve ser capaz de adicionar/retirar um aluno do curso.
Você pode usar uma classe para modelar os cursos, conforme mostrado na Figura 11.5.

Curso

-courseName: string O nome do curso.

-alunos: string* Uma série de alunos que fazem o curso. Students é um


ponteiro para o array.
-númeroDeAlunos:int O número de alunos (padrão: 0).
-capacidade: int O número máximo de alunos permitidos para o curso.

+Curso(nomedocurso: string&, capacidade: int) Cria um curso com o nome especificado e máximo
número de alunos permitido.

+~Curso() Destruidor

+getCourseName(): string const Retorna o nome do curso.

+addAluno(nome: string&): void Adiciona um novo aluno ao curso.

+dropStudent(nome: string&): void Tira um aluno do curso.


+getAlunos(): string* const Retorna a matriz de alunos do curso.
+getNumberOfStudents(): int const Retorna o número de alunos do curso.

Figura 11.5 A classe Course modela os cursos.


Machine Translated by Google

460 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

Um objeto Course pode ser criado usando o construtor Course(string courseName,


intcapacidade) passando o nome do curso e o número máximo de alunos permitidos.
Você pode adicionar um aluno ao curso usando a função addStudent(string name) , retirar
um aluno do curso usando a função dropStudent(string name) e retornar todos os alunos
do curso usando a função getStudents() .
Suponha que a classe seja definida conforme mostrado na Listagem 11.14. A Listagem 11.15 fornece uma classe de teste que
cria dois cursos e adiciona alunos a eles.

Listagem 11.14 Course.h


1 #ifndef CURSO_H
2 #define CURSO_H
usando classe string 3 #incluir <string>
4 usando namespace std;

Aula do curso Curso de 5 6 aulas


7{
membros públicos 8 público:
9 Curso (const string& courseName, int capacidade);
10 ~Curso();
11 string getCourseName() const;
12 void addStudent(const string& nome);
13 void dropStudent(const string& nome);
14 string* getAlunos() const;
15 int getNumberOfStudents() const;
16
membros privados 17 privado:
18 string nomeDoCurso;
19 alunos string*;
20 número interno de alunos;
21 capacidade interna ;
22};
23
24 #endif

Listagem 11.15 TestCourse.cpp


1 #include <iostream>
Cabeçalho do curso 2 #include "Curso.h"
3 usando namespace std;

4 5 int principal()
6{
criar curso1 7 Curso curso1(" Estruturas de Dados", 10);
criar curso2 Curso curso2("Sistemas de Banco de Dados", 15);

adicionar um aluno 8 curso1.addStudent("Peter Jones");


9 10 11 course1.addStudent("Brian Smith");
12 course1.addStudent("Anne Kennedy");
13
14 course2.addStudent("Peter Jones");
15 course2.addStudent("Steve Smith");
16
"
número de estudantes 17 cout << "Número de alunos no curso1: <<
18 course1.getNumberOfStudents() << "\n";
alunos que retornam 19 string* alunos = course1.getStudents();
20 for (int i = 0; i < course1.getNumberOfStudents(); i++)
exibir um aluno 21 cout << alunos[i] << ", ";
22
"
23 cout << "\nNúmero de alunos no curso2:
Machine Translated by Google

11.13 Estudo de Caso: A Classe do Curso 461

24 << course2.getNumberOfStudents() << "\n";


25 alunos = curso2.getStudents();
26 for (int i = 0; i < course2.getNumberOfStudents(); i++)
27 cout << alunos[i] << ", ";
28
29 retornar 0;
30 }

Número de alunos no curso 1: 3


Peter Jones, Brian Smith, Anne Kennedy,
Número de alunos no curso2: 2
Peter Jones, Steve Smith,

A classe Course é implementada na Listagem 11.16.

Listagem 11.16 Course.cpp


1 #include <iostream>
2 #include "Curso.h" Cabeçalho do curso

3 usando namespace std;

4 5 Curso::Curso(const string& courseName, capacidade interna )


6{
númeroDeAlunos = 0; inicializar campo de dados

this->nomedocurso = nomedocurso; definir o nome do curso

isto->capacidade = capacidade;
7 alunos = nova string[capacidade];
8 9 10 11 }
12
13 Curso::~Curso()
14 {
15 excluir [] alunos; destruir matriz dinâmica
16}
17
18 string Curso::getCourseName() const
19 {
20 return nomedocurso;
21}

22 23 void Curso::addStudent(const string& nome) adicionar um aluno

24 {
alunos[númeroDeAlunos] = nome;
25 númeroDeAlunos++; aumentar o número de alunos
26 27}

28 29 void Curso::dropStudent(const string& nome)


30 {
31 // Deixado como exercício
32}

33 34 string* Curso::getStudents() const


35 {
alunos que retornam ; alunos que retornam

36 37}

38 39 int Curso::getNumberOfStudents() const


40 {
41 retornar númeroDeAlunos;
42}
Machine Translated by Google

462 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

O construtor Course inicializa numberOfStudents como 0 (linha 7), define um novo curso
nome (linha 8), define uma capacidade (linha 9) e cria uma matriz dinâmica (linha 10).
A classe Course usa um array para armazenar os alunos do curso. A matriz é criada quando um objeto
Course é construído. O tamanho da matriz é o número máximo de alunos permitidos para o curso. Portanto,
o array é criado usando a nova string[capacity].
Quando um objeto Course é destruído, o destruidor é invocado para destruir adequadamente o array
(linha 15).
A função addStudent adiciona um aluno ao array (linha 23). Esta função não verifica se o número de
alunos da turma ultrapassa a capacidade máxima. No Capítulo 16, você aprenderá como revisar esta função
para tornar seu programa mais robusto, lançando uma exceção se o número de alunos na turma exceder a
lançar exceção capacidade máxima.
A função getStudents (linhas 34–37) retorna o endereço do array para armazenar os alunos.

A função dropStudent (linhas 29–32) remove um aluno do array. A implementação desta função fica
como exercício.
O usuário pode criar um Curso e manipulá-lo através das funções públicas addStudent, dropStudent,
getNumberOfStudents e getStudents. Porém, o usuário não precisa saber como essas funções são implementadas.
A classe Course encapsula a implementação interna. Este exemplo usa uma matriz para armazenar alunos. Você
pode usar uma estrutura de dados diferente para armazenar alunos. O programa que utiliza o Curso não precisa
sofrer alterações enquanto o contrato das funções públicas permanecer inalterado.

Observação

Quando você cria um objeto Course , um array de strings é criado (linha 10). Cada elemento
possui um valor de string padrão criado pelo construtor no-arg da classe string .

Cuidado
evitando vazamento de memória Você deverá personalizar um destruidor se a classe contiver um campo de dados de ponteiro que aponte para a
memória criada dinamicamente. Caso contrário, o programa poderá apresentar vazamento de memória.

11.35 Quando um objeto Curso é criado, qual é o valor do ponteiro dos alunos ?
ÿVerificação de ponto
11.36 Por que delele [] Students é usado na implementação do destruidor para o ponteiro de Students ?

11.14 Copiar Construtores


Cada classe possui um construtor de cópia, que é usado para copiar objetos.
Chave
Apontar
Cada classe pode definir vários construtores sobrecarregados e um destruidor. Além disso, toda classe
construtor de cópia possui um construtor de cópia, que pode ser usado para criar um objeto inicializado com os dados de outro
objeto da mesma classe.
A assinatura do construtor de cópia é

NomeDaClasse(constNomeDaClasse &)

Por exemplo, o construtor de cópia para a classe Circle é

Círculo(const Círculo&)

Um construtor de cópia padrão é fornecido implicitamente para cada classe, se não for definido
explicitamente. O construtor de cópia padrão simplesmente copia cada campo de dados em um objeto para
sua contraparte no outro objeto. A Listagem 11.17 demonstra isso.
Machine Translated by Google

11.14 Copiar Construtores 463

Listagem 11.17 CopyConstructorDemo.cpp


1 #include <iostream>
2 #include "CircleWithDestructor.h" // Definido na Listagem 11.11 incluir cabeçalho
3 usando namespace std;

4 5 int principal()
6{
Círculo círculo1(5); criar círculo1
7 8 Círculo círculo2(círculo1); //Usa construtor de cópia criar círculo2

9 10 cout << "Depois de criar o círculo2 a partir do círculo1:" << endl;


"
11 cout << "\tcircle1.getRadius() retorna <<
12 círculo1.getRadius() << endl; exibir círculo1
"
13 cout << "\tcircle2.getRadius() retorna <<
14 círculo2.getRadius() << endl; exibir círculo2
15
16 círculo1.setRadius(10.5); modificar círculo1
17 círculo2.setRadius(20,5); modificar círculo2
18
19 cout << "Depois de modificar círculo1 e círculo2: " << endl;
"
20 cout << "\tcircle1.getRadius() retorna <<
21 círculo1.getRadius() << endl; exibir círculo1
"
22 cout << "\tcircle2.getRadius() retorna <<
23 círculo2.getRadius() << endl; exibir círculo2
24
25 retornar 0;
26 }

Depois de criar o círculo2 a partir do círculo1:


círculo1.getRadius() retorna 5
círculo2.getRadius() retorna 5

Depois de modificar círculo1 e círculo2:


círculo1.getRadius() retorna 10,5
círculo2.getRadius() retorna 20,5

O programa cria dois objetos Circle : círculo1 e círculo2 (linhas 7–8). círculo2 é criado usando o
construtor de cópia copiando os dados de círculo1 .
O programa então modifica o raio em círculo1 e círculo2 (linhas 16–17) e exibe
reproduz seu novo raio nas linhas 20–23.
Observe que o operador de atribuição membro e o construtor de cópia são semelhantes no sentido de
que ambos atribuem valores de um objeto para outro. A diferença é que um novo objeto é criado usando
um construtor de cópia. Usar o operador de atribuição não cria novos objetos.
O construtor de cópia padrão ou operador de atribuição para copiar objetos executa uma cópia cópia superficial
superficial, em vez de uma cópia profunda, o que significa que se o campo for um ponteiro para algum cópia profunda

objeto, o endereço do ponteiro será copiado em vez de seu conteúdo. A Listagem 11.18 demonstra isso.

Listagem 11.18 ShallowCopyDemo.cpp


1 #include <iostream>
2 #include "Course.h" // Definido na Listagem 11.14 incluir cabeçalho do curso
3 usando namespace std;
4
Machine Translated by Google

464 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

5 int principal()
6{
criar curso1 Curso curso1("C++", 10);
criar curso2 Curso curso2(curso1);

adicionar um aluno 7 curso1.addAluno("Peter Pan"); //Adiciona um aluno ao course1


adicionar um aluno 8 course2.addStudent("Lisa Ma"); //Adiciona um aluno ao course2
9
"
10 cout << "alunos do curso1: <<
conseguir um aluno 11 course1.getStudents()[0] << endl;
"
12 cout << "alunos do curso2: <<
conseguir um aluno 13 course2.getStudents()[0] << endl;
14
15 retornar 0;
16 17 18 19 }

alunos do curso 1: Lisa Ma


alunos do curso 2: Lisa Ma

A classe Course foi definida na Listagem 11.14. O programa cria um objeto Course
course1 (linha 7) e cria outro objeto Course course2 usando o construtor de cópia (linha
8). course2 é uma cópia do course1. A classe Curso possui quatro campos de dados:
courseName, numberOfStudents, capacidade e alunos. O campo dos alunos é do tipo ponteiro.
Quando course1 é copiado para course2 (linha 8), todos os campos de dados são copiados para course2. Como os
alunos são um ponteiro, seu valor em curso1 é copiado para curso2. Agora, ambos os alunos do curso1 e do curso2
apontam para o mesmo objeto array, conforme mostrado na Figura 11.6.

curso1: Curso curso2: Curso

nomedocurso = "C++" matriz de strings nomedocurso = "C++"


para estudantes
estudantes estudantes

númeroDeAlunos = 0 númeroDeAlunos = 0

capacidade = 10 capacidade = 10

Figura 11.6 Após o curso1 ser copiado para o curso2, os campos de dados dos alunos do curso1
e course2 apontam para o mesmo array.

A linha 10 adiciona um aluno "Peter Pan" ao curso1, que deve definir "Peter Pan" no primeiro elemento do array.
A linha 11 adiciona uma aluna "Lisa Ma" ao curso2 (linha 11), que deve definir "Lisa Ma" no primeiro elemento do
array. Na verdade, isso substitui "Peter Pan" por "Lisa Ma" no primeiro elemento da matriz, uma vez que course1 e
course2 usam a mesma matriz para armazenar nomes de alunos. Então, a aluna do curso 1 e do curso 2 é "Lisa Ma"

(linhas 13–16).
Quando o programa termina, o curso1 e o curso2 são destruídos. Os destruidores course1 e course2 são
invocados para excluir o array do heap (linha 10 na Listagem 11.16).
Como o ponteiro dos alunos do curso1 e do curso2 apontam para o mesmo array, o array será excluído duas vezes.
Isso causará um erro de tempo de execução.

Para evitar todos esses problemas, você deve realizar uma cópia profunda para que o curso1 e
course2 possui matrizes independentes para armazenar nomes de alunos.

11.37 Toda classe possui um construtor de cópia? Como é nomeado um construtor de cópia? Pode estar
Verificar
ÿPonto sobrecarregado? Você pode redefinir um construtor de cópia? Como você invoca um?
Machine Translated by Google

11.15 Personalizando Construtores de Cópia 465

11.38 Qual é a saída do código a seguir?


#include <iostream>
#incluir <string>
usando namespace std;

int principal()
{
strings1 ("ABC");
string s2("DEFG");
s1 = string(s2);
cout << s1 << endl;
cout << s2 << endl;

retornar 0;
}

11.39 O código destacado na questão anterior é igual ao seguinte?


s1 = s2;

Qual é melhor?

11.15 Personalizando Construtores de Cópia


Você pode personalizar o construtor de cópia para executar uma cópia profunda.
Chave
Apontar
Conforme discutido na seção anterior, o construtor de cópia padrão ou operador de atribuição =
executa uma cópia superficial. Para realizar uma cópia profunda, você pode implementar o construtor de cópia.
A Listagem 11.19 revisa a classe Course para definir um construtor de cópia na linha 11.

Listagem 11.19 CourseWithCustomCopyConstructor.h


1 #ifndef CURSO_H
2 #define CURSO_H
3 #incluir <string>
4 usando namespace std;

Curso de 5 6 aulas
7{
8 público:
9 Curso (const string& courseName, int capacidade);
10 ~Curso(); // Destruidor
11 Curso(const Curso&); //Copia o construtor construtor de cópia
12 string getCourseName() const;
13 void addStudent(const string& nome);
14 void dropStudent(const string& nome);
15 string* getAlunos() const;
16 int getNumberOfStudents() const;
17
18 privado:
curso de 19 cordas;
20 alunos string*;
21 número interno de alunos;
22 capacidade interna ;
23};

24 25 #endif
Machine Translated by Google

466 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

A Listagem 11.20 implementa o novo construtor de cópia nas linhas 51–57. Ele copia courseName,
numberOfStudents ecapacidade de um objeto de curso para este objeto de curso (linhas 53–55).
Uma nova matriz é criada para conter os nomes dos alunos neste objeto na linha 56.

Listagem 11.20 CourseWithCustomCopyConstructor.cpp


1 #include <iostream>
incluir arquivo de cabeçalho 2 #include "CourseWithCustomCopyConstructor.h"
3 usando namespace std;

4 5 Curso::Curso(const string& courseName, capacidade interna )


6{
7 númeroDeAlunos = 0;
this->nomedocurso = nomedocurso;
isto->capacidade = capacidade;
8 alunos = nova string[capacidade];
9 10 11}
12
13 Curso::~Curso()
14 {
15 excluíram [] alunos;
16}
17
18 strings Curso::getCourseName() const
19 {
20 return nomedocurso;
21}

22 23 void Curso::addStudent(const string& nome)


24 {
25 if (númeroDeAlunos >= capacidade)
26 {
27 cout << "O tamanho máximo do array foi excedido" << endl;
28 cout << "O programa termina agora" << endl;
29 saída(0);
30 }
31
32 alunos[númeroDeAlunos] = nome;
33 númeroDeAlunos++;
34 }

35 36 void Curso::dropStudent(const string& nome)


37 {
// Deixado como exercício
38 39}
40
41 string* Curso::getStudents() const
42 {
alunos que retornam ;
43 44}

45 46 int Curso::getNumberOfStudents() const


47 {
48 retornar númeroDeAlunos;
49}
50
construtor de cópia 51 Course::Course(const Course& course) // Copiar construtor
52 {
53 nomedocurso = curso.nomedocurso;
Machine Translated by Google

11.15 Personalizando Construtores de Cópia 467

númeroDeAlunos = curso.númeroDeAlunos;
capacidade = curso.capacidade;
alunos = nova string[capacidade]; crie uma nova matriz
54 55 56 57}

A Listagem 11.21 fornece um programa para testar o construtor de cópia customizada. O programa é
idêntico à Listagem 11.18, ShallowCopyDemo.cpp, exceto pelo fato de usar CourseWithCustomCopyConstructor.h
em vez de Curso.h.

Listagem 11.21 CustomCopyConstructorDemo.cpp


1 #include <iostream>
2 #include "CourseWithCustomCopyConstructor.h"
3 usando namespace std;

4 5 int principal()
6{
Curso curso1(" Programação C++", 10);
Curso curso2(curso1); usar construtor de cópia

7 curso1.addAluno("Peter Pan"); //Adiciona um aluno ao course1


8 course2.addStudent("Lisa Ma"); //Adiciona um aluno ao course2
9
" <<
10 cout << "alunos do curso1:
11 course1.getStudents()[0] << endl;
" <<
12 cout << "alunos do curso2:
13 course2.getStudents()[0] << endl;
14
15 retornar 0;
16 17 18 19 }

alunos do curso 1: Peter Pan


alunos do curso 2: Lisa Ma

O construtor de cópia constrói um novo array em course2 para armazenar nomes de alunos que é
independente do array em course1. O programa adiciona um aluno "Peter Pan" ao curso1
(linha 10) e uma aluna "Lisa Ma" para o curso2 (linha 11). Como você pode ver na saída deste exemplo, o
primeiro aluno do curso1 agora é "Peter Pan" e do curso2 é "Lisa Ma".
A Figura 11.7 mostra os dois objetos Course e dois arrays de strings para estudantes.

curso1: Curso curso2: Curso

nomedocurso = "C++" matriz de strings nomedocurso = "C++" matriz de strings


para estudantes para estudantes
estudantes estudantes

númeroDeAlunos = 0 númeroDeAlunos = 0

capacidade = 10 capacidade = 10

Figura 11.7 Depois que course1 é copiado para course2, os campos de dados dos alunos de course1 e course2 apontam para dois
arrays diferentes.

Observação

O construtor de cópia personalizado não altera o comportamento do operador de cópia cópia para membros
membro = por padrão. O Capítulo 14 apresentará como personalizar o operador = .
Machine Translated by Google

468 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

ÿ
ÿVerificação de ponto
11.40 Use a classe Person no Ponto de Verificação 11.34 para demonstrar por que uma cópia profunda
é necessária. Forneça um construtor personalizado que execute uma cópia profunda para o
array filho .

Termos chave

operador de endereço (&) 432 loja gratuita 451


operador de seta (->) 454 pilha 451
ponteiro constante 438 operador indireto 434
copiar construtor 462 vazamento de memória 452
ponteiro pendurado 452 nova operadora 449
cópia profunda 463 ponteiro 442
excluir operador 452 string baseada em ponteiro 442
operador de desreferência (*) 434 cópia superficial 463
destruidor 456 esta palavra-chave 455

Resumo do capítulo

1. Ponteiros são variáveis que armazenam o endereço de memória de outras variáveis.

2. A declaração

int* pContagem;

declara pCount como um ponteiro que pode apontar para uma variável int .

3. O símbolo e comercial (&) é chamado de operador de endereço quando colocado na frente de uma variável.
capaz. É um operador unário que retorna o endereço da variável.

4. Uma variável de ponteiro é declarada com um tipo como int ou double. Você tem que atribuí-lo
com o endereço da variável do mesmo tipo.

5. Como uma variável local, um ponteiro local recebe um valor arbitrário se você não inicializar
isto.

6. Um ponteiro pode ser inicializado como NULL (igual a 0), que é um valor especial para um ponteiro
para indicar que o ponteiro aponta para nada.

7. O asterisco (*) colocado antes de um ponteiro é conhecido como operador de indireção ou operador de
desreferência (desreferência significa referência indireta).

8. Quando um ponteiro é desreferenciado, o valor no endereço armazenado no ponteiro é


recuperado.

9. A palavra-chave const pode ser usada para declarar ponteiro constante e dados constantes.

10. Um nome de array é na verdade um ponteiro constante que aponta para o endereço inicial do
variedade.

11. Você pode acessar os elementos do array usando ponteiros ou via índice.
Machine Translated by Google

Exercícios de Programação 469

12. Um número inteiro pode ser adicionado ou subtraído de um ponteiro. O ponteiro é incrementado ou
decrementado por esse número inteiro vezes o tamanho do elemento para o qual o ponteiro aponta.

13. Um argumento de ponteiro pode ser passado por valor ou por referência.

14. Um ponteiro pode ser retornado de uma função. Mas você não deve retornar o endereço de uma variável
local de uma função, porque uma variável local é destruída após o retorno da função.

15. O operador new pode ser usado para alocar memória persistente no heap.

16. Você deve usar o operador delete para liberar a memória criada usando a nova operação
ator, quando a memória não for mais necessária.

17. Você pode usar ponteiros para referenciar um objeto e acessar campos de dados do objeto e invocar
funções.

18. Você pode criar objetos dinamicamente em um heap usando o operador new .

19. A palavra-chave this pode ser usada como um ponteiro para o objeto de chamada.

20. Os destruidores são o oposto dos construtores.

21. Os construtores são invocados para criar objetos e os destruidores são invocados automaticamente
quando os objetos são destruídos.

22. Cada classe possui um destruidor padrão, se o destruidor não estiver definido explicitamente.

23. O destruidor padrão não executa nenhuma operação.

24. Cada classe tem um construtor de cópia padrão, se o construtor de cópia não for explicitamente
definiram.

25. O construtor de cópia padrão simplesmente copia cada campo de dados em um objeto para sua
contraparte no outro objeto.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 11.2–11.11

11.1 (Analisar entrada) Escreva um programa que primeiro leia um número inteiro para o tamanho do array,
depois leia os números no array, conte os números pares e os números ímpares e os exiba.

**11.2 (Imprimir as consoantes) Escreva um programa que primeiro leia um número inteiro para o tamanho do
array, depois leia os caracteres no array e exiba as consoantes (ou seja, um caractere será exibido
apenas se for uma consoante). (Dica: leia um caractere e armazene-o em uma matriz se não for uma
vogal. Se o caractere for uma vogal, descarte-o. Após a entrada, a matriz contém apenas as
consoantes.)
Machine Translated by Google

470 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

*11.3 (Classificar um array) No Exercício de Programação 7.14, você usou a classificação por bolha para classificar
um array. A função de classificação por bolha compara repetidamente os pares vizinhos sucessivos na
matriz e os troca se estiverem em ordem decrescente. Modifique o programa usando o seguinte
cabeçalho:

void bubbleSort(int* matriz const, tamanho interno )

A função retorna um array que contém os elementos classificados.

11.4 (Soma de localizações pares) Escreva duas funções sobrecarregadas que retornem a soma de valores em
localizações pares de um array com os seguintes cabeçalhos:

int sumOfEven(const int* matriz, tamanho interno);


double sumOfEven(const double* matriz, tamanho interno );

Escreva um programa de teste que leia cinco números inteiros ou valores duplos, invoque esta função e
exiba a soma dos valores em locais pares.

11.5 (Encontre o maior elemento) Use ponteiros para escrever uma função que encontre o maior elemento em um
array de inteiros. Use {6, 7, 9, 10, 15, 3, 99, -21} para testar a função.

**11.6 (Ocorrências de cada dígito em uma string) Escreva uma função que conte as ocorrências de cada dígito em uma
string usando o seguinte cabeçalho:
Nota de vídeo
Retornar um ponteiro
int* contagem(const string& s)

A função conta quantas vezes um dígito aparece na string. O valor de retorno é uma matriz de dez
elementos, cada um dos quais contém a contagem de um dígito. Por exemplo, após executar int* counts
= count("12203AB3"), counts[0] é 1, counts[1] é 1, counts[2] é 2, counts[3] é 2.

Escreva uma função principal para exibir a contagem de "SSN é 343 32 4545 e ID é 434 34 4323".

Redesenhe a função para passar a matriz de contagens em um parâmetro da seguinte maneira:

contagem de void (const string& s, int counts[], int size)

onde tamanho é o tamanho da matriz de contagens . Neste caso, é 10.

**11.7 (Negócio: caixa eletrônico) Use a classe Account criada no Exercício de programação 9.3 para simular um caixa
eletrônico. Crie 10 contas em um array com id 0, . , 9 e saldo inicial de $ 100. O sistema solicita que o
. usuário insira um ID. 1, .

Se o ID for inserido incorretamente, peça ao usuário para inserir o correto. Assim que um ID for aceito, o
menu principal será exibido, conforme mostrado na execução de amostra. Você pode inserir a opção 1
para visualizar o saldo atual, 2 para sacar dinheiro, 3 para depositar dinheiro e 4 para sair do menu
principal. Depois de sair, o sistema solicitará um ID novamente. Assim, uma vez iniciado o sistema, ele
não irá parar.

Insira um ID: 4

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma opção: 1 O saldo
é 100,0
Machine Translated by Google

Exercícios de Programação 471

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma opção: 2
Insira um valor para sacar: 3

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma escolha: 1 O
saldo é 97,0

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma opção: 3
Insira um valor para depositar: 10

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma escolha: 1 O
saldo é 107,0

Menu principal
1: verificar saldo
2: retirar
3: depósito
4: saída
Insira uma opção: 4
Insira um ID:

*11.8 (Geometria: A classe Circle2D ) Defina a classe Circle2D que contém:

n Dois campos de dados duplos chamados x e y que especificam o centro do círculo com
funções get constantes .
n Um raio de campo de dados duplo com uma função get constante .
n Um construtor sem argumentos que cria um círculo padrão com (0, 0) para (x, y) e 1 para
raio.
n Um construtor que cria um círculo com x, y e raio especificados.
n Uma função constante getArea() que retorna a área do círculo.
n Uma função constante getPerimeter() que retorna o perímetro do círculo.
n Uma função constante contém(duplo x, duplo y) que retorna verdadeiro se
o ponto especificado (x, y) está dentro deste círculo. Veja a Figura 11.8a.
n Uma função constante contém(const Circle2D& círculo) que retorna verdadeiro se o
círculo especificado estiver dentro deste círculo. Veja a Figura 11.8b.
n Uma função constante sobreposições (const Circle2D& círculo) que retorna verdadeiro
se o círculo especificado se sobrepõe a este círculo. Veja a Figura 11.8c.
Machine Translated by Google

472 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

(a) (b) (c)

Figura 11.8 (a) Um ponto está dentro do círculo. (b) Um círculo está dentro de outro círculo. (c) Um círculo se
sobrepõe a outro círculo.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um


programa de teste que crie um objeto Circle2D c1(2, 2, 5,5), c2(2, 2, 5,5) e
c3(4, 5, 10,5), exiba a área e o perímetro de c1, o resultado de c1.contains( 3,
3), c1.contém(c2) e c1.sobrepõe(c3).
*11.9 (Geometria: A classe Rectangle2D ) Defina a classe Rectangle2D que contém:

n Dois campos de dados duplos chamados x e y que especificam o centro do retângulo com
funções get constantes e funções set . (Suponha que os lados do retângulo sejam paralelos
aos eixos x ou y.)
n A largura e altura dos campos de dados duplos com funções get constantes e
definir funções.
n Um construtor sem argumentos que cria um retângulo padrão com (0, 0) para (x, y) e 1
tanto para largura quanto para altura.
n Um construtor que cria um retângulo com x, y, largura e
altura.
n Uma função constante getArea() que retorna a área do retângulo.
n Uma função constante getPerimeter() que retorna o perímetro do retângulo.
n Uma função constante contém (duplo x, duplo y) que retorna verdadeiro se o ponto especificado
(x, y) estiver dentro deste retângulo. Veja a Figura 11.9a.
n Uma função constante contém (const Rectangle2D &r) que retorna verdadeiro
se o retângulo especificado estiver dentro deste retângulo. Veja a Figura 11.9b.
n Uma função constante se sobrepõe (const Rectangle2D &r) que retorna verdadeiro
se o retângulo especificado se sobrepuser a este retângulo. Veja a Figura 11.9c.

(a) (b) (c) (d)

Figura 11.9 (a) Um ponto está dentro do retângulo. (b) Um retângulo está dentro de outro retângulo. (c) Um
retângulo se sobrepõe a outro retângulo. (d) Os pontos são colocados dentro de um retângulo.

Desenhe o diagrama UML para a classe. Implemente a classe. Escreva um


programa de teste que crie três objetos Rectangle2D r1(2, 2, 5,5, 4,9), r2(4,
5, 10,5, 3,2)) e r3(3, 5, 2,3, 5,4) e exiba a área e o perímetro de r1 e exibe o
resultado de r1.contains(3, 3), r1.contains(r2) e r1.
sobreposições (r3).
*11.10 (Contar ocorrências de cada dígito em um número inteiro) Reescreva a função countDigits
no Exercício de Programação 10.7 usando o seguinte cabeçalho:

int* contagemDígitos(const int& número)

Esta função retorna as contagens como um array de 10 elementos. Por exemplo, depois de invocar

int contagens[] = contagemDigits(11223)


Machine Translated by Google

Exercícios de Programação 473

contagens[0] é 0, contagens[1] é 2, contagens[2] é 2,


contagens[3] é 1, ...

Escreva um programa de teste que solicite ao usuário que insira um número inteiro, invoque
a função count-Digits e exiba as contagens de cada dígito no número inteiro fornecido.

*11.11 (Geometria: encontre o retângulo delimitador) Um retângulo delimitador é o retângulo mínimo que
envolve um conjunto de pontos em um plano bidimensional, conforme mostrado na Figura
11.9d. Escreva uma função que retorne um retângulo delimitador para um conjunto de pontos
em um plano bidimensional, como segue:

const int TAMANHO = 2;


Rectangle2D getRectangle(const double points[][SIZE]);

A classe Rectangle2D é definida no Exercício de Programação 11.9. Escreva um programa de


teste que solicite ao usuário que insira cinco pontos e exiba o centro, a largura e a altura do
retângulo delimitador. Aqui está um exemplo de execução:

Insira cinco pontos: 1,0 2,5 3 4 5 6 7 8 9 10


O centro do retângulo delimitador (5,0, 6,25), largura 8,0, altura 7,5

*11.12 (A classe MyDate ) Projete uma classe chamada MyDate. A classe contém:

n Os campos de dados ano, mês e dia que representam uma data. mês é baseado em 0,
ou seja, 0 é para janeiro.
n Um construtor sem argumentos que cria um objeto MyDate para a data atual.
n Um construtor que constrói um objeto MyDate com um tempo decorrido especificado
desde meia-noite de 1º de janeiro de 1970, em segundos.
n Um construtor que constrói um objeto MyDate com o ano, mês,
e dia.
n Três funções get constantes para os campos de dados ano, mês e dia,
respectivamente.
n Três funções definidas para os campos de dados ano, mês e dia, respectivamente.
n Uma função chamada setDate(long elapsedTime) que define uma nova data para o objeto
usando o tempo decorrido.

Desenhe o diagrama UML para a classe e depois implemente a classe. Escreva um programa
de teste que crie dois objetos MyDate (usando MyDate() e MyDate(3435555513)) e exiba seu
ano, mês e dia.

(Dica: os dois primeiros construtores extrairão o ano, o mês e o dia do tempo decorrido. Por
exemplo, se o tempo decorrido for 561555550 segundos, o ano será 1987, o mês será 9 e o dia
será 18.)

Seções 11.12–11.15

**11.13 (A classe Course ) Revise a implementação da classe Course na Listagem 11.16,


Curso.cpp, como segue:

n Ao adicionar um novo aluno ao curso, se a capacidade do array for excedida, aumente o


tamanho do array criando um novo array maior e copiando o conteúdo do array atual para
ele.
n Implemente a função dropStudent .
n Adicione uma nova função chamada clear() que remove todos os alunos do curso.
n Implemente o destruidor e o construtor de cópia para realizar uma cópia profunda no
aula.

Escreva um programa de teste que crie um curso, adicione três alunos, remova um e exiba os
alunos do curso.
Machine Translated by Google

474 Capítulo 11 Ponteiros e gerenciamento dinâmico de memória

11.14 (Implementando a classe string ) A classe string é fornecida na biblioteca C++.


Forneça sua própria implementação para as seguintes funções (nomeie a nova classe
MyString):

MinhaString();
MinhaString(const char* cString);
char at ( índice int ) const;
comprimento interno () const;
vazio claro();
bool vazio() const;
int comparar(const MyString& s) const;
int comparar( índice int , int n, const MyString& s) const;
void copy(char s[], índice int , int n);
char* dados() const;
int encontrar (char ch) const;
int find(char ch, índice int ) const;
int find(const MyString& s, índice int ) const;

11.15 (Implementando a classe string ) A classe string é fornecida na biblioteca C++.


Forneça sua própria implementação para as seguintes funções (nomeie a nova classe
MyString):

MyString(const char ch, tamanho interno );


MyString(const char chars[], tamanho interno );
MyString anexar(const MyString& s);
MyString anexar(const MyString& s, índice int , int n);
MyString anexar(int n, char ch);
MyString atribuir(const char* chars);
MyString atribuir(const MyString& s, índice int , int n);
MyString atribuir(const MyString& s, int n);
MyString atribuir(int n, char ch);
MyString substr( índice int , int n) const;
MyString substr( índice int ) const;
Apagar MyString ( índice int, int n);

11.16 (Classificar caracteres em uma string) Reescreva a função sort no Exercício de


Programação 10.4 usando a função sort introduzida na Seção 11.8. (Dica: obtenha
uma string C da string e aplique a função sort para classificar os caracteres na matriz
da string C e obtenha uma string da string C classificada.) Escreva um programa de
teste que solicite ao usuário que insira um string e exibe a nova string classificada. A
execução de amostra é a mesma do Exercício de Programação 10.4.
Machine Translated by Google

CAPÍTULO

12
Modelos, vetores e
pilhas

Objetivos
n Conhecer a motivação e os benefícios dos modelos (§12.2).

n Para definir uma função de modelo com parâmetros de tipo (§12.2).

n Desenvolver uma função de classificação genérica usando modelos (§12.3).

n Desenvolver classes genéricas usando modelos de classe (§§12.4–12.5).

n Para usar a classe vetorial C++ como um array redimensionável (§12.6).

n Para substituir matrizes usando vetores (§12.7).

n Para analisar e avaliar expressões usando pilhas (§12.8).


Machine Translated by Google

476 Capítulo 12 Modelos, vetores e pilhas

12.1 Introdução
Você pode definir funções e classes de modelo em C++ usando tipos genéricos. Funções e classes
Chave
Apontar de modelos permitem que os programas funcionem em muitos tipos de dados diferentes sem
serem reescritos para cada um.

o que é um modelo? C++ fornece funções e classes para o desenvolvimento de software reutilizável. Os modelos fornecem a
capacidade de parametrizar tipos em funções e classes. Com esse recurso, você pode definir uma função
ou uma classe com um tipo genérico que o compilador pode substituir por um tipo concreto. Por exemplo,
você pode definir uma função para encontrar o número máximo entre dois números de um tipo genérico. Se
você invocar esta função com dois argumentos int , o tipo genérico será substituído pelo tipo int . Se você
invocar esta função com dois argumentos duplos , o tipo genérico será substituído pelo tipo duplo .

Este capítulo apresenta o conceito de modelos e você aprenderá como definir modelos de função e
modelos de classe e usá-los com tipos concretos. Você também aprenderá um vetor de modelo genérico
muito útil, que pode ser usado para substituir arrays.

12.2 Fundamentos dos Modelos


Nota de vídeo Os modelos fornecem a capacidade de parametrizar tipos em funções e classes. Você pode definir
Noções básicas de modelos Chave
Apontar funções ou classes com tipos genéricos que podem ser substituídos por tipos concretos pelo
compilador.

modelo Comecemos com um exemplo simples para demonstrar a necessidade de modelos. Suponha que você
queira encontrar o máximo de dois inteiros, dois duplos, dois caracteres e duas strings. Você pode escrever
quatro funções sobrecarregadas da seguinte maneira:

tipo interno 1 int maxValue(int valor1, int valor2)


2{
se (valor1 > valor2)
valor de retorno1 ;
outro
valor de retorno2 ;
34567}
8
tipo duplo 9 duplo maxValue (duplo valor1, duplo valor2)
10 {
11 se (valor1 > valor2)
12 valor de retorno1 ;
13 outro
14 valor de retorno2 ;
15 }
16
tipo de caractere 17 char maxValue(char valor1, char valor2)
18 {
19 se (valor1 > valor2)
20 valor de retorno1 ;
21 outro
valor de retorno2 ;
22 23}
24
tipo de string 25 string maxValue(string valor1, string valor2)
26 {
27 se (valor1 > valor2)
28 valor de retorno1 ;
29 outro
valor de retorno2 ;
30 31}
Machine Translated by Google

12.2 Fundamentos de Modelos 477

Estas quatro funções são quase idênticas, exceto que cada uma utiliza um tipo diferente. A
primeira função usa o tipo int , a segunda o tipo double , a terceira o tipo char e a quarta o tipo
string . Isso economizaria digitação, espaço e facilitaria a manutenção do programa se você
pudesse simplesmente definir uma função com um tipo genérico como segue:

1 GenericType maxValue (GenericType valor1, GenericType valor2) tipo genérico


{
se (valor1 > valor2)
valor de retorno1 ;
outro
valor de retorno2 ;
234567}

Este GenericType se aplica a todos os tipos, como int, double, char e string.
C++ permite definir um modelo de função com tipos genéricos. A Listagem 12.1 define um
função de modelo para encontrar um valor máximo entre dois valores de um tipo genérico.

Listagem 12.1 GenericMaxValue.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;
4
5 modelo<nome do tipo T> prefixo do modelo
6 T maxValue (T valor1, T valor2) parâmetro de tipo
7{
se (valor1 > valor2)
valor de retorno1 ;
8 outro
9 valor de retorno2 ;
10 11 12 }
13
14 int principal()
15 {
16 cout << "O máximo entre 1 e 3 é " cout << "O máximo << maxValue(1, 3) << endl; invocar maxValue
17 entre 1,5 e 0,3 é "
18 << maxValue(1,5, 0,3) << endl; invocar maxValue
19 cout << "O máximo entre 'A' e 'N' é "
20 << maxValue('A', 'N') << endl; invocar maxValue
21 cout << "O máximo entre \"NBC\" e \"ABC\" é "
22 << maxValue(string("NBC"), string("ABC")) << endl; invocar maxValue
23
24 retornar 0;
25}

O máximo entre 1 e 3 é 3
O máximo entre 1,5 e 0,3 é 1,5
O máximo entre 'A' e 'N' é N
O máximo entre "NBC" e "ABC" é NBC

A definição do modelo de função começa com a palavra-chave template seguida por uma
lista de parâmetros. Cada parâmetro deve ser precedido pela palavra-chave intercambiável
typename ou class no formato <typename typeParameter> ou <class typeParameter>. Por
exemplo, linha 5

modelo<nome do tipo T>


Machine Translated by Google

478 Capítulo 12 Modelos, vetores e pilhas

inicia a definição do modelo de função para maxValue. Esta linha também é conhecida como prefixo do modelo.
prefixo do modelo Aqui T é um parâmetro de tipo. Por convenção, uma única letra maiúscula como T é usada para denotar um
parâmetro de tipo parâmetro de tipo.
A função maxValue é definida nas linhas 6–12. Um parâmetro de tipo pode ser usado na função como um
tipo normal. Você pode usá-lo para especificar o tipo de retorno de uma função, declarar parâmetros de função ou
declarar variáveis na função.
invocar uma função A função maxValue é invocada para retornar o máximo int, double, char e string nas linhas 16–22. Para a chamada
de função maxValue(1, 3), o compilador reconhece que o tipo de parâmetro é int e substitui o parâmetro de tipo T por int
para invocar a função maxValue com um tipo int concreto . Para a chamada de função maxValue(string("NBC"),
string("ABC")), o compilador reconhece que o tipo de parâmetro é string e substitui o parâmetro de tipo T por string para
invocar a função maxValue com um tipo de string concreto .

O que acontece se você substituir maxValue(string("NBC"), string("ABC")) na linha 22 por maxValue("NBC",


"ABC")? Você ficará surpreso ao ver que ele retorna ABC. Por que?
String C "NBC" e "ABC" são cordas C. Invocar maxValue("NBC", "ABC") passa os endereços de "NBC" e "ABC" para
o parâmetro da função. Ao comparar valor1 > valor2, os endereços de dois arrays são comparados, não o
conteúdo do array!

Cuidado
parâmetro de correspondência A função genérica maxValue pode ser usada para retornar no máximo dois valores de qualquer
tipo, desde que

n Os dois valores têm o mesmo tipo;

n Os dois valores podem ser comparados usando o operador > .

Por exemplo, se um valor for int e o outro for double (por exemplo, maxValue(1, 3.5)), o
compilador reportará um erro de sintaxe porque não consegue encontrar uma correspondência
para a chamada. Se você invocar maxValue(Circle(1), Circle(2)), o compilador reportará um erro
de sintaxe porque o operador > não está definido na classe Circle .

Dica
<nome do tipo T> preferido Você pode usar <typename T> ou <class T> para especificar um parâmetro de tipo. Usar
<typename T> é melhor porque <typename T> é descritivo. <class T> pode ser confundido com
definição de classe.

Observação

parâmetros de vários tipos Ocasionalmente, uma função de modelo pode ter mais de um parâmetro. Neste caso, coloque os
parâmetros juntos entre colchetes, separados por vírgula, como <nome_tipo T1, nome_tipo T2,
nome_tipo T3>.

Os parâmetros na função genérica na Listagem 12.1 são definidos como passagem por valor. Você pode
modificá-lo usando passagem por referência, conforme mostrado na Listagem 12.2.

Listagem 12.2 GenericMaxValuePassByReference.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;

prefixo do modelo 4 5 modelo<nome do tipo T>


parâmetro de tipo 6 T maxValue(const T& valor1, const T& valor2)
7{
8 se (valor1 > valor2)
9 valor de retorno1 ;
Machine Translated by Google

12.2 Fundamentos de Modelos 479

10 outro
11 valor de retorno2 ;
12}
13
14 int principal()
15 {
16 cout << "O máximo entre 1 e 3 é " cout << "O máximo << maxValue(1, 3) << endl; invocar maxValue
17 entre 1,5 e 0,3 é "
18 << maxValue(1,5, 0,3) << endl; invocar maxValue
19 cout << "O máximo entre 'A' e 'N' é "
20 << maxValue('A', 'N') << endl; invocar maxValue
21 cout << "O máximo entre \"NBC\" e \"ABC\" é "
22 << maxValue(string("NBC"), string("ABC")) << endl; invocar maxValue
23
24 retornar 0;
25 }

O máximo entre 1 e 3 é 3
O máximo entre 1,5 e 0,3 é 1,5
O máximo entre 'A' e 'N' é N
O máximo entre "NBC" e "ABC" é NBC

12.1 Para a função maxValue na Listagem 12.1, você pode invocá-la com dois
argumentos de tipos diferentes, como maxValue(1, 1.5)? ÿVerificação de ponto

12.2 Para a função maxValue na Listagem 12.1, você pode invocá-la com dois
argumentos de strings, como maxValue("ABC", "ABD")? Você pode invocá-lo
com dois argumentos de círculos, como maxValue(Circle(2), Circle(3))?
12.3 O template<typename T> pode ser substituído pelo template<class T>?
12.4 Um parâmetro de tipo pode ser nomeado usando qualquer identificador que não seja uma palavra-chave?

12.5 Um parâmetro de tipo pode ser de um tipo primitivo ou de um tipo de objeto?

12.6 O que há de errado no código a seguir?

#include <iostream>
#incluir <string>
usando namespace std;
modelo<nome do tipo T>
T maxValue(T valor1, T valor2)
{
resultado interno ;
se (valor1 > valor2)
resultado = valor1;
outro
resultado = valor2;
resultado de retorno ;
}

int principal()
{
cout << "O máximo entre 1 e 3 é "
<< maxValue(1, 3) << endl;
cout << "O máximo entre 1,5 e 0,3 é"
<< maxValue(1,5, 0,3) << endl;
cout << "O máximo entre 'A' e 'N' é "
<< maxValue('A', 'N') << endl;
Machine Translated by Google

480 Capítulo 12 Modelos, vetores e pilhas


cout << "O máximo entre \"ABC\" e \"ABD\" é "
<< maxValue("ABC", "ABD") << endl;

retornar 0;
}

12.7 Suponha que você defina a função maxValue da seguinte forma:

modelo<nome do tipo T1, nome do tipo T2>


T1 maxValue(T1 valor1, T2 valor2)
{
se (valor1 > valor2)
valor de retorno1 ;
outro
valor de retorno2 ;
}

Qual seria o valor de retorno da invocação de maxValue(1, 2.5),


maxValue(1.4, 2.5) e maxValue(1.5, 2)?

12.3 Exemplo: uma classificação genérica


Esta seção define uma função de classificação genérica.
Chave
Apontar
A Listagem 7.11, SelectionSort.cpp, fornece uma função para classificar um array de valores duplos . Aqui está uma
cópia da função:

tipo duplo 1 seleção nulaSort(double list[], int listSize) 2 {

for (int i = 0; i < tamanholista; i++) {


34

// Encontre o mínimo na lista[i..listSize-1]


tipo duplo 56 double currentMin = lista[i];
7 int currentMinIndex = i;
8

for (int j = i + 1; j < tamanholista; j++) {


9 10
11 if (Min atual > lista[j]) {
12
13 atualMin = lista[j];
14 índiceMin atual = j;
15 }
16 }
17
18 // Troque list[i] por list[currentMinIndex] se necessário
19 if (índiceMinAtual! = i) {
20
21 lista[currentMinIndex] = lista[i];
22 lista[i] = atualMin;
}
23 }
24 25}

É fácil modificar esta função para escrever novas funções sobrecarregadas para classificar uma matriz de valores
int , valores char , valores string e assim por diante. Tudo que você precisa fazer é substituir a palavra double por int,
char ou string em dois lugares (linhas 1 e 6).
Em vez de escrever diversas funções de classificação sobrecarregadas, você pode definir apenas uma função de
modelo que funcione para qualquer tipo. A Listagem 12.3 define uma função genérica para ordenar um array de elementos.
Machine Translated by Google

12.3 Exemplo: uma classificação genérica 481

Listagem 12.3 GenericSort.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;

4 5 modelo<nome do tipo T> prefixo do modelo


6 void sort(T lista[], int listSize) 7 { parâmetro de tipo

8 for (int i = 0; i < tamanholista; i++) {

9 10 // Encontre o mínimo na lista[i..listSize-1]


11 T atualMin = lista[i]; parâmetro de tipo
12 int currentMinIndex = i;
13
14 for (int j = i + 1; j < tamanholista; j++) {
15
16 if (Min atual > lista[j]) {
17
18 atualMin = lista[j];
19 índiceMin atual = j;
20 }
21 }
22
23 // Troque list[i] por list[currentMinIndex] se necessário;
24 if (índiceMinAtual! = i) {
25
26 lista[currentMinIndex] = lista[i];
27 lista[i] = atualMin;
28 }
29 }
30 }
31
32 modelo<nome do tipo T> prefixo do modelo
33 void printArray(const T lista[], int listSize) parâmetro de tipo
34 {
for (int i = 0; i <listSize; i++)
35 {
36 cout << lista[i] << " ";
37 }
38 cout << endl;
39 40 }
41
42 int principal()
43 {
44 int lista1[] = {3, 5, 1, 0, 2, 8, 7};
45 ordenar(lista1, 7); invocar classificação

46 printArray(lista1, 7); invocar printArray


47
48 lista dupla2 [] = {3,5, 0,5, 1,4, 0,4, 2,5, 1,8, 4,7};
49 ordenar(lista2, 7);
50 printArray(lista2, 7);
51
52 string list3[] = {"Atlanta", "Denver", "Chicago", "Dallas"};
53 classificar(lista3, 4);
54 printArray(lista3, 4);

55 retornar 0;
56 57}
Machine Translated by Google

482 Capítulo 12 Modelos, vetores e pilhas

0123578
0,4 0,5 1,4 1,8 2,5 3,5 4,7
Atlanta Chicago Dallas Denver

Duas funções de modelo são definidas neste programa. A função de modelo sort (linhas 5 a 30) usa o parâmetro de
tipo T para especificar o tipo de elemento em um array. Esta função é idêntica à função selectionSort , exceto que o
parâmetro double é substituído por um tipo genérico T.

A função de modelo printArray (linhas 32 a 40) usa o parâmetro de tipo T para especificar o
tipo de elemento em uma matriz. Esta função exibe todos os elementos do array no console.
A função principal invoca a função sort para classificar uma matriz de int, double e string
valores (linhas 45, 49, 53) e invoca a função printArray para exibir essas matrizes (linhas 46, 50, 54).

Dica
desenvolvendo função genérica Ao definir uma função genérica, é melhor começar com uma função não genérica, depurá-la e testá-la e,
em seguida, convertê-la em uma função genérica.

12.8 Suponha que você defina a função swap da seguinte maneira:


ÿVerificação de ponto
modelo<nome do tipo T>
troca nula (T& var1, T& var2)
{
Temperatura T = var1;
var1 = var2;
var2 = temp;
}

O que há de errado no código a seguir?

int principal()
{
int v1 = 1;
int v2 = 2;
trocar(v1,v2);

duplo d1 = 1;
duplo d2 = 2;
trocar(d1, d2);

trocar(v1, d2);
trocar(1, 2);

retornar 0;
}

12.4 Modelos de aula


Nota de vídeo Você pode definir tipos genéricos para uma classe.
Chave
Classe de modelos
Apontar
Nas seções anteriores, você definiu funções de modelo com parâmetros de tipo para a função. Você também pode
classe de modelo definir classes de modelo com parâmetros de tipo para a classe. Os parâmetros de tipo podem ser usados em
qualquer lugar da classe onde um tipo regular apareça.
Lembre-se de que a classe StackOfIntegers , definida na Seção 10.9, “Estudo de caso: a classe StackOfInteger ”,
pode ser usada para criar uma pilha para valores int . Aqui está uma cópia da classe com seu diagrama de classes
UML, conforme mostrado na Figura 12.1a.
Machine Translated by Google

12.4 Modelos de Classe 483

StackOfIntegers Pilha<T>

-elementos[100]: int -elementos[100]:T


-tamanho: int -tamanho: int

+StackOfIntegers() +Pilha()
+vazio(): bool const +vazio(): bool const
+ espiar(): int const + espiar (): T const
+push(valor: int): vazio +push(valor: T): vazio
+pop(): int +pop(): T
+getSize():int const +getSize():int const

(a) (b)

Figura 12.1 Stack<T> é uma versão genérica da classe Stack .

1 #ifndef STACK_H
2 #define STACK_H

3 4 classes StackOfIntegers
{
5 6 público:
StackOfIntegers();
7 8 bool vazio() const;
int espiar() const; tipo interno
9 10 void push( valor int); tipo interno
11 intpop (); tipo interno
12 int getSize() const;

13 14 privado:
15 elementos internos [100];
16 tamanho interno ; tipo interno
17};
18
19 StackOfIntegers::StackOfIntegers()
20 {
21 tamanho = 0;
22}

23 24 bool StackOfIntegers::empty() const


25 {
tamanho de retorno == 0;
26 27}

28 29 int StackOfIntegers::peek() const


30 {
retornar elementos[tamanho - 1];
31 32}

33 34 void StackOfIntegers::push( valor int)


35 {
elementos[tamanho++] = valor;
36 37}

38 39 int StackOfIntegers::pop()
40 {
41 retornar elementos[--tamanho];
42}
Machine Translated by Google

484 Capítulo 12 Modelos, vetores e pilhas

43 44 int StackOfIntegers::getSize() const


45 {
46 tamanho de retorno ;
47}
48
49 #endif

Ao substituir o int destacado no código anterior por double, char ou string, você pode
facilmente modificar essa classe para definir classes como StackOfDouble, StackOfChar e
StackOfString para representar uma pilha de valores double, char e string . Mas, em vez de
escrever código quase idêntico para essas classes, você pode definir apenas uma classe de
modelo que funcione para o elemento de qualquer tipo. A Figura 12.1b mostra o diagrama de
classes UML para a nova classe genérica Stack . A Listagem 12.4 define uma classe de pilha
genérica para armazenar elementos de um tipo genérico.

Listagem 12.4 GenericStack.h


1 #ifndef STACK_H
2 #define STACK_H

prefixo do modelo 3 4 modelo<nome do tipo T>


Pilha de 5 classes
6{
7 público:
8 Pilha();
bool vazio() const;
parâmetro de tipo 9 10 T peek() const;
parâmetro de tipo 11 nulo push (valor T);
12 T pop();
13 int getSize() const;
14
15 privado:
parâmetro de tipo 16 Elementos T[100];
17 tamanho interno ;
18};
19
modelo de função 20 modelo<nome do tipo T>
21 Pilha<T>::Pilha()
22 {
tamanho = 0;
23 24}

modelo de função 25 26 modelo<nome do tipo T>


27 bool Stack<T>::empty() const
28 {
tamanho de retorno == 0;
29 30}
31
modelo de função 32 modelo<nome do tipo T>
33 T Stack<T>::peek() const
34 {
retornar elementos[tamanho - 1];
35 36}

modelo de função 37 38 modelo<nome do tipo T>


39 void Stack<T>::push(valor T)
40 {
41 elementos[tamanho++] = valor;
42}
Machine Translated by Google

12.4 Modelos de Classe 485

43 44 modelo<nome do tipo T> modelo de função


Pilha de 45 T<T>::pop()
46 {
47 retornar elementos[--tamanho];
48}
49
50 modelo<nome do tipo T> modelo de função
51 int Stack<T>::getSize() const
52 {
tamanho de retorno ;
53 54}

55 56 #endif

A sintaxe dos modelos de classe é basicamente a mesma dos modelos de função. Você coloca o prefixo
do modelo antes da definição de classe (linha 4), assim como coloca o prefixo do modelo antes do modelo prefixo do modelo
de função.

modelo<nome do tipo T>

O parâmetro type pode ser usado na classe como qualquer tipo de dados normal. Aqui, o tipo T
é usado para definir as funções peek() (linha 10), push(T value) (linha 11) e pop() (linha 12).
T também é usado na linha 16 para declarar elementos do array.
Os construtores e funções são definidos da mesma forma que nas classes regulares, exceto que os definir construtores
próprios construtores e funções são modelos. Portanto, você deve colocar o prefixo do modelo antes do definir funções

construtor e do cabeçalho da função na implementação. Por exemplo,

modelo<nome do tipo T>


Pilha<T>::Pilha()
{
tamanho = 0;
}

modelo<nome do tipo T>


bool Stack<T>::empty()
{
tamanho de retorno == 0;
}

modelo<nome do tipo T>


Pilha<T>::peek()
{
retornar elementos[tamanho - 1];
}

Observe também que o nome da classe antes do operador de resolução de escopo :: é Stack<T>, não Stack.

Dica
GenericStack.h combina definição e implementação de classe em um arquivo. problema de compilação

Normalmente, você coloca a definição e a implementação da classe em dois arquivos separados.


Para modelos de classe, entretanto, é mais seguro colocá-los juntos, porque alguns compiladores
não podem compilá-los separadamente.

A Listagem 12.5 fornece um programa de teste que cria uma pilha para valores int na linha 9 e uma pilha
para strings na linha 18.

Listagem 12.5 TestGenericStack.cpp


1 #include <iostream>
2 #incluir <string>
Machine Translated by Google

486 Capítulo 12 Modelos, vetores e pilhas


pilha genérica 3 #include "GenericStack.h"
4 usando namespace std;

5 6 int principal()
7{
//Cria uma pilha de valores int
pilha interna 8 9 Pilha<int> intStack;
10 para (int i = 0; i < 10; i++)
11 intStack.push(i);
12
13 enquanto (!intStack.empty())
14 cout << intStack.pop() << " ";
15 cout << endl;
16
17 //Cria uma pilha de strings
pilha de cordas 18 Pilha<string> stringStack;
19 stringStack.push("Chicago");
20 stringStack.push("Denver");
21 stringStack.push("Londres");
22
23 enquanto (!stringStack.empty())
24 cout << stringStack.pop() << " ";
25 cout << endl;
26
27 retornar 0;
28 }

9876543210
Londres Denver Chicago

declarando objetos Para declarar um objeto de uma classe de modelo, você deve especificar um tipo concreto para o
parâmetro de tipo T. Por exemplo,

Pilha<int> intStack;

Esta declaração substitui o parâmetro de tipo T por int. Então, intStack é uma pilha para int
valores. O objeto intStack é como qualquer outro objeto. O programa invoca o push
função em intStack para adicionar dez valores int à pilha (linha 11) e exibe os elementos da pilha (linhas
13–14).
O programa declara um objeto de pilha para armazenar strings na linha 18, adiciona três strings na
pilha (linhas 19–21) e exibe as strings da pilha (linha 24).
Observe o código nas linhas 9–11:

enquanto (!intStack.empty())
cout << intStack.pop() << " ";
cout << endl;

e nas linhas 23–25:

enquanto (!stringStack.empty())
cout << stringStack.pop() << cout << " ";
endl;

Esses dois fragmentos são quase idênticos. A diferença é que o primeiro opera no intStack e o segundo
no stringStack. Você pode definir uma função com um parâmetro de pilha para exibir os elementos na
pilha. O novo programa é mostrado na Listagem 12.6.
Machine Translated by Google

12.4 Modelos de Classe 487

Listagem 12.6 TestGenericStackWithTemplateFunction.cpp


1 #include <iostream>
2 #incluir <string>
3 #include "GenericStack.h" Cabeçalho GenericStack
4 usando namespace std;

5 6 modelo<nome do tipo T>


7 void printStack(Pilha<T>& pilha) Parâmetro Stack<T>
8{
enquanto (!stack.empty())
9 10 cout << stack.pop() << cout << " ";
11 endl;
12}
13
14 int principal()
15 {
16 //Cria uma pilha de valores int
17 Pilha<int> intStack;
18 para (int i = 0; i < 10; i++)
19 intStack.push(i);
20 printStack(intStack); invocar printStack
21
22 //Cria uma pilha de strings
23 Pilha<string> stringStack;
24 stringStack.push("Chicago");
25 stringStack.push("Denver");
26 stringStack.push("Londres");
27 printStack(stringStack); invocar printStack
28
29 retornar 0;
30 }

O nome genérico da classe Stack<T> é usado como um tipo de parâmetro em uma função de modelo função de modelo
(linha 7).

Observação

C++ permite atribuir um tipo padrão para um parâmetro de tipo em um modelo de classe. Por exemplo, você pode tipo padrão
atribuir int como um tipo padrão na classe Stack genérica da seguinte forma:

modelo<nome do tipo T = int>


pilha de classe
{
...
};

Agora você pode declarar um objeto usando o tipo padrão assim:

Pilha<> pilha; // stack é uma pilha para valores int

Você pode usar o tipo padrão somente em modelos de classe, não em modelos de função.

Observação

Você também pode usar parâmetros que não sejam de tipo junto com parâmetros de tipo em um prefixo de modelo. parâmetro sem tipo
Por exemplo, você pode declarar a capacidade do array como um parâmetro para a classe Stack da seguinte
maneira:

modelo<nome do tipo T, capacidade interna >


pilha de classe
Machine Translated by Google

488 Capítulo 12 Modelos, vetores e pilhas

{
...
privado:
Elementos T[capacidade];
tamanho
interno ; };

Portanto, ao criar uma pilha, você pode especificar a capacidade do array. Por exemplo,

Pilha<string, 500> pilha;


declara uma pilha que pode conter até 500 strings.

Nota

membros estáticos Você pode definir membros estáticos em uma classe de modelo. Cada especialização de modelo possui sua
própria cópia de um campo de dados estáticos.

12.9 Você precisa usar o prefixo do modelo para cada função na definição da
ÿVerificação de ponto
classe? Você precisa usar o prefixo do modelo para cada função na
implementação da classe?
12.10 O que há de errado no código a seguir?

template<typename T = int> void


printArray(const T list[], int arraySize) { for (int i = 0; i <

arraySize; i++) { cout << list[i] <<

" ";

} cout << endl;


}

12.11 O que há de errado no código a seguir?

template<nome do tipo
T> class

Foo { público:
Foo();
T f1(valor T);
Tf2(); };

Foo::Foo() {

...
}

Pilha T::f1(valor T) {

...
}

Pilha T::f2() {

...
};

12.12 Suponha que o prefixo do modelo para a classe Stack seja

modelo<nome do tipo T = string>


Machine Translated by Google

12.5 Melhorando a Classe Stack 489

Você pode criar uma pilha de strings usando o seguinte?

Pilha de pilha;

12.5 Melhorando a classe Stack


Esta seção implementa uma classe de pilha dinâmica.
Chave
Apontar
Há um problema na classe Stack . Os elementos da pilha são armazenados em um array com tamanho
fixo 100 (ver linha 16 na Listagem 12.4). Portanto, você não pode armazenar mais de 100 elementos em
uma pilha. Você poderia alterar 100 para um número maior, mas se a pilha real for pequena, isso
desperdiçaria espaço. Uma maneira de resolver esse dilema é alocar mais memória dinamicamente quando
necessário.
A propriedade size na classe Stack<T> representa o número de elementos na pilha.
Vamos adicionar uma nova propriedade chamada capacidade que representa o tamanho atual do array
para armazenar os elementos. O construtor no-arg de Stack<T> cria um array com capacidade 16.
Ao adicionar um novo elemento à pilha, pode ser necessário aumentar o tamanho do array para armazenar
o novo elemento se a capacidade atual estiver cheia.
Como você aumenta a capacidade do array? Você não pode fazer isso depois que o array for declarado.
Para contornar essa restrição, você pode criar um array novo e maior, copiar o conteúdo do array antigo
para este novo e excluir o array antigo.
A classe Stack<T> aprimorada é mostrada na Listagem 12.7.

Listagem 12.7 ImprovedStack.h


1 #ifndef IMPROVEDSTACK_H
2 #define IMPROVEDSTACK_H

3 4 modelo<nome do tipo T> definir classe Stack


Pilha de 5 classes
6{
7 público:
8 Pilha();
Pilha(const Pilha&);
9 10 ~Pilha();
11 bool vazio() const;
12 T peek() const;
13 nulo push (valor T);
14 T pop();
15 int getSize() const;
16
17 privado:
18 Elementos T*;
19 tamanho interno ;
20 capacidade interna ;
21 void garantirCapacidade();
22};

23 24 modelo<nome do tipo T> implementar classe Stack


25 Stack<T>::Stack(): tamanho(0), capacidade(16) construtor sem argumento
26 {
27 elementos = novo T[capacidade];
28}
29
30 modelo<nome do tipo T>
31 Stack<T>::Stack(const Pilha e pilha) construtor de cópia
32 {
33 elementos = novo T[stack.capacity];
Machine Translated by Google

490 Capítulo 12 Modelos, vetores e pilhas


34 tamanho = pilha.tamanho;
35 capacidade = pilha.capacidade;
36 for (int i = 0; i <tamanho; i++)
37 {
38 elementos[i] = pilha.elementos[i];
39 }
40 }
41
destruidor 42 modelo<nome do tipo T>
43 Pilha<T>::~Stack()
44 {
excluir [] elementos;
45 46}
47
48 modelo<nome do tipo T>
49 bool Stack<T>::empty() const
50 {
tamanho de retorno == 0;
51 52}

53 54 modelo<nome do tipo T>


55T Stack<T>::peek() const
56 {
retornar elementos[tamanho - 1];
57 58}

59 60 modelo<nome do tipo T>


61 void Stack<T>::push(valor T)
62 {
garantirCapacidade();
63 elementos[tamanho++] = valor;
64 65}
66
aumentar a capacidade se necessário 67 modelo<nome do tipo T>
68 void Stack<T>::ensureCapacity()
69 {
70 if (tamanho >= capacidade)
71 {
72 T* antigo = elementos;
73 capacidade = 2 * tamanho;
crie uma nova matriz 74 elementos = novo T[tamanho * 2];
75
copie para o novo array 76 for (int i = 0; i <tamanho; i++)
77 elementos[i] = antigo[i];
78
destruir o array antigo 79 excluir [] antigo;
80 }
81 }

82 83 modelo<nome do tipo T>


84 T Pilha<T>::pop() 85 {

86 retornar elementos[--tamanho];
87}
88
89 modelo<nome do tipo T>
90 int Stack<T>::getSize() const
91 {
92 tamanho de retorno ;
Machine Translated by Google

12.6 O vetor C++ Classe 491


93}
94
95 #endif

Como os elementos internos do array são criados dinamicamente, um destruidor deve ser fornecido para destruir
adequadamente o array e evitar vazamento de memória (linhas 42–46). Observe que os elementos do array na Listagem
12.4, GenericStack.h, não são alocados dinamicamente, portanto não há necessidade de fornecer um destruidor nesse
caso.
A função push(T value) (linhas 60–65) adiciona um novo elemento à pilha. Esta função primeiro invoca
garantirCapacity() (linha 63), que garante que haja um espaço no array para o novo elemento.

A função garantirCapacity() (linhas 67–81) verifica se o array está cheio. Se for, crie um novo array que dobre o
tamanho do array atual, defina o novo array como o array atual, copie o array antigo para o novo array e exclua o array
antigo (linha 79).
Observe que a sintaxe para destruir um array criado dinamicamente é

excluir [] elementos; //Linha 45


excluir [] antigo; //Linha 79

O que acontece se você escrever por engano o seguinte?

excluir elementos; //Linha 45


excluir antigo; //Linha 79

O programa será compilado e executado corretamente para uma pilha de valores de tipo primitivo, mas não será correto
para uma pilha de objetos. A instrução delete [] elements primeiro chama o destruidor em cada objeto na matriz de
elementos e depois destrói a matriz, enquanto a instrução delete elements chama o destruidor apenas no primeiro
objeto da matriz.

12.13 O que há de errado se a linha 79 na Listagem 12.7 ImprovedStack.h for substituída por
ÿVerificação de ponto
excluir antigo;

12.6 A classe vetorial C++


C++ contém uma classe vetorial genérica para armazenar uma lista de objetos.
Chave
Apontar
Você pode usar um array para armazenar uma coleção de dados, como strings e valores int . Há uma limitação séria: o
tamanho do array é fixo quando o array é criado. C++ fornece o vetor
class, que é mais flexível que arrays. Você pode usar um objeto vetorial como um array, mas o tamanho de um vetor
pode aumentar automaticamente, se necessário. Nota de vídeo

Para criar um vetor, use a sintaxe: A classe vetorial

vetor<elementType> nomedovetor;

Por exemplo,

vetor<int> intVetor;

cria um vetor para armazenar valores inteiros .

vetor<string> stringVetor;

cria um vetor para armazenar objetos string .


A Figura 12.2 lista diversas funções usadas com frequência na classe vetorial em um diagrama de classes UML.
Machine Translated by Google

492 Capítulo 12 Modelos, vetores e pilhas

vetor<elementType>

+vector<elementType>() Constrói um vetor vazio com o tipo de elemento especificado.

+vector<elementType>(tamanho: int) Constrói um vetor com tamanho inicial, preenchido com valores padrão.

+vector<elementType>(tamanho: int, Constrói um vetor com tamanho inicial, preenchido com valores especificados.

valor padrão: elementType)

+push_back(elemento: elementType): void Acrescenta o elemento neste vetor.

+pop_back(): vazio Remove o último elemento deste vetor.

+tamanho(): const não assinado Retorna o número de elementos deste vetor.

+at(índice: int): elementType const Retorna o elemento no índice especificado neste vetor.

+vazio(): bool const Retorna verdadeiro se este vetor estiver vazio.

+claro(): vazio Remove todos os elementos deste vetor.

+swap(v2: vetor): vazio Troca o conteúdo deste vetor pelo vetor especificado.

Figura 12.2 A classe vetorial funciona como um array redimensionável.

Você também pode criar um vetor com o tamanho inicial, preenchido com valores padrão. Por exemplo,
o código a seguir cria um vetor de tamanho inicial 10 com valores padrão 0.

vetor<int> intVetor(10);

Um vetor pode ser acessado usando o operador de subscrito de array []. Por exemplo,

cout << intVetor[0];

exibe o primeiro elemento no vetor.

Cuidado
intervalo de índice vetorial Para usar o operador subscrito de array [], o elemento já deve existir no vetor. Assim como
o array, o índice é baseado em 0 em um vetor - ou seja, o índice do primeiro elemento no
vetor é 0 e o último é v.size() – 1. Usar um índice além desse intervalo seria
causar erros.

A Listagem 12.8 dá um exemplo de uso de vetores.

Listagem 12.8 TestVector.cpp


1 #include <iostream>
cabeçalho de vetor 2 #incluir <vetor>
cabeçalho de string 3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
criar um vetor vetor<int> intVetor;

8 // Armazena os números 1, 2, 3, 4, 5, ..., for (int i 10 elevado ao vetor


9 = 0; i < 10; i++)
acrescentar valor int 10 intVector.push_back(i + 1);
11
12 //Mostra os números do vetor
13 cout << "Números no vetor: ";
tamanho do vetor 14 for (int i = 0; i < intVector.size(); i++)
subscrito vetorial 15 16 17 cout << intVetor[i] << " ";
Machine Translated by Google

12.6 O vetor C++ Classe 493


18
19 vetor<string> stringVetor; criar um vetor
20
21 // Armazena strings no vetor
22 stringVector.push_back("Dallas"); anexar string
23 stringVector.push_back("Houston");
24 stringVector.push_back("Austin");
25 stringVector.push_back("Norman");
26
27 //Exibe a string no vetor
28 cout << "\nStrings no vetor de strings: ";
29 for (int i = 0; i < stringVector.size(); i++) tamanho do vetor

30 cout << stringVetor[i] << " "; subscrito vetorial


31
32 stringVector.pop_back(); //Remove o último elemento remover elemento
33
34 vetor<string> v2; criar vetor
35 v2.swap(stringVetor); vetor de troca
36 v2[0] = "Atlanta"; atribuir string
37
38 //Mostra novamente a string no vetor
39 cout << "\nStrings no vetor v2: ";
40 for (int i = 0; i < v2.size(); i++) tamanho do vetor

41 cout << v2.at(i) << " "; em função


42
43 retornar 0;
44}

Números no vetor: 1 2 3 4 5 6 7 8 9 10
Strings no vetor de strings: Dallas Houston Austin Norman
Strings no vetor v2: Atlanta Houston Austin

Como a classe vetorial é usada no programa, a linha 2 inclui seu arquivo de cabeçalho. Desde a corda
class também é usada, a linha 3 inclui o arquivo de cabeçalho da classe de string .
Um vetor para armazenar valores int é criado na linha 8. Os valores int são anexados ao vetor na linha 12. Não há
limite para o tamanho do vetor. O tamanho aumenta automaticamente à medida que mais elementos são adicionados ao
vetor. O programa exibe todos os valores int no vetor nas linhas 15–17. Observe que o operador de subscrito do array [] é
usado para recuperar um elemento na linha 17.
Um vetor para armazenar strings é criado na linha 19. Quatro strings são adicionadas ao vetor (linhas 22–25). O
programa exibe todas as strings do vetor nas linhas 29–30. Observe que o operador de subscript da matriz [] é usado
para recuperar um elemento na linha 30.
A linha 32 remove a última string do vetor. A linha 34 cria outro vetor v2. A linha 35 troca v2 por stringVector. A linha
36 atribui uma nova string a v2[0]. O programa exibe as strings em v2 (linhas 40–41). Observe que a função at é usada
para recuperar os elementos. Você também pode usar o operador subscrito [] para recuperar os elementos.

A função size() retorna o tamanho do vetor como um valor sem sinal (ou seja, um número inteiro sem sinal), e não
como um inteiro. Alguns compiladores podem avisá-lo porque um valor não assinado é usado com um int assinado
valor na variável i (linhas 16, 29, 40). Isto é apenas um aviso e não deve causar problemas, porque o valor não assinado
é automaticamente promovido para um valor assinado neste caso.
Para se livrar do aviso, declare i como unsigned int na linha 16 da seguinte forma: int não assinado

for (não assinado i = 0; i < intVector.size(); i++)

12.14 Como você declara um vetor para armazenar valores duplos ? Como você anexa um
dobrar para um vetor? Como você encontra o tamanho de um vetor? Como você remove um elemento ÿVerificação de ponto

de um vetor?
Machine Translated by Google

494 Capítulo 12 Modelos, vetores e pilhas

12.15 Por que o código em (a) está errado, mas o código em (b) está correto?

vetor<int> v; vetor<int> v(5);


v[0] = 4; v[0] = 4;

(a) (b)

12.7 Substituindo Arrays Usando a Classe Vector


Um vetor pode ser usado para substituir matrizes. Os vetores são mais flexíveis que os arrays, mas os arrays
Chave
Apontar são mais eficientes que os vetores.

vetor Um objeto vetorial pode ser usado como um array, mas existem algumas diferenças. A Tabela 12.1 lista suas semelhanças
e diferenças.
matriz versus vetor

Tabela 12.1 Diferenças e semelhanças entre matrizes e vetores

Operação Variedade vetor

Criando um array/vetor cadeia de caracteres a[10] vetor<string> v

Acessando um elemento um[índice] v[índice]

Atualizando um elemento a[índice] = "Londres" v[índice] = "Londres"

Tamanho de retorno v.tamanho()

Adicionando um novo elemento v.push_back("Londres")

Removendo um elemento v.pop_back()

Removendo todos os elementos v.claro()

Tanto matrizes quanto vetores podem ser usados para armazenar uma lista de elementos. Usar um array é mais
eficiente se o tamanho da lista for fixo. Um vetor é uma matriz redimensionável. A classe vector contém muitas funções-
membro para acessar e manipular um vetor. Usar vetores é mais flexível do que usar arrays. Em geral, você sempre pode
usar vetores para substituir arrays. Todos os exemplos dos capítulos anteriores que usam arrays podem ser modificados
usando vetores. Esta seção reescreve a Listagem 7.2, DeckOfCards.cpp, e a Listagem 8.1, PassTwoDimensionalArray.cpp,
usando vetores.
Lembre-se de que a Listagem 7.2 é um programa que escolhe aleatoriamente quatro cartas de um baralho de 52 cartas.
Usamos um vetor para armazenar as 52 cartas com valores iniciais de 0 a 51, como segue:

const int NUMBER_OF_CARDS = 52;


vetor<int> baralho(NUMBER_OF_CARDS);

// Inicializa os cartões
para (int i = 0; i < NUMBER_OF_CARDS; i++)
baralho[i] = i;

deck[0] a deck[12] são Paus, deck[13] a deck[25] são Ouros, deck[26] a deck[38] são Copas e deck[39] a deck[51] são
Espadas. A Listagem 12.9 fornece a solução para o problema.

Listagem 12.9 DeckOfCardsUsingVector.cpp


1 #include <iostream>
incluir vetor 2 #incluir <vetor>
3 #incluir <string>
4 #incluir <ctime>
Machine Translated by Google

12.7 Substituindo Arrays Usando o Vetor Classe 495

5 usando namespace std;

6 7 const int NUMBER_OF_CARDS = 52;


8 naipes[4] = {"Espadas", "Corações", "Diamantes", "Paus"}; se adequa

9 classificações de string [13] = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", classificações

10 "10", "Jack", "Rainha", "Rei"};


11
12 int principal()
13 {
14 vetor<int> baralho(NUMBER_OF_CARDS); criar deck de vetores
15
16 // Inicializa os cartões
17 para (int i = 0; i < NUMBER_OF_CARDS; i++) inicializar deck
18 baralho[i] = i;
19
20 //embaralhe as cartas
21 srand(tempo(0));
22 para (int i = 0; i < NUMBER_OF_CARDS; i++)
23 {
24 //Gera um índice aleatoriamente baralho embaralhado

25 índice interno = rand()% NUMBER_OF_CARDS;


26 temperatura interna = deck[i];
27 baralho[i] = baralho[índice];
28 deck[índice] = temp;
29 }
30
31 //Mostra as quatro primeiras cartas
32 para (int i = 0; i < 4; i++)
33 {
34 cout << classificações[deck[i] % 13] << " de " << classificação de exibição

35 naipes[deck[i] / 13] << endl; terno de exibição


36 }
37
38 retornar 0;
39 }

4 de Clubes
Ás de diamantes
6 de Copas
Valete de Paus

Este programa é idêntico à Listagem 7.2, exceto que a linha 2 inclui a classe vetorial e a linha 14 usa um vetor
para armazenar todos os cartões em vez de um array. Curiosamente, a sintaxe para usar arrays e vetores é muito
semelhante, porque você pode usar índices entre colchetes para acessar os elementos de um vetor, o que é o
mesmo que para acessar os elementos de um array.
Você também pode alterar os naipes e classificações das matrizes nas linhas 8–10 para vetores. Nesse caso,
você terá que escrever muitas linhas de código para inserir os naipes e as classificações no vetor. O código é mais
simples e melhor usando arrays.
Lembre-se de que a Listagem 8.1 cria um array bidimensional e invoca uma função para retornar a soma de
todos os elementos do array. Um vetor de vetores pode ser usado para representar uma matriz bidimensional. Aqui
está um exemplo para representar uma matriz bidimensional com quatro linhas e três colunas:

vetor<vetor<int> > matriz(4); //quatro linhas

para (int i = 0; i < 4; i++)


Machine Translated by Google

496 Capítulo 12 Modelos, vetores e pilhas


matriz[i] = vetor<int>(3);

matriz[0][0] = 1; matriz[0][1] = 2; matriz[0][2] = 3;


matriz[1][0] = 4; matriz[1][1] = 5; matriz[1][2] = 6;
matriz[2][0] = 7; matriz[2][1] = 8; matriz[2][2] = 9;
matriz[3][0] = 10; matriz[3][1] = 11; matriz[3][2] = 12;

Observação

Há um espaço separando > e > na linha

vetor<vetor<int> > matriz(4); //Quatro linhas

Sem espaço, alguns compiladores C++ antigos podem não compilar.

A Listagem 12.10 revisa a Listagem 8.1, PassTwoDimensionalArray.cpp, usando vetores.

Listagem 12.10 TwoDArrayUsingVector.cpp


1 #include <iostream>
incluir vetor 2 #incluir <vetor>
3 usando namespace std;

função com vetor 4 5 soma int ( vetor const<vetor<int>>& matriz)


6{
total interno = 0;
for ( linha não assinada = 0; linha <matriz.size(); linha++)
{
7 for ( coluna não assinada = 0; coluna <matriz[linha].tamanho(); coluna++)
8 {
9 total += matriz[linha][coluna];
10 }
11 }
12
13 retorno total;
14 15 16 17 }
18
19 int principal()
20 {
vetor para matriz 2-D 21 vetor<vetor<int>> matriz(4); //Quatro linhas
22
23 para (sem sinal i = 0; i < 4; i++)
24 matriz[i] = vetor<int>(3); // Cada linha possui três colunas
25
atribuir valores 26 matriz[0][0] = 1; matriz[0][1] = 2; matriz[0][2] = 3;
27 matriz[1][0] = 4; matriz[1][1] = 5; matriz[1][2] = 6;
28 matriz[2][0] = 7; matriz[2][1] = 8; matriz[2][2] = 9;
29 matriz[3][0] = 10; matriz[3][1] = 11; matriz[3][2] = 12;
30
31 cout << "A soma de todos os elementos é <<
" soma(matriz) << endl;
32
33 retornar 0;
34 }

A soma de todos os elementos é 78

A matriz variável é declarada como um vetor. Cada elemento da matriz vetorial[i] é outro
vetor. Portanto, matriz[i][j] representa a i-ésima linha e a j-ésima coluna em uma matriz bidimensional.
Machine Translated by Google

12.8 Estudo de Caso: Avaliando Expressões 497

A função soma retorna a soma de todos os elementos do vetor. O tamanho do vetor pode ser obtido na
função size() na classe de vetor . Portanto, você não precisa especificar o tamanho do vetor ao invocar a
função sum . A mesma função para array bidimensional requer dois parâmetros como segue:

soma int (const int a[][COLUMN_SIZE], int rowSize)

O uso de vetores para representar matrizes bidimensionais simplifica a codificação.

12.16 Escreva o código que representa o seguinte array usando um vetor:


ÿVerificação de ponto
lista interna [4] = {1, 2, 3, 4};

12.17 Escreva o código que representa o seguinte array usando um vetor:

matriz interna [4][4] =


{{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}};

12.8 Estudo de Caso: Avaliando Expressões


Pilhas podem ser usadas para avaliar expressões.
Chave
Apontar
As pilhas têm muitas aplicações. Esta seção fornece uma aplicação do uso de pilhas. Você pode inserir
uma expressão aritmética do Google para avaliar a expressão conforme mostrado na Figura 12.3.

Figura 12.3 Você pode avaliar uma expressão aritmética do Google.

Como o Google avalia uma expressão? Esta seção apresenta um programa que avalia uma expressão
composta com múltiplos operadores e parênteses (por exemplo, (15 + 2) * 34 – 2). expressão composta
Para simplificar, suponha que os operandos sejam inteiros e os operadores sejam de quatro tipos: +, –, * e /.
Machine Translated by Google

498 Capítulo 12 Modelos, vetores e pilhas

O problema pode ser resolvido usando duas pilhas, denominadas operandStack e operatorStack,
para armazenar operandos e operadores, respectivamente. Operandos e operadores são colocados nas
processar um operador pilhas antes de serem processados. Quando um operador é processado, ele é retirado do operadorStack
e aplicado nos dois primeiros operandos do operandoStack (os dois operandos são retirados do
operandoStack). O valor resultante é enviado de volta para operandoStack.
O algoritmo leva duas fases:

Fase 1: Expressão de digitalização


O programa varre a expressão da esquerda para a direita para extrair operandos, operadores e
parênteses.

1.1 Se o item extraído for um operando, envie-o para operandoStack.

1.2 Se o item extraído for um operador + ou - , processe todos os operadores no topo do OperatorStack
com precedência maior ou igual (ou seja, +, -, *, /) e envie o operador extraído para o OperatorStack.

1.3 Se o item extraído for um *


ou operador / , processe todos os operadores no topo do
operadorStack com precedência maior ou igual (ou seja, *, /), envie o operador extraído para o
operadorStack.

1.4 Se o item extraído for um símbolo ( , envie-o para operatorStack.

1.5 Se o item extraído for um símbolo ) , processe repetidamente os operadores do topo da pilha de
operadores até ver o símbolo ( na pilha.

Fase 2: Limpeza da pilha


Processe repetidamente os operadores a partir do topo do operadorStack até que o operadorStack
esteja vazio.
A Tabela 12.2 mostra como o algoritmo é aplicado para avaliar a expressão (1 + 2) * 4 - 3.

Tabela 12.2 Avaliar uma Expressão

Expressão Varredura Ação pilha de operandos operadorStack

(1 + 2) * 4 – 3 ( Fase 1.4 (

(1 + 2) * 4 – 3 1 Fase 1.1 1 (

(1 + 2) * 4 – 3 + Fase 1.2 1 +(

(1 + 2) * 4 – 3 2 Fase 1.1 21 +(

(1 + 2) * 4 – 3 ) Fase 1.5 3

* Fase 1.3 3 *
(1 + 2) * 4 – 3

4 Fase 1.1 43 *
(1 + 2) * 4 – 3

– Fase 1.2 12 –
(1 + 2) * 4 – 3

3 Fase 1.1 3 12 –
(1 + 2) * 4 – 3

(1 + 2) * 4 – 3 nenhum Fase 2 9
Machine Translated by Google

12.8 Estudo de Caso: Avaliando Expressões 499

A Listagem 12.11 fornece o programa.

Listagem 12.11 EvaluateExpression.cpp


1 #include <iostream>
2 #incluir <vetor>
3 #incluir <string>
4 #incluir <cctype>
5 #include "ImprovedStack.h"

6 7 usando namespace std;

8 9 // Divida uma expressão em números, operadores e parênteses


10 vetor<string> divisão(const string& expressão); expressão dividida
11
12 // Avalia uma expressão e retorna o resultado
13 int avaliarExpressão(const string& expressão); avaliar expressão
14
15 // Realiza uma operação
16 void processAnOperator( realizar uma operação
17 Stack<int>& operandStack, Stack<char>& operadorStack);
18
19 int principal()
20 {
21 expressão de cadeia de caracteres;
22 cout << "Digite uma expressão: ";
23 getline(cin,expressão); leia uma expressão
24
" = "
25 cout << expressão <<
26 << avaliarExpressão(expressão) << endl; avaliar expressão
27
retornar 0;
28 29}

30 31 vetor<string> divisão(const string& expressão) expressão dividida


32 {
33 vetor<string> v; // Um vetor para armazenar itens divididos como strings
34 string númeroString; // Uma string numérica
35
36 for (não assinado i = 0; i <expressão.comprimento(); i++)
37 {
38 if (édígito(expressão[i]))
39 numberString.append(1, expressão[i]); //Acrescenta um dígito acrescentar numeral
40 outro
41 {
42 if (númeroString.size() > 0)
43 {
44 v.push_back(númeroString); // Armazena a string numérica número da loja
45 númeroString.erase(); //Esvazia a string numérica
46 }
47
48 if (!isspace(expressão[i]))
49 {
50 cordas;
51 s.append(1, expressão[i]);
52 v.push_back(s); // Armazena um operador e parênteses operador de loja/parênteses
53 }
54 }
55 }
56
Machine Translated by Google

500 Capítulo 12 Modelos, vetores e pilhas


// Armazena a última string numérica
if (númeroString.size() > 0)
armazenar último número v.push_back(númeroString);
57
58 retornar v;
59 60 61 62 }
63
64 // Avalia uma expressão 65 int
avaliaExpressão(const string& expressão)
66 {
67 // Cria operandoStack para armazenar operandos
pilha de operandos 68 Pilha<int> operandoStack;
69
70 // Cria operatorStack para armazenar operadores
operadorStack 71 Stack<char> operadorStack;
72
73 // Extrai operandos e operadores
expressão dividida 74 vetor<string> tokens = divisão(expressão);
75
76 // Fase 1: Escanear tokens
digitalizar cada token 77 for (não assinado i = 0; i < tokens.size(); i++)
78 {
+ ou - digitalizado 79 if (tokens[i][0] == '+' || tokens[i][0] == '-')
80 {
81 // Processa todos os +, -, *, / no topo da pilha de operadores
82 enquanto (!operatorStack.empty() && (operatorStack.peek() == '+'
83 '-' '*'
|| operadorStack.peek() == || operadorStack.peek() == || operadorStack.peek()
84 == '/'))
85 {
86 processAnOperator(operandStack, operadorStack);
87 }
88
89 // Coloca o operador + ou - na pilha de operadores
90 operadorStack.push(tokens[i][0]);
91 }
* ou / digitalizado 92 senão if (tokens[i][0] == { '*'
|| tokens[i][0] == '/')
93
94 // Processa todos *, / no topo da pilha de operadores
95 enquanto (!operatorStack.empty() && (operatorStack.peek() == '*'
96 || operadorStack.peek() == '/'))
97 {
98 processAnOperator(operandStack, operadorStack);
99 }
100
101 // Coloca o operador * ou / na pilha de operadores
102 operadorStack.push(tokens[i][0]);
103 }
(digitalizado 104 senão if (tokens[i][0] == '(')
105 {
106 operadorStack.push('('); // Empurre '(' para empilhar
107 }
) digitalizado 108 senão if (tokens[i][0] == ')')
109 {
110 // Processa todos os operadores da pilha até ver '('
111 enquanto (operatorStack.peek()! = '(')
112 {
113 processAnOperator(operandStack, operadorStack);
114 }
115
Machine Translated by Google

12.8 Estudo de Caso: Avaliando Expressões 501

116 operadorStack.pop(); //Retira o símbolo '(' da pilha


117 }
118 outro
119 { // Um operando verificado. Empurre um operando para a pilha como número inteiro
120 operandStack.push(atoi(tokens[i].c_str())); um operando digitalizado
121 }
122 }
123
124 // Fase 2: processa todos os operadores restantes na pilha
125 enquanto (!operatorStack.empty())
126 {
127 processAnOperator(operandStack, operadorStack); limpar operadorStack
128 }
129
130 //Retorna o resultado
131 retornar operandoStack.pop(); resultado de retorno

132 }
133
134 // Processa um operador: Pegue um operador do operadorStack e 135 // aplique-o nos operandos
do operandoStack 136 void processAnOperator(

137 Pilha<int>& operandoStack, Pilha<char>& operadorStack)


138 {
139 char op = operadorStack.pop();
140 int op1 = operandStack.pop();
141 int op2 = operandStack.pop();
142 se (ligado == '+')
143 operandoStack.push(op2 + op1); processo +
144 senão se (op == '-')
145 operandoStack.push(op2 - op1); processo -
146 senão se (op == '*')
147 operandStack.push(op2 * op1); *
processo
148 senão se (op == '/')
149 operandStack.push(op2/op1); processo /
150 }

Insira uma expressão: (13 + 2) * 4 - 3 (13 + 2) * 4 - 3 =


57

Insira uma expressão: 5/4 + (2 - 3) * 5 5/4 + (2 - 3) * 5 = -4

O programa lê uma expressão como uma string (linha 23) e invoca a avaliaçãoExpression
função (linha 26) para avaliar a expressão.
A função avaliarExpression cria duas pilhas operandStack e operatorStack (linhas 68, 71) e
invoca a função split para extrair números, operadores e parênteses da expressão (linha 74) em
tokens. Os tokens são armazenados em um vetor de strings. Por exemplo, se a expressão for (13 + 2)
* 4 – 3, os tokens serão (, 13, +, 2, ), *, 4, - e 3.

A função avaliarExpression verifica cada token no loop for (linhas 77–122).


Se um token for um operando, envie-o para operandStack (linha 120). Se um token for um operador +
ou – (linha 79), processe todos os operadores do topo do operatorStack , se houver (linhas 81 a 87) e
empurre o operador recém-digitalizado para a pilha (linha 90). Se um token *
ou do operador / (linha 92),
for um processo, todos os operadores * e / do topo do operatorStack , se houver (linhas 95–99) e
Machine Translated by Google

502 Capítulo 12 Modelos, vetores e pilhas

empurre o operador recém-digitalizado para a pilha (linha 102). Se um token for um símbolo ( (linha 104), envie-o
para o operadorStack. Se um token for um símbolo ) (linha 108), processe todos os operadores do topo do
operadorStack até ver o símbolo ) (linhas 111–114) e retire o símbolo ) da pilha (linha 116).

Após todos os tokens serem considerados, o programa processa os operadores restantes em operatorStack
(linhas 125–128).
A função processAnOperator (linhas 136–150) processa um operador. A função retira o operador do
operadorStack (linha 139) e retira dois operandos do operandoStack (linhas 140–141). Dependendo do operador,
a função executa uma operação e envia o resultado da operação de volta para operandStack (linhas 143, 145, 147,
149).

12.18 Trace como a expressão (3 + 4) * (1 - 3) - ((1 + 3) * 5 - 4) é avaliada usando o programa da Listagem


ÿVerificação de ponto
12.11.

Termos chave

modelo 476 prefixo de modelo 478


classe de modelo 482 digite parâmetro 478
função de modelo 487

Resumo do capítulo

1. Os modelos fornecem a capacidade de parametrizar tipos em funções e classes.

2. Você pode definir funções ou classes com tipos genéricos que podem ser substituídos
tipos concretos pelo compilador.

3. A definição do modelo de função começa com a palavra-chave template seguida por uma lista de parâmetros.
Cada parâmetro deve ser precedido pelas palavras-chave intercambiáveis class ou typename no formato

<typename typeParameter> ou
<class typeParameter>

4. Ao definir uma função genérica, é melhor começar com uma função não genérica,
depure e teste-o e, em seguida, converta-o em uma função genérica.

5. A sintaxe dos modelos de classe é basicamente a mesma dos modelos de função. Você coloca o prefixo do
modelo antes da definição de classe, assim como coloca o prefixo do modelo antes do modelo de função.

6. Se os elementos precisarem ser processados do modo último a entrar, primeiro a sair, use uma pilha para armazenar os
elementos.

7. O tamanho do array é fixo após sua criação. C++ fornece a classe vetorial , que é mais flexível que arrays.

8. A classe vetorial é uma classe genérica. Você pode usá-lo para criar objetos para tipos concretos.

9. Você pode usar um objeto vetorial como um array, mas o tamanho de um vetor pode crescer automaticamente.
automaticamente se necessário.
Machine Translated by Google

Exercícios de Programação 503

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 12.2–12.3

12.1 (Mínimo em array) Projete uma função genérica que retorne um elemento mínimo de um array.
A função deve ter dois parâmetros. Um é o array de um tipo genérico e o outro é o tamanho
do array. Teste a função com uma matriz de valores int, double e string .

12.2 (Classificação por seleção) Reescreva a função de classificação por seleção na Listagem 7.11,
SelectionSort.cpp, para usar um tipo genérico para elementos de array. Teste a função
com uma matriz de valores int, double e string .
12.3 (Bubble Sort) Reescreva a função bubble sort no Exercício de Programação 7.14, para usar um
tipo genérico para elementos de array. Teste a função com uma matriz de valores int, double
e string .
12.4 (São estritamente idênticos?) Escreva a seguinte função para verificar se os dois arrays são
estritamente idênticos

modelo<nome do tipo T>


bool sãoStrictlyIdentical(const T lista1[], const T lista2[], tamanho interno )

Teste a função com uma matriz de valores int, double e string .


12.5 (Maiores valores) Escreva uma função genérica que encontre o maior entre os três valores. Sua
função deve ter três parâmetros do mesmo tipo. Teste a função com valores int, double e
string .

Seções 12.4–12.5
*12.6 (Função printStack) Adicione a função printStack à classe Stack como uma função de instância
para exibir todos os elementos na pilha. A classe Stack foi introduzida na Listagem 12.4,
GenericStack.h.
*12.7 ( Posição da função) Adicione a função position(elemento T) na pilha
class como uma função de instância para encontrar a posição do elemento na pilha. A classe
Stack foi introduzida na Listagem 12.4, GenericStack.h.

Seções 12.6–12.7

**12.8 (Implementar classe vetorial ) A classe vetorial é fornecida na biblioteca C++ padrão. Implemente
a classe vetorial como um exercício. A classe vetorial padrão possui muitas funções. Para
este exercício, implemente apenas as funções definidas no diagrama de classes UML,
conforme mostrado na Figura 12.2.
12.9 (Implementar uma classe de pilha usando um vetor) Na Listagem 12.4, GenericStack é
implementado usando arrays. Implemente-o usando um vetor.
12.10 (A classe Course ) Reescreva a classe Course na Listagem 11.19, CourseWithCust-
omCopyConstructor.h. Use um vetor para substituir uma matriz para armazenar alunos.
Nota de vídeo
**12.11 (Simulação: problema do coletor de cupons) Reescreva o Exercício de Programação 7.21 usando Use vetor para substituir matrizes
vetores para representar matrizes.
**12.12 (Geometria: mesma linha?) Reescreva o Exercício de Programação 8.16 usando vetores para
representam matrizes.
Machine Translated by Google

504 Capítulo 12 Modelos, vetores e pilhas


Seção 12.8

**12.13 (Avaliar expressão) Modifique a Listagem 12.11 EvaluateExpression.cpp para adicionar operadores ^
para expoente e % para módulo. Por exemplo, 3 ^ 2 é 9 e 3% 2 é 1. O operador ^ tem a
precedência mais alta e o operador & tem a mesma precedência que os operadores * e / . Aqui
está um exemplo de execução do programa:

Insira uma expressão: (5 * 2 ^ 3 + 2 * 3% 2) * 4 (5 * 2 ^ 3 + 2 * 3% 2) * 4 = 160

*12.14 (Par mais próximo) A Listagem 8.3 encontra um par mais próximo de dois pontos. O programa solicita
que o usuário insira 8 pontos. O número 8 é fixo. Reescreva o programa.
Primeiro, solicite ao usuário que insira o número de pontos e, em seguida, solicite ao usuário
que insira todos os pontos.

**12.15 (Corresponder símbolos de agrupamento) Um programa C++ contém vários pares de símbolos de
agrupamento, como os seguintes:
Parênteses: ( e ).

Chaves: { e }.

Colchetes: [ e ].

Observe que os símbolos de agrupamento não podem se sobrepor. Por exemplo, (a{b)} é
ilegal. Escreva um programa que verifique se um arquivo de código-fonte C++ possui pares
corretos de símbolos de agrupamento. O arquivo é lido pelo programa usando o redirecionamento
de entrada usando o seguinte comando:

Exercício12_15 <arquivo.cpp

**12.16 (notação Postfix) A notação Postfix é uma forma de escrever expressões sem usar parênteses. Por
exemplo, a expressão (1 + 2) * 3 seria escrita como 1 2 + 3 *. Uma expressão postfix é avaliada
usando uma pilha. Digitalize uma expressão postfix da esquerda para a direita. Uma variável ou
constante é colocada na pilha. Quando um operador for encontrado, aplique o operador aos dois
operandos superiores da pilha e substitua os dois operandos pelo resultado. O diagrama a
seguir mostra como avaliar 1 2 + 3 *.

2 3
1 1 3 3 9

123 * 123 * 123 * 123 * 123 *

digitalizado digitalizado digitalizado digitalizado digitalizado

Escreva um programa que solicite ao usuário que insira uma expressão pós-fixada e a avalie.

***12.17 (Teste 24) Escreva um programa que solicite ao usuário que insira quatro números entre 1 e 13 e teste
se esses quatro números podem formar uma expressão que produza 24. A expressão pode usar
os operadores (adição, subtração, multiplicação). cação e divisão) e parênteses em qualquer
combinação. Cada número deve ser usado uma vez e apenas uma vez. Aqui está um exemplo
de execução do programa:

Insira quatro números (entre 1 e 13): 5 4 12 13


A solução é 4+12+13-5
Machine Translated by Google

Exercícios de Programação 505

Insira quatro números (entre 1 e 13): 5 6 5 12


Não há solução

**12.18 (Converter infixo em postfix) Escreva uma função que converte uma expressão infixa em
uma expressão postfix usando o seguinte cabeçalho.

string infixToPostfix(const string& expressão)

Por exemplo, a função deve converter a expressão infixa (1 + 2) * 3 em 1 2 + 3 * e 2 *


(1 + 3) em 2 1 3 + *.
***12.19 (Jogo: o jogo de cartas de 24 pontos) O jogo de cartas de 24 pontos consiste em escolher
quaisquer quatro cartas de 52 cartas (observe que dois Jokers estão excluídos). Cada
cartão representa um número. Um Ás, Rei, Dama e Valete representam 1, 13, 12 e
11, respectivamente. Escreva um programa que escolha aleatoriamente quatro cartas
e solicite ao usuário que insira uma expressão que utilize os quatro números das cartas
selecionadas. Cada número deve ser usado uma vez e apenas uma vez. Você pode
usar os operadores (adição, subtração, multiplicação e divisão) e parênteses em
qualquer combinação na expressão. A expressão deve ser avaliada como 24. Se tal
expressão não existir, insira 0. Aqui está um exemplo de execução do programa:

4 de Clubes
Ás (1) de Ouros
6 de Copas
Valete (11) de Paus
Insira uma expressão: (11 + 1 – 6) * 4 Parabéns! Você
entendeu!

Ás (1) de Ouros
5 de Ouros
9 de Espadas
Rainha (12) de Copas
Insira uma expressão: (13 – 9) * (1 + 5)
Parabéns! Você entendeu!

6 de Clubes
5 de Clubes
Valete (11) de Paus
5 de Espadas
Insira uma expressão: 0
Desculpe, uma expressão correta seria (5 * 6) - (11 – 5)

6 de Clubes
5 de Clubes
Rainha (12) de Paus
5 de Espadas
Insira uma expressão: 0 Sim. Não
24 pontos

**12.20 (Adicionar vetor) Escreva uma função que adicione o conteúdo de dois vetores usando o
seguinte cabeçalho:

modelo<nome do tipo T>


void addvector(vetor<T> &v1, vetor<T> &v2)
Machine Translated by Google

506 Capítulo 12 Modelos, vetores e pilhas

Escreva um programa de teste que leia 6 valores int em dois vetores, some ambos e exiba
o resultado.
**12.21 (Jogo: proporção sem solução para o jogo de 24 pontos) Para o jogo de 24 pontos, apresentado no
Exercício de Programação 12.19, escreva um programa para descobrir a proporção sem
solução para o jogo de 24 pontos, ou seja, o número de não soluções/número de soluções,
entre todas as combinações possíveis de quatro cartas.
*12.22 (São estritamente idênticos?: dois vetores) Escreva a seguinte função para verificar se
os dois vetores são estritamente idênticos

modelo<nome do tipo T>


bool sãoStrictlyIdentical(vetor<T> &v1, vetor<T> &v2)

Escreva um programa que leia os elementos de dois vetores, invoque a função are Strict-
lyIdentical e mostre se ambos são estritamente idênticos.
**12.23 (Reconhecimento de padrão: quatro números iguais consecutivos) Reescreva a função
isConsecutiveFour no Exercício de Programação 8.21 usando um vetor como segue:

bool isConsecutiveFour(const vector<vector<int>>& valores)

Escreva um programa de teste como o do Exercício de Programação 8.21.


*12.24 (Álgebra: resolver equações lineares 3 × 3 ) Você pode usar os seguintes cálculos para resolver
um sistema de equações lineares 3 × 3:
a11x + a12y + a13z = b1
a21x + a22y + a23z = b2
a31x + a32y + a33z = b3

(a22a33-a23a32)b1 + (a13a32-a12a33)b2 + (a12a23-a13a22)b3


x=
A

= (a23a31-a21a33)b1 + (a11a33-a13a31)b2 + (a13a21-a11a23)b3


e
A

= (a21a32-a22a31)b1 + (a12a31-a11a32)b2 + (a11a22-a12a21)b3


Com

a21 a22 a23 a11a22a33 + a31a12a23 + a13a21a32


UMA = 3a31
a11a32
a12a33
a133 =

- a13a22a31 - a11a23a32 - a33a21a12.

Escreva um programa que solicite ao usuário que insira a11, a12, a13, a21, a22,
a23, a31, a32, a33, b1, b2 e b3 e exiba o resultado. Se |A| for 0, informe que “A
equação não tem solução”.

Digite a11, a12, a13, a21, a22, a23, a31, a32, a33:
121231453
Digite b1, b2, b3: 2 5 3
A solução é 0 3 –4
Machine Translated by Google

Exercícios de Programação 507

Digite a11, a12, a13, a21, a22, a23, a31, a32, a33:
1 2 1 0,5 1 0,5 1 4 5
Digite b1, b2, b3: 2 5 3
Nenhuma solução

**12.25 (Nova classe de conta ) Uma classe de conta foi especificada em Programação
Exercício 9.3. Modifique a classe Account da seguinte forma:
n Suponha que a taxa de juros seja a mesma para todas as contas. Portanto, a propriedade
anualInterestRate deve ser estática.
n Adicione um novo nome de campo de dados do tipo string para armazenar o nome do
cliente.
n Adicione um novo construtor que construa uma conta com o nome especificado, id,
e equilíbrio.
n Adicione um novo campo de dados chamado transaction cujo tipo é vector<Transaction>
que armazena a transação para as contas. Cada transação é uma instância da classe
Transaction . A classe Transaction é definida conforme mostrado na Figura 12.4.

n Modifique as funções de retirada e depósito para adicionar uma transação ao vetor de


transações .
n Todas as outras propriedades e funções são iguais às do Exercício de Programação 9.3.

As funções get e set para esses campos de dados são


fornecido na classe, mas omitido no diagrama UML
Transação por brevidade.

-data: Data A data desta transação. A data é definida no Exercício 9.8.

-tipo: char O tipo de transação, como 'W' para retirada, 'D'


para depósito, etc.

-quantidade: duplo O valor da transação.

-equilíbrio: duplo O novo saldo após esta transação.

-descrição: string A descrição desta transação.

+Transação(tipo: char, Construa uma transação com a data, tipo, saldo e data especificados.
valor: duplo, saldo: e descrição.

duplo, descrição: string)

Figura 12.4 A classe Transaction descreve uma transação para uma conta bancária.

Escreva um programa de teste que crie uma conta com taxa de juros anual de 1,5%, saldo
1.000, id 1.122 e nome George. Deposite $30, $40, $50 na conta e retire $5, $4, $2 da conta.
Imprima o resumo da conta que mostra o nome do titular da conta, taxa de juros, saldo e
todas as transações.

*12.26 (Nova classe Location ) Revise o Exercício de Programação 10.17 para definir o
localizarLargest função como

Localização localizarLargest(const vector<vector<double>> v);

Onde v é um vetor que representa uma matriz bidimensional. Escreva um programa de teste
que solicite ao usuário que insira o número de linhas e colunas de um array bidimensional e
exiba a localização do maior elemento do array. Um exemplo de execução é igual ao do
Exercício de Programação 10.17.
Machine Translated by Google

508 Capítulo 12 Modelos, vetores e pilhas

**12.27 (Maior bloco) Dada uma matriz quadrada com elementos 0 ou 1, escreva um programa para encontrar
uma submatriz quadrada máxima cujos elementos sejam todos 1s. Seu programa deve
solicitar ao usuário que insira o número de linhas na matriz e depois na matriz e exibe a
localização do primeiro elemento na submatriz quadrada máxima e o número de linhas na
submatriz. Suponha que o número máximo de linhas seja 100. Aqui está um exemplo de
execução:

Insira o número de linhas da matriz: 5


Insira a matriz linha por linha:
10101
11101
10111
10111
10111
A submatriz quadrada máxima está em (2, 2) com tamanho 3

Seu programa deve implementar e usar a seguinte função para encontrar a submatriz
quadrada máxima:

vetor<int> findLargestBlock(const vetor<vetor<int>>& m)

O valor de retorno é um vetor que consiste em três valores. Os primeiros dois valores são os
índices de linha e coluna do primeiro elemento da submatriz e o terceiro valor é o número de
linhas da submatriz.

*12.28 (Menores linhas e colunas) Reescreva o Exercício de Programação 8.14 usando vetores.
O programa preenche aleatoriamente 0s e 1s em uma matriz 5 * 5, imprime a matriz e
encontra as linhas e colunas com menos 1s. Aqui está um exemplo de execução:

00110
00101
11100
11101
01001
O menor índice da linha: 0, 1, 4
O menor índice da coluna: 3

**12.29 (quadrado latino) Um quadrado latino é uma matriz n por n preenchida com n letras latinas
diferentes, cada uma ocorrendo exatamente uma vez em cada linha e uma vez em cada
coluna. Escreva um programa que solicite ao usuário que insira o número ne a matriz de
caracteres, conforme mostrado no exemplo de saída e verifique se a matriz de entrada é um
quadrado latino. Os caracteres são os primeiros n caracteres começando em A.

Digite o número n: 4
Insira 4 linhas de letras separadas por espaços:
ABCD
BADC
CDBA
DCAB
A matriz de entrada é um quadrado latino

Digite o número n: 3
Insira 3 linhas de letras separadas por espaços:
AFD
Entrada errada: as letras devem ser de A a C
Machine Translated by Google

Exercícios de Programação 509

**12h30 (Explorar matriz) Reescreva o Exercício de Programação 8.7 usando vetores. O programa solicita
ao usuário que insira o comprimento de uma matriz quadrada, preenche aleatoriamente 0s e
1s na matriz, imprime a matriz e encontra as linhas, colunas e diagonais com todos 0s ou 1s.
Aqui está um exemplo de execução:

Insira o tamanho da matriz: 4 1111

0000
0100
1111
Todos os 0s na linha 1
Todos os 1s na linha 1, 3
Não há números iguais em uma coluna
Não há números iguais na diagonal maior
Não há números iguais na subdiagonal

**12.31 (Interseção) Escreva uma função que retorne a interseção de dois vetores usando o seguinte
cabeçalho:

modelo<nome do tipo T>


vetor<T> intersecção( vetor const<T>& v1, vetor const <T>& v2)

A interseção de dois vetores contém os elementos comuns que aparecem em ambos os


vetores. Por exemplo, a intersecção de dois vetores {2, 3, 1, 5} e {3, 4, 5} é {3, 5}. Escreva um
programa de teste que solicite ao usuário que insira dois vetores, cada um com cinco strings,
e exiba sua interseção. Aqui está um exemplo de execução:

Insira cinco strings para vector1:


Atlanta Dallas Chicago Boston Denver
Insira cinco strings para vector2:
Dallas Tampa Miami Boston Richmond
As cordas comuns são Dallas Boston

**12.32 (Remover duplicatas) Escreva uma função que remova os elementos duplicados de um vetor usando
o seguinte cabeçalho:

modelo<nome do tipo T>


void removeDuplicate(vector<T>& v)

Escreva um programa de teste que solicite ao usuário que insira 10 inteiros em um vetor e
exiba os inteiros distintos. Aqui está um exemplo de execução:

Insira dez números inteiros: 34 5 3 5 6 4 33 2 2 4


Os inteiros distintos são 34 5 3 6 4 33 2

*12.33 (Área de um polígono) Revise o Exercício de Programação 7.29 para solicitar que o usuário insira
o número de pontos em um polígono convexo, depois insira os pontos no sentido horário e
exiba a área do polígono. Aqui está um exemplo de execução do programa:

Insira o número dos pontos: 7 Insira as


coordenadas dos pontos: -12 0 -8,5 10 0 11,4 5,5
7,8 6 -5,5 0 -7 -3,5 -3,5
A área total é 250.075
Machine Translated by Google

510 Capítulo 12 Modelos, vetores e pilhas

12.34 (Teste de subtração) Reescreva a listagem 5.1 RepeatSubtractionQuiz.cpp para alertar o usuário se a
mesma resposta for inserida novamente. Dica: use um vetor para armazenar as respostas. Aqui está
um exemplo de execução:

Quanto é 4 - 3? 4
Resposta errada. Tente novamente. Quanto é 4 - 3? 5
Resposta errada. Tente novamente. Quanto é 4 - 3? 4
Você já digitou 4
Resposta errada. Tente novamente. Quanto é 4 - 3? 1
Você conseguiu!

**12.35 (Álgebra: quadrado perfeito) Escreva um programa que solicite ao usuário que insira um número inteiro m e

*
encontre o menor número inteiro n tal que mn seja um quadrado perfeito.
(Dica: armazene todos os menores fatores de m em um vetor. n é o produto dos fatores que aparecem
um número ímpar de vezes no vetor. Por exemplo, considere m = 90, armazene os fatores 2, 3, 3, 5
em um vetor. 2 e 5 aparecem um número ímpar de vezes no vetor. Portanto, n é 10.) Aqui estão
alguns exemplos de execuções:

Insira um número inteiro m: 1500


O menor número n para mn é 22500 *
n para ser um quadrado perfeito é 15
eu
*

Insira um número inteiro m: 63


O menor número n para m *
n para ser um quadrado perfeito é 7
eu
* n é 441

***12.36 (Jogo: Connect Four) Reescreva o jogo Connect Four no Exercício de Programação
cise 8.22 usando vetores.
Machine Translated by Google

CAPÍTULO

13
Entrada e
saída de arquivo

Objetivos
n Para usar ofstream para saída (§13.2.1) e ifstream para entrada
(§13.2.2).

n Para testar se um arquivo existe (§13.2.3).

n Para testar o final de um arquivo (§13.2.4).

n Para permitir que o usuário insira um nome de arquivo (§13.2.5).

n Para escrever dados no formato desejado (§13.3).

n Para ler e gravar dados usando as funções getline, get e put


(§13.4).

n Para usar um objeto fstream para ler e escrever dados (§13.5).

n Para abrir um arquivo com modos especificados (§13.5).

n Para usar as funções eof(), fail(), bad() e good() para testar estados
de fluxo (§13.6).
n Compreender a diferença entre E/S de texto e E/S binária (§13.7).

n Para escrever dados binários usando a função write (§13.7.1).

n Para ler dados binários usando a função de leitura (§13.7.2).

n Para converter valores e objetos de tipo primitivo em matrizes de bytes usando o


Operador reinterpret_cast (§13.7).

n Para ler/escrever matrizes e objetos (§§13.7.3–13.7.4).

n Para usar as funções seekp e seekg para mover os ponteiros de arquivo para
acesso aleatório a arquivos (§13.8).

n Para abrir um arquivo de entrada e saída para atualizar arquivos (§13.9).


Machine Translated by Google

512 Capítulo 13 Entrada e saída de arquivo

13.1 Introdução
Você pode ler/ gravar dados de/ para um arquivo usando as funções nas classes ifstream,
Chave
Apontar
ofstream e fstream .

Os dados armazenados em variáveis, arrays e objetos são temporários; eles são perdidos quando o programa
termina. Para armazenar permanentemente os dados criados em um programa, você deve salvá-los em um
arquivo em um meio de armazenamento permanente, como um disco. O arquivo pode ser transportado e lido
posteriormente por outros programas. A Seção 4.11, “Entrada e saída simples de arquivos”, introduziu E/S de
texto simples envolvendo valores numéricos. Este capítulo apresenta E/S em detalhes.
C++ fornece as classes ifstream, ofstream e fstream para processar e manipular arquivos.
Essas classes são definidas no arquivo de cabeçalho <fstream> . A classe ifstream serve para
ler dados de um arquivo, a classe ofstream serve para gravar dados em um arquivo e a classe fstream
classe pode ser usada para ler e gravar dados em um arquivo.
C++ usa o termo fluxo para descrever um fluxo de dados. Se fluir para o seu programa, o fluxo será
fluxo de entrada chamado de fluxo de entrada. Se fluir do seu programa, será chamado de fluxo de saída. C++ usa objetos para
fluxo de saída ler/gravar um fluxo de dados. Por conveniência, um objeto de entrada é chamado de fluxo de entrada e um
objeto de saída é chamado de fluxo de saída.
fluxo cin Você já usou o fluxo de entrada e o fluxo de saída em seus programas. cin (entrada do console) é um
fluxo de corte objeto predefinido para ler a entrada do teclado e cout (saída do console) é um objeto predefinido para enviar
caracteres para o console. Esses dois objetos são definidos no arquivo de cabeçalho <iostream> . Neste
capítulo, você aprenderá como ler/escrever dados de/
para arquivos.

13.2 E/S de texto


Os dados em um arquivo de texto podem ser lidos por um editor de texto.
Chave
Apontar
Esta seção demonstra como realizar entrada e saída de texto simples.
nome de arquivo absoluto Cada arquivo é colocado em um diretório no sistema de arquivos. Um nome de arquivo absoluto contém
um nome de arquivo com seu caminho completo e letra de unidade. Por exemplo, c:\example\scores.txt é o
nome de arquivo absoluto para o arquivo score.txt no sistema operacional Windows. Aqui c:\example é
caminho de diretório referido como o caminho do diretório do arquivo. Os nomes absolutos dos arquivos dependem da máquina. No
UNIX, o nome absoluto do arquivo pode ser /home/liang/example/scores.txt, onde /home/liang/
exemplo é o caminho do diretório para o arquivo score.txt.
nome de arquivo relativo Um nome de arquivo relativo é relativo ao seu diretório de trabalho atual. O caminho completo do diretório
para um nome de arquivo relativo é omitido. Por exemplo, score.txt é um nome de arquivo relativo. Se seu
diretório de trabalho atual for c:\example, o nome absoluto do arquivo seria c:\example\scores.txt.

13.2.1 Gravando dados em um arquivo


A classe ofstream pode ser usada para gravar valores de tipos de dados primitivos, matrizes, strings e objetos
em um arquivo de texto. A Listagem 13.1 demonstra como gravar dados. O programa cria uma instância
ofstream e grava duas linhas no arquivo score.txt. Cada linha consiste em nome (uma string), inicial do nome
do meio (um caractere), sobrenome (uma string) e pontuação (um número inteiro).

Listagem 13.1 TextFileOutput.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
Machine Translated by Google

13.2 E/S de Texto 513

saída a jusante; pontuações.txt declarar objeto


John T Smith 90
// Cria um arquivo
7 output.open("scores.txt"); abrir arquivo
Eric K Jones 85
8
9 //Escreve duas linhas
"" ""
10 saída << "João" << << 90 << "T" << << "Smith" saída para arquivo
""
11 << << endl;
"" ""
12 saída << "Eric" << << 85 << "K" << << "Jones"
""
13 << << endl;
14
15 saída.close(); fechar arquivo

16
17 cout << "Concluído" << endl;
18
19 retornar 0;
20 21 22 23 }

Como a classe ofstream é definida no arquivo de cabeçalho fstream , a linha 2 inclui esse arquivo de incluir cabeçalho <fstream>
cabeçalho.
A linha 7 cria um objeto, saída, da classe ofstream usando seu construtor no-arg. criar objeto
A linha 10 abre um arquivo chamado score.txt para o objeto de saída . Se o arquivo não existir, um novo abrir arquivo

arquivo é criado. Se o arquivo já existir, seu conteúdo será destruído sem aviso prévio.
Você pode gravar dados no objeto de saída usando o operador de inserção de fluxo (<<) da mesma
forma que envia dados para o objeto cout . As linhas 13–16 escrevem strings e valores numéricos na corte
saída, conforme mostrado na Figura 13.1.

"" ""
saída << "João" << << "T" << "Smith" << << 90 << fim;

John T Smith 90
arquivo

pontuações.txt
Eric K Jones 85

"" ""
saída << "Érico" << << "K" << "Jones" << << 85 << fim;

Figura 13.1 O fluxo de saída envia dados para o arquivo.

A função close() (linha 18) deve ser usada para fechar o fluxo do objeto. Se esta função fechar arquivo

Se a ação não for invocada, os dados poderão não ser salvos corretamente no arquivo.
Você pode abrir um fluxo de saída usando o seguinte construtor: sintaxe alternativa

saída ofstream ("pontuações.txt");

Esta afirmação equivale a

saída a jusante;
output.open("pontuações.txt");

Cuidado
Se um arquivo já existir, seu conteúdo será destruído sem aviso prévio. o arquivo existe?

Cuidado
O separador de diretório do Windows é uma barra invertida (\). A barra invertida é um caractere \ em nomes de arquivos

de escape especial e deve ser escrita como \\ em uma string literal (veja Tabela 4.5). Por exemplo,

output.open("c:\\exemplo\\pontuações.txt");
Machine Translated by Google

514 Capítulo 13 Entrada e saída de arquivo

Observação

nome de arquivo relativo Um nome de arquivo absoluto depende da plataforma. É melhor usar um nome de arquivo relativo

sem letras de unidade. Se você usar um IDE para executar C++, o diretório do nome do arquivo relativo poderá ser especificado

no IDE. Por exemplo, o diretório padrão para arquivos de dados é o mesmo diretório do código-fonte no Visual C++.

13.2.2 Lendo dados de um arquivo


Nota de vídeo A classe ifstream pode ser usada para ler dados de um arquivo de texto. A Listagem 13.2 demonstra
E/S de texto como ler dados. O programa cria uma instância de ifstream e lê os dados do arquivo score.txt, que
foi criado no exemplo anterior.

Listagem 13.2 TextFileInput.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
objeto de entrada ifstream input("pontuações.txt");

8 //Lê os dados
9 string primeiroNome;
10 char mi;
11 string sobrenome;
12 pontuação interna ;
entrada do arquivo 13 entrada >> primeiroNome >> mi >> sobrenome >> pontuação;
"" "" ""
14 << nós
cout << primeiroNome << << últimoNome << <<
<< pontuação << endl;
15
16
entrada do arquivo 17 entrada >> primeiroNome >> mi >> sobrenome >> pontuação;
"" "" ""
18 << nós
cout << primeiroNome << << últimoNome << <<
<< pontuação << endl;
19
20
fechar arquivo 21 input.close();
22
23 cout << "Concluído" << endl;
24
25 retornar 0;
26 27 28 }

John T Smith 90
Eric K Jones 85
Feito

incluir cabeçalho <fstream> Como a classe ifstream é definida no arquivo de cabeçalho fstream , a linha 2 inclui esse arquivo
de cabeçalho.
A linha 8 cria um objeto, input, da classe ifstream para o arquivo score.txt.
Você pode ler dados do objeto de entrada usando o operador de extração de fluxo (>>) da mesma
comendo
forma que lê dados do objeto cin . As linhas 15 e 19 leem strings e valores numéricos do arquivo de
entrada, conforme mostrado na Figura 13.2.
fechar arquivo A função close() (linha 23) é usada para fechar o fluxo do objeto. Não é necessário fechar o
arquivo de entrada, mas fazê-lo é uma boa prática para liberar os recursos ocupados pelo arquivo.

sintaxe alternativa Você pode abrir um fluxo de entrada usando o seguinte construtor:

ifstream input("pontuações.txt");
Machine Translated by Google

13.2 E/S de Texto 515

entrada >> primeiroNome >> mi >> sobrenome >> pontuação;

John T Smith 90
arquivo

pontuações.txt
Eric K Jones 85

entrada >> primeiroNome >> mi >> sobrenome >> pontuação;

Figura 13.2 O fluxo de entrada lê dados do arquivo.

Esta afirmação equivale a

entrada ifstream;
input.open("pontuações.txt");

Cuidado
Para ler os dados corretamente, você precisa saber exatamente como os dados são armazenados. Por conhecer o formato dos dados

exemplo, o programa da Listagem 13.2 não funcionaria se o arquivo contivesse pontuação como um
valor duplo com ponto decimal.

13.2.3 Testando Existência de Arquivo


Se o arquivo não existir durante a leitura de um arquivo, seu programa será executado e produzirá
resultados incorretos. Seu programa pode verificar se existe um arquivo? Sim. Você pode invocar a arquivo não existe?

função fail() imediatamente após invocar a função open . Se fail() retornar verdadeiro, indica que o
arquivo não existe.

1 //Abra um arquivo
2 input.open("pontuações.txt");

3 4 se (input.fail()) verifique a operação do arquivo


5{
cout << "Arquivo não existe" << endl;
cout << "Sair do programa" << endl;

retornar 0;
6 7 8 9 10 }

13.2.4 Testando o Fim do Arquivo


A Listagem 13.2 lê duas linhas do arquivo de dados. Se você não sabe quantas linhas há no arquivo e
deseja ler todas elas, como reconhecer o final do arquivo? Você pode invocar a função eof() no objeto
de entrada para detectá-lo, conforme discutido na Listagem 5.6, ReadAllData.cpp. função eof

No entanto, este programa não funcionará se houver caracteres em branco extras após o último número.
Para entender isso, vejamos o arquivo que contém os números mostrados na Figura 13.3. Observe que
há um caractere em branco extra após o último número.

95.5 6 70. 2 1 .55 \n

12 3 . 3 12 .9 85 .6

Caractere em branco

Figura 13.3 O arquivo contém números separados por espaços.

Se você usar o código a seguir para ler todos os dados e somar o total, o último número será somado
duas vezes.
Machine Translated by Google

516 Capítulo 13 Entrada e saída de arquivo

ifstream input("pontuação.txt");

soma dupla = 0;
número duplo ;
while (!input.eof()) // Continua se não for o fim do arquivo
{
entrada >> número; //Lê os dados
cout << número << " "; // Mostrar dados
soma += número;
}

A razão para isso é que quando o último número 85.6 é lido, o sistema de arquivos não sabe que é o
último número porque há caracteres em branco após o último número. Então, o eof()
função retorna falso. Quando o programa lê o número novamente, a função eof() retorna verdadeiro,
mas a variável número não é alterada, pois nada é lido do arquivo.
A variável número ainda contém o valor 85,6, que é adicionado novamente para somar.
Existem duas maneiras de corrigir esse problema. Uma é verificar a função eof() logo após ler um
número. Se a função eof() retornar verdadeiro, saia do loop, conforme mostrado no código a seguir:

ifstream input("pontuação.txt");

soma dupla = 0;
número duplo ;
while (!input.eof()) // Continua se não for o fim do arquivo
{
entrada >> número; //Lê os dados
if (input.eof()) pausa;
cout << número << " "; // Mostrar dados
soma += número;
}

A outra maneira de corrigir esse problema é escrever o código assim:

while (input >> number) // Continua a ler os dados até falhar


{
cout << número << " "; // Mostrar dados
soma += número;
}

A instrução input >> number serve, na verdade, para invocar uma função de operador. As funções
de operador serão apresentadas no Capítulo 14. Esta função retorna um objeto se um número for lido;
caso contrário, retorna NULL. NULL é um valor constante 0. C++ o converte automaticamente em um
valor bool falso quando é usado como uma condição em uma instrução de loop ou instrução de
seleção. Se nenhum número for lido no fluxo de entrada, input >> number retornará NULL e o loop terminará.
A Listagem 13.3 fornece um programa completo que lê números do arquivo e exibe sua soma.

Listagem 13.3 TestEndOfFile.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
7 //Abre um arquivo
criar objeto de entrada 8 ifstream input("pontuação.txt");
9
Machine Translated by Google

13.2 E/S de Texto 517

10 se (entrada.falha()) o arquivo existe?

11 {
12 cout << "Arquivo não existe" << endl;
13 cout << "Sair do programa" << endl;
14 retornar 0;
15 }
16
17 soma dupla = 0;
18 número duplo ;
19 while (input >> number) // Continua se não for o fim do arquivo fim do arquivo?
20 {
21 cout << número << soma " "; // Mostrar dados
22 += número;
23 }
24
25 input.close(); fechar arquivo

26
27 cout << "\nEu sou " << soma << endl; mostrar dados
28
29 retornar 0;
30 }

95,5 6 70,2 1,55 12 3,3 12,9 85,6


O total é 287,05

O programa lê os dados em um loop (linhas 19–23). Cada iteração do loop lê um número


e adiciona à soma. O loop termina quando a entrada atinge o final do arquivo.

13.2.5 Permitindo que o usuário insira um nome de arquivo


Nos exemplos anteriores, os nomes dos arquivos são literais de string codificados no programa. Em muitos
casos, é desejável permitir que o usuário insira o nome de um arquivo em tempo de execução. A Listagem
13.4 fornece um exemplo que solicita ao usuário que insira um nome de arquivo e verifica se o arquivo existe.

Listagem 13.4 CheckFile.cpp


1 #include <iostream>
2 #include <fstream> incluir cabeçalho fstream
3 #incluir <string> incluir cabeçalho de string
4 usando namespace std;

5 6 int principal()
7{
string nome do arquivo; criar objeto de entrada
cout << "Digite o nome do arquivo: ";
8 cin >> nome do arquivo; insira o arquivo

9
10 11 12 entrada ifstream(nomedoarquivo.c_str());
13
14 se (entrada.falha()) o arquivo existe?

15 cout << nome do arquivo << "não existe" << endl;


16 else
17 cout << nome do arquivo << "existe" << endl;
18
19 retornar 0;
20 }
Machine Translated by Google

518 Capítulo 13 Entrada e saída de arquivo

Digite um nome de arquivo: c:\example\Welcome.cpp c:


\example\Welcome.cpp existe

Digite um nome de arquivo: c:\example\TTTT.cpp c:


\example\TTTT.cpp não existe

O programa solicita que o usuário insira um nome de arquivo como uma string (linha 10). No entanto, o nome do
arquivo passado para o construtor do fluxo de entrada e saída ou para a função aberta deve ser uma string C no C++
padrão. Portanto, a função c_str() na classe string é invocada para retornar uma string C de um objeto string (linha 12).

Observação

Alguns compiladores, como Visual C++, permitem passar um nome de arquivo como uma string para o
construtor de fluxo de entrada e saída ou para a função open . Para permitir que seu programa funcione
em todos os compiladores C++, passe um nome de arquivo como uma string C.

13.1 Como você declara e abre um arquivo para saída? Como você declara e abre um arquivo
ÿVerificação de ponto para entrada?

13.2 Por que você deve sempre fechar um arquivo após ele ser processado?

13.3 Como você detecta se um arquivo existe?

13.4 Como você detecta se o final do arquivo foi alcançado?

13.5 Você deve passar um nome de arquivo como uma string ou uma string C para criar uma entrada e uma saída
objeto stream ou para a função open ?

13.3 Formatando Saída


Os manipuladores de fluxo podem ser usados para formatar a saída do console, bem como a saída do arquivo.
Chave
Apontar
Você usou os manipuladores de fluxo para formatar a saída para o console na Seção 4.10, “Formatando a saída do
console”. Você pode usar o mesmo manipulador de fluxo para formatar a saída em um arquivo. A Listagem 13.5 dá um
exemplo que formata os registros dos alunos para o arquivo chamado format-tedscores.txt.

Listagem 13.5 WriteFormattedData.cpp


1 #include <iostream>
incluir cabeçalho iomanip 2 #include <iomanip>
incluir cabeçalho fstream 3 #incluir <fstream>
4 usando namespace std;

5 6 int principal()
7{
declarar objeto saída a jusante;

8 //Cria um arquivo
9 output.open("formattedscores.txt");
10
11 //Escreve duas linhas
saída com formato 12 saída << setw(6) << "John" << setw(2) << "T" << setw(6) << "Smith"
13 << " " << setw(4) << 90 << endl;
saída com formato 14 15 16 saída << setw(6) << "Eric" << setw(2) << "K" << setw(6) << "Jones"
Machine Translated by Google

13.4 Funções: getline, get e put 519


""
17 << << conjunto(4) << 85;
18
19 saída.close(); fechar arquivo

20
21 cout << "Concluído" << endl;
22
23 retornar 0;
24 }

O conteúdo do arquivo é mostrado abaixo:

John TS mi º 9 0 \n

Eric KJ aqueles 85

13.6 Você pode usar os manipuladores de fluxo para formatar a saída de texto?

ÿVerificação de ponto

13.4 Funções: getline, get e put


A função getline pode ser usada para ler uma string que inclui caracteres de espaço em
Chave
branco e a função get/ put pode ser usada para ler e escrever um único caractere. Apontar

Há um problema na leitura de dados usando o operador de extração de fluxo (>>). Os dados são delimitados
por espaços em branco. O que acontece se os caracteres de espaço em branco fizerem parte de uma string?
Na Seção 4.8.4, “Lendo Strings”, você aprendeu como usar a função getline para ler uma string com espaços
em branco. Você pode usar a mesma função para ler strings de um arquivo. Lembre-se de que a sintaxe da
função getline é

getline(ifstream& entrada, int string s, char delimitChar)

A função interrompe a leitura de caracteres quando o caractere delimitador ou a marca de fim de arquivo é
encontrado. Se o delimitador for encontrado, ele será lido, mas não armazenado na matriz. O terceiro
argumento delimitChar possui um valor padrão ('\n'). A função getline é definida no arquivo de cabeçalho Obter linha
iostream .
Suponha que seja criado um arquivo chamado state.txt que contém os nomes dos estados delimitados pelo
símbolo de libra (#) . O diagrama a seguir mostra o conteúdo do arquivo:

N e você
e R # k Novo M ex ico

#T exas # EU n Diana

A Listagem 13.6 fornece um programa que lê os estados do arquivo.

Listagem 13.6 ReadCity.cpp


1 #include <iostream>
2 #include <fstream> incluir cabeçalho fstream
3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
//Abre um arquivo
8 9 ifstream input("estado.txt"); objeto de entrada
10
11 se (entrada.falha()) arquivo de entrada existe?
Machine Translated by Google

520 Capítulo 13 Entrada e saída de arquivo

12 {
13 cout << "Arquivo não existe" << endl;
14 cout << "Sair do programa" << endl;
15 retornar 0;
16 }
17
18 //Lê os dados
cidade de cordas 19 cidade de cordas;
20
fim do arquivo? 21 while (!input.eof()) // Continua se não for o fim do arquivo
22 {
entrada do arquivo 23 getline(entrada, cidade, '#');
mostrar dados 24 cout << cidade << endl;
25 }
26
fechar arquivo 27 input.close();
28
29 cout << "Concluído" << endl;
30
31 retornar 0;
32 }

Nova Iorque
Novo México
Texas
Indiana
Feito

Invocar getline(input, state, '#') (linha 23) lê caracteres para o estado do array
até encontrar o caractere # ou o final do arquivo.
Duas outras funções úteis são get e put. Você pode invocar a função get em um objeto de entrada para ler
um caractere e invocar a função put em um objeto de saída para escrever um caractere.
obter função A função get possui duas versões:

char get() // Retorna um caractere


ifstream* get(char& ch) // Lê um caractere para ch

A primeira versão retorna um caractere da entrada. A segunda versão passa um argumento de referência de
caractere, lê um caractere da entrada e o armazena em ch. Esta função também retorna a referência ao objeto
de entrada que está sendo usado.
colocar função O cabeçalho da função put é

void put(char ch)

Ele grava o caractere especificado no objeto de saída.


A Listagem 13.7 dá um exemplo do uso dessas duas funções. O programa solicita ao usuário
para inserir um arquivo e copiá-lo para um novo arquivo.

Listagem 13.7 CopyFile.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
Machine Translated by Google

13.4 Funções: getline, get e put 521


//Insira um arquivo fonte
cout << "Digite o nome do arquivo fonte: ";
8 string inputFilename;
9 cin >> nome do arquivo de entrada; insira o nome do arquivo de entrada
10
11 //Insira um arquivo de destino
12 cout << "Digite um nome de arquivo de destino: ";
13 string nome do arquivo de saída;
14 cin >> saídaNome do arquivo; insira o nome do arquivo de saída
15
16 // Cria fluxos de entrada e saída
17 entrada ifstream(inputFilename.c_str()); objeto de entrada
18 saída ofstream (outputFilename.c_str()); objeto de saída
19
20 se (entrada.falha()) arquivo existe?

21 {
22 cout << inputFilename << "não existe" << endl;
23 cout << "Sair do programa" << endl;
24 retornar 0;
25 }
26
27 char ch = input.get();
28 while (!input.eof()) // Continua se não for o fim do arquivo fim do arquivo?
29 {
30 saída.put(ch); colocar função
31 ch = input.get(); //Lê o próximo caractere obter função
32 }
33
34 input.close(); fechar arquivo

35 saída.close(); fechar arquivo

36
37 cout << "\nCópia concluída" << endl;
38
39 retornar 0;
40 41 42 }

Insira um nome de arquivo de origem: c:\example\CopyFile.cpp


Insira um nome de arquivo de destino: c:\example\temp.txt
Cópia concluída

O programa solicita que o usuário insira um nome de arquivo de origem na linha 11 e um nome de arquivo de
destino na linha 16. Um objeto de entrada para inputFilename é criado na linha 19 e um objeto de saída para
outputFilename na linha 20. Os nomes dos arquivos devem ser strings C. . inputFilename.c_str()
retorna uma string C da string inputFilename.
As linhas 22–27 verificam se o arquivo de entrada existe. As linhas 30 a 34 leem os caracteres repetidamente,
um de cada vez, usando a função get e gravam o caractere no arquivo de saída usando a função put.
função.
Suponha que as linhas 29–34 sejam substituídas pelo seguinte código:

while (!input.eof()) // Continua se não for o fim do arquivo


{
saída.put(entrada.get());
}

O que vai acontecer? Se você executar o programa com este novo código, verá que o novo arquivo é um byte
maior que o original. O novo arquivo contém um caractere de lixo extra no final. Isso ocorre porque quando o último
caractere é lido do arquivo de entrada usando input.get(),
Machine Translated by Google

522 Capítulo 13 Entrada e saída de arquivo

input.eof() ainda é falso. Depois, o programa tenta ler outro caractere; input.eof() agora se torna verdadeiro. No entanto, o
caractere lixo estranho já foi enviado para o arquivo de saída.

O código correto na Listagem 13.7 lê um caractere (linha 29) e verifica eof() (linha 30).
Se eof() for verdadeiro, o caractere não será colocado na saída; caso contrário, é copiado (linha 32). Este processo continua
até que eof() retorne verdadeiro.

13.7 Quais são as diferenças entre as funções getline e get ?


ÿVerificação de ponto
13.8 Que função você usa para escrever um caractere?

13.5 Modos fstream e abertura de arquivo


Você pode usar fstream para criar um objeto de arquivo para entrada e saída.
Chave
Apontar
Nas seções anteriores, você usou ofstream para gravar dados e ifstream para ler dados. Alternativamente,
você pode usar a classe fstream para criar um fluxo de entrada ou saída. É conveniente usar fstream se seu
programa precisar usar o mesmo objeto stream para entrada e saída. Para abrir um arquivo fstream , você
modo de abertura de arquivo deve especificar um modo de abertura de arquivo para informar ao C++ como o arquivo será usado. Os modos
de arquivo estão listados na Tabela 13.1.

Tabela 13.1 Modos de arquivo

Modo Descrição

ios::em Abre um arquivo para entrada.

ios::fora Abre um arquivo para saída.

ios::aplicativo Acrescenta toda a saída ao final do arquivo.

ios::comeu Abre um arquivo para saída. Se o arquivo já existir, vá para o final do arquivo. Os dados podem ser
gravados em qualquer lugar do arquivo.

ios::truto Descarta o conteúdo do arquivo se o arquivo já existir. (Esta é a ação padrão


para iOS: fora.)

ios::binário Abre um arquivo para entrada e saída binária.

Observação

Alguns dos modos de arquivo também podem ser usados com objetos ifstream e ofstream para
abrir um arquivo. Por exemplo, você pode usar o modo ios:app para abrir um arquivo com um
objeto ofstream para poder anexar dados ao arquivo. Entretanto, para consistência e simplicidade,
é melhor usar os modos de arquivo com os objetos fstream .

Observação

combinar modos Vários modos podem ser combinados usando o | operador. Este é um operador OR inclusivo bit a
bit. Consulte o Apêndice E para obter mais detalhes. Por exemplo, para abrir um arquivo de saída
chamado city.txt para anexar dados, você pode usar a seguinte instrução:

stream.open("cidade.txt", ios::out | ios::app);

A Listagem 13.8 fornece um programa que cria um novo arquivo chamado city.txt (linha 11) e grava dados
no arquivo. O programa então fecha o arquivo e o reabre para acrescentar novos dados (linha 19), em vez de
substituí-los. Finalmente, o programa lê todos os dados do arquivo.
Machine Translated by Google

13.5 Modos fstream e abertura de arquivo 523

Listagem 13.8 AppendFile.cpp


1 #include <iostream>
2 #include <fstream> incluir cabeçalho fstream
3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
8 entrada de fluxo; objeto fstream

9 10 //Cria um arquivo
11 inout.open("cidade.txt", ios::out); abrir arquivo de saída
12
13 //Escreve cidades
"" ""
14 entrada << "Dallas" << << "Houston" << << "Atlanta" << " "; gravar dados
15
16 inout.close(); fechar fluxo
17
18 //Acrescenta ao arquivo
19 inout.open("cidade.txt", ios::out | ios::app); saída aberta para anexar
20
21 //Escreve cidades
"" ""
22 inout << "Savannah" << << "Austin" << << "Chicago"; gravar dados
23
24 inout.close(); fechar fluxo
25
26 cidade de cordas;
27
28 //Abre o arquivo
29 inout.open("cidade.txt", ios::in); aberto para entrada
30 while (!inout.eof()) // Continua se não for o fim do arquivo fim do arquivo?
31 {
32 inout >> cidade; ler dados
33 cout << cidade << " ";
34 }
35
36 inout.close(); fechar fluxo
37
38 retornar 0;
39 }

Dallas Houston Atlanta Savana Austin Chicago

O programa cria um objeto fstream na linha 8 e abre o arquivo city.txt para saída usando o modo de arquivo
ios::out na linha 11. Após escrever os dados na linha 14, o programa fecha o stream na linha 16.

O programa usa o mesmo objeto de fluxo para reabrir o arquivo de texto com os modos combinados ios::out
| ios::app na linha 19. O programa então anexa novos dados ao final do arquivo na linha 22 e fecha o fluxo na
linha 24.
Finalmente, o programa usa o mesmo objeto de fluxo para reabrir o arquivo de texto com o modo de entrada
ios::in na linha 29. O programa então lê todos os dados do arquivo (linhas 30–34).

13.9 Como você abre um arquivo para poder anexar dados a ele?
ÿVerificação de ponto
13.10 Qual é o modo de abertura do arquivo ios::truct?
Machine Translated by Google

524 Capítulo 13 Entrada e saída de arquivo

13.6 Testando estados de fluxo


Nota de vídeo As funções eof(), fail(), good() e bad() podem ser usadas para testar os estados das operações de fluxo.
Chave
Estados de fluxo de teste
Apontar

Você usou a função eof() e a função fail() para testar os estados de um fluxo. C++ fornece várias outras funções
estado do fluxo em um fluxo para testar estados de fluxo. Cada objeto de fluxo contém um conjunto de bits que atuam como
sinalizadores. Esses valores de bits (0 ou 1) indicam o estado de um fluxo.
A Tabela 13.2 lista esses bits.

Tabela 13.2 Valores de bits de estado de fluxo

Pedaço
Descrição

ios::eofbit Definido quando o final de um fluxo de entrada é atingido.

ios::failbit Definido quando uma operação falhou.

ios::falha difícil Definido quando ocorreu um erro irrecuperável.

ios::badbit Definido quando uma operação inválida foi tentada.

ios::goodbit Definido se nenhum dos bits anteriores estiver definido.

Os estados das operações de E/S são representados nestes bits. Não é conveniente acessar diretamente esses
bits. C++ fornece funções de membro no objeto de fluxo de E/S para testá-las. Essas funções estão listadas na
Tabela 13.3.

Tabela 13.3 Funções de estado de fluxo

Função Descrição

eof() Retorna verdadeiro se o sinalizador eofbit estiver definido.

falhar() Retorna verdadeiro se o sinalizador failbit ou hardfail estiver definido.

ruim() Retorna verdadeiro se o badbit estiver definido.

bom() Retorna verdadeiro se o goodbit estiver definido.

claro() Limpa todos os sinalizadores.

A Listagem 13.9 dá um exemplo para detectar os estados do fluxo.

Listagem 13.9 ShowStreamState.cpp


1 #include <iostream>
incluir cabeçalho fstream 2 #include <fstream>
3 #incluir <string>
4 usando namespace std;

protótipo de função 5 6 void showState(const fstream&);

7 8 int principal()
9{
objeto de entrada 10 entrada de fluxo;
11
12 //Cria um arquivo de saída
abrir arquivo de entrada 13 inout.open("temp.txt", ios::out);
14 entrada << "Dallas";
15 cout << "Operação normal (sem erros)" << endl;
mostrar estado 16 showState(inout);
fechar arquivo 17 inout.close();
18
Machine Translated by Google

13.6 Testando Estados de Fluxo 525

19 //Cria um arquivo de entrada


20 inout.open("temp.txt", ios::in); abrir arquivo de saída
21
22 //Lê uma string
23 cidade de cordas;
24 inout >> cidade; leia cidade
25 cout << "Fim do arquivo (sem erros)" << endl;
26 showState(inout); mostrar estado
27
28 inout.close(); fechar arquivo

29
30 //Tenta ler após fechar o arquivo
31 inout >> cidade;
32 cout << "Operação incorreta (erros)" << endl;
33 showState(inout); mostrar estado
34
35 retornar 0;
36 }

37 38 void showState(const fstream& stream) mostrar estado

39 {
"
40 cout << "Status da transmissão: << endl;
41 conta << " eof(): " << stream.eof() << endl;
42 conta << "fail():" << stream.fail() << endl;
43 conta << " bad(): " << stream.bad() << endl;
44 conta << "bom():" << stream.good() << endl;
45 }

Operação normal (sem erros)


Status da transmissão:
eof(): 0
falha(): 0
ruim(): 0
bom(): 1
Fim do arquivo (sem erros)

Status da transmissão:
eof(): 1
falha(): 0
ruim(): 0
bom(): 0

Operação incorreta (erros)


Status da transmissão:
eof(): 1
falha(): 1
ruim(): 0
bom(): 0

O programa cria um objeto fstream usando seu construtor no-arg na linha 10, abre temp.txt
para saída na linha 13 e escreve uma string Dallas na linha 14. O estado do stream é exibido
na linha 15. Não há erros até aqui.
O programa então fecha o fluxo na linha 17, reabre temp.txt para entrada na linha 20 e lê
uma string Dallas na linha 24. O estado do fluxo é exibido na linha 26. Não há erros até agora,
mas o fim do arquivo é alcançado.
Finalmente, o programa fecha o fluxo na linha 28 e tenta ler os dados após o arquivo ser
fechado na linha 31, o que causa um erro. O estado do fluxo é exibido na linha 33.
Ao invocar a função showState nas linhas 16, 26 e 33, o objeto stream é passado
para a função por referência.
Machine Translated by Google

526 Capítulo 13 Entrada e saída de arquivo

13.11 Como você determina o estado das operações de E/S?


ÿVerificação de ponto

13.7 E/S binária


Nota de vídeo O modo ios::binary pode ser usado para abrir um arquivo para entrada e saída binária.
Chave
E/S binária
Apontar
Até agora, você usou arquivos de texto. Os arquivos podem ser classificados em texto e binários. Um arquivo que
pode ser processado (lido, criado ou modificado) usando um editor de texto como o Notepad no Windows ou vi no
arquivo de texto UNIX é chamado de arquivo de texto. Todos os outros arquivos são chamados de arquivos binários. Você não pode
arquivo binário ler arquivos binários usando um editor de texto. Eles são projetados para serem lidos por programas. Por exemplo, os
por que arquivo binário? programas de origem C++ são armazenados em arquivos de texto e podem ser lidos por um editor de texto, mas os
arquivos executáveis em C++ são armazenados em arquivos binários e lidos pelo sistema operacional.
Embora não seja tecnicamente preciso e correto, você pode imaginar um arquivo de texto como consistindo em uma sequência de caracteres e um

arquivo binário como consistindo em uma sequência de bits. Por exemplo, o número inteiro decimal 199 é armazenado como a sequência dos três

caracteres, '1', '9', '9', em um arquivo de texto, e o mesmo número inteiro é armazenado como um número inteiro C7 em um arquivo binário, porque

decimal 199

é igual ao hexadecimal C7 (199 = 12 * 161 + 7). A vantagem dos arquivos binários é que eles são mais eficientes de
processar do que os arquivos de texto.

Observação

Os computadores não diferenciam arquivos binários de arquivos de texto. Todos os arquivos são armazenados em

texto versus E/S binária formato binário e, portanto, todos os arquivos são essencialmente arquivos binários. A E/S de texto é construída sobre

E/S binária para fornecer um nível de abstração para codificação e decodificação de caracteres.

A E/S binária não requer conversões. Se você gravar um valor numérico em um arquivo usando E/S binária, o
valor exato na memória será copiado para o arquivo. Para realizar E/S binária em C++, você deve abrir um arquivo
ios::binário usando o modo binário ios::binary. Por padrão, um arquivo é aberto em modo texto.
Você usou o operador << e a função put para gravar dados em um arquivo de texto e o operador >> , as funções
get e getline para ler dados de um arquivo de texto. Para ler/gravar dados de/para um arquivo binário, você deve usar
as funções de leitura e gravação em um fluxo.

13.7.1 A função de gravação


A sintaxe para a função de gravação é

função de gravação streamObject.write(const char* s, tamanho interno )

que grava um array de bytes no tipo char*. Cada caractere é um byte.


A Listagem 13.10 mostra um exemplo de uso da função write .

Listagem 13.10 BinaryCharOutput.cpp


1 #include <iostream>
2 #include <fstream>
3 #incluir <string>
4 usando namespace std;

5 6 int principal()
7{
objeto fstream fstream binárioio("cidade.dat", ios::out | ios::binary);
corda strings = "Atlanta";
gravar dados 8 binárioio.write(s.c_str(), s.size()); // Escreve s no arquivo
fechar arquivo 9 binárioio.close();
10 11 12
Machine Translated by Google

13.7 E/S Binária 527


13 cout << "Concluído" << endl;
14
15 retornar 0;
16 }

A linha 8 abre o arquivo binário city.dat para saída. Invocando binaryio.write(s.c_str(),


s.size()) (linha 10) grava a string s no arquivo.
Muitas vezes você precisa gravar outros dados além de caracteres. Como você pode conseguir
isso? Você pode usar o operador reinterpret_cast . O operador reinterpret_cast pode converter
qualquer tipo de ponteiro para outro tipo de ponteiro de classes não relacionadas. Ele simplesmente
executa uma cópia binária do valor de um tipo para outro sem alterar os dados. A sintaxe de uso do
operador reinterpret_cast é a seguinte:

reinterpret_cast<dataType*>(endereço) reinterpretar_cast

Aqui, endereço é o endereço inicial dos dados (primitivo, array ou objeto) e dataType
é o tipo de dados para o qual você está transmitindo. Neste caso, para E/S binária, é char*.
Por exemplo, veja o código na Listagem 13.11.

Listagem 13.11 BinaryIntOutput.cpp


1 #include <iostream>
2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
fstream binárioio("temp.dat", ios::out | ios::binary); objeto fstream
valor interno = 199; valor interno
binárioio.write(reinterpret_cast<char*>(&valor), sizeof(valor)); saída binária
7 binárioio.close(); fechar arquivo

8
9 cout << "Concluído" << endl;
10
11 retornar 0;
12 13 14 15 }

A linha 9 grava o conteúdo em valor variável no arquivo. reinterpretar_cast<char*>


(&value) converte o endereço do valor int para o tipo char*. sizeof(value) retorna o tamanho de
armazenamento da variável value, que é 4, pois é uma variável do tipo int .

Observação

Para maior consistência, este livro usa a extensão .txt para nomear arquivos de texto e .dat para .txt e .dat
nomear arquivos binários.

13.7.2 A função de leitura


A sintaxe da função de leitura é

streamObject.read(char* endereço, tamanho interno ) função de leitura

O parâmetro size indica o número máximo de bytes lidos. O número real de


bytes lidos podem ser obtidos de uma função membro gcount.
Suponha que o arquivo city.dat foi criado na Listagem 13.10. A Listagem 13.12 lê os bytes usando
a função de leitura .
Machine Translated by Google

528 Capítulo 13 Entrada e saída de arquivo

Listagem 13.12 BinaryCharInput.cpp


1 #include <iostream>
2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
objeto fstream 7 fstream binárioio("cidade.dat", ios::in | ios::binary);
matriz de bytes 8 caractere s[10]; //Array de 10 bytes. Cada caractere é um byte.
ler dados binárioio.read(s, 10);
gconta() 9 10 cout << "Número de caracteres lidos: " <<binaryio.gcount() << endl;
11 s[binaryio.gcount()] = '\0'; // Acrescenta um terminador de string C
12 cout << s << endl;
fechar arquivo 13 binárioio.close();
14
15 retornar 0;
16}

número de capítulos lidos: 7


Atlanta

A linha 7 abre o arquivo binário city.dat para entrada. A invocação de binaryio.read(s,10) (linha 9) lê até
10 bytes do arquivo para o array. O número real de bytes lidos pode ser determinado invocando
binaryio.gcount() (linha 11).
Suponha que o arquivo temp.dat foi criado na Listagem 13.11. A Listagem 13.13 lê o inteiro usando a
função read .

Listagem 13.13 BinaryIntInput.cpp


1 #include <iostream>
2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
abrir arquivo binário fstream binárioio("temp.dat", ios::in | ios::binary);
7 valor interno ;
saída binária 8 binárioio.read(reinterpret_cast<char*>(&valor), sizeof(valor));
9 cout << valor << endl;
fechar arquivo 10 binárioio.close();
11 12
13 retornar 0;
14}

199

Os dados no arquivo temp.dat foram criados na Listagem 13.11. Os dados consistiam em um número
inteiro e foram convertidos em bytes antes de serem armazenados. Este programa primeiro leu os dados como
bytes e depois usou o operador reinterpret_cast para converter bytes em um valor int (linha 9).
Machine Translated by Google

13.7 E/S Binária 529

13.7.3 Exemplo: E/S de array binário


Você pode usar o operador reinterpret_cast para converter dados de qualquer tipo em bytes e vice-versa. Esta
seção dá um exemplo na Listagem 13.14 para escrever um array de valores duplos em um arquivo binário e lê-
lo de volta a partir do arquivo.

Listagem 13.14 BinaryArrayIO.cpp


1 #include <iostream>
2 #include <fstream>
3 usando namespace std;

4 5 int principal()
6{
7 const int TAMANHO = 5; //Tamanho da matriz tamanho de matriz constante
8

fstream binárioio; // Cria objeto de fluxo objeto fstream


9 10
11 //Escreve o array no arquivo
12 binárioio.open("array.dat", ios::out | ios::binary); abrir arquivo binário
13 matriz dupla [TAMANHO] = {3,4, 1,3, 2,5, 5,66, 6,9}; criar matriz
14 binárioio.write(reinterpret_cast<char*>(&array), sizeof(array)); escrever no arquivo

15 binárioio.close(); fechar arquivo

16
17 //Lê o array do arquivo
18 binárioio.open("array.dat", ios::in | ios::binary); abrir arquivo de entrada
19 resultado duplo [TAMANHO]; criar matriz
20 binárioio.read(reinterpret_cast<char*>(&resultado), sizeof(resultado)); ler do arquivo
21 binárioio.close(); fechar arquivo

22
23 //Mostra matriz
24 for (int i = 0; i < TAMANHO; i++)
25 cout << resultado[i] << " ";
26
27 retornar 0;
28 }

3,4 1,3 2,5 5,66 6,9

O programa cria um objeto stream na linha 9, abre o arquivo array.dat para saída binária na linha 12, grava
uma matriz de valores duplos no arquivo na linha 14 e fecha o arquivo na linha 15.
O programa então abre o arquivo array.dat para entrada binária na linha 18, lê um array de
duplica os valores do arquivo na linha 20 e fecha o arquivo na linha 21.
Finalmente, o programa exibe o conteúdo do resultado da matriz (linhas 24–25).

13.7.4 Exemplo: E/S de objeto binário


Esta seção fornece um exemplo de gravação de objetos em um arquivo binário e leitura dos objetos do arquivo.

A Listagem 13.1 grava os registros dos alunos em um arquivo de texto. O registro do aluno consiste em
nome, inicial do meio, sobrenome e pontuação. Esses campos são gravados no arquivo separadamente. Uma
maneira melhor de processar é definir uma classe para modelar registros. Cada registro é um objeto da classe Student .
Deixe a classe ser chamada de Student com os campos de dados firstName, mi, lastName
e score, seus acessadores e modificadores de suporte e dois construtores. O diagrama UML de
classes é mostrado na Figura 13.4.
Machine Translated by Google

530 Capítulo 13 Entrada e saída de arquivo

As funções get e set para esses campos de


dados são fornecidas na classe, mas por
questões de brevidade são omitidas no diagrama UML.

Estudante

-primeiroNome: char[25] O primeiro nome deste aluno.

-mi: char A inicial do meio deste aluno.

-sobrenome: char[25] O sobrenome deste aluno.

-pontuação: int A pontuação deste aluno.

+Aluno() Constrói um objeto Student padrão.

+Aluno(nome: string, mi: char, sobrenome: string, pontuação: int) Constrói um aluno com nome especificado, mi, sobrenome
nome e pontuação

Figura 13.4 A classe Aluno descreve as informações do aluno.

A Listagem 13.15 define a classe Student no arquivo de cabeçalho e a Listagem 13.16 implementa a
classe. Observe que o nome e o sobrenome são armazenados em duas matrizes de caracteres com
comprimento fixo 25 internamente (linhas 22, 24), de forma que cada registro de aluno terá o mesmo tamanho.
Isso é necessário para garantir que os alunos possam ler o arquivo corretamente. Como é mais fácil usar
o tipo string do que string C, o tipo string é usado nas funções get e set para firstName e lastName
(linhas 12, 14, 16, 18).

Listagem 13.15 Aluno.h


1 #ifndef ESTUDANTE_H
2 #define STUDENT_H
3 #incluir <string>
4 usando namespace std;

5 6 alunos da turma
7{
membros públicos 8 público:
construtor sem argumento Estudante();
construtor 9 10 Aluno(const string& firstName, char mi, const string& lastName,
11 int score);
função mutadora 12 void setFirstName(const string& s);
13 void setMi(char mi);
14 void setLastName(const string& s);
15 void setScore(int pontuação);
função de acessador 16 string getPrimeiroNome() const;
17 char getMi() const;
18 string getÚltimoNome() const;
19 int getScore() const;
20
campos de dados privados 21 privado:
22 caracteres primeiroNome[25];
23 char mi;
24 caracteres sobrenome[25];
25 pontuação interna ;
26};
27
28 #endif
Machine Translated by Google

13.7 E/S Binária 531

Listagem 13.16 Aluno.cpp


1 #include "Aluno.h" incluir arquivo de cabeçalho

2 #incluir <cstring>

3 4 // Construa um aluno padrão


5 Aluno::Aluno() construtor sem argumento
6{
7}
8
9 // Construa um objeto Student com dados especificados
10 Aluno::Aluno(const string& firstName, char mi, const string& lastName, construtor
11 int score)
12 {
13 setPrimeiroNome(primeiroNome);
14 definirE(E);
15 setSobrenome(sobrenome);
16 setScore(pontuação);
17}
18
19 void Aluno::setFirstName(const string& s) definirPrimeiroNome

20 {
21 strcpy(primeiroNome, s.c_str());
22}

23 24 void Aluno::setMi(char mi)


25 {
26 isto->mi = mi;
27}
28
29 void Aluno::setLastName(const string& s)
30 {
31 strcpy(sobrenome, s.c_str());
32}

33 34 void Aluno::setScore( pontuação int )


35 {
36 isto->pontuação = pontuação;
37}

38 39 string Aluno::getPrimeiroNome() const


40 {
41 return string(primeiroNome);
42}
43
44 caracteres Aluno::getMi() const pegar meu()
45 {
46 retornar mi;
47}
48
49 string Aluno::getLastName() const
50 {
51 return string(sobrenome);
52}

53 54 int Aluno::getScore() const


55 {
pontuação de retorno ;
56 57}
Machine Translated by Google

532 Capítulo 13 Entrada e saída de arquivo

A Listagem 13.17 fornece um programa que cria quatro objetos Student e os grava em um arquivo
chamado student.dat e os lê do arquivo.

Listagem 13.17 BinaryObjectIO.cpp


1 #include <iostream>
2 #include <fstream>
incluir cabeçalho do aluno 3 #include "Aluno.h"
4 usando namespace std;

exibir dados do aluno 5 6 void displayStudent(const Aluno e aluno)


7{
cout << estudante.getFirstName() << cout << " ";
estudante.getMi() << " ";
8 cout << estudante.getLastName() << " ";
9 cout << estudante.getScore() << endl;
10 11 12 }
13
14 int principal()
15 {
objeto fstream 16 fstream binárioio; // Cria objeto de fluxo
abrir arquivo de saída 17 binárioio.open("aluno.dat", ios::out | ios::binary);
18
criar aluno1 19 Aluno aluno1("John", 'T', "Smith", 90);
criar aluno2 20 Aluno aluno2("Eric", 'K', "Jones", 85);
criar aluno3 21 Aluno aluno3("Susan", 'T', "Rei", 67);
criar aluno4 22 Aluno aluno4("Kim", 'K', "Peterson", 95);
23
escreva aluno1 24 binárioio.write(reinterpret_cast<char*>
25 (&aluno1), sizeof(Aluno));
escreva aluno2 26 binárioio.write(reinterpret_cast<char*>
27 (&aluno2), sizeof(Aluno));
escreva aluno3 28 binárioio.write(reinterpret_cast<char*>
29 (&aluno3), sizeof(Aluno));
escreva aluno4 30 binárioio.write(reinterpret_cast<char*>
31 (&aluno4), sizeof(Aluno));
32
fechar arquivo 33 binárioio.close();
34
35 // Lê o aluno do arquivo
abrir arquivo de entrada 36 binárioio.open("aluno.dat", ios::in | ios::binary);
37
criar aluno 38 Aluno estudanteNovo;
39
ler do arquivo 40 binárioio.read(reinterpret_cast<char*>
41 (&alunoNovo), sizeof(Aluno));
42
exibir estudante 43 displayAluno(alunoNovo);
44
45 binárioio.read(reinterpret_cast<char*>
46 (&alunoNovo), sizeof(Aluno));
47
48 displayAluno(alunoNovo);
49
50 binárioio.close();
51
52 retornar 0;
53 }
Machine Translated by Google

13.8 Arquivo de Acesso Aleatório 533

John T Smith 90
Eric K Jones 85

O programa cria um objeto stream na linha 16, abre o arquivo student.dat para saída binária na linha 17, cria quatro objetos
Student nas linhas 19–22, grava-os no arquivo nas linhas 24–31 e fecha o arquivo na linha 33.

A instrução para escrever um objeto no arquivo é

binárioio.write(reinterpret_cast<char*>
(&aluno1), sizeof(Aluno));

O endereço do objeto student1 é convertido no tipo char*. O tamanho de um objeto é determinado pelos campos de dados do
objeto. Cada aluno tem o mesmo tamanho, que é sizeof(Student).
O programa abre o arquivo student.dat para entrada binária na linha 36, cria um Student
objeto usando seu construtor sem argumento na linha 38, lê um objeto Student do arquivo nas linhas 40 a 41 e exibe os dados
do objeto na linha 43. O programa continua a ler outro objeto (linhas 45 a 46) e exibe seus dados na linha 48.

Finalmente, o programa fecha o arquivo na linha 50.

13.12 O que é um arquivo de texto e o que é um arquivo binário? Você pode visualizar um arquivo de texto ou binário usando
um editor de texto? ÿVerificação de ponto

13.13 Como você abre um arquivo para E/S binária?

13.14 A função write pode escrever apenas um array de bytes. Como você escreve um primitivo-
digite valor ou um objeto em um arquivo binário?

13.15 Se você escrever a string "ABC" em um arquivo de texto ASCII, quais valores serão armazenados em um arquivo?

13.16 Se você escrever a string “100” em um arquivo de texto ASCII, quais valores serão armazenados em um arquivo?
Se você escrever um valor numérico do tipo byte 100 usando E/S binária, quais valores serão armazenados
em um arquivo?

13.8 Arquivo de acesso aleatório


As funções seekg() e seekp() podem ser usadas para mover o ponteiro do arquivo para qualquer posição em um
Chave
arquivo de acesso aleatório para entrada e saída. Apontar

Um arquivo consiste em uma sequência de bytes. Um marcador especial chamado ponteiro de arquivo é posicionado em um ponteiro de arquivo

desses bytes. Uma operação de leitura ou gravação ocorre no local do ponteiro do arquivo. Quando um arquivo é aberto, o
ponteiro do arquivo é colocado no início do arquivo. Quando você lê ou grava dados no arquivo, o ponteiro do arquivo avança
para o próximo item de dados. Por exemplo, se você ler um byte usando a função get() , C++ lerá um byte do ponteiro do
arquivo e agora o ponteiro do arquivo estará 1 byte à frente da localização anterior, conforme mostrado na Figura 13.5.

ponteiro de arquivo

arquivo
byte byte ... byte byte byte byte byte ... byte byte byte byte byte (a) Antes de get()

ponteiro de arquivo

arquivo
byte byte ... byte byte byte byte byte ... byte byte byte byte byte (b) Depois de obter ()

Figura 13.5 Após a leitura de um byte, o ponteiro do arquivo é movido um byte à frente.
Machine Translated by Google

534 Capítulo 13 Entrada e saída de arquivo

Todos os programas que você desenvolveu até agora leem/gravam dados sequencialmente. Isso é
arquivo de acesso sequencial chamado de arquivo de acesso sequencial. Ou seja, o ponteiro do arquivo sempre avança. Se um arquivo
estiver aberto para entrada, ele começará a ler os dados do início ao fim. Se um arquivo estiver aberto para
saída, ele grava os dados um item após o outro, do início ou do fim (com o modo de acréscimo ios::app).
O problema do acesso sequencial é que para ler um byte em um local específico, todos os bytes que o
precedem devem ser lidos. Isso não é eficiente. C++ permite que o ponteiro do arquivo retroceda ou avance
livremente usando as funções de membro seekp e seekg em um objeto de fluxo. Esse recurso é conhecido
arquivo de acesso aleatório como arquivo de acesso aleatório.
função de busca A função seekp (“seek put”) é para o fluxo de saída, e a função seekg (“seek get”) é para o fluxo de
função de busca entrada. Cada função possui duas versões com um ou dois argumentos. Com um argumento, o argumento é
a localização absoluta. Por exemplo,

entrada.busca(0);
saída.seekp(0);

move o ponteiro do arquivo para o início do arquivo.


Com dois argumentos, o primeiro argumento é um número inteiro longo que indica um deslocamento, e o
segundo argumento, conhecido como base de busca, especifica de onde calcular o deslocamento.
A Tabela 13.4 lista os três argumentos base de busca possíveis.

Tabela 13.4 Base de Busca

Procurar Base Descrição

ios::implorar Calcula o deslocamento desde o início do arquivo.


ios::fim Calcula o deslocamento a partir do final do arquivo.

ios::colocar Calcula o deslocamento do ponteiro do arquivo atual.

A Tabela 13.5 dá alguns exemplos de uso das funções seekp e seekg .

Tabela 13.5 Exemplos de busca e busca

Declaração Descrição

seekg(100, ios::beg); Move o ponteiro do arquivo para o 100º byte desde o início do arquivo.

procureg(-100, ios::fim); Move o ponteiro do arquivo para o 100º byte para trás a partir do final
do arquivo.

procurep(42,ios::cur); Move o ponteiro do arquivo para o 42º byte a partir do ponteiro do arquivo
atual.

procurep(-42, ios::cur); Move o ponteiro do arquivo para o 42º byte para trás em relação ao ponteiro do
arquivo atual.

buscap(100); Move o ponteiro do arquivo para o 100º byte do arquivo.

função contar Você também pode usar as funções Tellp e Tellg para retornar a posição do ponteiro do arquivo no
arquivo.
função contar
A Listagem 13.18 demonstra como acessar um arquivo aleatoriamente. O programa primeiro armazena 10
os objetos do aluno no arquivo e, em seguida, recupera o terceiro aluno do arquivo.

Listagem 13.18 RandomAccessFile.cpp


1 #include <iostream>
2 #include <fstream>
3 #include "Aluno.h"
4 usando namespace std;
5
Machine Translated by Google

13.8 Arquivo de Acesso Aleatório 535

6 exibição nula Aluno (const Aluno e aluno)


7{
cout << estudante.getFirstName() << cout << " ";
estudante.getMi() << " ";
8 cout << estudante.getLastName() << " ";
9 cout << estudante.getScore() << endl;
10 11 12 }
13
14 int principal()
15 { 16
17 fstream binárioio; // Cria objeto de fluxo
18 binárioio.open("aluno.dat", ios::out | ios::binary); abrir arquivo de saída
19
20 Aluno aluno1("Nome1", 'A', "Sobrenome1", 10); criar alunos
21 Aluno aluno2("Nome2", 'B', "Sobrenome2", 20);
22 Aluno aluno3("Nome3", 'C', "Sobrenome3", 30);
23 Aluno aluno4("Nome4", 'D', "Sobrenome4", 40);
24 Aluno aluno5("Nome5", 'E', "Sobrenome5", 50);
25 Aluno aluno6("Nome6", 'F', "Sobrenome6", 60);
26 Aluno aluno7("Nome7", 'G', "Sobrenome7", 70);
27 Aluno aluno8("Nome8", 'H', "Sobrenome8", 80);
28 Aluno aluno9("Nome9", 'Eu', "Sobrenome9", 90);
29 Aluno aluno10("Nome10", 'J', "Sobrenome10", 100);
30
31 binárioio.write(reinterpret_cast<char*> escreva para os alunos

32 (&aluno1), sizeof(Aluno));
33 binárioio.write(reinterpret_cast<char*>
34 (&aluno2), sizeof(Aluno));
35 binárioio.write(reinterpret_cast<char*>
36 (&aluno3), sizeof(Aluno));
37 binárioio.write(reinterpret_cast<char*>
38 (&aluno4), sizeof(Aluno));
39 binárioio.write(reinterpret_cast<char*>
40 (&aluno5), sizeof(Aluno));
41 binárioio.write(reinterpret_cast<char*>
42 (&aluno6), sizeof(Aluno));
binárioio.write(reinterpret_cast<char*>
43 (&aluno7), sizeof(Aluno));
44 binárioio.write(reinterpret_cast<char*>
45 (&aluno8), sizeof(Aluno));
46 binárioio.write(reinterpret_cast<char*>
47 (&aluno9), sizeof(Aluno));
48 binárioio.write(reinterpret_cast<char*>
49 (&aluno10), tamanhode(Aluno));
50
51 binárioio.close(); fechar arquivo

52
53 // Lê o aluno do arquivo
54 binárioio.open("aluno.dat", ios::in | ios::binary); abrir arquivo de entrada
55
56 Aluno estudanteNovo; criar aluno
57
58 binarioio.seekg(2 * sizeof(Aluno)); passar para o terceiro aluno
59
60 cout << "A posição atual é " << binaryio.tellg() << endl;
61
62 binárioio.read(reinterpret_cast<char*> leia estudante
63 (&alunoNovo), sizeof(Aluno));
64
65 displayAluno(alunoNovo); exibir estudante
66
Machine Translated by Google

536 Capítulo 13 Entrada e saída de arquivo

67 cout << "A posição atual é " << binaryio.tellg() << endl;
68
69 binárioio.close();
70
71 retornar 0;
72 }

A posição atual é 112


Nome3 C Sobrenome3 30
A posição atual é 168

O programa cria um objeto stream na linha 16, abre o arquivo student.dat para saída binária na linha 17,
cria dez objetos Student nas linhas 19–28, grava-os no arquivo nas linhas 30–49 e fecha o arquivo na linha
51.
O programa abre o arquivo student.dat para entrada binária na linha 54, cria um Student
objeto usando sua construção sem argumento na linha 56 e move o ponteiro do arquivo para o endereço do
terceiro aluno no arquivo na linha 58. A posição atual é agora 112. ( Observe que sizeof(Student) é 56.)
Depois o terceiro objeto é lido, o ponteiro do arquivo é movido para o quarto objeto. Portanto, a posição atual
passa a ser 168.

13.17 Qual é o ponteiro de arquivo?


ÿVerificação de ponto
13.18 Quais são as diferenças entre seekp e seekg?

13.9 Atualizando Arquivos


Você pode atualizar um arquivo binário abrindo um arquivo usando o modo ios::in | ios:fora |
Chave
Apontar ios::binário.

Muitas vezes você precisa atualizar o conteúdo do arquivo. Você pode abrir um arquivo para entrada e saída.
Por exemplo,

binárioio.open("aluno.dat", ios::in | ios::out | ios::binary);

Esta instrução abre o arquivo binário student.dat para entrada e saída.


A Listagem 13.19 demonstra como atualizar um arquivo. Suponha que o arquivo student.dat já tenha
sido criado com dez objetos Student da Listagem 13.18. O programa primeiro lê o segundo aluno do
arquivo, altera o sobrenome, grava o objeto revisado de volta no arquivo e lê o novo objeto de volta do
arquivo.

Listagem 13.19 UpdateFile.cpp


1 #include <iostream>
2 #include <fstream>
incluir arquivo de cabeçalho 3 #include "Aluno.h"
4 usando namespace std;

5 6 void displayStudent(const Aluno e aluno)


7{
cout << estudante.getFirstName() << cout << " ";
estudante.getMi() << " ";
8 cout << estudante.getLastName() << " ";
9 cout << estudante.getScore() << endl;
10 11 12 }
13
14 int principal()
Machine Translated by Google

Resumo do Capítulo 537

15 {
16 fstream binárioio; // Cria objeto de fluxo
17
18 //Abre arquivo para entrada e saída
19 binárioio.open("aluno.dat", ios::in | ios::out | ios::binary); entrada/saída aberta
20
21 Aluno aluno1; aluno1
22 binarioio.seekg(sizeof(Aluno)); passar para o segundo aluno
23 binárioio.read(reinterpret_cast<char*> leia aluno1
24 (&aluno1), sizeof(Aluno));
25 exibirAluno(aluno1); exibir aluno1
26
27 aluno1.setLastName("Yao");
28 binárioio.seekp(sizeof(Aluno));
29 binárioio.write(reinterpret_cast<char*> atualizar aluno1
30 (&aluno1), sizeof(Aluno));
31
32 Aluno aluno2; aluno2
33 binarioio.seekg(sizeof(Aluno)); passar para o segundo aluno
34 binárioio.read(reinterpret_cast<char*> leia aluno2
35 (&aluno2), sizeof(Aluno));
36 exibirAluno(aluno2); exibir aluno2
37
38 binárioio.close();
39
40 retornar 0;
41 }

Nome2 B Sobrenome2 20
Nome2 B Yao 20

O programa cria um objeto stream na linha 16 e abre o arquivo student.dat para entrada e saída binária na linha 19.

O programa passa para o segundo aluno no arquivo (linha 22) e lê o aluno (linhas 23–24), exibe-o (linha 25),
altera seu sobrenome (linha 27) e grava o objeto revisado de volta no arquivo (linhas 29–30).

O programa então passa para o segundo aluno no arquivo novamente (linha 33) e lê o aluno (linhas 34–35) e
o exibe (linha 36). Você verá que o sobrenome deste objeto foi alterado na saída de exemplo.

Termos chave
nome de arquivo absoluto 512 arquivo de acesso aleatório 534

arquivo binário 526 nome de arquivo relativo 512


modo de abertura de arquivo 522 arquivo de acesso sequencial 534
ponteiro de arquivo 533 estado de fluxo 524
fluxo de entrada 512 arquivo de texto 526

fluxo de saída 512

Resumo do capítulo

1. C++ fornece as classes ofstream, ifstream e fstream para facilitar a entrada de arquivos
e saída.

2. Você pode usar a classe ofstream para gravar dados em um arquivo, usar ifstream para ler dados de um
arquivo e usar a classe fstream para ler e gravar dados.
Machine Translated by Google

538 Capítulo 13 Entrada e saída de arquivo

3. Você pode usar a função open para abrir um arquivo, a função close para fechar um arquivo, a função
fail para testar se um arquivo existe, a função eof para testar se o final do arquivo foi atingido.

4. Os manipuladores de fluxo (por exemplo, setw, setprecision, fixo, showpoint, esquerda e


direita) podem ser usados para formatar a saída.

5. Você pode usar a função getline para ler uma linha de um arquivo, a função get para ler um caractere de
um arquivo e a função put para escrever um caractere em um arquivo.

6. Os modos de abertura de arquivo (iso::in, iso::out, iso::app, iso::truct e


iso::binary) pode ser usado para especificar como um arquivo é aberto.

7. A E/S de arquivo pode ser classificada em E/S de texto e E/S binária.

8. A E/S de texto interpreta dados em sequências de caracteres. A forma como o texto é armazenado em um
arquivo depende do esquema de codificação do arquivo. C++ executa automaticamente codificação e
decodificação para E/S de texto.

9. A E/S binária interpreta os dados como valores binários brutos. Para realizar E/S binária, abra o arquivo
usando o modo iso::binary .

10. Para saída binária, use a função write . Para entrada binária, use a função de leitura .

11. Você pode usar o operador reinterpret_cast para converter qualquer tipo de dados em uma matriz de
bytes para entrada e saída binária.

12. Você pode processar um arquivo sequencialmente ou de maneira aleatória.

13. As funções seekp e seekg podem ser usadas para mover o ponteiro de acesso ao arquivo para qualquer
lugar do arquivo antes de invocar as funções put/write e get/read .

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 13.2–13.6

*13.1 (Imprimir em um arquivo) Escreva um programa que leia caracteres de um arquivo chamado
Exercício13_1.txt , se existir. Se existir, o programa conta o número total de caracteres no
arquivo e imprime a contagem anexando-a ao mesmo arquivo.
*13.2 (Contar vogais) Escreva um programa que solicite ao usuário que insira um nome de arquivo e
exibe o número de vogais no arquivo.
*13.3 (Soma, média e produto dos números em um arquivo) Suponha que um arquivo de texto
Exercício13_3.txt contenha seis inteiros. Escreva um programa que leia números inteiros do
arquivo e exiba sua soma, média e produto. Os inteiros são separados por espaços em branco.
*13.4 (Classificar em ordem inversa/ Substituir) Suponha que um arquivo de texto Exercício13_4.txt contenha
20 números inteiros. Escreva um programa que leia os inteiros do arquivo, classifique os inteiros
na ordem inversa e substitua os números não classificados pelos números ordenados no arquivo.
Machine Translated by Google

Exercícios de Programação 539

*13.5 (Classificação de popularidade de nomes de bebês) A classificação de popularidade de nomes de bebês


dos anos 2001 a 2010 é baixada de www.ssa.gov/oact/Babynames e armazenada em arquivos
chamados Babynameranking2001.txt, Babynameranking2002.txt, . e . .,
Babynameranking2010.txt. Cada arquivo contém mil linhas. Cada linha contém uma classificação,
o nome do menino, o número do nome do menino, o nome da menina e o número do nome da
menina. Por exemplo, as duas primeiras linhas do arquivo Babynameranking2010.txt são as
seguintes:

1 Jacó 21.875 Isabel 22.731


doisEtã 17.866 Sofia 20.477

Portanto, o nome do menino Jacob e o nome da menina Isabella estão em primeiro lugar e o nome
do menino Ethan e o nome da menina Sophia estão em segundo lugar. 21.875 meninos se
chamam Jacob e 22.731 meninas se chamam Isabella. Escreva um programa que solicite ao
usuário que insira o ano e o gênero, seguidos de um nome, e exiba a classificação do nome para
o ano. Aqui está um exemplo de execução:

Digite o ano: 2010


Digite o gênero: M
Digite o nome: Javier
Javier está classificado em 190º lugar no ano de 2010

Digite o ano: 2010


Digite o gênero: F
Digite o nome: ABC
O nome ABC não está classificado no ano de 2010

*13.6 (Nome para ambos os sexos) Escreva um programa que solicite ao usuário que insira um dos nomes de
arquivo descritos no Exercício de Programação 13.5 e exiba os nomes usados para ambos os
sexos no arquivo. Aqui está um exemplo de execução:

Insira um nome de arquivo para classificação de nomes de bebês:


Babynameranking2001.txt 69 nomes
usados para ambos os sexos Eles são Tyler Ryan Christian ...

*13.7 (Classificar nomes sem duplicatas) Escreva um programa que leia os nomes dos 10 arquivos descritos
no Exercício de Programação 13.5, classifique todos os nomes (nomes de meninos e meninas
juntos, duplicatas removidas) e armazene os nomes classificados em um arquivo, 10 por linha.

*13.8 (Classificar nomes com duplicatas) Escreva um programa que leia os nomes dos 10 arquivos descritos
no Exercício de Programação 13.5, classifique todos os nomes (nomes de meninos e meninas
juntos, duplicatas são permitidas) e armazene os nomes classificados em um arquivo, 10 por linha.

*13.9 (Classificação cumulativa) Escreva um programa que obtenha a classificação cumulativa dos nomes nos
10 anos usando os dados dos 10 arquivos descritos no Exercício de Programação 13.5. Seu
programa deve exibir a classificação cumulativa para nomes de meninos e nomes de meninas
separadamente. Para cada nome, exiba sua classificação, nome e contagem cumulativa.

*13.10 (Remover classificação) Escreva um programa que solicite ao usuário que insira um dos nomes de
arquivo descritos no Exercício de Programação 13.5, leia os dados do arquivo e armazene os
dados em um novo arquivo sem as classificações. O novo arquivo é igual ao arquivo original,
exceto que não possui a classificação de cada linha. O novo arquivo é nomeado como arquivo de
entrada com a extensão .new.
Machine Translated by Google

540 Capítulo 13 Entrada e saída de arquivo

*13.11 (Dados classificados?) Escreva um programa que leia as strings do arquivo SortedStrings.
txt e informar se as strings nos arquivos estão armazenadas em ordem crescente. Se as strings não
estiverem classificadas no arquivo, exiba as duas primeiras strings que estão fora de ordem.

*13.12 (Resumo de classificação) Escreva um programa que use os arquivos descritos no Exercício de Programação
13.5 e exiba uma tabela de resumo de classificação para os primeiros cinco nomes de meninas e
meninos, como segue:

Classificação do ano 1 Classificação 2 Classificação 3 Classificação 4 Classificação 5 Classificação 1 Classificação 2 Classificação 3 Classificação 4 Classificação 5

2010 Isabella Sophia Emma Olivia Ava Jacob Ethan Michael Jayden William
2009 Isabella Emma Olivia Sofia Ava Jacob Ethan Miguel Alexandre William
...

Emilly 2001 Madison Hannah Ashley Alexis Jacob Michael Matthew Joshua Cristóvão

Seção 13.7
*13.13 (Escrita/ Leitura: arquivo de dados binários) Escreva um programa que crie um arquivo binário
Exercício13_13.dat e solicite ao usuário que grave dados nele. O programa então lê os dados
gravados no arquivo e os exibe de volta.

*13.14 (Armazenar objetos Loan ) Escreva um programa que crie cinco objetos Loan e os armazene em um arquivo
chamado Exercício13_14.dat. A classe Loan foi introduzida na Listagem 9.13.

*13.15 (Restaurar objetos de um arquivo) Suponha que um arquivo chamado Exercício13_15.dat tenha sido criado
a partir do exercício anterior. Escreva um programa que leia os objetos Loan do arquivo e calcule o
valor total do empréstimo. Suponha que você não saiba quantos objetos Loan existem no arquivo.
Use o eof() para detectar o final do arquivo.

*13.16 (Copiar arquivos) Listagem 13.7, CopyFile.cpp, copia arquivos usando E/S de texto. Revise o programa que
copia arquivos usando E/S binária. Aqui está um exemplo de execução do programa:

Insira um nome de arquivo de origem: c:\exercise.zip


Insira um nome de arquivo de destino: c:\exercise.bak
Copiar concluído

*13.17 (Dividir arquivos) Suponha que você queira fazer backup de um arquivo enorme (por exemplo, um arquivo
AVI de 10 GB) em um CD-R. Você pode fazer isso dividindo o arquivo em partes menores e fazendo
Nota de vídeo
backup dessas partes separadamente. Escreva um programa utilitário que divida um arquivo grande
Dividir um arquivo grande
em arquivos menores. Seu programa deve solicitar que o usuário insira um arquivo de origem e o
número de bytes em cada arquivo menor. Aqui está um exemplo de execução do programa:

Insira um nome de arquivo de origem: c:\exercise.zip


Insira o número de bytes em cada arquivo menor: 9343400 Arquivo c:
\exercise.zip.0 produzido Arquivo c:
\exercise.zip.1 produzido Arquivo c:
\exercise.zip .2 arquivo produzido c:
\exercise.zip.3 produzido Split Done

*13.18 (Combinar arquivos) Escreva um programa utilitário que combine os arquivos em um novo arquivo.
Seu programa deve solicitar que o usuário insira o número de arquivos de origem, o nome de cada
arquivo de origem e o nome do arquivo de destino. Aqui está um exemplo de execução do programa:
Machine Translated by Google

Exercícios de Programação 541

Insira o número de arquivos de origem: 4


Insira um arquivo de origem: c:\exercise.zip.0 Insira um
arquivo de origem: c:\exercise.zip.1 Insira um arquivo de
origem: c:\exercise.zip.2 Insira um arquivo de origem: c:
\exercise.zip .3 Insira um arquivo de destino: c:\temp.zip
Combinar Concluído

13.19 (Criptografar arquivos) Codifique o arquivo adicionando o índice de cada byte a esse byte, para
cada byte. Escreva um programa que solicite ao usuário que insira um nome de arquivo de
entrada e um nome de arquivo de saída e salve a versão criptografada no arquivo de saída.

13.20 (Descriptografar arquivos) Suponha que um arquivo seja criptografado usando o esquema do
Exercício de Programação 13.19. Decodifique o arquivo subtraindo o índice de cada byte
desse byte, para cada byte. Escreva um programa que leia o nome de um arquivo criptografado
e o nome de um arquivo de saída e salve a versão descriptografada no arquivo de saída.

***13.21 (Jogo: carrasco) Reescrever o Exercício de Programação 10.15. O programa lê as palavras


armazenadas em um arquivo de texto denominado Exercício13_21.txt. As palavras são
delimitadas por espaços. Dica: leia as palavras do arquivo e armazene-as em um vetor.

Seção 13.8

*13.22 (Contagem de atualizações) Suponha que você queira monitorar quantas vezes um programa foi
executado. Você pode armazenar um int para contar o arquivo. Aumente a contagem em 1
cada vez que este programa for executado. Deixe o programa ser Exercício13_22 e armazene
a contagem em Exercício13_22.dat.
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

CAPÍTULO

14
Operador
Sobrecarga

Objetivos
n Compreender a sobrecarga do operador e seus benefícios (§14.1).

n Definir a classe Rational para criação de números racionais (§14.2).

n Para descobrir como um operador pode ser sobrecarregado em C++ usando um


função (§14.3).

n Para sobrecarregar os operadores relacionais (<, <=, ==, !=, >=, >)
e operadores aritméticos (+, -, *, /) (§14.3).

n Para sobrecarregar o operador subscrito [] (§14.4).

n Para sobrecarregar os operadores de atribuição aumentada +=, -=, *= e /=


(§14.5).

n Para sobrecarregar os operadores unários + e - (§14.6).

n Para sobrecarregar os operadores prefix e postfix ++ e -- (§14.7).

n Para permitir que funções de amigos e classes de amigos acessem o


membros privados (§14.8).

n Para sobrecarregar os operadores de inserção e extração de fluxo << e >> como


funções de amigo não-membro (§14.9).

n Definir funções de operador para realizar conversões de objetos para um


tipo primitivo (§14.10.1).

n Definir construtores apropriados para realizar conversões de um valor numérico


para um tipo de objeto (§14.10.2).

n Para definir funções não membros para permitir conversões implícitas de tipo
(§14.11).

n Para definir uma nova classe Rational com operadores sobrecarregados (§14.12).

n Para sobrecarregar o operador = para realizar uma cópia profunda (§14.13).


Machine Translated by Google

544 Capítulo 14 Sobrecarga do Operador

14.1 Introdução
C++ permite definir funções para operadores. Isso é chamado de sobrecarga do operador.
Chave
Apontar
Nota de vídeo Na Seção 10.2.10, “Operadores de String”, você aprendeu como usar operadores para simplificar
O que é sobrecarga do operador? operações de string. Você pode usar o operador + para concatenar duas strings, os operadores relacionais
(==, !=, <, <=, >, >=) para comparar duas strings e o operador subscrito [] para acessar um caractere. Na
Seção 12.6, “A classe vetorial C++ ”, você aprendeu como usar o operador [] para acessar um elemento
em um vetor. Por exemplo, o código a seguir usa o operador [] para retornar um caractere de uma string
(linha 3), o operador + para combinar duas strings (linha 4), o operador < para comparar duas strings
(linha 5), o [] operador para retornar um elemento de um vetor (linha 10).

1 string s1("Washington");
2 strings s2("Califórnia");
[] operador 3 cout << "O primeiro caractere em s1 é " 4 cout << "s1 + s2 é << s1[0] << endl;
+ operador " 5 cout << "s1 < s2? << (s1 < s2) << << (s1 + s2) << endl;
"
< operador endl;

6 7 vetor<int> v;
8 v.push_back(3);
9 v.push_back(5);
[] operador 10 cout << "O primeiro elemento em v é " << v[0] << endl;

Os operadores são na verdade funções definidas em uma classe. Essas funções são nomeadas com
a palavra-chave operador seguida pelo operador real. Por exemplo, você pode reescrever o código
anterior usando a sintaxe da função como segue:

1 string s1("Washington");
2 strings s2("Califórnia");
[] função do operador 3 cout << "O primeiro caractere em s1 é " << endl; << s1.operador[](0)

+ função do operador 4 cout << "s1 + s2 é" 5 cout << "s1 << operador+(s1, s2) << endl;
"
< função do operador < s2? 6 7 vetor<int> v; << operador<(s1, s2) << endl;

8 v.push_back(3);
9 v.push_back(5);
[] função do operador 10 cout << "O primeiro elemento em v é " << v.operador[](0) << endl;

A função operador[] é uma função membro na classe string , e o vetor


classe e operador+ e operador< são funções não-membros na classe de string .
Observe que uma função membro deve ser invocada por um objeto usando a sintaxe objectName
.functionName(...), como s1.operator[](0). Obviamente, é mais intuitivo e conveniente usar a
sintaxe do operador s1[0] do que a sintaxe da função s1.operator[](0).
sobrecarga do operador Definir funções para operadores é chamado de sobrecarga de operador. Operadores como +, ==, !=,
<, <=, >, >= e [] estão sobrecarregados na classe de string . Como você sobrecarrega operadores em
suas classes personalizadas? Este capítulo usa a classe Rational como exemplo para demonstrar como
sobrecarregar uma variedade de operadores. Primeiro, você aprenderá como projetar uma classe
Rational para suportar operações com números racionais e depois sobrecarregar os operadores para
simplificar essas operações.

14.2 A Classe Racional


Esta seção define a classe Rational para modelagem de números racionais.
Chave
Apontar
Nota de vídeo Um número racional tem um numerador e um denominador na forma a/b, onde a é o numerador e b é o
A classe Racional denominador. Por exemplo, 1/3, 3/4 e 10/4 são números racionais.
Machine Translated by Google

14.2 A Classe Racional 545


Um número racional não pode ter denominador 0, mas um numerador 0 é adequado. Todo inteiro i é
equivalente a um número racional i/1. Os números racionais são usados em cálculos exatos envolvendo
frações — por exemplo, 1/3 = 0,33333.... Este número não pode ser representado com precisão no formato
de ponto flutuante usando o tipo de dados double ou float. Para obter o resultado exato, devemos usar
números racionais.
C++ fornece tipos de dados para inteiros e números de ponto flutuante, mas não para números racionais.
bers. Esta seção mostra como projetar uma classe para representar números racionais.
Um número Racional pode ser representado usando dois campos de dados: numerador e
denominador. Você pode criar um número Racional com numerador e denominador especificados ou criar
um número Racional padrão com numerador 0 e denominador 1. Você pode adicionar, subtrair, multiplicar,
dividir e comparar números racionais. Você também pode converter um número racional em um número
inteiro, valor de ponto flutuante ou string. O diagrama de classes UML para o Rational
classe é dada na Figura 14.1.

Racional

-numerador: int O numerador deste número racional.

-denominador: int O denominador deste número racional.

+Racional() Cria um número racional com numerador 0 e denominador 1.

+Racional(numerador: int, denominador: Cria um número racional com numerador especificado e


int) denominador.

+getNumerador(): int const Retorna o numerador deste número racional.

+getDenominator():int const Retorna o denominador deste número racional.

+adicionar(segundoRacional: Racional): Retorna a adição deste racional com outro.


Const racional

+subtrair(segundoRacional: Retorna a subtração deste racional por outro.


Racional): Const Racional

+multiplicar(segundoRacional: Retorna a multiplicação deste racional por outro.


Racional): Const Racional

+dividir(segundoRacional: Retorna a divisão deste racional por outro.


Racional): Const Racional

+compareTo(segundoRacional: Retorna um valor int 1, 0 ou 1 para indicar se este número racional é


Racional): int const menor, igual ou maior que o número especificado.

+igual(segundoRacional: Retorna verdadeiro se este número racional for igual ao número especificado.
Racional): bool const

+intValue(): int const Retorna o numerador/denominador.

+doubleValue(): const duplo Retorna o numerador/denominador 1,0*.

+toString(): string const Retorna uma string no formato “numerador/denominador”. Retorna o


numerador se o denominador for 1.

-gcd (n: int, d: int): int Retorna o máximo divisor comum entre n e d.

Figura 14.1 As propriedades, construtores e funções da classe Rational são ilustrados em UML.

Um número racional consiste em um numerador e um denominador. Existem muitos números racionais


equivalentes; por exemplo, 1/3 = 2/6 = 3/9 = 4/12. Por conveniência, usamos 1/3 para representar todos os
números racionais equivalentes a 1/3. O numerador e o denominador de 1/3 não têm divisor comum exceto
1, então diz-se que 1/3 está em termos mais baixos. termo mais baixo
Machine Translated by Google

546 Capítulo 14 Sobrecarga do Operador

Para reduzir um número racional aos seus termos mais baixos, você precisa encontrar o máximo divisor
comum (MDC) dos valores absolutos de seu numerador e denominador e depois dividir o numerador e o
denominador por esse valor. Você pode usar a função para calcular o MDC de dois inteiros n e d, conforme
sugerido na Listagem 6.4, GreatestCommonDivisor.cpp. O numerador e o denominador em um objeto
Rational são reduzidos aos seus termos mais baixos.
Como sempre, primeiro escrevemos um programa de teste para criar objetos Rational e testar as
funções na classe Rational . A Listagem 14.1 mostra o arquivo de cabeçalho da classe Rational e a
Listagem 14.2 é um programa de teste.

Listagem 14.1 Rational.h


incluir guarda 1 #ifndef RATIONAL_H
definir constante 2 #define RATIONAL_H
3 #incluir <string>
4 usando namespace std;

5 6 classe Racional
7{
membros públicos 8 público:
9 Racional();
10 Racional( numerador interno, denominador interno );
11 int getNumerator() const;
12 int getDenominator() const;
13 Racional add(const Rational& secondRational) const;
14 Subtração racional (const Racional e segundoRacional) const;
15 Multiplicação racional (const Racional e segundoRacional) const;
16 Divisão racional (const Rational& secondRational) const;
17 int compareTo(const Rational& secondRational) const;
18 bool é igual a (const Racional e segundoRacional) const;
19 int intValue() const;
20 double doubleValue() const;
21 string toString() const;

membros privados 22 23 privado:


24 numerador interno ;
25 denominador interno ;
função estática 26 estático int gcd(int n, int d);
27};
28
29 #endif

Listagem 14.2 TestRationalClass.cpp


1 #include <iostream>
incluir Racional 2 #include "Rational.h"
3 usando namespace std;

4 5 int principal()
6{
// Cria e inicializa dois números racionais r1 e r2
criar Racional 7 8 Racional r1(4, 2);
Racional r2(2, 3);
9
10 //Teste toString, adicione, subtraia, multiplique e divida
" " " = "
invocar toString 11 12 cout << r1.toString() << << r2.toString()+<< << r1.add(r2).toString() <<
invocar adicionar 13 endl;
" " " = "
14 cout << r1.toString() << << r2.toString()- << << r1.subtract(r2).toString()
invocar subtrair 15 << endl;
"*" " = "
16 cout << r1.toString() << << r2.toString() <<
Machine Translated by Google

14.2 A Classe Racional 547


17 << r1.multiply(r2).toString() << endl; invocar multiplicar
" "
18 cout << r1.toString() << " / " << r2.toString() << << r1.divide(r2).toString() =
19 << endl; invocar divisão
20
21 //Teste intValue e double
22 cout << "r2.intValue()" << "is" << r2.intValue() << endl; invocar intValue
23 cout << "r2.doubleValue()" << "is" << r2.doubleValue() << endl; invocar valor duplo
24
25 //Teste compareTo e igual
26 cout << "r1.compareTo(r2) é " << r1.compareTo(r2) << endl; invocar compararTo
27 cout << "r2.compareTo(r1) é " << r2.compareTo(r1) << endl;
28 cout << "r1.compareTo(r1) é " << r1.compareTo(r1) << endl;
29 cout << "r1.equals(r1) é" invocar igual
30 << (r1.equals(r1) ? "verdadeiro" : "falso") << endl;
31 cout << "r1.equals(r2) é"
32 << (r1.equals(r2) ? "verdadeiro" : "falso") << endl;
33
34 retornar 0;
35 }

2 + 2/3 = 8/3
2 - 2/3 = 4/3
2 * 2/3 = 4/3
2/2/3 = 3
r2.intValue() é 0
r2.doubleValue() é 0,666667
r1.compareTo(r2) é 1
r2.compareTo(r1) é -1
r1.compareTo(r1) é 0
r1.equals(r1) é verdadeiro
r1.equals(r2) é falso

A função principal cria dois números racionais, r1 e r2 (linhas 8–9), e exibe os


resultados de r1 + r2, r1 - r2, r1 x r2 e r1 / r2 (linhas 12–19). Para executar r1 + r2,
invoque r1.add(r2) para retornar um novo objeto Rational . Da mesma forma, r1.subtract(r2)
retorna um novo objeto Rational para r1 - r2, r1.multiply(r2) para r1 x r2 e r1.divide(r2) ,
para r1 / r2.
A função intValue() exibe o valor int de r2 (linha 22). O valor duplo()
função exibe o valor duplo de r2 (linha 23).
Invocar r1.compareTo(r2) (linha 26) retorna 1, pois r1 é maior que r2. Invocar
r2.compareTo(r1) (linha 27) retorna -1, já que r2 é menor que r1. Invocar r1.compareTo(r1)
(linha 28) retorna 0, pois r1 é igual a r1. Invocando r1.equals(r1)
(linha 29) retorna verdadeiro, pois r1 é igual a r1. Invocar r1.equals(r2) (linha 30) retorna
falso, pois r1 e r2 não são iguais.
A classe Rational é implementada na Listagem 14.3.

Listagem 14.3 Rational.cpp


1 #include "Rational.h" Cabeçalho racional
2 #include <sstream> // Usado em toString para converter números em strings
3 #include <cstdlib> // Para a função abs
4 Racional::Racional() construtor sem argumento
{
56 numerador = 0; inicializar campos de dados
7 denominador = 1;
Machine Translated by Google

548 Capítulo 14 Sobrecarga do Operador

8}
9
construtor 10 Racional::Racional( numerador interno, denominador interno )
11 {
inicializar campos de dados fator interno = mdc(numerador, denominador);
isto->numerador = ((denominador > 0) ? 1 : -1) * numerador / fator;
12 isto->denominador = abs(denominador) / fator;
13 14 15 }
16
17 int Rational::getNumerator() const
18 {
19 numerador de retorno ;
20}
21
22 int Rational::getDenominator() const
23 {
24 denominador de retorno ;
25}
26
27 // Encontre o MDC de dois números 28 int
mdc Rational::gcd(int n, int d) 29 {

30 int n1 = abs(n);
31 int n2 = abs(d);
32 int mdc = 1;
33
34 para (int k = 1; k <= n1 && k <= n2; k++)
35 {
36 se (n1% k == 0 && n2% k == 0)
37 mdc = k;
38 }
39
40 retornar mdc;
41 }
42
adicionar
43 Racional Racional::add(const Racional& segundoRacional) const
44 {
a c = anúncio + BC
b + d bd int n = numerador * segundoRational.getDenominator() +
45 denominador * segundoRational.getNumerator();
int d = denominador * segundoRational.getDenominator();
46 retornar Racional(n, d);
47 48 49 }
50
subtrair 51 Racional Racional::subtrair(const Racional& segundoRacional) const
52 {
a - c = anúncio - BC
b d bd int n = numerador * segundoRational.getDenominator()
- denominador * segundoRational.getNumerator();
int d = denominador * segundoRational.getDenominator();
53 retornar Racional(n, d);
54 55 56 57 }
58
multiplicar 59 Racional Racional::multiply(const Racional& segundoRacional) const
a c e
60 {
* =
b d bd 61 int n = numerador * segundoRational.getNumerator();
62 int d = denominador * segundoRational.getDenominator();
63 retornar Racional(n, d);
64}
65
dividir 66 Racional Racional::divide(const Racional& segundoRacional) const
Machine Translated by Google

14.2 A Classe Racional 549


67 {
a , c = de Anúncios

int n = numerador * segundoRational.getDenominator(); b d a.C.

int d = denominador * segundoRacional.numerador;


retornar Racional(n, d);
68 69 70 71}
72
73 int Rational::compareTo(const Rational& segundoRational) const compareTo
74 {
75 Temperatura racional = subtrair(segundoRacional);
76 if (temp.getNumerator() < 0)
77 retornar -1;
78 senão if (temp.getNumerator() == 0)
79 retornar 0;
80 outro
81 retornar 1;
82 }
83
84 bool Racional::equals(const Racional& segundoRacional) const é igual a
85 {
86 if (compareTo(secondRational) == 0)
retornar verdadeiro;
outro
87 retorna falso;
88 89 90 }
91
92 int Rational::intValue() const valorintt
93 {
94 return getNumerator() / getDenominator();
95}
96
97 duplo Rational::doubleValue() const valor duplo
98 {
99 return 1,0 * getNumerator() / getDenominator();
100}
101
102 string Rational::toString() const para sequenciar
103 {
104 stringstream ss;
105 ss << numerador;
106
107 se (denominador > 1)
108 ss << "/" << denominador;
109
110 retornar ss.str();
111 }

O número racional é encapsulado em um objeto Racional . Internamente, um número racional é


representado nos seus termos mais baixos (linhas 13–14), e o numerador determina o seu sinal (linha 13).
O denominador é sempre positivo (linha 14).
A função gcd() (linhas 28–41) é privada; não se destina ao uso por clientes. A função gcd() é apenas
para uso interno da classe Rational . A função gcd() também é estática, pois não depende de nenhum
objeto Rational específico .
A função abs(x) (linhas 30–31) é definida na biblioteca C++ padrão que retorna o
valor absoluto de x.
Dois objetos Rational podem executar operações de adição, subtração, multiplicação e divisão. Esses
funções retornam um novo objeto Rational (linhas 43–71).
A função compareTo(&secondRational) (linhas 73–82) compara este número racional com o outro
número racional. Primeiro subtrai o segundo racional deste racional
Machine Translated by Google

550 Capítulo 14 Sobrecarga do Operador

e salva o resultado em temp (linha 75). Retorne -1, 0 ou 1, se o numerador de temperatura for menor,
igual ou maior que 0.
A função equals(&secondRational) (linhas 84–90) utiliza a função compareTo para comparar este
número racional com o outro. Se esta função retornar 0, o valor é igual
função retorna verdadeiro; caso contrário, retornará falso.
As funções intValue e doubleValue retornam um valor int e um valor double , respectivamente, para
esse número racional (linhas 92 a 100).
A função toString() (linhas 102–111) retorna uma representação em string de um Rational
objeto na forma numerador/denominador ou simplesmente numerador se o denominador for 1. O fluxo
de string é usado aqui para converter um número em uma string, que foi introduzido na Seção 10.2.11,
“Convertendo números em strings”.

Dica
O numerador e o denominador são representados por meio de duas variáveis. Podemos representá-
los também usando um array de dois inteiros. Veja o Exercício de Programação 14.2. As assinaturas
das funções públicas da classe Racional não são alteradas, embora a representação interna de um
número racional seja alterada. Esta é uma boa ilustração da ideia de que os campos de dados de uma

encapsulamento classe devem ser mantidos privados para encapsular a implementação da classe a partir do uso da
classe.

14.3 Funções do Operador


A maioria dos operadores em C++ pode ser definida como funções para realizar operações
Chave
Apontar desejáveis.
Nota de vídeo
Sobrecarregue o operador < É conveniente comparar dois objetos string usando uma sintaxe intuitiva como

string1 < string2

Você pode comparar dois objetos Rational usando uma sintaxe semelhante à seguinte?

r1 < r2

como sobrecarregar operadores? Sim. Você pode definir uma função especial chamada função de operador na classe. A função do operador
é como uma função normal, exceto que deve ser nomeada com a palavra-chave operador
seguido pelo operador real. Por exemplo, o seguinte cabeçalho de função

operador bool<(const Racional& segundoRacional) const

define a função do operador < que retorna true se este objeto Rational for menor que secondRational.
Você pode invocar a função usando

r1.operador<(r2)

ou simplesmente

r1 < r2

operador de sobrecarga < Para usar este operador, você deve adicionar o cabeçalho da função para operator< na seção public
na Listagem 14.1 Rational.h e implementar a função no Rational.cpp na Listagem 14.3 da seguinte maneira:

operador de função 1 bool Rational::operator<(const Rational& secondRational) const


2 {
invocar compararTo // compareTo já está definido Rational.h
34 if (compareTo(secondRational) < 0) retorne
verdadeiro;
outro
retorna falso;
5678}
Machine Translated by Google

14.3 Funções do Operador 551

O seguinte código

Racional r1(4, 2);


Racional r2(2, 3);
cout << "r1 < r2 é " << (r1.operator<(r2) ? "true" : "false");
cout << "\nr1 < r2 é " << ((r1 < r2) ? "true" : "false");
cout << "\nr2 < r1 é " << (r2.operator<(r1) ? "true" : "false");

exibições

r1 <r2 é falso
r1 <r2 é falso
r2 <r1 é verdadeiro

Observe que r1.operator<(r2) é igual a r1 <r2. Este último é mais simples e, portanto, preferido.

C++ permite sobrecarregar os operadores listados na Tabela 14.1. A Tabela 14.2 mostra os quatro
operadores que não podem ser sobrecarregados. C++ não permite criar novos operadores. operadores sobrecarregáveis

Tabela 14.1 Operadores que podem estar sobrecarregados

+ - * / % ^ & ~ ! =
|
< > += -= *= /= %= ^= &= |= <<
>> >>= <<= == != <= >= && ++ --
||
->* , -> [] () novo excluir

Tabela 14.2 Operadores que não podem ser sobrecarregados

?: . .* ::

Observação

C++ define a precedência e associatividade do operador (consulte a Seção 3.15, “Precedência e precedência e associatividade
associatividade do operador”). Você não pode alterar a precedência e a associatividade do
operador sobrecarregando.

Observação

A maioria dos operadores são operadores binários. Alguns são unários. Você não pode alterar o número de operandos
número de operandos sobrecarregando. Por exemplo, o operador / divide é binário e ++ é unário.

Aqui está outro exemplo que sobrecarrega o operador binário + na classe Rational . Adicionar sobrecarga binária +
o seguinte cabeçalho de função em Rational.h na Listagem 14.1.

Operador racional + (const Racional e segundoRacional) const

Implemente a função em Rational.cpp na Listagem 14.3 da seguinte maneira:

1 Racional Racional::operador+(const Racional& segundoRacional) const + operador de função


2 {
3 // add já está definido Rational.h invocar
return add(segundoRacional);
4 5}
Machine Translated by Google

552 Capítulo 14 Sobrecarga do Operador

O seguinte código

Racional r1(4, 2);


Racional r2(2, 3);
cout << "r1 + r2 é" << (r1 + r2).toString() << endl;

exibições

r1 + r2 é 8/3

14.1 Como você define uma função de operador para sobrecarregar um operador?
ÿVerificação de ponto
14.2 Liste os operadores que não podem ser sobrecarregados.

14.3 Você pode alterar a precedência ou associatividade do operador por sobrecarga?

14.4 Sobrecarregando o Operador Subscrito []


O operador subscrito [] é comumente definido para acessar e modificar um campo de dados ou um
elemento em um objeto.

Em C++, o par de colchetes [] é chamado de operador subscrito. Você usou este operador para acessar
operador de subscrito elementos de array e os elementos em um objeto string e um objeto vetorial .
Você pode sobrecarregar este operador para acessar o conteúdo do objeto, se desejar. Por exemplo, você
pode desejar acessar o numerador e o denominador de um objeto Rational r usando r[0]
e r[1].
Primeiro damos uma solução incorreta para sobrecarregar o operador [] . Em seguida, identificaremos o
problema e daremos uma solução correta. Para permitir que um objeto Rational acesse seu numerador e
denominador usando o operador [] , defina o seguinte cabeçalho de função no arquivo de cabeçalho Rational.h:

operador int[]( índice int);

Implemente a função no Rational.cpp da seguinte forma:

[] operador de função 1 int Rational::operador[]( índice int) 2 3 4 parcialmente correta


{
numerador de acesso se (índice == 0)
numerador de retorno ;
denominador de acesso caso
contrário, retorne o denominador;
5 6 7}

O seguinte código

Racional r(2, 3);


cout << "r[0] é " << r[0] << endl;
cout << "r[1] é " << r[1] << endl;

exibições

r[0] é 2
r[1] é 3

Você pode definir um novo numerador ou denominador como uma atribuição de matriz como a seguinte?

r[0] = 5;
r[1] = 6;
Machine Translated by Google

14.4 Sobrecarregando o Operador Subscrito [] 553

Se você compilar, receberá o seguinte erro:

Lvalor necessário na função main()

Em C++, Lvalue (abreviação de valor esquerdo) refere-se a qualquer coisa que pode aparecer no lado Lvalor
esquerdo do operador de atribuição (=) e Rvalue (abreviação de valor direito) refere-se a qualquer coisa Rvalor
que pode aparecer no lado direito do operador de atribuição ( =). Como você pode transformar r[0] e r[1]
em um Lvalue para poder atribuir um valor a r[0] e r[1]? A resposta é que você pode definir o []
operador para retornar uma referência da variável.
Adicione o seguinte cabeçalho de função correto em Rational.h:

operador int& []( índice int);

Implemente a função no Rational.cpp:

int& Rational::operador[]( índice int) { Correto cabeçalho de função correto

se (índice == 0)
numerador de retorno ;
outro
denominador de retorno ;
}

Você está familiarizado com passagem por referência. Retorno por referência e passagem por retorno por referência
referência são o mesmo conceito. Na passagem por referência, o parâmetro formal e o parâmetro real são
aliases. No retorno por referência, a função retorna um alias para uma variável.
Nesta função, se o índice for 0, a função retorna um alias do numerador da variável. Se o índice for
1, a função retorna um alias do denominador da variável.
Observe que esta função não verifica os limites do índice. No Capítulo 16, você aprenderá como revisar
esta função para tornar seu programa mais robusto, lançando uma exceção se o índice não for 0 ou 1.

O seguinte código

1 Racional r(2, 3);


2r [0] = 5; // Define o numerador para 5 atribuir a r[0]
3r [1] = 6; //Define o denominador como 6 atribuir a r[1]
4 cout << "r[0] é " << r[0] << endl;
5 cout << "r[1] é " << r[1] << endl;
6 cout << "r.doubleValue() é " << r.doubleValue() << endl;

exibições

r[0] é 5
r[1] é 6
r.doubleValue() é 0,833333

Em r[0], r é um objeto e 0 é o argumento para a função membro []. Quando r[0] é usado como
expressão, ele retorna um valor para o numerador. Quando r[0] é usado no lado esquerdo do
operador de atribuição, é um alias para o numerador da variável. Então, r[0] = 5 atribui 5 ao
numerador.
O operador [] funciona como acessador e modificador. Por exemplo, você usa r[0] como acessador [] acessador e modificador
para recuperar o numerador em uma expressão e usa r[0] = value como modificador.
Por conveniência, chamamos um operador de função que retorna uma referência de operador Lvalue. Operador Lvalue
Vários outros operadores, como +=, -=, *=, /= e %= também são operadores Lvalue.
Machine Translated by Google

554 Capítulo 14 Sobrecarga do Operador

14.4 O que é um Lvalor? O que é um Rvalor?


ÿVerificação de ponto
14.5 Explique passagem por referência e retorno por referência.

14.6 Qual deve ser a assinatura da função para o operador [] ?

14.5 Sobrecarregando Operadores de Atribuição Aumentada


Você pode definir os operadores de atribuição aumentados como funções para retornar um
Chave
Apontar valor por referência.

C++ possui operadores de atribuição aumentados +=, -=, *=, /= e %= para adicionar, subtrair,
multiplicar, dividir e modular um valor em uma variável. Você pode sobrecarregar esses
operadores na classe Rational .
Observe que os operadores aumentados podem ser usados como Lvalues. Por exemplo, o código

interno x = 0;
(x += 2) += 3;

é legal. Portanto, os operadores de atribuição aumentada são operadores Lvalue e você deve sobrecarregá-los
para retornar por referência.
Aqui está um exemplo que sobrecarrega o operador de atribuição de adição +=. Adicione o cabeçalho da
função na Listagem 14.1, Rational.h.

Racional& operador+=(const Racional& segundoRacional)

Implemente a função na Listagem 14.3, Rational.cpp.

+= operador de função 1 Racional& Racional::operador+=(const Racional& segundoRacional)


{
adicionar ao objeto de chamada *isto = add(segundoRacional); retorne
retornar objeto de chamada *isto;
2345}

A linha 3 invoca a função add para incluir o objeto Rational de chamada com o segundo Rational
objeto. O resultado é copiado para o objeto chamador *this na linha 3. O objeto chamador é retornado na linha
4.
Por exemplo, o seguinte código

1 Racional r1(2, 4);


+= operador de função 2 Racional r2 = r1 += Racional(2, 3);
3 cout << "r1 é " << r1.toString() << endl;
4 cout << "r2 é " << r2.toString() << endl;

exibições

r1 é 7/6
r2 é 7/6

14.7 Quando você sobrecarrega um operador aumentado como +=, a função deve ser nula
ÿVerificação de ponto ou não vazio?

14.8 Por que as funções para operadores de atribuição aumentada deveriam retornar uma referência?
Machine Translated by Google

14.7 Sobrecarregando os Operadores ++ e –– 555

14.6 Sobrecarregando os Operadores Unários


Os operadores unários + e – podem estar sobrecarregados.
Chave
Apontar
Os + e - são operadores unários. Eles também podem estar sobrecarregados. Como o operador unário opera em
um operando que é o próprio objeto de chamada, o operador de função unário não possui parâmetros.

Aqui está um exemplo que sobrecarrega o operador - . Adicione o cabeçalho da função na Listagem 14.1,
Rational.h.

Operador racional- ()

Implemente a função na Listagem 14.3, Rational.cpp.

1 Racional Racional::operador-()
{
retornar Racional (-numerador, denominador); negar numerador
234} retornar objeto de chamada

Negar um objeto Rational é o mesmo que negar seu numerador (linha 3). A linha 4 retorna o objeto de chamada.
Observe que o operador de negação retorna um novo Rational. O próprio objeto de chamada não é alterado.

O seguinte código

1 Racional r2(2, 3);


2 Racional r3 = -r2; //Nega r2 unário - operador
3 cout << "r2 é " << r2.toString() << endl;
4 cout << "r3 é " << r3.toString() << endl;

exibições

r2 é 2/3
r3 é -2/3

14.9 Qual deve ser a assinatura da função para o operador unário + ?


ÿVerificação de ponto
14.10 Por que a seguinte implementação para o operador unário está errada ?

Racional Racional::operador-()
{
numerador *= -1;
retorne *isto;
}

14.7 Sobrecarregando os operadores ++ e ––


Os operadores de pré-incremento, pré-decremento, pós-incremento e pós-decremento podem
Chave
estar sobrecarregados. Apontar

Os operadores ++ e -- podem ser prefixos ou pós-fixados. O prefixo ++var ou --var primeiro adiciona ou subtrai 1
da variável e depois avalia o novo valor na var. O postfix var++
ou var-- adiciona ou subtrai 1 da variável, mas avalia o valor antigo na var.
Se ++ e –– forem implementados corretamente, o código a seguir

1 Racional r2(2, 3);


2 Racional r3 = ++r2; //Incremento de prefixo atribuir a r2[0]
3 cout << "r3 é " << r3.toString() << endl; atribuir a r2[1]
4 cout << "r2 é " << r2.toString() << endl;
Machine Translated by Google

556 Capítulo 14 Sobrecarga do Operador

5 6 Racional r1(2, 3);


7 Racional r4 = r1++; //Incremento pós-fixado
8 cout << "r1 é " << r1.toString() << endl;
9 cout << "r4 é " << r4.toString() << endl;

deve exibir

r3 é 5/3
r2 é 5/3
r1 é 5/3
r4 é 2/3 r4 armazena o valor original de r1

Como o C++ distingue os operadores de função prefixo ++ ou -- dos operadores de função postfix ++ ou
-- ? C++ define operadores de função postfix ++/-- com um parâmetro fictício especial do tipo int e define o
operador de função prefixo ++ sem parâmetros como segue:

operador prefixo ++ Racional& operador++();

operador postfix ++ Operador racional ++(int fictício)

Observe que os operadores prefixo ++ e –– são operadores Lvalue, mas os operadores postfix ++ e –– não
são. Essas funções de operador prefixo e postfix ++ podem ser implementadas da seguinte forma:

1 // Incremento de prefixo
2 Racional e Racional::operador++()
3{
a +1= um + b
b
numerador += denominador;
b retornar objeto de chamada retorne *isto;
456}
7
8 // Incremento pós-fixado
9 Racional Racional::operador++(int fictício)
10 {
criar temperatura 11 Temperatura racional (numerador, denominador);
a +1= um + b 12 numerador += denominador;
b b
retornar objeto temporário 13 temperatura de retorno ;
14}

Na função prefixo ++ , a linha 4 adiciona o denominador ao numerador. Este é o novo numerador para o
objeto de chamada após adicionar 1 ao objeto Rational . A linha 5 retorna o objeto de chamada.

Na função postfix ++ , a linha 11 cria um objeto Rational temporário para armazenar o objeto de chamada
original. A linha 12 incrementa o objeto de chamada. A linha 13 retorna o objeto de chamada original.

14.11 Qual deve ser a assinatura da função para o operador prefixo ++ ? para o postfix ++
ÿVerificação de ponto operador?

14.12 Suponha que você implemente o postfix ++ da seguinte forma

Racional Racional::operador++(int fictício)


{
Temperatura racional (*this);
adicionar(Racional(1, 0));
temperatura de retorno ;
}
Machine Translated by Google

14.8 Funções de amigos e classes de amigos 557

Esta implementação está correta? Em caso afirmativo, compare-o com a implementação no texto; qual é o
melhor?

14.8 Funções de amigos e classes de amigos


Você pode definir uma função de amigo ou uma classe de amigo para permitir o acesso a
Chave
membros privados em outra classe. Apontar

C++ permite sobrecarregar o operador de inserção de fluxo (<<) e o operador de extração de fluxo (>>).
Esses operadores devem ser implementados como funções amigas não membros. Esta seção apresenta
funções e classes de amigos para prepará-lo para sobrecarregar esses operadores.
Membros privados de uma classe não podem ser acessados de fora da classe. Ocasionalmente, é
conveniente permitir que algumas funções e classes confiáveis acessem os membros privados de uma classe.
C++ permite que você use a palavra-chave amigo para definir funções e classes amigas para que essas
funções e classes confiáveis possam acessar os membros privados de outra classe.
A Listagem 14.4 dá um exemplo que define uma classe amiga . turma de amigo

Listagem 14.4 Data.h


1 #ifndef DATA_H
2 #define DATE_H
3ª aula Data
4{
5 público:
6 Data( ano interno, mês interno , dia interno )
{
7 este->ano = ano;
8 este->mês = mês;
9 este->dia = dia;
10 }
11 12
13 classe amigo AccessDate; uma aula de amigo
14
15 privado:
16 ano interno ;
17 mês interno ;
18 dia interno ;
19};
20
21 #endif

A classe AccessDate (linha 4) é definida como uma classe amiga . Assim, você pode acessar diretamente
os campos de dados privados ano, mês e dia da classe AccessDate na Listagem 14.5.

Listagem 14.5 TestFriendClass.cpp


1 #include <iostream>
2 #include "Data.h" cabeçalho Data1.h
3 usando namespace std;

4 5 classe Data de acesso


6{
7 público:
8 vazio estático p() função estática
9{
10 DatanascimentoData(2010, 3, 4); criar uma data
11 datanascimento.ano = 2000; modificar dados privados
Machine Translated by Google

558 Capítulo 14 Sobrecarga do Operador

acessar dados privados cout <<datanascimento.ano << endl;


13
12}
14};
15
16 int principal()
17 {
invocar função estática 18 DatadeAcesso::p();
19
20 retornar 0;
21}

A classe AccessDate é definida nas linhas 5–14. Um objeto Date é criado na classe. Como AccessDate é
uma classe amiga da classe Date , os dados privados em um objeto Date podem ser acessados na classe
AccessDate (linhas 11–12). A função principal invoca a função estática AccessDate::p() na linha 18.

função de amigo A Listagem 14.6 dá um exemplo de como usar uma função de amigo . O programa define a classe Date
com uma função amiga p (linha 13). A função p não é membro da classe Date , mas pode acessar os dados
privados em Date. Na função p, um objeto Date é criado na linha 23, e o ano de dados do campo privado é
modificado na linha 24 e recuperado na linha 25.

Listagem 14.6 TestFriendFunction.cpp


1 #include <iostream>
2 usando namespace std;

3 4 aula Data
5{
6 público:
7 Data( ano interno, mês interno , dia interno )
{
este->ano = ano;
8 este->mês = mês;
9 este->dia = dia;
10 }
definir função de amigo 11 amigo vazio p();
12
13 14 15 privado:
16 ano interno ;
17 mês interno ;
18 dia interno ;
19};
20
21 vazio p()
22 {
Data data(2010, 5, 9);
modificar dados privados 23 data.ano = 2000;
acessar dados privados cout << data.ano << endl;
24 25 26 }

27 28 int principal()
29 {
invocar função de amigo p();
30
retornar 0;
31 32 33 }

14.13 Como você define uma função Friend para acessar os membros privados de uma classe?
ÿVerificação de ponto
14.14 Como você define uma classe amiga para acessar os membros privados de uma classe?
Machine Translated by Google

14.9 Sobrecarregando os Operadores << e >> 559

14.9 Sobrecarregando os Operadores << e >>


Os operadores de extração de fluxo (>>) e inserção (<<) podem ser sobrecarregados para executar
Chave
operações de entrada e saída. Apontar

Até agora, para exibir um objeto Rational , você invoca a função toString() para retornar uma representação de
sequência para o objeto Rational e, em seguida, exibir a sequência. Por exemplo, para exibir um objeto Rational r,
você escreve

cout << r.toString();

Não seria bom poder exibir um objeto Rational diretamente usando uma sintaxe como a seguinte?

cout << r;

O operador de inserção de fluxo (<<) e o operador de extração de fluxo (>>) são como outros operadores binários em
C++. cout << r é na verdade o mesmo que <<(cout, r) ou operador<<(cout, r).

Considere a seguinte afirmação:

r1 + r2;

O operador é + com dois operandos r1 e r2. Ambos são instâncias da classe Rational .
Portanto, você pode sobrecarregar o operador + como uma função membro com r2 como argumento. No entanto, para
a afirmação

cout << r;

o operador é << com dois operandos cout e r. O primeiro operando é uma instância da classe ostream , não da classe
Rational . Portanto, você não pode sobrecarregar o operador << como uma função membro na classe Rational . No por que função não-membro
entanto, é possível definir a função como uma função amiga da classe Rational no arquivo de cabeçalho Rational.h: para <<?

amigo ostream& operador<<(ostream& out, const Racional& racional);

Observe que esta função retorna uma referência de ostream, porque você pode usar o operador << em uma
cadeia de expressões. Considere a seguinte afirmação: cadeias de <<

cout << r1 << "seguido por" << r2;

Isto é equivalente a

((cout << r1) << " seguido de ") << r2;

Para que isso funcione, cout << r1 deve retornar uma referência de ostream. Portanto, a função << pode ser
implementada da seguinte forma:

operador ostream& <<(ostream& out, const Racional& racional)


{
out << racional.numerador << "/" << racional.denominador;
retornar ;
}

Da mesma forma, para sobrecarregar o operador >> , defina o seguinte cabeçalho de função no arquivo de
cabeçalho Rational.h:

amigo istream& operador>>(istream& in, Rational& racional);


Machine Translated by Google

560 Capítulo 14 Sobrecarga do Operador

Implemente esta função no Rational.cpp da seguinte forma:

Operador istream& >>(istream& in, Racional& racional)


{
cout << "Insira o numerador: ";
em >> racional.numerador;

cout << "Insira o denominador: ";


em >> racional.denominador;
retornar ;
}

O código a seguir fornece um programa de teste que usa os operadores de funções << e >> sobrecarregados .

1 Racional r1, r2;


2 cout << "Insira o primeiro número racional" << endl;
>> operador 3 comem >> r1;

4 5 cout << "Insira o segundo número racional" << endl;


>> operador 6 coma >> r2;
"
<< operador 7 8 cout << r1 << + " << r2 << " = "
<< r1 + r2 << fim;

Insira o primeiro número racional


Insira o numerador: 1
Insira o denominador: 2
Insira o segundo número racional
Insira o numerador: 3
Insira o denominador: 4
1/2 + 3/4 é 5/4

A linha 3 lê valores para um objeto racional de cin. Na linha 8, r1 + r2 é avaliado como um novo número racional, que
é então enviado para cout.

14.15 Qual deve ser a assinatura da função para o operador << ? para o operador >> ?
ÿVerificação de ponto
14.16 Por que os operadores << e >> deveriam ser definidos como funções não membros?

14.17 Suponha que você sobrecarregue o operador << da seguinte forma:

operador ostream& <<(ostream& stream, const Racional& racional)


{
fluxo << racional.getNumerator() << << "/"
racional.getDenominator();
fluxo de retorno ;
}

Você ainda precisa definir

amigo ostream& operador<<(ostream& stream, Rational& racional)

na classe Racional ?
Machine Translated by Google

14.10 Conversões Automáticas de Tipo 561

14.10 Conversões Automáticas de Tipo


Você pode definir funções para realizar a conversão automática de um objeto para um valor
Chave
de tipo primitivo e vice-versa. Apontar

C++ pode realizar certas conversões de tipo automaticamente. É possível definir funções para ativar conversões
de um objeto Rational para um valor de tipo primitivo ou vice-versa.

14.10.1 Convertendo para um tipo de dados primitivo


Você pode adicionar um valor int com um valor duplo , como

4 + 5,5

Nesse caso, C++ realiza conversão automática de tipo para converter um valor int 4 em um valor duplo 4,0.

Você pode adicionar um número racional com um valor int ou double ? Sim. Você deve definir um operador
de função para converter um objeto em int ou double. Aqui está a implementação da função para converter
um objeto Rational em um valor duplo .

Racional::operador duplo()
{
return valorduplo(); // doubleValue() já em Rational.h
}

Não esqueça que é necessário incluir o cabeçalho da função de membro no arquivo de cabeçalho Rational.h.

operador duplo();

Esta é uma sintaxe especial para definir funções de conversão para um tipo primitivo em C++. Não existe sintaxe da função de conversão
um tipo de retorno como um construtor. O nome da função é o tipo para o qual você deseja que o objeto seja
convertido.
Então, o seguinte código

1 Racional r1(1, 4);


2 duplo d = r1 + 5,1; 3 cout << "r1 adicionar racional com duplo
+ 5.1 é" << d << endl;

exibições

r1 + 5,1 é 5,35

A afirmação na linha 2 adiciona um número racional r1 com um valor duplo 5,1. Como a função
de conversão é definida para converter um número racional em duplo, r1 é convertido em um
valor duplo 0,25, que é então adicionado a 5,1.

14.10.2 Convertendo para um tipo de objeto


Um objeto Rational pode ser convertido automaticamente em um valor numérico. Um valor numérico pode ser
convertido automaticamente em um objeto Rational ? Sim pode.
Para conseguir isso, defina o seguinte construtor no arquivo de cabeçalho:

Racional( numerador interno);


Machine Translated by Google

562 Capítulo 14 Sobrecarga do Operador

e implemente-o no arquivo de implementação da seguinte forma:

Racional::Racional( numerador interno)


{
isto->numerador = numerador;
isto->denominador = 1;
}

Desde que o operador + também esteja sobrecarregado (consulte a Seção 14.3), o código a seguir

Racional r1(2, 3);


Racional r = r1 + 4; // Convertendo automaticamente 4 para Rational
cout << r << endl;

exibições

14/3

Quando C++ vê r1 + 4, ele primeiro verifica se o operador + foi sobrecarregado para adicionar um Rational com um
número inteiro. Como essa função não está definida, o sistema procura em seguida o operador + para adicionar um
Rational a outro Rational. Como 4 é um número inteiro, C++ usa o construtor que constrói um objeto Rational a partir
de um argumento inteiro. Em outras palavras, C++ executa uma conversão automática para converter um número
inteiro em um objeto Rational . Esta conversão automática é possível porque o construtor adequado está disponível.
Agora dois Racionais
objetos são incluídos usando o operador + sobrecarregado para retornar um novo objeto Rational (14/3 ).
Uma classe pode definir a função de conversão para converter um objeto em um valor de tipo primitivo ou definir
um construtor de conversão para converter um valor de tipo primitivo em um objeto, mas não ambos simultaneamente
na classe. Se ambos estiverem definidos, o compilador reportará um erro de ambiguidade.

14.18 Qual deve ser a assinatura da função para converter um objeto para o tipo int ?
ÿVerificação de ponto
14.19 Como você converte um valor de tipo primitivo em um objeto?

14.20 Uma classe pode definir a função de conversão para converter um objeto em um valor de tipo primitivo e
definir um construtor de conversão para converter um valor de tipo primitivo em um objeto
simultaneamente na classe?

14.11 Definindo funções não membros para


operadores de sobrecarga
Se um operador puder ser sobrecarregado como uma função não-membro, defina-o como uma função não-
Chave
Apontar membro para permitir conversões implícitas de tipo.

C++ pode realizar certas conversões de tipo automaticamente. Você pode definir funções para permitir conversões.

Você pode adicionar um objeto Rational r1 com um número inteiro como este:

r1 + 4

Você pode adicionar um número inteiro com um objeto Rational r1 assim?

4 + r1

Naturalmente você pensaria que o operador + é simétrico. Porém, não funciona, porque o operando esquerdo é o
objeto de chamada do operador + e o operando esquerdo deve ser um Rational
Machine Translated by Google

14.12 A Classe Racional com Operadores de Função Sobrecarregados 563

objeto. Aqui, 4 é um número inteiro, não um objeto Rational . C++ não realiza conversão automática neste caso. Para
contornar esse problema, execute as duas etapas a seguir:

1. Defina e implemente o construtor a seguir, conforme discutido na seção anterior.

Racional( numerador interno);

Este construtor permite que o número inteiro seja convertido em um objeto Rational .

2. Defina o operador + como uma função não-membro no arquivo de cabeçalho Rational.h conforme a seguir:

Operador racional + (const Racional & r1, const Racional & r2)

Implemente a função no Rational.cpp da seguinte forma:

1 Operador Racional +(const Racional& r1, const Racional& r2) 2 3 4 } +operador de função
{
retornar r1.add(r2); invocar adicionar

A conversão automática de tipo para o objeto definido pelo usuário também funciona para operadores de comparação
(<, <=, ==, !=, >, >=).
Observe que os exemplos para o operador< e o operador+ são definidos como funções de membro
na Seção 14.3. De agora em diante, iremos defini-las como funções não membros.

14.21 Por que é preferível definir uma função não membro para um operador?
ÿVerificação de ponto

14.12 A Classe Racional com Operadores de


Função Sobrecarregados
Esta seção revisa a classe Rational com operadores de função sobrecarregados.
Chave
Apontar
As seções anteriores introduziram como sobrecarregar operadores de função. Os seguintes pontos são dignos de nota:

n As funções de conversão de um tipo de classe para um tipo primitivo ou de um tipo primitivo para um tipo de
classe não podem ser definidas na mesma classe. Fazer isso causaria erros de ambiguidade, porque o
compilador não pode decidir qual conversão executar. Freqüentemente, a conversão de um tipo primitivo para
um tipo de classe é mais útil. Portanto, definiremos nossa classe Rational para suportar a conversão
automática de um tipo primitivo para o tipo Rational . conversão automática de tipo

n A maioria dos operadores pode ser sobrecarregada como funções membros ou não membros. No entanto, os membro versus não membro
operadores =, [], -> e () devem ser sobrecarregados como funções-membro e os operadores << e >> devem
ser sobrecarregados como funções não-membros.

n Se um operador (ou seja, +, -, *, /, %, <, <=, ==, !=, >, e >=) puder ser implementado como uma função membro
ou não-membro, é melhor sobrecarregar é uma função não-membro para permitir a conversão automática preferência de não-membro
de tipo com operandos simétricos.

n Se você deseja que o objeto retornado seja usado como um Lvalue (ou seja, usado no Lvalor

lado esquerdo da instrução de atribuição), você precisa definir a função para retornar
uma referência. Os operadores de atribuição aumentados +=, -=, *=, /= e %=, os
operadores de prefixo ++ e –– , o operador de subscrito [] e os operadores de atribuição
= são operadores Lvalue.

A Listagem 14.7 fornece um novo arquivo de cabeçalho denominado RationalWithOperators.h para a classe Rational
com operadores de função. As linhas 10–22 no novo arquivo são iguais às da Listagem 14.1 Rational.h.
As funções para operadores de atribuição aumentada (+=, -=, *=, /=), operador subscrito [],
Machine Translated by Google

564 Capítulo 14 Sobrecarga do Operador

prefix ++ e prefixo -- são definidos para retornar uma referência (linhas 27–37). Os operadores de
extração de fluxo << e inserção de fluxo >> são definidos nas linhas 48–49. As funções não membros
para operadores de comparação (<, <=, >, >=, ==, !=) e operadores aritméticos (+, -, *, /) são definidas
nas linhas 57–69.

Listagem 14.7 RationalWithOperators.h


1 #ifndef RATIONALWITHOPERATORS_H
2 #define RATIONALWITHOPERATORS_H
3 #incluir <string>
4 #include <iostream>
5 usando namespace std;

6 7 classe Racional
8{
9 público:
10 Racional();
11 Racional( numerador interno, denominador interno );
12 int getNumerator() const;
13 int getDenominator() const;
14 Racional add(const Rational& secondRational) const;
15 Subtração racional (const Racional e segundoRacional) const;
16 Multiplicação racional (const Racional e segundoRacional) const;
17 Divisão racional (const Rational& secondRational) const;
18 int compareTo(const Rational& secondRational) const;
19 bool é igual a (const Racional e segundoRacional) const;
20 int intValue() const;
21 double doubleValue() const;
22 string toString() const;
23
construtor para 24 Racional( numerador interno); // Adequado para conversão de tipo
conversão de tipo 25
26 // Define operadores de função para operadores aumentados
operadores aumentados 27 Racional& operador+=(const Racional& segundoRacional);
28 Racional& operador-=(const Racional& segundoRacional);
29 Racional& operador*=(const Racional& segundoRacional);
30 Racional& operador/=(const Racional& segundoRacional);
31
32 // Definir operador de função []
operador de subscrito 33 operador int& []( índice int);
34
35 // Define operadores de função para prefixo ++ e --
operador prefixo ++ 36 Racional& operador++();
prefixo - operador 37 Operador & racional --();
38
39 // Define operadores de função para postfix ++ e --
operador postfix ++ 40 Operador racional ++(int fictício);
postfix – operador 41 Operador racional --(int dummy);
42
43 // Define operadores de função para + e - unários
unário + operador 44 Operador racional +();
45 Operador racional- ();
46
47 //Define os operadores << e >>
<< operador 48 amigo ostream& operador<<(ostream& , amigo const Racional&);
>> operador 49 istream& operador>>(istream& , Rational&);
50
51 privado:
52 numerador interno ;
Machine Translated by Google

14.12 A Classe Racional com Operadores de Função Sobrecarregados 565

53 denominador interno ;
estático int gcd(int n, int d);
54 55};

56 57 // Definir operadores de função não membros para operadores relacionais


58 operador bool<(const Rational& r1, const Rational& r2); funções de não membros
59 operador bool <=(const Rational& r1, const Rational& r2);
60 operador bool>(const Rational& r1, const Rational& r2);
61 operador bool >=(const Rational& r1, const Rational& r2);
62 operador bool ==(const Rational& r1, const Rational& r2);
63 operador bool !=(const Racional& r1, const Racional& r2);
64
65 // Definir operadores de função não membros para operadores aritméticos
66 Operador Racional+(const Racional& r1, const Racional& r2); funções de não membros
67 Operador Racional - (const Rational& r1, const Rational& r2);
68 Operador Racional*(const Rational& r1, const Rational& r2);
69 Operador racional/(const Rational& r1, const Rational& r2);
70
71 #endif

A Listagem 14.8 implementa o arquivo de cabeçalho. As funções-membro para operadores de


atribuição aumentada +=, -=, *= e /= alteram o conteúdo do objeto de chamada (linhas 120–142).
Você tem que atribuir o resultado da operação a isso. Os operadores de comparação são
implementados invocando r1.compareTo(r2) (linhas 213–241). Os operadores aritméticos +, -, *
e / são implementados invocando as funções de adição, subtração, multiplicação e divisão (linhas 244–262).

Listagem 14.8 RationalWithOperators.cpp


1 #include "RationalWithOperators.h" incluir cabeçalho
2 #include <sstream>
3 #include <cstdlib> // Para a função abs
4 Racional::Racional()
{
numerador = 0;
denominador = 1;
5678}
9
10 Racional::Racional( numerador interno, denominador interno )
11 {
12 fator interno = mdc(numerador, denominador);
13 isto->numerador = (denominador > 0 ? 1 : -1) * numerador / fator;
14 isto->denominador = abs(denominador) / fator;
15 }
16
17 int Rational::getNumerator() const
18 {
19 numerador de retorno ;
20}
21
22 int Rational::getDenominator() const
23 {
24 denominador de retorno ;
25}
26
27 // Encontre o MDC de dois números
28 int Rational::gcd(int n, int d) 29 {

30 int n1 = abs(n);
31 int n2 = abs(d);
Machine Translated by Google

566 Capítulo 14 Sobrecarga do Operador

32 int mdc = 1;
33
34 para (int k = 1; k <= n1 && k <= n2; k++)
35 {
36 se (n1% k == 0 && n2% k == 0)
37 mdc = k;
38 }
39
40 retornar mdc;
41 }
42
43 Racional Racional::add(const Racional& segundoRacional) const
44 {
int n = numerador * segundoRational.getDenominator() +
45 denominador * segundoRational.getNumerator();
int d = denominador * segundoRational.getDenominator();
46 retornar Racional(n, d);
47 48 49 }
50
51 Racional Racional::subtrair(const Racional& segundoRacional) const
52 {
int n = numerador * segundoRational.getDenominator()
- denominador * segundoRational.getNumerator();
int d = denominador * segundoRational.getDenominator();
53 retornar Racional(n, d);
54 55 56 57 }
58
59 Racional Racional::multiply(const Racional& segundoRacional) const
60 {
61 int n = numerador * segundoRational.getNumerator();
int d = denominador * segundoRational.getDenominator();
retornar Racional(n, d);
62 63 64}
65
66 Racional Racional::divide(const Racional& segundoRacional) const
67 {
68 int n = numerador * segundoRational.getDenominator();
69 int d = denominador * segundoRacional.numerador;
70 retornar Racional(n, d);
71}
72
73 int Rational::compareTo(const Rational& secondRational) const
74 {
75 Temperatura racional = subtrair(segundoRacional);
76 if (temp.getNumerator() < 0)
77 retornar -1;
78 senão if (temp.getNumerator() == 0)
79 retornar 0;
80 outro
81 retornar 1;
82 }

83 84 bool Racional::equals(const Racional& segundoRacional) const


85 {
86 if (compareTo(secondRational) == 0)
retornar verdadeiro;
outro
87 retorna falso;
88 89 90 }
91
Machine Translated by Google

14.12 A Classe Racional com Operadores de Função Sobrecarregados 567

92 int Rational::intValue() const


93 {
94 return getNumerator() / getDenominator();
95}
96
97 duplo Rational::doubleValue() const
98 {
99 return 1,0 * getNumerator() / getDenominator();
100}
101
102 string Rational::toString() const
103 {
104 stringstream ss;
105 ss << numerador;
106
107 se (denominador > 1)
108 ss << "/" << denominador;
109
110 retornar ss.str();
111 }
112
113 Rational::Rational(int numerator) // Adequado para conversão de tipo construtor
114 {
115 isto->numerador = numerador;
116 isto->denominador = 1;
117}
118
119 // Definir operadores de função para operadores aumentados
120 Racional& Racional::operador+=(const Racional& segundoRacional) operadores de atribuição
121 { aumentada
122 *isto = add(segundoRacional);
123 retorne *isto;
124}
125
126 Racional& Racional::operador-=(const Racional& segundoRacional)
127 {
128 *isto = subtrair(segundoRacional);
129 retorne *isto;
130}
131
132 Racional& Racional::operador*=(const Racional& segundoRacional)
133 {
134 *isto = multiplicar(segundoRacional);
135 retorne *isto;
136}
137
138 Racional& Racional::operador/=(const Racional& segundoRacional)
139 {
140 *this = divide(segundoRacional);
141 retorne *isto;
142}
143
144 // Definir operador de função []
145 int& Rational::operador[]( índice int ) [] operador
146 {
147 se (índice == 0)
148 numerador de retorno ;
149 outro
150 denominador de retorno ;
151 }
Machine Translated by Google

568 Capítulo 14 Sobrecarga do Operador

152
153 // Definir operadores de função para prefixo ++ e --
prefixo ++ 154 Racional& Racional::operador++()
155 {
156 numerador += denominador;
157 retorne *isto;
158}
159
160 Racional e Racional::operador--()
161 {
162 numerador -= denominador;
163 retorne *isto;
164}
165
166 // Define operadores de função para postfix ++ e --
postfix ++ 167 Rational Rational::operator++(int fictício)
168 {
169 Temperatura racional (numerador, denominador);
170 numerador += denominador;
171 temperatura de retorno ;
172 }
173
174 Racional Racional::operador--(int fictício)
175 {
176 Temperatura racional (numerador, denominador);
177 numerador -= denominador;
178 temperatura de retorno ;
179 }
180
181 // Definir operadores de função para + e - unários
unário + operador 182 Racional Racional ::operador+()
183 {
184 retorne *isto;
185}
186
187 Racional Racional::operador-()
188 {
189 retornar Racional (-numerador, denominador);
190}
191
192 // Defina o operador de saída e entrada
<< operador 193 operador ostream& <<(ostream& out, const Racional& racional)
194 {
195 se (racional.denominador == 1)
196 fora << racional.numerador;
197 outro
198 out << racional.numerador << "/" << racional.denominador;
199 retornar ;
200 }
201
202 istream& operador>>(istream& in, Rational& racional)
203 {
204 cout << "Insira o numerador: ";
205 em >> racional.numerador;
206
207 cout << "Insira o denominador: ";
208 em >> racional.denominador;
209 retornar ;
210 }
211
Machine Translated by Google

14.12 A Classe Racional com Operadores de Função Sobrecarregados 569

212 // Definir operadores de função para operadores relacionais


213 operador bool<(const Rational& r1, const Rational& r2) operadores relacionais
214 {
215 retornar r1.compareTo(r2) < 0;
216}
217
218 operador bool <=(const Rational& r1, const Rational& r2)
219 {
220 retornar r1.compareTo(r2) <= 0;
221}
222
223 operador bool>(const Rational& r1, const Rational& r2)
224 {
225 retornar r1.compareTo(r2) > 0;
226}
227
228 operador bool >=(const Rational& r1, const Rational& r2)
229 {
230 retornar r1.compareTo(r2) >= 0;
231}
232
233 operador bool ==(const Racional& r1, const Racional& r2)
234 {
235 retornar r1.compareTo(r2) == 0;
236}
237
238 operador bool !=(const Racional& r1, const Racional& r2)
239 {
240 retornar r1.compareTo(r2) != 0;
241}
242
243 // Definir operadores de função não membros para operadores aritméticos
244 Operador Racional+(const Racional& r1, const Racional& r2) operadores aritméticos
245 {
246 retornar r1.add(r2);
247}
248
249 Operador Racional - (const Rational& r1, const Rational& r2)
250 {
251 retornar r1.subtrair(r2);
252}
253
254 Operador Racional*(const Rational& r1, const Rational& r2)
255 {
256 retornar r1.multiply(r2);
257}
258
259 Operador Racional/(const Rational& r1, const Rational& r2)
260 {
261 retornar r1.divide(r2);
262}

A Listagem 14.9 fornece um programa para testar a nova classe Rational .

Listagem 14.9 TestRationalWithOperators.cpp


1 #include <iostream>
2 #incluir <string>
3 #include "RationalWithOperators.h" incluir novo Rational
4 usando namespace std;
Machine Translated by Google

570 Capítulo 14 Sobrecarga do Operador

5 6 int principal()
7{
// Cria e inicializa dois números racionais r1 e r2.
8 9 Racional r1(4, 2);
10 Racional r2(2, 3);
11
12 // Testa operadores relacionais
" "
13 cout << r1 << << r2 <<>"é" ((r1 > r2) ? <<
operador relacional 14 "verdadeiro" : "falso") << endl;
" < " << r2 << <<
15 cout << r1 << "é" ((r1 <
16 r2) ? "verdadeiro" : "falso") << endl;
" "
17 cout << r1 << << r2 <<=="é" ((r1 == r2) ? <<
18 "verdadeiro" : "falso") << endl;
" " <<
19 cout << r1 << << r2 <<!="é" ((r1 != r2) ?
20 "verdadeiro" : "falso") << endl;
21
22 // Testa toString, soma, subtrai, multiplica e divide operadores
" + " << r2 << " = "
operador aritmético 23 cout << r1 << << r1 + r2 << fim;
" - " << r2 << " = "
24 cout << r1 << << r1 - r2 << fim;
"*" << r2 << " = "
25 cout << r1 << << r1 * r2 << endl;
<< r2 << " = "
26 cout << r1 << "/" << r1 / r2 << endl;
27
28 // Testa operadores aumentados
29 Racional r3(1, 2);
30 r3 += r1;
31 cout << "r3 é" << r3 << fim;
32
33 // Operador de função de teste []
34 Racional r4(1, 2);
operador subscrito [] 35 r4[0] = 3; r4[1] = 4;
36 cout << "r4 é " << r4 << endl;
37
38 // Testa operadores de função para prefixo ++ e --
postfix ++ 39 r3 = r4++;
40 cout << "r3 é " cout << r3 << fim;
41 << "r4 é " << r4 << fim;
42
43 // Operador de função de teste para conversão
"
conversão de tipo 44 cout << "1 + " é " <<<<
(1 r4 << << endl;
+ r4)
45
46 retornar 0;
47 }

2 > 2/3 é verdade


2 <2/3 é falso
2 == 2/3 é falso
2! = 2/3 é verdade
2 + 2/3 = 8/3
2 - 2/3 = 4/3
2 * 2/3 = 4/3
2/2/3 = 3
r3 é 5/2
r4 é 3/4
r3 é 3/4
r4 é 7/4
1 + 7/4 é 11/4
Machine Translated by Google

14.13 Sobrecarregando os Operadores = 571

14.22 O operador [] pode ser definido como uma função não membro?
ÿVerificação de ponto
14.23 O que há de errado se a função + for definida da seguinte forma:

Operador racional + (const Racional & r1, const Racional & r2) const

14.24 Se você remover o construtor Rational(int numerator) de ambos


RationalWithOperators.h e RationalWithOperators.cpp, haverá um erro de compilação na linha 44
em TestRationalWithOperators.cpp? Qual será o erro?

14.25 A função mdc na classe Rational pode ser definida como uma função constante?

14.13 Sobrecarregando os Operadores =


Você precisa sobrecarregar o operador = para executar uma operação de cópia personalizada
Chave
para um objeto. Apontar

Por padrão, o operador = executa uma cópia entre membros de um objeto para outro. Por exemplo, o código a
seguir copia r2 para r1.

1 Racional r1(1, 2);


2 Racional r2(4, 5);
3r1 =r2; copie r2 para r1
4 cout << "r1 é" 5 cout << << r1 << fim;
"r2 é" << r2 << fim;

Então, a saída é

r1 é 4/5
r2 é 4/5

O comportamento do operador = é o mesmo do construtor de cópia padrão. Ele executa uma cópia superficial,
o que significa que se o campo de dados for um ponteiro para algum objeto, o endereço do ponteiro será copiado cópia superficial
em vez de seu conteúdo. Na Seção 11.15, “Personalizando construtores de cópia”, você aprendeu como
personalizar o construtor de cópia para realizar uma cópia profunda. No entanto, personalizar o construtor de
cópia não altera o comportamento padrão do operador de cópia de atribuição =. Por exemplo, a classe Course
definida na Listagem 11.19, CourseWithCustomCopy-Constructor.h, possui um campo de dados de ponteiro
chamado Students que aponta para uma matriz de strings
objetos. Se você executar o código a seguir usando o operador de atribuição para atribuir course1 a course2,
conforme mostrado na linha 9 da Listagem 14.10, verá que tanto course1 quanto course2
têm a mesma matriz de alunos .

Listagem 14.10 DefaultAssignmentDemo.cpp


1 #include <iostream>
2 #include "CourseWithCustomCopyConstructor.h" // Veja a Listagem 11.19 incluir cabeçalho do curso
3 usando namespace std;

4 5 int principal()
6{
7 Curso curso1(" Programação Java", 10); criar curso1
Curso curso2(" Programação C++", 14); criar curso2
curso2 = curso1; atribuir ao curso2
8
9 10 11 curso1.addAluno("Peter Pan"); //Adiciona um aluno ao course1 adicionar um aluno
12 course2.addStudent("Lisa Ma"); //Adiciona um aluno ao course2 adicionar um aluno
Machine Translated by Google

572 Capítulo 14 Sobrecarga do Operador

13
" <<
14 cout << "alunos do curso1:
conseguir um aluno 15 course1.getStudents()[0] << endl;
" <<
16 cout << "alunos do curso2:
conseguir um aluno 17 course2.getStudents()[0] << endl;
18
19 retornar 0;
20 }

alunos do curso 1: Lisa Ma


alunos do curso 2: Lisa Ma

Para alterar a maneira como o operador de atribuição padrão = funciona, você precisa sobrecarregar o operador = ,
conforme mostrado na linha 17 da Listagem 14.11.

Listagem 14.11 CourseWithEqualsOperatorOverloaded.h


1 #ifndef CURSO_H
2 #define CURSO_H
3 #incluir <string>
4 usando namespace std;

Curso de 5 6 aulas
7{
8 público:
9 Curso (const string& courseName, int capacidade);
10 ~Curso(); // Destruidor
11 Curso(const Curso&); //Copia o construtor
12 string getCourseName() const;
13 void addStudent(const string& nome);
14 void dropStudent(const string& nome);
15 string* getAlunos() const;
16 int getNumberOfStudents() const;
sobrecarga = operador 17 const Curso& operador=(const Curso& curso);
18
19 privado:
curso de 20 cordas;
21 alunos string*;
22 número interno de alunos;
23 capacidade interna ;
24};

25 26 #endif

Na Listagem 14.11, definimos

const Curso& operador=(const Curso& curso);

Por que o tipo de retorno Curso não é nulo? C++ permite expressões com múltiplas atribuições, como:

curso1 = curso2 = curso3;

Nesta instrução, course3 é copiado para course2 e, em seguida, retorna course2 e, em seguida, course2 é copiado
para course1. Portanto, o operador = deve ter um tipo de valor de retorno válido.
A implementação do arquivo de cabeçalho é dada na Listagem 14.12.
Machine Translated by Google

14.13 Sobrecarregando os Operadores = 573

Listagem 14.12 CourseWithEqualsOperatorOverloaded.cpp


1 #include <iostream>
2 #include "CourseWithEqualsOperatorOverloaded.h"
3 usando namespace std;
4
5 Curso::Curso(const string& courseName, capacidade interna )
6{
7 númeroDeAlunos = 0;
8 this->nomedocurso = nomedocurso;
isto->capacidade = capacidade;
9 alunos = nova string[capacidade];
10 11}
12
13 Curso::~Curso()
14 {
15 excluir [] alunos;
16}
17
18 string Curso::getCourseName() const
19 {
20 return nomedocurso;
21}
22
23 void Curso::addStudent(const string& nome)
24 {
25 if (númeroDeAlunos >= capacidade)
26 {
27 cout << "O tamanho máximo do array foi excedido" << endl;
28 cout << "O programa termina agora" << endl;
29 saída(0);
30 }
31
32 alunos[númeroDeAlunos] = nome;
33 númeroDeAlunos++;
34 }

35 36 void Curso::dropStudent(const string& nome)


37 {
// Deixado como exercício
38 39}
40
41 string* Curso::getStudents() const
42 {
alunos que retornam ;
43 44}

45 46 int Curso::getNumberOfStudents() const


47 {
48 retornar númeroDeAlunos;
49}
50
51 Course::Course(const Course& course) // Copiar construtor
52 {
53 nomedocurso = curso.nomedocurso;
54 númeroDeAlunos = curso.númeroDeAlunos;
capacidade = curso.capacidade;
alunos = nova string[capacidade];
55 56 57}
58
Machine Translated by Google

574 Capítulo 14 Sobrecarga do Operador

sobrecarga = operador 59 const Curso& Curso::operator=(const Curso& curso)


60 {
copiar nomedocurso 61 nomedocurso = curso.nomedocurso;
copiar número de alunos 62 númeroDeAlunos = curso.númeroDeAlunos;
capacidade de cópia 63 capacidade = curso.capacidade;
criar matriz 64 alunos = nova string[capacidade];
65
retornar objeto de chamada 66 retorne *isto;
67 }

A linha 66 retorna o objeto de chamada usando *this. Observe que este é o ponteiro para o objeto de
chamada, então *this se refere ao objeto de chamada.
A Listagem 14.13 fornece um novo programa de teste que usa o operador = sobrecarregado para
copiar um objeto Course . Conforme mostrado no exemplo de saída, os dois cursos têm alunos diferentes
variedade.

Listagem 14.13 CustomAssignmentDemo.cpp


1 #include <iostream>
incluir cabeçalho do curso 2 #include "CourseWithEqualsOperatorOverloaded.h"
3 usando namespace std;

4 5 int principal()
6{
criar curso1 Curso curso1(" Programação Java", 10);
criar curso2 7 8 Curso course2(" Programação C++", 14);
atribuir ao curso2 9 curso2 = curso1;
10
adicionar um aluno 11 curso1.addAluno("Peter Pan"); //Adiciona um aluno ao course1
adicionar um aluno 12 course2.addStudent("Lisa Ma"); //Adiciona um aluno ao course2
13
"
14 cout << "alunos do curso1: <<
conseguir um aluno 15 course1.getStudents()[0] << endl;
"
16 cout << "alunos do curso2: <<
conseguir um aluno 17 course2.getStudents()[0] << endl;
18
19 retornar 0;
20 }

alunos do curso 1: Peter Pan


alunos do curso 2: Lisa Ma

Observação

O construtor de cópia, o operador de atribuição = e o destruidor são chamados de regra de três


regra de três ou Três Grandes. Se não forem definidos explicitamente, todos os três serão criados
automaticamente pelo compilador. Você deverá personalizá-los se um campo de dados na
classe for um ponteiro que aponta para uma matriz ou objeto gerado dinamicamente. Se você
precisar personalizar um dos três, deverá personalizar os outros dois também.

14.26 Em que situação você deve sobrecarregar o operador = ?


ÿVerificação de ponto

Termos chave
amigo classe 557 Rvalor 553
função amigo 558 retorno por referência 553
Lvalor 553 regra de três 574
Operador Lvalue 553
Machine Translated by Google

Exercícios de Programação 575

Resumo do capítulo

1. C++ permite sobrecarregar operadores para simplificar operações para objetos.

2. Você pode sobrecarregar quase todos os operadores, exceto ?:, ., .* e ::.

3. Você não pode alterar a precedência e a associatividade do operador por sobrecarga.

4. Você pode sobrecarregar o operador subscrito [] para acessar o conteúdo do objeto se


desejável.

5. Uma função C++ pode retornar uma referência, que é um alias para a variável retornada.

6. Os operadores de atribuição aumentada (+=, -=, *=, /=), operador de subscrito [], prefixo +
+ e prefixo -- são operadores Lvalue. As funções de sobrecarga destes operadores
deverão retornar uma referência.

7. A palavra-chave amigo pode ser usada para fornecer às funções e classes confiáveis acesso aos
membros privados de uma classe.

8. Os operadores [], ++, -- e () devem ser sobrecarregados como funções-membro.

9. Os operadores << e >> devem ser sobrecarregados como funções amigas não membros .

10. Os operadores aritméticos (+, -, *, /) e operadores de comparação (>, >=, ==, !=, <, <=)
devem ser implementadas como funções não membros.

11. C++ pode realizar certas conversões de tipo automaticamente se funções e construtores
apropriados forem definidos.

12. Por padrão, a cópia superficial entre membros é usada para o operador = . Para realizar um profundo
copy para o operador = , você precisa sobrecarregar o operador = .

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seção 14.2
14.1 (Use a classe Rational ) Escreva um programa que calcule a seguinte série de soma usando a
classe Rational :

1 2 3 98 99
+ + + c + +
2 3 4 99 100

*14.2 (Demonstre os benefícios do encapsulamento) Reescreva a classe Rational na Seção 14.2 usando
uma nova representação interna para o numerador e o denominador.
Declare uma matriz de dois inteiros da seguinte maneira:

int r[2];

Use r[0] para representar o numerador e r[1] para representar o denominador. As assinaturas
das funções na classe Rational não são alteradas, então um cliente
Machine Translated by Google

576 Capítulo 14 Sobrecarga do Operador

O aplicativo que usa a classe Rational anterior pode continuar a usar essa nova classe
Rational sem qualquer modificação.

Seções 14.3–14.13
*14.3 (A classe Circle ) Implemente os operadores relacionais (<, <=, ==, !=, >, >=) na classe Circle da
Listagem 10.9, CircleWithConstantMemberFunctions.h, para ordenar os objetos Circle de
acordo com seus raios.
*14.4 (A classe StackOfIntegers ) A Seção 10.9, “Estudo de caso: A classe StackOfIn-tegers ”,
definiu a classe StackOfIntegers . Implemente o operador subscrito [] nesta classe para
acessar os elementos por meio do operador [] .
**14.5 (Implementar operadores de string ) A classe de string na biblioteca padrão C++ suporta os
operadores sobrecarregados, conforme mostrado na Tabela 10.1. Implemente os seguintes
operadores: >>, ==, !=, >, >= na classe MyString no Exercício de Programação 11.15.

**14.6 (Implementar operadores de string ) A classe de string na biblioteca padrão C++ suporta os
operadores sobrecarregados, conforme mostrado na Tabela 10.1. Implemente os seguintes
operadores: [], + e += na classe MyString no Exercício de Programação 11.14.
*14.7 (Matemática: A classe Complexa ) Um número complexo tem a forma a + bi, onde a
e b são números reais e i é2-1. Os números aeb são conhecidos como parte real e parte
Nota de vídeo
imaginária do número complexo, respectivamente . Você pode realizar adição, subtração,
A classe Complexa
multiplicação e divisão de números complexos usando as seguintes fórmulas:

a + bi + c + di = (a + c) + (b + d)eu
a + bi - (c + di) = (a - c) + (b - d)eu
(a + bi) * (c + di) = (ac - bd) + (bc + ad)i
(a + bi) / (c + di) = (ac + bd) / (c2 + d2 ) + (bc - ad)i / (c2 + d2 )

Você também pode obter o valor absoluto de um número complexo usando a seguinte
fórmula:

a + bi = 2a2 + b2

(Um número complexo pode ser interpretado como um ponto em um plano identificando os
valores (a, b) como as coordenadas do ponto. O valor absoluto do número complexo
corresponde à distância do ponto à origem, como mostrado na Figura 14.2.)

eixo y

2 3i

eixo x

32i

Figura 14.2 Um número complexo pode ser interpretado como um ponto num plano.
Machine Translated by Google

Exercícios de Programação 577

Projete uma classe chamada Complex para representar números complexos e as


funções add, subtrair, multiplicar, dividir, abs para realizar operações com
números complexos e a função toString para retornar uma representação de string
para um número complexo. A função toString retorna a + bi como uma string. Se b
for 0, ele simplesmente retorna a.
Forneça três construtores Complex(a, b), Complex(a) e Complex().
Complex() cria um objeto Complex para o número 0 e Complex(a) cria um objeto Complex
com 0 para b. Forneça também as funções getRealPart() e getImaginaryPart() para retornar a
parte real e imaginária do número complexo, respectivamente.

Sobrecarregar os operadores +, -, *, /, +=, -=, *=, /=, [], unário + e -, prefixo ++


e --, postfix ++ e --, <<, >>.

Sobrecarregue os operadores +, -, *, / como funções não membros. Sobrecarregue [] para que [0]
retorne a e [1] retorne b.

Escreva um programa de teste que solicite ao usuário que insira dois números complexos e exiba
o resultado de sua adição, subtração, multiplicação e divisão. Aqui está um exemplo de execução:

Insira o primeiro número complexo: 3,5 5,5 Insira o


segundo número complexo: -3,5 1 (3,5 + 5,5i) + (-3,5
+ 1,0i) = 0,0 + 6,5i
(3,5 + 5,5i) - (-3,5 + 1,0i) = 7,0 + 4,5i
(3,5 + 5,5i) * (-3,5 + 1,0i) = -17,75 + -15,75i
(3,5 + 5,5i) / (-3,5 + 1,0i) = -0,5094 + -1,7i
|3,5 + 5,5i| = 6,519202405202649

*14.8 (conjunto Mandelbrot) Um conjunto Mandelbrot, em homenagem a Benoît Mandelbrot, é um conjunto de


pontos no plano complexo, definidos usando a seguinte iteração:

+c
zn+1 = z2 n

c é um número complexo e o ponto inicial da iteração é z0 = 0. Para um determinado c, a iteração


produzirá uma sequência de números complexos: {z0, z1, . . . , zn, . . .}.
Pode-se mostrar que a sequência tende ao infinito ou permanece limitada, dependendo do valor
de c. Por exemplo, se c for 0, a sequência será {0, 0, . . .}, que é limitado. Se c for i, a sequência
é {0, i, -1 + i, -i, -1 + i, . . .},
que é limitado. Se c for 1 + i, a sequência é {0, 1 + i, 1 + 3i ,. . .}, que é ilimitado. Sabe-se que se
o valor absoluto de um valor complexo zi na sequência for maior que 2, então a sequência é
ilimitada. O conjunto de Mandelbrot consiste no valor c tal que a sequência é limitada. Por
exemplo, 0 e eu
estão no conjunto Mandelbrot.

Escreva um programa que solicite ao usuário que insira um número complexo c e determine se
ele está no conjunto de Mandelbrot. Seu programa deve calcular z1, z2, . z60. Se nenhum ..,
dos seus valores absolutos exceder 2, assumimos que c está no conjunto de Mandelbrot. É claro
que sempre há um erro, mas 60 iterações geralmente são suficientes. Você pode usar a classe
Complex definida no Exercício de Programação 14.7 ou usar a classe complexa C++ . A classe
complexa C++ é uma classe de modelo definida no arquivo de cabeçalho <complex>. Você deve
usar complex<double> para criar um número complexo neste programa.
Machine Translated by Google

578 Capítulo 14 Sobrecarga do Operador

**14.9 (A classe EvenNumber ) Revise a classe EvenNumber no Exercício de Programação 9.11 para
implementar os operadores de pré-incremento, pré-decremento, pós-incremento e pós-
decremento para funções getNext() e getPrevious() . Escreva um programa de teste que
crie um objeto EvenNumber para o valor 16 e invoque o ++
e -- operadores para obter os números pares seguintes e anteriores.
**14.10 (Converter decimais em frações) Escreva um programa que solicite ao usuário que insira um
número decimal e exiba o número em uma fração. (Dica: leia o número decimal como uma
string, extraia a parte inteira e a parte fracionária da string e use a classe Rational para
obter um número racional para o número decimal.)
Aqui estão alguns exemplos de execução:

Insira um número decimal: 3,25 O


número da fração é 13/4

Insira um número decimal: 0,45452 O


número da fração é 11363/25000
Machine Translated by Google

CAPÍTULO

15
Herança e
Polimorfismo

Objetivos
n Para definir uma classe derivada de uma classe base por meio de herança
(§15.2).

n Para habilitar a programação genérica passando objetos de um tipo derivado para


um parâmetro de um tipo de classe base (§15.3).

n Saber como invocar os construtores da classe base com argumentos


(§15.4.1).

n Compreender o encadeamento de construtor e destruidor (§15.4.2).

n Para redefinir funções na classe derivada (§15.5).

n Distinguir entre redefinição e sobrecarga de funções (§15.5).

n Definir funções genéricas usando polimorfismo (§15.6).

n Para ativar a ligação dinâmica usando funções virtuais (§15.7).

n Distinguir entre redefinição e substituição de funções (§15.7).

n Distinguir entre correspondência estática e ligação dinâmica (§15.7).

n Para acessar membros protegidos de uma classe base a partir de classes derivadas
(§15.8).

n Definir classes abstratas com funções virtuais puras (§15.9).

n Converter um objeto de um tipo de classe base em um tipo de classe


derivado usando os operadores static_cast e dynamic_cast e
conhecer as diferenças entre os dois operadores (§15.10).
Machine Translated by Google

580 Capítulo 15 Herança e Polimorfismo

15.1 Introdução
A programação orientada a objetos permite definir novas classes a partir de classes existentes.
Chave
herança Isso é chamado de herança.
Apontar

por que herança? A herança é um recurso importante e poderoso em C++ para reutilização de software. Suponha que você
defina classes para modelar círculos, retângulos e triângulos. Essas classes têm muitos recursos comuns.
Qual é a melhor maneira de projetá-los para evitar redundância? A resposta é usar herança – o assunto deste
capítulo.

15.2 Classes Base e Classes Derivadas


A herança permite definir uma classe geral (ou seja, uma classe base) e posteriormente estendê-la
Chave
Apontar para classes mais especializadas (ou seja, classes derivadas).
Nota de vídeo

Definir classes derivadas Você usa uma classe para modelar objetos do mesmo tipo. Classes diferentes podem ter algumas
propriedades e comportamentos comuns, que podem ser generalizados em uma classe que pode ser
compartilhada por outras classes. A herança permite definir uma classe geral e posteriormente estendê-la
para classes mais especializadas. As classes especializadas herdam propriedades e funções da classe geral.
Considere objetos geométricos. Suponha que você queira projetar classes para modelar objetos
geométricos como círculos e retângulos. Objetos geométricos têm muitas propriedades e comportamentos
comuns. Eles podem ser desenhados em uma determinada cor, preenchidos ou não. Assim, uma classe geral
GeometricObject pode ser usada para modelar todos os objetos geométricos. Esta classe contém as
propriedades color e fill e suas funções get e set apropriadas . Suponha que esta classe também contenha a
função toString() , que retorna uma representação de string para o objeto.
Como o círculo é um tipo especial de objeto geométrico, ele compartilha propriedades e funções comuns com
outros objetos geométricos. Portanto, faz sentido definir a classe Circle que estende a classe GeometricObject .
Da mesma forma, Rectangle também pode ser definido como uma classe derivada de GeometricObject. A
Figura 15.1 mostra os relacionamentos entre essas classes. Um triângulo apontando para a classe base é
usado para denotar o relacionamento de herança entre as duas classes envolvidas.

classe derivada Na terminologia C++, uma classe C1 estendida de outra classe C2 é chamada de classe derivada e C2 é
classe base chamada de classe base. Também nos referimos a uma classe base como classe pai ou superclasse e a uma
classe pai
classe derivada como classe filha ou subclasse. Uma classe derivada herda campos de dados e funções

superclasse acessíveis de sua classe base e também pode adicionar novos campos de dados e funções.
classe infantil A classe Circle herda todos os campos de dados e funções acessíveis da classe GeometricObject . Além
subclasse disso, possui um novo campo de dados, radius, e seu get associado.
e definir funções. Ele também contém getArea(), getPerimeter() e getDiameter()
funções para retornar a área, perímetro e diâmetro do círculo.
A classe Rectangle herda todos os campos de dados e funções acessíveis da classe GeometricObject .
Além disso, possui campos de dados de largura e altura e suas funções get e set associadas . Ele também
contém as funções getArea() e getPerimeter() para retornar a área e o perímetro do retângulo.

A definição de classe para GeometricObject é mostrada na Listagem 15.1. As diretivas do pré-processador


nas linhas 1 e 2 protegem contra múltiplas inclusões. O cabeçalho da classe de string C++ está incluído na
linha 3 para suportar o uso da classe de string em GeometricObject. A função isFilled() é o acessador do
campo de dados preenchido . Como este campo de dados é do tipo bool , a função acessadora é denominada
isFilled() por convenção.

Listagem 15.1 GeometricObject.h


guarda de inclusão 1 #ifndef GEOMETRICOBJECT_H
2 #define GEOMETRICOBJECT_H
3 #incluir <string>
4 usando namespace std;
Machine Translated by Google

15.2 Classes Base e Classes Derivadas 581

5 6 classe GeometricObject 7
{8
público: 9 membros públicos
GeometricObject();
GeometricObject (const string e cor, bool preenchido); 10 11 string
getColor() const; void setColor(const
string& cor); 12 13 bool isFilled() const; void
setFilled(bool preenchido); 14 15
string toString() const; 16 17 privado:
cor do barbante; 18 19 bool preenchido;

membros privados

20}; // Deve colocar ponto e vírgula aqui 21 22

#endif

Objeto Geométrico

-color: string -filled: A cor do objeto (padrão: branco).


bool Indica se o objeto está preenchido com uma cor (padrão: false).

+ObjetoGeométrico() Cria um GeometricObject.


+GeometricObject(cor: string, preenchido: bool) Cria um GeometricObject com a cor especificada e preenchido
valores.
+getColor(): string const +setColor(cor: Retorna a cor.
string): void +isFilled(): bool const Define uma nova cor.
+setFilled(preenchido: bool): void Retorna a propriedade preenchida.
+toString(): string const Define uma nova propriedade preenchida.

Retorna uma representação de sequência de caracteres deste objeto.

Círculo Retângulo

-raio: duplo -largura: duplo


-altura: duplo
+Círculo()
+Círculo(raio: duplo) +Retângulo()
+Círculo(raio: duplo, cor: string, +Retângulo (largura: duplo, altura: duplo)
preenchido: bool)
+Retângulo(largura: duplo, altura: duplo, cor: string, preenchido:
+getRadius(): const duplo +setRadius(raio: bool)
duplo): void +getArea(): const duplo +getPerimeter(): +getWidth(): double const
const duplo +getDiameter(): const +setWidth(largura: double): void +getHeight():
duplo +toString(): string const double const +setHeight(height: double):
void +getArea(): double const +getPerimeter():
double const +toString() : string const

Figura 15.1 A classe GeometricObject é a classe base para Circle e Rectangle.

A classe GeometricObject é implementada na Listagem 15.2. A função toString (linhas


35–38) retorna uma string que descreve o objeto. O operador string + é usado para
concatenar duas strings e retorna um novo objeto string .
Machine Translated by Google

582 Capítulo 15 Herança e Polimorfismo

Listagem 15.2 GeometricObject.cpp


arquivo de cabeçalho 1 #include "GeometricObject.h"
2
construtor sem argumento 3 GeometricObject::GeometricObject()
4{
cor = "branco";
preenchido = falso;
5 6 7}
8
construtor 9 GeometricObject::GeometricObject(const string e cor, bool preenchido)
10 {
11 isto->cor = cor;
12 isto->preenchido = preenchido;
13}
14
getColor 15 strings GeometricObject::getColor() const
16 {
17 retornar cor;
18}
19
definirCor 20 void GeometricObject::setColor(const string& cor)
21 {
22 isto->cor = cor;
23}
24
está cheio 25 bool GeometricObject::isFilled() const
26 {
27 retorno preenchido;
28}
29
setFilled 30 void GeometricObject::setFilled(bool preenchido)
31 {
32 isto->preenchido = preenchido;
33}
34
para sequenciar 35 string GeometricObject::toString() const
36 {
37 retornar "Objeto Geométrico";
38}

A definição de classe para Circle é mostrada na Listagem 15.3. A linha 5 define que o Círculo
classe é derivada da classe base GeometricObject. A sintaxe

Classe derivada Classe base

classe Círculo: GeometricObject público

informa ao compilador que a classe é derivada da classe base. Portanto, todos os membros
públicos em GeometricObject são herdados em Circle.

Listagem 15.3 DerivedCircle.h


guarda de inclusão 1 #ifndef CÍRCULO_H
2 #define CÍRCULO_H
3 #include "GeometricObject.h"

estende GeometricObject 4 5 classe Círculo: GeometricObject público


Machine Translated by Google

15.2 Classes Base e Classes Derivadas 583

6{
7 público: membros públicos
8 Círculo();
Círculo(duplo);
9 10 Círculo ( raio duplo, string const e cor, bool preenchido);
11 double getRadius() const;
12 void setRadius(duplo);
13 double getArea() const;
14 double getPerimeter() const;
15 double getDiameter() const;
16 string toString() const;
17
18 privado: membros privados
19 raio duplo ;
20}; //Deve colocar ponto e vírgula aqui
21
22 #endif

A classe Circle é implementada na Listagem 15.4.

Listagem 15.4 DerivedCircle.cpp


1 #include "DerivedCircle.h" Cabeçalho do círculo

2 3 // Construa um objeto circular padrão


4 Círculo::Círculo() construtor sem argumento
{
raio = 1;
5 6 7}

8 9 // Construa um objeto circular com raio especificado


10 Círculo::Círculo( raio duplo) construtor
11 {
12 setRaio(raio);
13}
14
15 // Construa um objeto circular com raio especificado,
16 // cor e valores preenchidos
17 Círculo::Círculo( raio duplo, string const e cor, bool preenchido) construtor
18 {
19 setRaio(raio);
setColor(cor);
20 setFilled(preenchido);
21 22 }

23 24 // Retorna o raio deste círculo


25 círculo duplo ::getRadius() const getRadius
26 {
raio de retorno ;
27 28}
29
30 // Defina um novo raio
31 círculo vazio ::setRadius( raio duplo) setRadius
32 {
isto->raio = (raio >= 0) ? raio: 0;
33 34}

35 36 // Retorna a área deste círculo


37 círculo duplo ::getArea() const Getaria
Machine Translated by Google

584 Capítulo 15 Herança e Polimorfismo


38 {
raio de retorno * raio * 3,14159;
39 40}
41
42 // Retorna o perímetro deste círculo
getPerímetro 43 círculo duplo ::getPerimeter() const
44 {
retornar 2 * raio * 3,14159;
45 46}
47
48 // Retorna o diâmetro deste círculo
getDiameter 49 círculo duplo ::getDiameter() const
50 {
retornar 2 * raio;
51 52}
53
54 // Redefinir a função toString
55 strings Circle::toString() const
56 {
return "Objeto Círculo";
57 58}

O construtor Circle (raio duplo, string const e cor, bool preenchido)


é implementado invocando as funções setColor e setFilled para definir as propriedades de cor e
preenchimento (linhas 17–22). Essas duas funções públicas são definidas na classe base GeometricObject
e são herdadas em Circle. Portanto, eles podem ser usados na classe derivada.
Você pode tentar usar a cor dos campos de dados e preenchê-los diretamente no construtor da
seguinte maneira:

Círculo::Círculo( raio duplo, const string& c, bool f)


{
isto->raio = raio; // Isto é bom
membro privado na classe base cor =c; // Ilegal já que a cor é privada na classe base
preenchido = f; // Ilegal já que preenchido é privado na classe base
}

Isso está errado, porque os campos de dados privados são coloridos e preenchidos no GeometricObject
class não pode ser acessada em nenhuma classe diferente da própria classe GeometricObject . A única
maneira de ler e modificar cores e preenchimentos é por meio das funções get e set .
A classe Rectangle é definida na Listagem 15.5. A linha 5 define que a classe Rectangle é derivada da
classe base GeometricObject. A sintaxe

Classe derivada Classe base

classe Retângulo: público GeometricObject

informa ao compilador que a classe é derivada da classe base. Portanto, todos os membros públicos em
GeometricObject são herdados em Rectangle.

Listagem 15.5 DerivedRectangle.h


guarda de inclusão 1 #ifndef RECTANGLE_H
2 #define RECTANGLE_H
3 #include "GeometricObject.h"
4
Machine Translated by Google

15.2 Classes Base e Classes Derivadas 585

Retângulo de 5 classes : GeometricObject público estende GeometricObject


6{
7 público: membros públicos
8 Retângulo();
9 Retângulo ( largura dupla, altura dupla );
e Retângulo ( largura dupla, altura dupla , 10 11 const string
cor, bool preenchido);
12 double getWidth() const;
13 void setWidth(duplo);
14 double getHeight() const;
15 void setHeight(duplo);
16 double getArea() const;
17 double getPerimeter() const;
18 string toString() const;
19
20 privado: membros privados
21 largura dupla ;
22 altura dupla ;
23}; //Deve colocar ponto e vírgula aqui
24
25 #endif

A classe Rectangle é implementada na Listagem 15.6.

Listagem 15.6 DerivedRectangle.cpp


1 #include "DerivadoRectangle.h" Cabeçalho retangular

2 3 // Construa um objeto retângulo padrão


4 Retângulo::Retângulo() construtor sem argumento
{
largura = 1;
altura = 1;
5678}
9
10 // Construa um objeto retângulo com largura e altura especificadas
11 Retângulo::Retângulo ( largura dupla, altura dupla ) construtor
12 {
13 setWidth(largura);
14 setAltura(altura);
15}
16
17 Retângulo::Retângulo( construtor
18 largura dupla , altura dupla , string const e cor, bool preenchido)
19 {
20 setWidth(largura);
21 setAltura(altura);
setColor(cor);
setFilled(preenchido);
22 23 24 }
25
26 // Retorna a largura deste retângulo
27 retângulo duplo ::getWidth() const obterLargura
28 {
29 largura de retorno ;
30}
31
32 // Defina um novo raio
Machine Translated by Google

586 Capítulo 15 Herança e Polimorfismo


definirLargura 33 retângulo vazio ::setWidth( largura dupla)
34 {
isto->largura = (largura >= 0) ? largura: 0;
35 36}

37 38 // Retorna a altura deste retângulo


obterAltura 39 retângulo duplo ::getHeight() const
40 {
41 altura de retorno ;
42}

43 44 // Defina uma nova altura


definirAltura 45 Retângulo vazio ::setHeight( altura dupla)
46 {
47 isto->altura = (altura >= 0) ? altura: 0;
48}
49
50 // Retorna a área deste retângulo
Getaria 51 retângulo duplo ::getArea() const
52 {
retornar largura * altura;
53 54}

55 56 // Retorna o perímetro deste retângulo


getPerímetro 57 retângulo duplo ::getPerimeter() const
58 {
return 2 * (largura + altura);
59 60}
61
62 // Redefinir a função toString, a ser abordada na Seção 15.5
63 string Rectangle::toString() const
64 {
return "Objeto retângulo";
65 66}

A Listagem 15.7 fornece um programa de teste que usa estas três classes – GeometricObject,
Círculo e Retângulos.

Listagem 15.7 TestGeometricObject.cpp


Cabeçalho GeometricObject 1 #include "GeometricObject.h"
Cabeçalho do círculo 2 #include "DerivedCircle.h"
Cabeçalho retangular 3 #include "DerivadoRectangle.h"
4 #include <iostream>
5 usando namespace std;

6 7 int principal()
8{
crie um GeometricObject Forma geométrica do objeto;
9 shape.setColor("vermelho");
10 shape.setFilled(true);
11 cout << shape.toString() << endl
12 << "cor:" << forma.getColor()
13 << " preenchido: " << (shape.isFilled() ? "true" : "false") << endl;
14
crie um círculo 15 Círculo círculo(5);
16 círculo.setColor("preto");
17 círculo.setFilled(falso);
18 cout << círculo.toString()<< endl
19 20 << "cor:" << círculo.getColor()
Machine Translated by Google

15.2 Classes Base e Classes Derivadas 587

21 << " preenchido: " << (circle.isFilled()? "true" : "falso") << círculo.getRadius()
22 << " raio: "
" "
23 << área: << círculo.getArea()
24 << "perímetro:" << círculo.getPerimeter() << endl;
25
26 Retângulo retângulo(2, 3); crie um retângulo
27 retângulo.setColor("laranja");
28 retângulo.setFilled(true);
29 cout << retângulo.toString()<< endl
30 << "cor:" << círculo.getColor()
31 << " preenchido: " << (circle.isFilled() ? "true" : "false") " largura: " <<
32 << retângulo.getWidth()
33 << "altura:" << retângulo.getHeight()
" "
34 << área: << retângulo.getArea()
35 << "perímetro:" << retângulo.getPerimeter() << endl;
36
37 retornar 0;
38 }

Objeto Geométrico
cor: vermelho preenchido: verdadeiro

Objeto circular
cor: preto preenchido: falso raio: 5 área: 78,5397 perímetro: 31,4159
Objeto retângulo
cor: preto preenchido: falso largura: 2 altura: 3 área: 6 perímetro: 10

O programa cria um GeometricObject e invoca suas funções setColor,


setFilled, toString, getColor e isFilled nas linhas 9–14.
O programa cria um objeto Circle e invoca suas funções setColor, setFilled, toString,
getColor, isFilled, getRadius, getArea e getPerimeter nas linhas 16–24. Observe que
as funções setColor e setFilled são definidas na classe GeometricObject e herdadas na
classe Circle .
O programa cria um objeto Rectangle e invoca suas funções setColor, setFilled,
toString, getColor, isFilled, getWidth, getHeight, getArea e getPerimeter nas linhas 26–
35. Observe que as funções setColor e setFilled são definidas na classe GeometricObject
e herdadas na classe Rectangle .
Observe os seguintes pontos sobre herança:

n Os campos de dados privados em uma classe base não são acessíveis fora da classe. Portanto, eles
campos de dados privados
não podem ser usados diretamente em uma classe derivada. Eles podem, no entanto, ser acessados/
mutados por meio de acessador/mutador público se definido na classe base.

n Nem todos os relacionamentos é-a devem ser modelados usando herança. Por exemplo, um quadrado inextensível é-a
é um retângulo, mas você não deve definir uma classe Square para estender um Rectangle
classe, porque não há nada que se estenda (ou complemente) de um retângulo a um quadrado.
Em vez disso, você deve definir uma classe Square para estender a classe GeometricObject .
Para que a classe A estenda a classe B, A deve conter informações mais detalhadas do que B.

n A herança é usada para modelar o relacionamento é-um . Não estenda cegamente uma classe apenas sem extensão cega
para reutilizar funções. Por exemplo, não faz sentido que uma classe Árvore estenda uma classe
Pessoa , mesmo que elas compartilhem propriedades comuns, como altura e peso. Uma classe
derivada e sua classe base devem ter o relacionamento é-um .

n C++ permite derivar uma classe derivada de várias classes. Esta capacidade é conhecida como herança
múltipla, discutida no Suplemento IV.A.
herança múltipla
Machine Translated by Google

588 Capítulo 15 Herança e Polimorfismo


15.1 Verdadeiro ou falso? Uma classe derivada é um subconjunto de uma classe base.

ÿVerificação de ponto
15.2 Uma classe pode ser derivada de múltiplas classes base em C++?

15.3 Identifique os problemas nas aulas a seguir.

classe Círculo

{ público:
Círculo( raio duplo) {

raio = raio;
}

double getRadius() {

raio de retorno ;
}

double getArea() {

raio de retorno * raio * 3,14159;


}

privado:
raio duplo ; };

classe B: Círculo

{ público:
B( raio duplo, comprimento duplo ) {

raio = raio;
comprimento =
comprimento; }

// Retorna getArea() do Círculo * length double


getArea() {

return getArea() * comprimento;


}

privado:
comprimento
duplo ; };

15.3 Programação Genérica


Chave
Apontar Um objeto de uma classe derivada pode ser passado sempre que um objeto de um parâmetro de tipo
base for necessário. Assim, uma função pode ser usada genericamente para uma ampla gama de
programação genérica argumentos de objetos. Isso é conhecido como programação genérica.

Se o tipo de parâmetro de uma função for uma classe base (por exemplo, GeometricObject), você poderá passar para
esta função um objeto de qualquer uma das classes derivadas do parâmetro (por exemplo, Circle ou Rectangle).
Por exemplo, suponha que você defina uma função da seguinte maneira:

void displayGeometricObject(const GeometricObject& forma) {

cout << shape.getColor() << endl;


}
Machine Translated by Google

15.4 Construtores e Destruidores 589

O tipo de parâmetro é GeometricObject. Você pode invocar esta função no seguinte código:

displayGeometricObject(GeometricObject("preto", verdadeiro));
displayGeometricObject(Círculo(5));
displayGeometricObject(Retângulo(2, 3));

Cada instrução cria um objeto anônimo e o passa para invocar displayGeometric-ricObject. Como Circle
e Rectangle são derivados de GeometricObject, você pode passar um objeto Circle ou Rectangle para o tipo
de parâmetro GeometricObject na função displayGeometricObject .

15.4 Construtores e Destruidores


O construtor de uma classe derivada primeiro chama o construtor da sua classe base antes de Chave
executar seu próprio código. O destruidor de uma classe derivada executa seu próprio código e Apontar

depois chama automaticamente o destruidor de sua classe base.

Uma classe derivada herda campos de dados e funções acessíveis de sua classe base. Ele herda construtores
ou destruidores? Os construtores e destruidores da classe base podem ser invocados a partir de classes
derivadas? Consideraremos agora essas questões e suas ramificações.

15.4.1 Chamando Construtores de Classe Base


Um construtor é usado para construir uma instância de uma classe. Ao contrário dos campos de dados e funções,
os construtores de uma classe base não são herdados na classe derivada. Eles só podem ser invocados a partir
dos construtores das classes derivadas para inicializar os campos de dados na classe base. Você pode invocar o
construtor da classe base a partir da lista de inicializadores do construtor da classe derivada.
A sintaxe é a seguinte:

DerivedClass (lista de parâmetros): BaseClass ()


{
//Executa a inicialização
}

ou

DerivedClass (lista de parâmetros): Classe Base (lista de argumentos)


{
//Executa a inicialização
}

O primeiro invoca o construtor sem argumentos de sua classe base e o último invoca o construtor da classe
base com os argumentos especificados.
Um construtor em uma classe derivada sempre invoca um construtor em sua classe base, explícita ou
implicitamente. Se um construtor base não for invocado explicitamente, o construtor sem argumentos da classe
base será invocado por padrão. Por exemplo,

Círculo público () Círculo público (): GeometricObject()


{ é equivalente a {
raio = 1; raio = 1;
} }

Círculo público ( raio duplo) Círculo público ( raio duplo)


{ é equivalente a : Objeto Geométrico()
isto->raio = raio; {
} isto->raio = raio;
}
Machine Translated by Google

590 Capítulo 15 Herança e Polimorfismo

O construtor Circle(double radius, const string& color, bool fill) (linhas 17–22) na Listagem 15.4,
DerivedCircle.cpp, também pode ser implementado invocando o construtor da classe base GeometricObject(const
string& color, bool fill) como segue:

1 // Construa um objeto circular com raio, cor e preenchimento especificados


2 Círculo::Círculo( raio duplo, string const e cor, bool preenchido)
invocar o construtor base : GeometricObject (cor, preenchido)
34{
setRaio(raio);
5 6}

ou

1 // Construa um objeto circular com raio, cor e preenchimento especificados


2 Círculo::Círculo( raio duplo, string const e cor, bool preenchido)
inicializar campo de dados 3 : GeometricObject(cor, preenchido), raio(raio)
4{
5}

Este último também inicializa o raio do campo de dados no inicializador do construtor. raio é um
campo de dados definido na classe Circle .

15.4.2 Encadeamento de Construtor e Destruidor


Construir uma instância de uma classe invoca os construtores de todas as classes base ao longo da cadeia de
herança. Ao construir um objeto de uma classe derivada, o construtor da classe derivada primeiro invoca seu
construtor de classe base antes de executar suas próprias tarefas. Se uma classe base for derivada de outra
classe, o construtor da classe base invocará seu construtor de classe pai antes de executar suas próprias
tarefas. Esse processo continua até que o último construtor da hierarquia de herança seja chamado. Isso é
encadeamento de construtor chamado de encadeamento de construtor. Por outro lado, os destruidores são invocados automaticamente na
ordem inversa. Quando um objeto de uma classe derivada é destruído, o destruidor da classe derivada é
chamado. Após terminar suas tarefas, ele invoca seu destruidor de classe base.
Esse processo continua até que o último destruidor da hierarquia de herança seja chamado. Isso é chamado
encadeamento de destruidor de encadeamento de destruidor.
Considere o seguinte código na Listagem 15.8:

Listagem 15.8 ConstructorDestructorCallDemo.cpp


1 #include <iostream>
2 usando namespace std;

Classe de pessoa 3 4 classe Pessoa


{
5 6 público:
7 Pessoa()
8{

cout << "Executa tarefas para o construtor da Pessoa" << endl;


9 }
10
11 ~Pessoa()
12 {
13 cout << "Executa tarefas para o destruidor de Person" << endl;
14 15 }
16};
17
Classe de funcionário 18 classe Funcionário: Pessoa Pública
Machine Translated by Google

15.4 Construtores e Destruidores 591


19 {
20 público:
21 Funcionário()
22 {
23 cout << "Executa tarefas para o construtor do Employee" << endl;
24 }
25
26 ~Funcionário()
27 {
28 cout << "Executa tarefas para o destruidor do Funcionário" << endl;
29 }
30};
31
32 turma Docente: Funcionário público Aula do corpo docente

33 {
34 público:
35 Faculdade()
36 {
37 cout << "Executa tarefas para o construtor do corpo docente" << endl;
38 }
39
40 ~Faculdade()
41 {
42 cout << "Executa tarefas para o destruidor do corpo docente" << endl;
43}
44};
45
46 int principal()
47 {
48 Corpo docente; criar uma Faculdade
49
50 retornar 0;
51}

Executa tarefas para o construtor de Person


Executa tarefas para o construtor do Funcionário
Executa tarefas para o construtor do corpo docente
Executa tarefas para o destruidor do corpo docente
Executa tarefas para o destruidor do Funcionário
Executa tarefas para o destruidor de Person

O programa cria uma instância de Faculdade na linha 48. Como Faculdade é derivado de
Funcionário e Funcionário é derivado de Pessoa, o construtor de Faculdade invoca o
construtor de Funcionário antes de executar sua própria tarefa. O construtor de Employee
invoca o construtor de Person antes de executar sua própria tarefa, conforme mostrado na figura a seguir:

Faculdade() Funcionário() Pessoa() {


{ {

Desempenha tarefas da Faculdade; Executa as tarefas do Colaborador; Executa as tarefas da Pessoa;


}
} }
Machine Translated by Google

592 Capítulo 15 Herança e Polimorfismo

Quando o programa é encerrado, o objeto Faculdade é destruído. Portanto, o destruidor da Faculdade é


chamado, depois de Funcionário e, por fim, de Pessoa, conforme mostrado na figura a seguir:

~Faculdade() ~Funcionário() ~Pessoa() {


{ {
Realiza Faculdades...; Executa Colaborador...; Executa Pessoa...;
} } }

Cuidado Se

construtor sem argumento uma classe for projetada para ser estendida, é melhor fornecer um construtor sem argumentos para
evitar erros de programação. Considere o seguinte código:

classe Fruta

{ público:
Fruta(int id) { } };

class Maçã: fruta pública { público:

Maçã()

{ } };

Como nenhum construtor é definido explicitamente na Apple, o construtor no-arg padrão da Apple
é definido implicitamente. Como Apple é uma classe derivada de Fruit, o construtor padrão da Apple
invoca automaticamente o construtor sem argumentos de Fruit . No entanto, Fruit não possui um
construtor sem argumentos, porque Fruit possui um construtor explícito definido.
Portanto, o programa não pode ser compilado.

Nota

copiar operador de Se a classe base tiver um construtor de cópia e um operador de atribuição personalizados, você
atribuição de construtor deverá personalizá-los nas classes derivadas para garantir que os campos de dados na classe base
sejam copiados corretamente. Suponha que a classe Child seja derivada de Parent. O código para
o construtor de cópia em Child normalmente seria assim:

Filho::Criança(const Filho& objeto): Pai(objeto) {

// Escreva o código para copiar campos de dados em Child }

O código para o operador de atribuição em Child normalmente seria assim:

Filho& Filho::operador=(const Filho& objeto) {

Pai::operador(objeto);
// Escreva o código para copiar campos de dados em Child }

destruidor Quando um destruidor de uma classe derivada é invocado, ele invoca automaticamente o destruidor da classe
base. O destruidor na classe derivada só precisa destruir a memória criada dinamicamente na classe derivada.
Machine Translated by Google

15.4 Construtores e Destruidores 593

15.4 Verdadeiro ou falso? Ao invocar um construtor de uma classe derivada, o construtor sem
argumento de sua classe base é sempre invocado. ÿVerificação de ponto

15.5 Qual é o resultado da execução do programa em (a)? Que problema surge em


compilando o programa em (b)?

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

classe Pai classe Pai

{ público: { público:
Pai() { Pai(int x) { } };

conta <<
"O construtor sem argumento dos pais é invocado";

} }; classe Filho: pai público { };

classe Filho: pai público { };

int principal()
{
int principal() Criança c;
{
Criança c; retornar 0;
}
retornar 0;
}

(a) (b)

15.6 Mostre a saída do seguinte código:

#include <iostream>
usando namespace std;

classe Pai

{ público:
Parent()

{ cout << "O construtor sem argumento do pai é invocado" << endl; }

~Parente() {

cout << "O destruidor do pai é invocado" << endl; } };

classe Filho: público Pai { público:

Criança()
Machine Translated by Google

594 Capítulo 15 Herança e Polimorfismo


{
cout << "O construtor sem argumento do filho é invocado" << endl;
}

~Criança()
{
cout << "O destruidor da criança é invocado" << endl;
}
};

int principal()
{
Criança c1;
Criança c2;

retornar 0;
}

15.7 Se uma classe base tiver um construtor de cópia personalizado e um operador de atribuição,
como você deve definir o construtor de cópia e o operador de atribuição na classe derivada?

15.8 Se uma classe base tiver um destruidor customizado, você é obrigado a implementar o
destruidor na classe derivada?

15.5 Redefinindo Funções


Uma função definida na classe base pode ser redefinida nas classes derivadas.
Chave
Apontar
A função toString() é definida na classe GeometricObject para retornar uma string "Objeto geométrico"
(linhas 35 a 38 na Listagem 15.2) como segue:

string GeometricObject::toString() const


{
return "Objeto geométrico";
}

Para redefinir a função de uma classe base na classe derivada, você precisa adicionar o protótipo da
função no arquivo de cabeçalho da classe derivada e fornecer uma nova implementação para a função no
arquivo de implementação da classe derivada.
A função toString() é redefinida na classe Circle (linhas 55–58 na Listagem 15.4) como segue:

string Circle::toString() const


{
return "Objeto Círculo";
}

A função toString() é redefinida na classe Rectangle (linhas 63–66 na Listagem 15.6) como segue:

string Rectangle::toString() const


{
return "Objeto retângulo";
}

Então, o seguinte código

criar GeometricObject 1 forma GeometricObject;


"
invocar toString 2 cout << "shape.toString() retorna 3 << shape.toString() << endl;
Machine Translated by Google

15.6 Polimorfismo 595

4 Círculo círculo(5); criar círculo


"
5 cout << "circle.toString() retorna 6 7 Rectangle << círculo.toString() << endl; invocar toString

retângulo(4, 6); criar retângulo


"
8 cout << "retangle.toString() retorna 9
<< retângulo.toString() << endl; invocar toString
exibe:

shape.toString() retorna objeto geométrico


Circle.toString() retorna o objeto Circle
Rectangle.toString() retorna o objeto Rectangle

O código cria um GeometricObject na linha 1. A função toString definida em


GeometricObject é invocado na linha 2, pois o tipo da forma é GeometricObject.
O código cria um objeto Circle na linha 4. A função toString definida em Circle é invocada na linha 5, pois
o tipo de círculo é Circle.
O código cria um objeto Rectangle na linha 7. A função toString definida em
Rectangle é invocado na linha 9, pois o tipo do retângulo é Rectangle.
Se você deseja invocar a função toString definida na classe GeometricObject no objeto de chamada
invocar função na base
círculo, use o operador de resolução de escopo (::) com o nome da classe base.
Por exemplo, o seguinte código

Círculo círculo(5);
"
cout << "circle.toString() retorna cout << "invoca << círculo.toString() << endl;
"
toString() da classe base para retornar << circle.GeometricObject::toString();

exibições

Circle.toString() retorna o objeto Circle


invocar toString() da classe base para retornar o objeto Geometric

Observação

Na Seção 6.7, “Sobrecarregando funções”, você aprendeu sobre sobrecarga de funções.


redefinindo versus sobrecarregando
Sobrecarregar uma função é uma forma de fornecer mais de uma função com o mesmo
nome, mas com assinaturas diferentes para distingui-las. Para redefinir uma função, a função
deve ser definida na classe derivada usando a mesma assinatura e o mesmo tipo de retorno
de sua classe base.

15.9 Qual é a diferença entre sobrecarregar uma função e redefinir uma função?

15.10 Verdadeiro ou falso? (1) Você pode redefinir uma função privada definida em uma classe base. ÿVerificação de ponto

(2) Você pode redefinir uma função estática definida em uma classe base. (3) Você pode redefinir
um construtor.

15.6 Polimorfismo
Polimorfismo significa que uma variável de um supertipo pode referir-se a um objeto de subtipo.
Chave
Os três pilares da programação orientada a objetos são encapsulamento, herança e polimorfismo. Você já Apontar

aprendeu os dois primeiros. Esta seção introduz o polimorfismo.


Primeiro, vamos definir dois termos úteis: subtipo e supertipo. Uma classe define um tipo. Um tipo definido
por uma classe derivada é chamado de subtipo, e um tipo definido por sua classe base é chamado de Nota de vídeo

supertipo. Portanto, você pode dizer que Circle é um subtipo de GeometricObject e GeometricObject é um Polimorfismo e funções
virtuais
supertipo de Circle.
Machine Translated by Google

596 Capítulo 15 Herança e Polimorfismo

subtipo O relacionamento de herança permite que uma classe derivada herde recursos de sua classe base com
supertipo novos recursos adicionais. Uma classe derivada é uma especialização de sua classe base; toda instância
de uma classe derivada também é uma instância de sua classe base, mas não vice-versa. Por exemplo,
todo círculo é um objeto geométrico, mas nem todo objeto geométrico é um círculo. Portanto, você sempre
pode passar uma instância de uma classe derivada para um parâmetro do seu tipo de classe base. Considere
o código na Listagem 15.9.

Listagem 15.9 PolimorfismoDemo.cpp


1 #include <iostream>
2 #include "GeometricObject.h"
3 #include "DerivadoCircle.h"
4 #include "DerivadoRectangle.h"

5 6 usando namespace std;


7
displayGeometricObject 8 displayGeometricObject vazio (const GeometricObject & g)
9{
invocar toString 10 cout << g.toString() << endl;
11}
12
13 int principal()
14 {
15 GeometricObject GeometricObject;
passe um GeometricObject 16 displayGeometricObject(geometricObject);
17
18 Círculo círculo(5);
passe um círculo 19 displayGeometricObject(círculo);
20
21 Retângulo retângulo(4, 6);
passe um retângulo 22 displayGeometricObject(retângulo);
23
24 retornar 0;
25 }

Objeto geométrico
Objeto geométrico
Objeto geométrico

A função displayGeometricObject (linha 8) recebe um parâmetro do tipo Geometric-Object . Você


pode invocar displayGeometricObject passando qualquer instância de GeometricObject, Circle e
Rectangle (linhas 16, 19, 22). Um objeto de uma classe derivada pode ser usado sempre que seu objeto de
polimorfismo classe base for usado. Isso é comumente conhecido como polimorfismo
(de uma palavra grega que significa “muitas formas”). Em termos simples, polimorfismo significa que uma
variável de um supertipo pode referir-se a um objeto de subtipo.

15.11 O que é um subtipo e um supertipo? O que é polimorfismo?


ÿVerificação de ponto

15.7 Funções Virtuais e Vinculação Dinâmica


Uma função pode ser implementada em diversas classes ao longo da cadeia de herança. As funções
Chave
Apontar virtuais permitem que o sistema decida qual função será invocada em tempo de execução com
base no tipo real do objeto.

O programa na Listagem 15.9 define a função displayGeometricObject que invoca a função toString em
um GeometricObject (linha 10).
Machine Translated by Google

15.7 Funções Virtuais e Ligação Dinâmica 597

A função displayGeometricObject é invocada nas linhas 16, 19, 22 passando um objeto de GeometricObject,
Circle e Rectangle, respectivamente. Conforme mostrado na saída, a função toString() definida na classe
GeometricObject é invocada.
Você pode invocar a função toString() definida em Circle ao executar display-GeometricObject(circle), a função
toString() definida em Rectangle ao executar displayGeometicObject(rectangle) e a função toString() definida
em GeometricObject ao executar displayGeometricObject(geometricObject) ?

Você pode fazer isso simplesmente declarando toString como uma função virtual na classe base GeometricObject. função virtual

Suponha que você substitua a linha 15 na Listagem 15.1 pela seguinte declaração de função:

string virtual toString() const; definir função virtual

Agora, se você executar novamente a Listagem 15.9, verá a seguinte saída:

Objeto geométrico
Objeto circular
Objeto retângulo

Com a função toString() definida como virtual na classe base, C++ determina dinamicamente qual função virtual
toString() invocar em tempo de execução. Ao invocar displayGeometricObject(circle), um objeto Circle é passado
para g por referência. Como g se refere a um objeto do tipo Circle , a função toString definida na classe Circle

é invocado. A capacidade de determinar qual função invocar em tempo de execução é conhecida como ligação
dinâmica. vinculação dinâmica

Observação

Em C++, redefinir uma função virtual em uma classe derivada é chamado de substituir uma função. substituindo uma função

Para habilitar a vinculação dinâmica para uma função, você precisa fazer duas coisas:

n A função deve ser definida como virtual na classe base.

n A variável que faz referência ao objeto deve ser passada por referência ou passada como
ponteiro na função virtual.

A Listagem 15.9 passa o objeto para um parâmetro por referência (linha 8); alternativamente, você pode
reescreva as linhas 8–11 passando um ponteiro, como na Listagem 15.10:

Listagem 15.10 VirtualFunctionDemoUsingPointer.cpp


1 #include <iostream>
2 #include "GeometricObject.h" // toString() está definido como virtual agora
3 #include "DerivadoCircle.h"
4 #include "DerivadoRectangle.h"

5 6 usando namespace std;

7 8 void displayGeometricObject(const GeometricObject* g) passar um ponteiro


9{
10 cout << (*g).toString() << endl; invocar toString
11}
12
13 int principal()
Machine Translated by Google

598 Capítulo 15 Herança e Polimorfismo


14 {
passe um GeometricObject 15 displayGeometricObject(&GeometricObject());
passe um círculo 16 displayGeometricObject(&Circle(5));
passe um retângulo 17 displayGeometricObject(&Rectangle(4, 6));
18
19 retornar 0;
20 }

Objeto geométrico
Objeto circular
Objeto retângulo

Entretanto, se o argumento do objeto for passado por valor, as funções virtuais não serão vinculadas
dinamicamente. Conforme mostrado na Listagem 15.11, mesmo que a função seja definida como virtual, a saída
é a mesma que seria sem o uso da função virtual.

Listagem 15.11 VirtualFunctionDemoPassByValue.cpp


1 #include <iostream>
2 #include "GeometricObject.h"
3 #include "DerivadoCircle.h"
4 #include "DerivadoRectangle.h"

5 6 usando namespace std;

passar objeto por valor 7 8 void displayGeometricObject (GeometricObject g)


9{
invocar toString 10 cout << g.toString() << endl;
11}
12
13 int principal()
14 {
passe um GeometricObject 15 displayGeometricObject(GeometricObject());
passe um círculo 16 displayGeometricObject(Círculo(5));
passe um retângulo 17 displayGeometricObject(Retângulo(4, 6));
18
19 retornar 0;
20 }

Objeto geométrico
Objeto geométrico
Objeto geométrico

Observe os seguintes pontos em relação às funções virtuais:

virtual n Se uma função for definida como virtual em uma classe base, ela será automaticamente virtual em
todas as suas classes derivadas. A palavra-chave virtual não precisa ser adicionada na declaração
da função na classe derivada.

n Corresponder uma assinatura de função e vincular uma implementação de função são duas questões
distintas. O tipo declarado da variável decide qual função corresponder em tempo de compilação.
Esta é uma ligação estática. O compilador encontra uma função correspondente de acordo com o
tipo de parâmetro, número de parâmetros e ordem dos parâmetros em tempo de compilação.
Uma função virtual pode ser implementada em diversas classes derivadas. C++ dinamicamente
Machine Translated by Google

15.7 Funções Virtuais e Vinculação Dinâmica 599

vincula a implementação da função em tempo de execução, decidida pela classe real do objeto ligação estática versus ligação
referenciado pela variável. Esta é uma ligação dinâmica. dinâmica

n Se uma função definida em uma classe base precisar ser redefinida em suas classes derivadas, você
deverá defini-la como virtual para evitar confusões e erros. Por outro lado, se uma função não for
redefinida, é mais eficiente não declará-la virtual, porque são necessários mais tempo e recursos do
sistema para vincular funções virtuais dinamicamente em tempo de execução. Chamamos uma
classe com função virtual de tipo polimórfico. tipo polimórfico

15.12 Responda às seguintes perguntas para o programa abaixo:


ÿVerificação de ponto
1 #include <iostream>
2 usando namespace std;

3 4 classe Pai
{
5 6 público:
7 vazio f()
8{

cout << "invocar f do pai" << endl;


9 10}
11};
12
13ª série Criança: Pai público
14 {
15 público:
16 vazio f()
17 {
18} cout << "invocar f do filho" << endl;
19
20};
21
22 nulo p(pai a)
23 {
24 de();
25}
26
27 int principal()
28 {
29 Pai a;
30 de();
31 p(uma);
32
33 Criança b;
34 namorado();
35 p(b);
36
retornar 0;
37 38}

a. Qual é o resultado deste programa?


b. Qual será o resultado se a linha 7 for substituída por virtual void f()? c. Qual será a
saída se a linha 7 for substituída por virtual void f() e a linha 22 for substituída por void p(Parent&
a)?

15.13 O que é ligação estática e o que é ligação dinâmica?

15.14 A declaração de funções virtuais é suficiente para permitir a vinculação dinâmica?


Machine Translated by Google

600 Capítulo 15 Herança e Polimorfismo

15.15 Mostre a saída do seguinte código:

#include <iostream> #include <iostream>


#include <string> #include <string>
usando namespace std; usando namespace std;

classe Pessoa classe Pessoa

{ público: { público:
void printInfo() { void printInfo() {

cout << getInfo() << endl; } cout << getInfo() << endl; }

string virtual getInfo() { string getInfo() {

retornar "Pessoa"; retornar "Pessoa";

} }; } };

class Aluno: public Person { public: classe Aluno: public Person { public:

string string
virtual getInfo() { getInfo() {

retornar "Aluno"; retornar "Aluno";

} }; } };

int principal() int principal()


{ {
Pessoa().printInfo(); Pessoa().printInfo();
Aluno().printInfo(); Aluno().printInfo();
} }

(a) (b)

15.16 É uma boa prática definir todas as funções como virtuais?

15.8 A palavra-chave protegida


Um membro protegido de uma classe pode ser acessado a partir de uma classe derivada.
Chave
Apontar
Até agora, você usou as palavras-chave private e public para especificar se os campos de dados e funções podem ser
acessados de fora da classe. Membros privados podem ser acessados apenas de dentro da classe ou de funções de
amigos e classes de amigos , e membros públicos podem ser acessados de quaisquer outras classes.

Muitas vezes é desejável permitir que classes derivadas acessem campos de dados ou funções definidas na classe

base, mas não permitir que classes não derivadas o façam. Para este propósito, você pode usar a palavra-chave
protegido protegida . Um campo de dados protegido ou uma função protegida em uma classe base pode ser acessada em suas
classes derivadas.

palavra-chave de visibilidade As palavras-chave private, protected e public são conhecidas como palavras-chave de visibilidade ou acessibilidade
porque especificam como a classe e os membros da classe são acessados. Sua visibilidade aumenta nesta ordem:

A visibilidade aumenta

privado, protegido, público


Machine Translated by Google

15.9 Classes Abstratas e Funções Virtuais Puras 601

A Listagem 15.12 demonstra o uso de palavras-chave protegidas .

Listagem 15.12 VisibilityDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 classe B
5{
6 público: público
7int eu ;

8 9 protegido: protegido
10 intj ;
11
12 privados: privado
13intk ; _
14};
15
16 classe A: público B
17 {
18 público:
19 exibição nula () const
20 {
21 cout << i << endl; //Tudo bem, posso acessá-lo
22 cout << j << endl; //Tudo bem, posso acessá-lo
23} cout << k << endl; // Errado, não é possível acessá-lo

24 25};
26
27 int principal()
28 {
29 Uma;
30 cout << ai << endl; //Tudo bem, posso acessá-lo
31 cout << aj << endl; // Errado, não é possível acessá-lo
32 cout << ak << endl; // Errado, não é possível acessá-lo
33
34 retornar 0;
35 }

Como A é derivado de B e j é protegido, j pode ser acessado da classe A na linha 22.


Como k é privado, k não pode ser acessado pela classe A.
Como i é público, i pode ser acessado a partir de ai na linha 30. Como j e k não são públicos, eles não podem
ser acessados a partir do objeto a nas linhas 31–32.

15.17 Se um membro for declarado privado em uma classe, ele poderá ser acessado de outras classes? Se um
membro for declarado protegido em uma classe, ele poderá ser acessado por outras classes? Se ÿVerificação de ponto

um membro for declarado público em uma classe, ele poderá ser acessado de outras classes?

15.9 Classes abstratas e funções virtuais puras


Uma classe abstrata não pode ser usada para criar objetos. Uma classe abstrata pode conter Nota de vídeo
Chave
funções abstratas, que são implementadas em classes derivadas concretas. Classes abstratas de pontos

Na hierarquia de herança, as classes tornam-se mais específicas e concretas a cada nova classe derivada. Se
você passar de uma classe derivada de volta para suas classes pai e ancestral, as classes se tornarão mais
gerais e menos específicas. O design da classe deve garantir que uma classe base contenha
Machine Translated by Google

602 Capítulo 15 Herança e Polimorfismo


características comuns de suas classes derivadas. Às vezes, uma classe base é tão abstrata que não pode
classe abstrata ter nenhuma instância específica. Essa classe é chamada de classe abstrata.
Na Seção 15.2, GeometricObject foi definido como a classe base para Circle e Rectangle.
GeometricObject modela recursos comuns de objetos geométricos. Tanto Circle quanto Rectangle contêm
as funções getArea() e getPerimeter() para calcular a área e o perímetro de um círculo e de um retângulo.
Como você pode calcular áreas e perímetros para todos os objetos geométricos, é melhor definir as funções
getArea() e getPerime-ter() na classe GeometricObject . Porém, essas funções não podem ser
implementadas na classe GeometricObject , pois sua implementação depende do tipo específico de objeto
geométrico. Essas funções são chamadas de funções abstratas. Depois de definir as funções abstratas em
função abstrata GeometricObject, GeometricObject se torna uma classe abstrata. A nova classe GeometricObject é
mostrada na Figura 15.2. Na notação gráfica UML, os nomes das classes abstratas e suas funções abstratas
estão em itálico, conforme mostrado na Figura 15.2.

função virtual pura Em C++, funções abstratas são chamadas de funções virtuais puras. Uma classe que contém vírus puro
funções reais se torna uma classe abstrata. Uma função virtual pura é definida desta forma:

Coloque const opcional Indica função virtual pura


para função constante aqui.

duplo virtual getArea() = 0;

A notação = 0 indica que getArea é uma função virtual pura. Uma função virtual pura
não possui corpo ou implementação na classe base.
A Listagem 15.13 define a nova classe abstrata GeometricObject com duas funções virtuais puras nas
linhas 18–19.

Listagem 15.13 AbstractGeometricObject.h


1 #ifndef GEOMETRICOBJECT_H
2 #define GEOMETRICOBJECT_H
3 #incluir <string>
4 usando namespace std;

5 6 classe GeometricObject
7{
8 protegido:
GeometricObject();
9 10 GeometricObject (const string e cor, bool preenchido);
11
12 público:
13 string getColor() const;
14 void setColor(const string& cor);
15 bool isFilled() const;
16 void setFilled(bool preenchido);
17 string toString() const;
função virtual pura 18 virtual duplo getArea() const = 0;
função virtual pura 19 duplo virtual getPerimeter() const = 0;
20
21 privado:
Cor de 22 cordas;
23 bool preenchido;
24}; //Deve colocar ponto e vírgula aqui

25 26 #endif
Machine Translated by Google

15.9 Classes Abstratas e Funções Virtuais Puras 603

O nome da classe
Objeto Geométrico
abstrata está em itálico

-color: string -filled:


bool

#GeometricObject()
O sinal # indica #GeometricObject(cor: string&, preenchido: bool)
modificador protegido +getColor(): string
const +setColor(cor: string&): void
+isFilled(): bool const +setFilled(preenchido: bool):
void +toString(): string const
+getArea(): const duplo +getPerimeter(): const
duplo
Funções abstratas
estão em itálico

Círculo Retângulo

-raio: duplo -largura: duplo


-altura: duplo
+Círculo()
+Círculo(raio: duplo) +Retângulo()
+Círculo(raio: duplo, cor: string&, +Retângulo (largura: duplo, altura: duplo)
preenchido: bool)
+Retângulo(largura: duplo, altura: duplo, cor: string&, preenchido:
+getRadius(): const duplo +setRadius(raio: bool)
duplo): void +getDiameter(): const duplo +getWidth(): double const
+setWidth(largura: double): void +getHeight():
double const +setHeight(height: double):
void

Figura 15.2 A nova classe GeometricObject contém funções abstratas.

GeometricObject é como uma classe normal, exceto que você não pode criar objetos a partir
dela porque é uma classe abstrata. Se você tentar criar um objeto a partir de GeometricObject,
o compilador reportará um erro.
A Listagem 15.14 fornece uma implementação da classe GeometricObject .

Listagem 15.14 AbstractGeometricObject.cpp


1 #include "AbstractGeometricObject.h" 2 3 incluir cabeçalho

GeometricObject::GeometricObject() 4 { 5 6 7 } 8 9

cor = "branco";
preenchido = falso;

GeometricObject::GeometricObject(const string& cor, bool preenchido) 10 { 11 12 13 } 14 15 string

setColor(cor);
setFilled(preenchido);

GeometricObject::getColor( ) const
Machine Translated by Google

604 Capítulo 15 Herança e Polimorfismo


16 {
17 retornar cor;
18}
19
20 void GeometricObject::setColor(const string& cor)
21 {
22 isto->cor = cor;
23}
24
25 bool GeometricObject::isFilled() const
26 {
27 retorno preenchido;
28}
29
30 void GeometricObject::setFilled(bool preenchido)
31 {
32 isto->preenchido = preenchido;
33}
34
35 string GeometricObject::toString() const
36 {
37 retornar "Objeto Geométrico";
38}

As Listagens 15.15, 15.16, 15.17 e 15.18 mostram os arquivos para o novo Círculo e Retângulo
classes derivadas do GeometricObject abstrato.

Listagem 15.15 DerivedCircleFromAbstractGeometric-Object.h

incluir cabeçalho 1 #ifndef CÍRCULO_H


2 #define CÍRCULO_H
3 #include "AbstractGeometricObject.h"

4 5 classe Círculo: GeometricObject público


6{
7 público:
8 Círculo();
Círculo(duplo);
9 10 Círculo ( raio duplo, string const e cor, bool preenchido);
11 double getRadius() const;
12 void setRadius(duplo);
13 double getArea() const;
14 double getPerimeter() const;
15 double getDiameter() const;
16
17 privado:
18 raio duplo ;
19}; //Deve colocar ponto e vírgula aqui
20
21 #endif

Listagem 15.16 DerivedCircleFromAbstractGeometric-


Object.cpp
guarda de inclusão 1 #include "DerivedCircleFromAbstractGeometricObject.h"

ResumoGeométrico- 2 3 // Construa um objeto circular padrão


Cabeçalho do objeto 4 Círculo::Círculo()
Machine Translated by Google

15.9 Classes Abstratas e Funções Virtuais Puras 605

5{
raio = 1;
6 7}
8
9 // Construa um objeto circular com raio especificado
10 Círculo::Círculo( raio duplo)
11 {
12 setRaio(raio);
13}
14
15 // Construa um objeto circular com raio e cor especificados e preenchido
16 Círculo::Círculo( raio duplo, string const e cor, bool preenchido)
17 {
18 setRaio(raio);
19 setColor(cor);
setFilled(preenchido);
20 21}
22
23 // Retorna o raio deste círculo
24 círculo duplo ::getRadius() const
25 {
26 raio de retorno ;
27}
28
29 // Defina um novo raio
30 círculo vazio ::setRadius( raio duplo)
31 {
isto->raio = (raio >= 0) ? raio: 0;
32 33}
34
35 // Retorna a área deste círculo
36 círculo duplo ::getArea() const
37 {
raio de retorno * raio * 3,14159;
38 39}
40
41 // Retorna o perímetro deste círculo
42 círculo duplo ::getPerimeter() const
43 {
44 retornar 2 * raio * 3,14159;
45}
46
47 // Retorna o diâmetro deste círculo
48 círculo duplo ::getDiameter() const
49 {
retornar 2 * raio;
50 51}

Listagem 15.17 DerivedRectangleFromAbstractGeometric-


Object.h
1 #ifndef RECTANGLE_H guarda de inclusão
2 #define RECTANGLE_H
3 #include "AbstractGeometricObject.h" ResumoGeométrico-
Cabeçalho do objeto

4 5 classes Retângulo: GeometricObject público


6{
7 público:
8 Retângulo();
Machine Translated by Google

606 Capítulo 15 Herança e Polimorfismo


Retângulo ( largura dupla, altura dupla );
9 Retângulo ( largura dupla, altura dupla , string const
10 e cor, bool preenchido);
11 double getWidth() const;
12 void setWidth(duplo);
13 double getHeight() const;
14 void setHeight(duplo);
15 double getArea() const;
16 double getPerimeter() const;
17
18 19 privado:
20 largura dupla ;
21 altura dupla ;
22}; //Deve colocar ponto e vírgula aqui

23 24 #endif

Listagem 15.18 DerivedRectangleFromAbstractGeometric-


Object.cpp
guarda de inclusão 1 #include "DerivedRectangleFromAbstractGeometricObject.h"

ResumoGeométrico- 2 3 // Construa um objeto retangle padrão


Cabeçalho do objeto 4 Retângulo::Retângulo()
{
largura = 1;
altura = 1;
5678}
9
10 // Construa um objeto retângulo com largura e altura especificadas
11 Retângulo::Retângulo ( largura dupla, altura dupla )
12 {
13 setWidth(largura);
14 setAltura(altura);
15}
16
17 // Construa um objeto retângulo com largura, altura, cor, preenchido
18 Retângulo::Retângulo ( largura dupla, altura dupla ,
19 const string& cor, bool preenchido)
20 {
21 setWidth(largura);
setAltura(altura);
setColor(cor);
22 setFilled(preenchido);
23 24 25 }
26
27 // Retorna a largura deste retângulo
28 retângulo duplo ::getWidth() const
29 {
largura de retorno ;
30 31}
32
33 // Defina um novo raio
34 retângulo vazio ::setWidth( largura dupla)
35 {
isto->largura = (largura >= 0) ? largura: 0;
36 37}
38
39 // Retorna a altura deste retângulo
Machine Translated by Google

15.9 Classes Abstratas e Funções Virtuais Puras 607

40 retângulo duplo ::getHeight() const


41 {
altura de retorno ;
42 43}
44
45 // Defina uma nova altura
46 Retângulo vazio ::setHeight( altura dupla)
47 {
48 isto->altura = (altura >= 0) ? altura: 0;
49}
50
51 // Retorna a área deste retângulo
52 retângulo duplo ::getArea() const
53 {
retornar largura * altura;
54 55}
56
57 // Retorna o perímetro deste retângulo
58 retângulo duplo ::getPerimeter() const
59 {
60 return 2 * (largura + altura);
61}

Você pode estar se perguntando se as funções abstratas getArea e getPerimeter


deve ser removido da classe GeometricObject . O exemplo a seguir na Listagem 15.19
mostra os benefícios de defini-los na classe GeometricObject .
Este exemplo apresenta um programa que cria dois objetos geométricos (um círculo
e um retângulo), invoca a função equalArea para verificar se os dois objetos têm áreas
iguais e invoca a função displayGeometricObject para exibir os objetos.

Listagem 15.19 TestAbstractGeometricObject.cpp


1 #include "AbstractGeometricObject.h" incluir arquivo de cabeçalho

2 #include "DerivedCircleFromAbstractGeometricObject.h"
3 #include "DerivedRectangleFromAbstractGeometricObject.h"
4 #include <iostream>
5 usando namespace std;

6 7 // Uma função para comparar as áreas de dois objetos geométricos


8 bool equalArea(const GeometricObject& g1,
const GeometricObject& g2)
9 10 {
11 retornar g1.getArea() == g2.getArea(); vinculação dinâmica
12}
13
14 // Uma função para exibir um objeto geométrico
15 displayGeometricObject vazio (const GeometricObject & g)
16 {
17 cout << "A área é " cout << << g.getArea() << endl; vinculação dinâmica
18 "O perímetro é " << g.getPerimeter() << endl; vinculação dinâmica
19}
20
21 int principal()
22 {
Círculo círculo(5);
23 24 Retângulo retângulo(5, 3);
25
26 cout << "Informações do círculo: " << endl;
27 displayGeometricObject(círculo);
Machine Translated by Google

608 Capítulo 15 Herança e Polimorfismo


28
29 cout << "\nInformações do retângulo: " << endl;
30 displayGeometricObject(retângulo);
31
"
32 cout << "\nOs dois objetos possuem a mesma área? <<
33 (equalArea(círculo, retângulo) ? "Sim" : "Não") << endl;
34
35 retornar 0;
36 }

Informações do círculo:

A área é 78,5397
O perímetro é 31,4159

Informações do retângulo:
A área é 15
O perímetro é 16

Os dois objetos têm a mesma área? Não

O programa cria um objeto Circle e um objeto Rectangle nas linhas 23–24.


As funções virtuais puras getArea() e getPerimeter() definidas no
As classes GeometricObject são substituídas nas classes Circle e Rectangle .
Ao invocar displayGeometricObject(circle1) (linha 27), são utilizadas as funções getArea e
getPerimeter definidas na classe Circle , e ao invocar displayGeometricObject(rectangle) (linha 30),
são utilizadas as funções getArea e getPerimeter definidas na classe Rectangle . C++ determina
dinamicamente quais dessas funções invocar em tempo de execução, dependendo do tipo de objeto.

Da mesma forma, ao invocar equalArea(circle, retângulo) (linha 33), o getArea


A função definida na classe Circle é usada para g1.getArea(), pois g1 é um círculo. Além disso, a
função getArea definida na classe Rectangle é usada para g2.getArea(), já que g2
é um retângulo.
Observe que se as funções getArea e getPerimeter não foram definidas em GeometricObject, você
não poderá definir as funções equalArea e displayObject neste programa. Então, agora você vê os
por que funções abstratas? benefícios de definir as funções abstratas em GeometricObject.

15.18 Como você define uma função virtual pura?


ÿVerificação de ponto
15.19 O que há de errado no código a seguir?

classe A
{
público:
vazio virtual f() = 0;
};

int principal()
{
Uma;

retornar 0;
}
Machine Translated by Google

15.10 Transmissão: static_cast versus dynamic_cast 609

15.20 Você pode compilar e executar o código a seguir? Qual será o resultado?

#include <iostream>
usando namespace std;

classe A

{ público:
vazio virtual f() = 0; };

classe B: público A

{ público:
void f() {

cout << "invocar f de B" << endl; } };

classe C: público B

{ público:
vazio virtual m() = 0; };

classe D: público C

{ público:
virtual void m() { cout

<< "invocar m de D" << endl;

} };

void p(A& a)

{ af();
}

int principal()
{
Dd;
p(d);
dm();

retornar 0;
}

15.21 As funções getArea e getPerimeter podem ser removidas do


Classe GeometricObject . Quais são os benefícios de definir getArea e
getPerimeter como funções abstratas na classe GeometricObject ?

15.10 Transmissão: static_cast versus dynamic_cast


O operador dynamic_cast pode ser usado para converter um objeto em seu tipo real em tempo de execução.
Chave
Apontar
Suponha que você queira reescrever a função displayGeometricObject na Listagem
15.19, TestAbstractGeometricObject.cpp, para exibir o raio e o diâmetro de um objeto círculo.
Machine Translated by Google

610 Capítulo 15 Herança e Polimorfismo

e a largura e altura de um objeto retângulo. Você pode tentar implementar a função da seguinte maneira:

void displayGeometricObject(GeometricObject& g)
{
cout << "O raidus é " << g.getRadius() << endl;
cout << "O diâmetro é " << g.getDiameter() << endl;

cout << "A largura é " << g.getWidth() << endl;


cout << "A altura é " << g.getHeight() << endl;

cout << "A área é " << g.getArea() << endl;


cout << "O perímetro é " << g.getPerimeter() << endl;
}

Existem dois problemas com este código. Primeiro, o código não pode ser compilado porque o tipo de g é
GeometricObject, mas a classe GeometricObject não contém as funções getRadius(), getDiameter() ,
getWidth() e getHeight() . Segundo, seu código deve detectar se o objeto geométrico é um círculo ou um
retângulo e então exibir o raio e o diâmetro para um círculo e a largura e a altura para um retângulo.

Operador static_cast Os problemas podem ser corrigidos convertendo g em Círculo ou Retângulo, conforme mostrado no código
a seguir:

void displayGeometricObject(GeometricObject& g)
{
GeometricObject* p = &g;
cout << "O raidus é " <<
lançando para Círculo* static_cast<Circle*>(p)->getRadius() << endl;
cout << "O diâmetro é " <<
static_cast<Circle*>(p)->getDiameter() << endl;

cout << "A largura é " <<


static_cast<Rectângulo*>(p)->getWidth() << endl;
cout << "A altura é " <<
static_cast<Rectângulo*>(p)->getHeight() << endl;

cout << "A área é " << g.getArea() << endl;


cout << "O perímetro é " << g.getPerimeter() << endl;
}

A conversão estática é realizada em p que aponta para um GeometricObject g (linha 3). Esta nova função
pode ser compilada, mas ainda está incorreta. Um objeto Circle pode ser convertido em Rectangle para invocar
getWidth() na linha 10. Da mesma forma, um objeto Rectangle pode ser convertido em Circle para invocar
getRadius() na linha 5. Precisamos garantir que o objeto é de fato um objeto Circle antes de invocar getRaio().
por que fundição dinâmica? Isso pode ser feito usando dynamic_cast.
operador dinâmico_cast O dynamic_cast funciona como o static_cast. Além disso, ele executa verificação de tempo de execução
para garantir que a transmissão seja bem-sucedida. Se a conversão falhar, ele retornará NULL. Então, se você
executar o código a seguir,

1 retângulo retângulo (5, 3);


2 Objeto Geométrico* p = &retângulo;
3 Círculo* p1 = dynamic_cast<Círculo*>(p);
4 cout << (*p1).getRadius() << endl;

NULO p1 será NULO. Ocorrerá um erro de tempo de execução ao executar o código na linha 4. Lembre-se de que
NULL é definido como 0, o que indica que um ponteiro não aponta para nenhum objeto. A definição de NULL
está em diversas bibliotecas padrão, incluindo <iostream> e <cstddef>.
Machine Translated by Google

15.10 Transmissão: static_cast versus dynamic_cast 611

Observação

Atribuir um ponteiro de um tipo de classe derivada a um ponteiro de seu tipo de classe base é chamado
de upcasting, e atribuir um ponteiro de um tipo de classe base a um ponteiro de seu tipo de classe upcasting e downcasting
derivada é chamado de downcasting. O upcasting pode ser executado implicitamente sem usar o
operador static_cast ou dynamic_cast . Por exemplo, o código a seguir está correto:

GeometricObject* p = novo Círculo(1);


Círculo* p1 = novo Círculo(2);
p=p1;

No entanto, o downcasting deve ser executado explicitamente. Por exemplo, para atribuir p a p1, você
deve usar

p1 = static_cast<Círculo*>(p);
ou

p1 = dynamic_cast<Círculo*>(p);

Observação

dynamic_cast pode ser executado apenas no ponteiro ou na referência de um tipo polimórfico; ou dynamic_cast para função
seja, o tipo contém uma função virtual. dynamic_cast pode ser usado para verificar se a conversão foi virtual
executada com sucesso em tempo de execução. static_cast é executado em tempo de compilação.

Agora você pode reescrever a função displayGeometricObject usando conversão dinâmica, como
na Listagem 15.20, para verificar se a conversão foi bem-sucedida em tempo de execução.

Listagem 15.20 DynamicCastingDemo.cpp


1 #include "AbstractGeometricObject.h" incluir arquivo de cabeçalho

2 #include "DerivedCircleFromAbstractGeometricObject.h"
3 #include "DerivedRectangleFromAbstractGeometricObject.h"
4 #include <iostream>
5 usando namespace std;

6 7 // Uma função para exibir um objeto geométrico


8 exibição vaziaGeometricObject(GeometricObject& g)
9{
10 cout << "A área é " cout << << g.getArea() << endl;
11 "O perímetro é " << g.getPerimeter() << endl;
12
13 GeometricObject* p = &g;
14 Círculo* p1 = dynamic_cast<Círculo*>(p); lançando para Círculo
15 Retângulo* p2 = dynamic_cast<Retângulo*>(p); lançando para retângulo
16
17 se (p1! = NULO)
18 {
19 cout << "O raio é " << p1->getRadius() << endl;
20 cout << "O diâmetro é " << p1->getDiameter() << endl;
21 }
22
23 se (p2! = NULO)
24 {
25 cout << "A largura é " << p2->getWidth() << endl;
26 cout << "A altura é " << p2->getHeight() << endl;
27 }
28 }

29 30 int principal()
31 {
32 Círculo círculo(5);
Machine Translated by Google

612 Capítulo 15 Herança e Polimorfismo


33 Retângulo retângulo(5, 3);
34
35 cout << "Informações do círculo: " << endl;
36 displayGeometricObject(círculo);
37
38 cout << "\nInformações do retângulo: " << endl;
39 displayGeometricObject(retângulo);
40
41 retornar 0;
42 }

Informações do círculo:

A área é 78,5397
O perímetro é 31,4159
O raio é 5
O diâmetro é 10

Informações do retângulo:
A área é 15
O perímetro é 16
A largura é 5

A altura é 3

A linha 13 cria um ponteiro para um GeometricObject g. O operador dynamic_cast


(linha 14) verifica se o ponteiro p aponta para um objeto Circle . Se sim, o endereço do
objeto é atribuído a p1; caso contrário, p1 é NULO. Se p1 não for NULL, o getRadius()
e as funções getDiameter() do objeto Circle (apontadas por p1) são invocadas nas linhas
19–20. Da mesma forma, se o objeto for um retângulo, sua largura e altura serão exibidas
nas linhas 25–26.
O programa invoca a função displayGeometricObject para exibir um Círculo
objeto na linha 36 e um objeto Rectangle na linha 39. A função converte o parâmetro g em
um ponteiro Circle p1 na linha 14 e um ponteiro Rectangle p2 na linha 15. Se for um objeto
Circle , getRadius() e getDiameter( ) as funções são invocadas nas linhas 19–20. Se for
um objeto Rectangle , as funções getWidth() e getHeight() do objeto serão invocadas nas
linhas 25–26.
A função também invoca getArea() e getPerimeter() do GeometricObject
funções nas linhas 10–11. Como essas duas funções são definidas no GeometricObject
class, não há necessidade de reduzir o parâmetro do objeto para Circle ou Rectangle para
invocá-los.

Dica
Ocasionalmente, é útil obter informações sobre a classe do objeto. Você pode usar o
operador typeid operador typeid para retornar uma referência a um objeto da classe type_
informações. Por exemplo, você pode usar a seguinte instrução para exibir o nome da
classe do objeto x:

cadeia x;
cout << typeid(x).name() << endl;

Ele exibe string, porque x é um objeto da classe string . Para usar o typeid
operador, o programa deve incluir o arquivo de cabeçalho <typeinfo> .
Machine Translated by Google

15.10 Transmissão: static_cast versus dynamic_cast 613

Dica
É uma boa prática sempre definir destruidores virtuais. Suponha que a classe Child seja definir destruidor virtual
derivada da classe Parent e os destruidores não sejam virtuais. Considere o seguinte código:

Pai* p = novo Filho;


...
excluir p;

Quando delete é invocado com p, o destruidor de Parent é chamado, pois p é declarado um


ponteiro para Parent. p na verdade aponta para um objeto de Child, mas o destruidor de
Child nunca é chamado. Para resolver o problema, defina o destruidor virtual na classe
Parent. Agora, quando delete é invocado com p, o destruidor de Child é chamado e então o
destruidor de Parent é chamado, já que os construtores são virtuais.

15.22 O que é upcasting? O que é downcasting?


ÿVerificação de ponto
15.23 Quando você precisa fazer downcast de um objeto de um tipo de classe base para um tipo derivado?
tipo de classe?

15.24 Qual será o valor em p1 após as seguintes afirmações?

GeometricObject* p = new Rectangle(2, 3);


Círculo* p1 = novo Círculo(2); p1 =
dynamic_cast<Círculo*>(p);

15.25 Analise o seguinte código:

#include <iostream>
usando namespace std;

classe Pai { };

class Filho: public Parent { public:

void m()
{ cout <<

"invoke m" << endl;

} };

int principal()
{
Pai* p = new Filho();

// A ser substituído nas questões abaixo

retornar 0;
}

a. Quais erros de compilação você obterá se a linha destacada for substituída pela seguinte
código?

(*PM();
Machine Translated by Google

614 Capítulo 15 Herança e Polimorfismo

b. Quais erros de compilação você obterá se a linha destacada for substituída pela seguinte
código?

Filho* p1 = dynamic_cast<Filho*>(p);
(*p1).m();

c. O programa será compilado e executado se a linha destacada for substituída pelo


seguinte código?

Filho* p1 = static_cast<Filho*>(p);
(*p1).m();

d. O programa será compilado e executado se virtual void m() { } for adicionado no pai
class e a linha destacada é substituída dynamic_cast<Child*>(p)->m();?

15.26 Por que você deveria definir um destruidor virtual?

Termos chave
classe abstrata 602 classe pai 580
função abstrata 602 tipo polimórfico 599
classe base 580 polimorfismo 595
classe infantil 580 protegido 600
encadeamento de construtor 590 função virtual pura 602
classe derivada 580 subclasse 580
encadeamento de destruidor 590 subtipo 595
abatido 611 superclasse 580
ligação dinâmica 597 supertipo 595
programação genérica 588 upcasting 611
herança 580 função virtual 597
função de substituição 597

Resumo do capítulo
1. Você pode derivar uma nova classe de uma classe existente. Isso é conhecido como herança de classe.
A nova classe é chamada de classe derivada, classe filha ou subclasse. A classe existente é chamada
de classe base, classe pai ou superclasse.

2. Um objeto de uma classe derivada pode ser passado sempre que um objeto de um parâmetro de tipo base for
necessário. Então uma função pode ser usada genericamente para uma ampla gama de argumentos de objetos.
Isso é conhecido como programação genérica.

3. Um construtor é usado para construir uma instância de uma classe. Diferentemente dos campos de
dados e funções, os construtores de uma classe base não são herdados na classe derivada. Eles só
podem ser invocados a partir dos construtores das classes derivadas para inicializar os campos de
dados na classe base.

4. Um construtor de classe derivada sempre invoca seu construtor de classe base. Se um construtor base
não for invocado explicitamente, o construtor no-arg da classe base será invocado por padrão.

5. Construir uma instância de uma classe invoca os construtores de todas as classes base junto
a cadeia de herança.
Machine Translated by Google

Exercícios de Programação 615

6. Um construtor de classe base é chamado a partir de um construtor de classe derivada. Por outro lado, os
destruidores são invocados automaticamente na ordem inversa, com o destruidor da classe derivada
invocado primeiro. Isso é chamado de encadeamento de construtor e destruidor.

7. Uma função definida na classe base pode ser redefinida na classe derivada. Uma função redefinida deve
corresponder à assinatura e ao tipo de retorno da função na classe base.

8. Uma função virtual permite ligação dinâmica. Uma função virtual é frequentemente redefinida nas classes
derivadas. O compilador decide qual implementação de função usar dinamicamente em tempo de
execução.

9. Se uma função definida em uma classe base precisar ser redefinida em suas classes derivadas, você
deverá defini-la como virtual para evitar confusões e erros. Por outro lado, se uma função não for
redefinida, é mais eficiente não declará-la virtual, porque são necessários mais tempo e recursos do
sistema para vincular funções virtuais dinamicamente em tempo de execução.

10. Um campo de dados protegido ou uma função protegida em uma classe base pode ser acessada em seu
classes derivadas.

11. Uma função virtual pura também é chamada de função abstrata.

12. Se uma classe contém uma função virtual pura, a classe é chamada de classe abstrata.

13. Você não pode criar instâncias de uma classe abstrata, mas classes abstratas podem ser usadas como
tipos de dados para parâmetros em uma função para permitir programação genérica.

14. Você pode usar os operadores static_cast e dynamic_cast para converter um objeto de um tipo de classe
base em um tipo de classe derivado. static_cast é executado em tempo de compilação e dynamic_cast
é executado em tempo de execução para verificação de tipo de tempo de execução. A dinamica_
O operador cast só pode ser executado em um tipo polimórfico (ou seja, o tipo com funções virtuais).

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 15.2–15.4
15.1 (A classe Triangle ) Projete uma classe chamada Triangle que estenda GeometricObject. A classe
contém o seguinte:

n Três campos de dados duplos denominados side1, side2 e side3 para indicar três
lados do triângulo.
n Um construtor sem argumentos que cria um triângulo padrão com cada lado 1,0.
n Um construtor que cria um retângulo com os lados1, lado2 e
lado3.
n O acessador constante funciona para todos os três campos de dados.
n Uma função constante chamada getArea() que retorna a área deste triângulo.
n Uma função constante chamada getPerimeter() que retorna o perímetro deste
triângulo.
Machine Translated by Google

616 Capítulo 15 Herança e Polimorfismo

Desenhe o diagrama UML que envolve as classes Triângulo e Objeto Geométrico. Implemente
a classe. Escreva um programa de teste que solicite ao usuário que insira três lados do triângulo,
insira uma cor e insira 1 ou 0 para indicar se o triângulo está preenchido. O programa deve criar um
objeto Triângulo com esses lados e definir a cor e as propriedades de preenchimento usando a
entrada. O programa deve exibir área, perímetro, cor e verdadeiro ou falso para indicar se preenchido
ou não.

Seções 15.5–15.10

15.2 (As classes Pessoa, Aluno, Funcionário , Corpo Docente e Funcionários ) Projete uma
classe chamada Pessoa e suas duas classes derivadas denominadas Aluno e Funcionário.
Crie classes de funcionários derivadas de professores e funcionários. Uma pessoa tem nome,
endereço, número de telefone e endereço de e-mail. Um aluno tem um status de turma (calouro,
segundo ano, júnior ou sênior). Um funcionário tem cargo, salário e data de contratação. Defina
uma classe chamada MyDate que contenha os campos ano, mês e dia. Um membro do corpo
docente tem horário de expediente e uma classificação. Um membro da equipe tem um título.
Defina uma função toString virtual constante na classe Person e substitua-a em cada classe para
exibir o nome da classe e o nome da pessoa.

Desenhe o diagrama UML para as classes. Implemente as aulas. Escreva um programa


de teste que crie uma Pessoa, um Aluno, um Funcionário, um Corpo Docente e uma
Equipe e invoque suas funções toString() .
15.3 (Estender MyPoint) No Exercício de Programação 9.4, a classe MyPoint foi criada para modelar um
ponto em um espaço bidimensional. A classe MyPoint possui as propriedades xey que representam
Nota de vídeo
as coordenadas xey, duas funções get para xey e a função para retornar a distância entre dois
A classe MyPoint
pontos. Crie uma classe chamada ThreeDPoint para modelar um ponto em um espaço tridimensional.
Deixe ThreeDPoint
ser derivado do MyPoint com os seguintes recursos adicionais:

n Um campo de dados denominado z que representa a coordenada z.


n Um construtor sem argumentos que constrói um ponto com coordenadas (0, 0, 0).
n Um construtor que constrói um ponto com três coordenadas especificadas.
n Uma função get constante que retorna o valor z .
n Uma função de distância constante (const MyPoint&) para retornar a distância entre este ponto
e o outro ponto no espaço tridimensional.

Desenhe o diagrama UML para as classes envolvidas. Implemente as aulas. Escreva um programa
de teste que crie dois pontos (0, 0, 0) e (10, 30, 25,5) e exiba a distância entre eles.

15.4 (Classes derivadas de Account) No Exercício de Programação 9.3, a classe Account foi criada para
modelar uma conta bancária. Uma conta possui o número da conta da propriedade, saldo e taxa de
juros anual, data de criação e funções para depósito e retirada. Crie duas classes derivadas para
contas correntes e poupanças.
A conta corrente tem limite de cheque especial, mas a conta poupança não pode estar com saque
a descoberto. Defina uma função toString() virtual constante na classe Account e substitua-a nas
classes derivadas para retornar o número da conta e o saldo como uma string.

Desenhe o diagrama UML para as classes. Implemente as aulas. Escreva um programa de teste que
crie objetos Account, SavingsAccount e CheckingAccount e invoque suas funções toString() .

15.5 (Implementar uma classe de pilha usando herança) Na Listagem 12.4, GenericStack é implementado
usando arrays. Crie uma nova classe de pilha que estenda o vetor. Desenhe o diagrama UML para
as classes. Implementá-lo.
Machine Translated by Google

CAPÍTULO

16
Manipulação de exceção

Objetivos
n Para obter uma visão geral das exceções e do tratamento de exceções (§16.2).

n Saber como lançar uma exceção e como capturá-la (§16.2).

n Explorar as vantagens de usar o tratamento de exceções (§16.3).

n Para criar exceções usando classes de exceção padrão C++ (§16.4).

n Para definir classes de exceção personalizadas (§16.5).

n Para capturar múltiplas exceções (§16.6).

n Explicar como uma exceção é propagada (§16.7).

n Para relançar exceções em um bloco catch (§16.8).


n Para declarar funções com especificação de exceção (§16.9).

n Usar adequadamente o tratamento de exceções (§16.10).


Machine Translated by Google

618 Capítulo 16 Tratamento de exceções

16.1 Introdução
O tratamento de exceções permite que um programa lide com situações excepcionais e continue
Chave
sua execução normal.
Apontar

Uma exceção indica uma situação incomum que ocorre durante a execução de um programa. Por exemplo, suponha
que seu programa use um vetor v para armazenar elementos. O programa acessa um elemento do vetor usando v[i],
assumindo que o elemento no índice i existe. A situação excepcional ocorre quando o elemento no índice i não
existe. Você deve escrever o código no programa para lidar com exceções. Este capítulo apresenta o conceito de
tratamento de exceções em C++. Você aprenderá como lançar, capturar e processar uma exceção.

16.2 Visão Geral do Tratamento de Exceções


Uma exceção é lançada usando uma instrução throw e capturada em um bloco try-catch .
Chave
Apontar
Para demonstrar o tratamento de exceções, incluindo como uma exceção é criada e lançada, vamos começar com
um exemplo que lê dois inteiros e exibe seu quociente, conforme mostrado na Listagem 16.1.

Listagem 16.1 Quociente.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
//Lê dois inteiros
6 7 cout << "Insira dois números inteiros: ";
int número1, número2;
lê dois inteiros cin >> número1 >> número2;
8
9 10 11 cout << número1 << "/" << número2 << "é"
divisão inteira 12 << (número1 / número2) << endl;
13
14 retornar 0;
15 }

Insira dois números inteiros: 5


2 5/2 é 2

Se você inserir 0 para o segundo número, ocorrerá um erro de tempo de execução, porque você não pode dividir
um número inteiro por 0. (Lembre-se de que um número de ponto flutuante dividido por 0 não gera uma exceção.)
Uma maneira simples de corrigir o erro é adicionar uma instrução if para testar o segundo número, conforme mostrado
na Listagem 16.2.

Listagem 16.2 QuocienteWithIf.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
{
56 //Lê dois inteiros
7 cout << "Digite dois números inteiros: ";
8 int número1, número2;
Machine Translated by Google

16.2 Visão Geral do Tratamento de Exceções 619

cin >> número1 >> número2; lê dois inteiros


9
10 se (número2! = 0) teste número2
11 {
12 cout << número1 << << número2
"/" << << (número1 / "é"
13 número2) << endl;
14 }
15 outro
16 {
17 cout << "Divisor não pode ser zero" << endl;
18 }
19
20 retornar 0;
21 22 }

Insira dois números inteiros: 5 0


O divisor não pode ser zero

Demonstrar o conceito de tratamento de exceções, incluindo como criar, lançar, capturar,


e tratar uma exceção, reescrevemos a Listagem 16.2 conforme mostrado na Listagem 16.3.

Listagem 16.3 QuotientWithException.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
//Lê dois inteiros
cout << "Digite dois números inteiros: ";
int número1, número2;
cin >> número1 >> número2; lê dois inteiros
6
7 tentar tente bloquear
8 {
9 se (número2 == 0)
10 lançar número1;
11
12 cout << número1 << << número2
"/" << << (número1 / "é"
13 número2) << endl;
14 }
15 pegar (int ex) pegar bloco
16 {
17 cout << "Exceção: um número inteiro" <<ex<<
"
18 não pode ser dividido por zero" << endl;
19 }
20
21 cout << "A execução continua..." << endl;
22
23 retornar 0;
24 25 26 27 28 }

Insira dois números inteiros: 5


3 5/3 é 1
A execução continua...
Machine Translated by Google

620 Capítulo 16 Tratamento de exceções

Insira dois números inteiros: 5 0


Exceção: um número inteiro 5 não pode ser dividido por zero
A execução continua. . .

O programa contém um bloco try e um bloco catch . O bloco try (linhas 11–18) contém o código que é
executado em circunstâncias normais. O bloco catch contém o código que é executado quando number2 é
zero. Quando number2 é zero, o programa lança uma exceção executando

declaração de lançamento lançar número1;

exceção O valor lançado, neste caso número1, é chamado de exceção. A execução de uma instrução throw é
lançando exceção chamada de lançamento de uma exceção. Você pode lançar um valor de qualquer tipo. Neste caso, o valor
é do tipo int .
Quando uma exceção é lançada, o fluxo normal de execução é interrompido. Como o nome sugere,
“lançar uma exceção” é passar a exceção de um lugar para outro. A exceção é capturada pelo bloco catch .
lidar com exceção O código no bloco catch é executado para tratar a exceção. Posteriormente, a instrução (linha 25) após o
bloco catch ser executada.
A instrução throw é análoga a uma chamada de função, mas em vez de chamar uma função, ela chama
um bloco catch . Nesse sentido, um bloco catch é como uma definição de função com um parâmetro que
corresponde ao tipo do valor que está sendo lançado. No entanto, após a execução do bloco catch , o
controle do programa não retorna à instrução throw ; em vez disso, ele executa a próxima instrução após o
bloco catch .
O identificador ex no cabeçalho do bloco catch

pegar (int ex)

parâmetro de bloco de captura atua muito como um parâmetro em uma função. Portanto, é conhecido como parâmetro de bloco catch .
O tipo (por exemplo, int) que precede ex especifica o tipo de exceção que o bloco catch pode capturar.
Depois que a exceção for detectada, você poderá acessar o valor lançado desse parâmetro no corpo de um
bloco catch.
Em resumo, um modelo para um bloco try-throw-catch pode ser assim:

tentar
{
Código para tentar;
Lançar uma exceção com uma instrução throw ou
da função, se necessário;
Mais código para testar;
}
pegar (digite ex)
{
Código para processar a exceção;
}

Uma exceção pode ser lançada diretamente usando uma instrução throw em um bloco try ou uma função
pode ser invocado para lançar uma exceção.

Observação

omitir parâmetro do bloco catch Se você não estiver interessado no conteúdo de um objeto de exceção, o parâmetro do
bloco catch poderá ser omitido. Por exemplo, o seguinte bloco catch é legal.

tentar
{
// ...
}
Machine Translated by Google

16.3 Vantagens do Tratamento de Exceções 621

pegar (int) {

cout << "Ocorreu um erro" << fim;


}

16.1 Mostre a saída do código a seguir com entrada 120.


ÿVerificação de ponto
#include <iostream>
usando namespace std;

int principal()
{
cout << "Insira a temperatura: "; temperatura
dupla ; cin >> temperatura;

tentar
{
cout << "Início do bloco try ..." << endl;

se (temperatura > 95)


temperatura de lançamento ;

cout << "Fim do bloco try ..." << endl; } catch

( temperatura dupla) { cout << "A

temperatura é " << temperatura << endl; cout << "Está muito quente" <<
endl; }

cout << "Continuar..." << endl;

retornar 0;
}

16.2 Qual seria a saída do código anterior se a entrada fosse 80?

16.3 Seria um erro se você mudasse


pegar ( temperatura dupla) {

cout << "A temperatura é " << temperatura << endl; cout << "Está muito
quente" << endl;
}

no código anterior para o seguinte?

pegar (duplo) {

cout << "Está muito quente" << endl;


}

16.3 Vantagens do Tratamento de Exceções


O tratamento de exceções permite que o chamador da função processe a exceção lançada de
Chave
uma função. Apontar

A Listagem 16.3 demonstra como uma exceção é criada, lançada, capturada e tratada. Você pode se
perguntar sobre os benefícios. Para vê-los, reescrevemos a Listagem 16.3 para calcular um quociente
usando uma função mostrada na Listagem 16.4.
Machine Translated by Google

622 Capítulo 16 Tratamento de exceções

Listagem 16.4 QuocienteWithFunction.cpp


Nota de vídeo
1 #include <iostream>
Vantagens do tratamento de exceções
2 usando namespace std;

função quociente 3 4 quociente interno (int número1, int número2)


{
56 se (número2 == 0)
lançar exceção 7 lançar número1;
8

retornar número1 / número2;


9 10}
11
12 int principal()
13 {
14 //Lê dois inteiros
15 cout << "Digite dois números inteiros: ";
16 int número1, número2;
lê dois inteiros 17 cin >> número1 >> número2;
18
tente bloquear 19 tentar
20 {
invocar função 21 int resultado = quociente(número1, número2);
22 cout << número1 << " / " " é " << número2 <<
23 << resultado << endl;
24 }
pegar bloco 25 pegar (int ex)
26 {
27 cout << "Exceção da função: um número inteiro "não pode ser dividido <<ex<<
"
28 por zero" << endl;
29 }
30
31 cout << "A execução continua..." << endl;
32
33 retornar 0;
34 }

Insira dois números inteiros: 5 3 5/3


é1
A execução continua...

Insira dois números inteiros: 5 0


Exceção da função: um número inteiro 5 não pode ser dividido por zero
A execução continua...

A função quociente (linhas 4–10) retorna o quociente de dois inteiros. Se número2 for 0, não poderá
retornar um valor. Portanto, uma exceção é lançada na linha 7.
A função principal invoca a função quociente (linha 21). Se a função quociente for executada
normalmente, ela retornará um valor ao chamador. Se a função quociente encontrar uma exceção, ela
devolve a exceção ao seu chamador. O bloco catch do chamador trata a exceção.

Agora você vê as vantagens de usar o tratamento de exceções. Ele permite que uma função lance
vantagem
uma exceção ao seu chamador. Sem esta capacidade, uma função deve tratar a exceção ou
Machine Translated by Google

16.4 Classes de Exceção 623

encerrar o programa. Muitas vezes, a função chamada não sabe o que fazer em caso de erro.
Este é normalmente o caso da função de biblioteca. A função da biblioteca pode detectar o erro, mas somente quem
chama sabe o que precisa ser feito quando ocorre um erro. A ideia fundamental do tratamento de exceções é separar
a detecção de erros (feita em uma função chamada) do tratamento de erros (feito na função de chamada).

16.4 Qual é a vantagem de usar o tratamento de exceções?


ÿVerificação de ponto

16.4 Classes de Exceção


Você pode usar classes de exceção padrão C++ para criar objetos de exceção e lançar exceções. Chave
Apontar

O parâmetro do bloco catch nos exemplos anteriores é do tipo int . Um tipo de classe geralmente é mais útil porque
um objeto pode conter mais informações que você deseja lançar em um bloco catch . C++ fornece diversas classes
padrão que podem ser usadas para criar objetos de exceção. Essas classes são mostradas na Figura 16.1. Nota de vídeo
Classes de exceção C++

overflow_error
erro_de_execução
underflow_error
bad_alloc

bad_cast
exceção
bad_typeid

exceção_ruim argumento inválido

erro_lógico comprimento_erro

fora de alcance

Figura 16.1 Você pode usar classes de biblioteca padrão para criar objetos de exceção.

A classe raiz nesta hierarquia é exceção (definida no cabeçalho <exception>). Isso con- exceção
contém a função virtual what() que retorna uma mensagem de erro de um objeto de exceção. o que()
A classe runtime_error (definida no cabeçalho <stdexcept>) é uma classe base para várias classes de exceção erro_de_execução
padrão que descrevem erros de tempo de execução. A classe overflow_error descreve um overflow aritmético – ou exceção padrão
seja, um valor é muito grande para ser armazenado. Classe underflow_error
descreve um underflow aritmético – ou seja, um valor é muito pequeno para ser armazenado.
A classe logic_error (definida no cabeçalho <stdexcept>) é uma classe base para várias classes de exceção erro_lógico
padrão que descrevem erros lógicos. A classe invalid_argument indica que um argumento inválido foi passado para
uma função. A classe length_error indica que o comprimento de um objeto excedeu o comprimento máximo permitido.
A classe out_of_range indica que um valor excedeu seu intervalo permitido.
bad_alloc
As classes bad_alloc, bad_cast, bad_typeid e bad_exception descrevem as exceções lançadas pelos operadores C+ bad_cast
+. Por exemplo, uma exceção bad_alloc será lançada pelo operador new se a memória não puder ser alocada. Uma exceção bad_typeid
bad_cast é lançada pelo operador dynamic_cast como resultado de uma falha na conversão para um tipo de referência. Um exceção_ruim
bad_typeid
exceção é lançada pelo operador typeid quando o operando para typeid é um ponteiro NULL .
A classe bad_exception é usada na especificação de exceção de uma função, que será discutida na Seção 16.9.
Machine Translated by Google

624 Capítulo 16 Tratamento de Exceções

Essas classes são usadas por algumas funções na biblioteca padrão C++ para lançar exceções.
Você também pode usar essas classes para lançar exceções em seus programas. A Listagem 16.5
reescreve a Listagem 16.4, QuotientWithFunction.cpp, lançando um runtime_error.

Listagem 16.5 QuotientThrowRuntimeError.cpp


1 #include <iostream>
2 #include <stdexcept>
3 usando namespace std;

função quociente 4 5 quociente interno (int número1, int número2)


6{
7 se (número2 == 0)
lançar exceção throw runtime_error("Divisor não pode ser zero");

8 retornar número1 / número2;


9 10 11}
12
13 int principal()
14 {
15 //Lê dois inteiros
16 cout << "Digite dois números inteiros: ";
17 int número1, número2;
lê dois inteiros 18 cin >> número1 >> número2;
19
tente bloquear 20 tentar
21 {
invocar função 22 int resultado = quociente(número1, número2);
23 cout << número1 << " / " " é " << número2 <<
24 << resultado << endl;
25 }
pegar bloco 26 pegar (runtime_error& ex)
27 {
28 cout << ex.what() << endl;
29 }
30
31 cout << "A execução continua..." << endl;
32
33 retornar 0;
34 }

Insira dois números inteiros: 5 3 5/3


é1
A execução continua...

Insira dois números inteiros: 5 0


O divisor não pode ser zero
A execução continua...

A função quociente na Listagem 16.4 lança um valor int , mas a função neste programa lança
um objeto runtime_error (linha 8). Você pode criar um objeto runtime_error passando uma string
que descreve a exceção.
O bloco catch captura uma exceção runtime_error e invoca a função what para retornar uma
descrição de string da exceção (linha 28).
Machine Translated by Google

16.4 Classes de Exceção 625

A Listagem 16.6 mostra um exemplo de tratamento da exceção bad_alloc .

Listagem 16.6 BadAllocExceptionDemo.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int principal()
5{
6 tentar tente bloquear
7 {
para (int i = 1; i <= 100; i++)
{
8 novo int[70000000]; crie uma grande matriz
"
9 10 11 cout << eu << matrizes foram criadas" << endl;
12 }
13 }
14 pegar (bad_alloc& ex) pegar bloco
15 {
16 cout << "Exceção: " << ex.what() << endl; invocar ex.what()
17 }
18
19 retornar 0;
20}

1 matriz foi criada


2 matrizes foram criadas
3 matrizes foram criadas
4 matrizes foram criadas
5 matrizes foram criadas
6 matrizes foram criadas
Exceção: exceção de alocação incorreta lançada

A saída mostra que o programa cria seis arrays antes de falhar no sétimo novo operador. Quando
falha, uma exceção bad_alloc é lançada e capturada no bloco catch , que exibe a mensagem
retornada de ex.what().
A Listagem 16.7 mostra um exemplo de tratamento da exceção bad_cast .

Listagem 16.7 BadCastExceptionDemo.cpp


1 #incluir <typeinfo> incluir informações de tipo
2 #include "DerivedCircleFromAbstractGeometricObject.h" veja Listagem 15.15
3 #include "DerivedRectangleFromAbstractGeometricObject.h" veja Listagem 15.17
4 #include <iostream>
5 usando namespace std;

6 7 int principal()
8{
tentar tente bloquear
9 {
10 Retângulo r(3, 4);
11 Círculo& c = dynamic_cast<Círculo&>(r); elenco

12 }
13 14 pegar (bad_cast&ex) pegar bloco
Machine Translated by Google

626 Capítulo 16 Tratamento de exceções

15 {
invocar ex.what() 16 cout << "Exceção: " << ex.what() << endl;
17 }
18
19 retornar 0;
20 }

Saída do VC++ Exceção: Dynamic_cast ruim!

A conversão dinâmica foi introduzida na Seção 15.10, “Conversão: static_cast versus


dynamic_cast”. Na linha 12, uma referência de um objeto Rectangle é convertida em um
tipo de referência Circle , o que é ilegal, e uma exceção bad_cast é lançada. A exceção é
capturada no bloco catch na linha 14.
A Listagem 16.8 mostra um exemplo de lançamento e tratamento de um invalid_argument
exceção.

Listagem 16.8 InvalidArgumentExceptionDemo.cpp


1 #include <iostream>
2 #include <stdexcept>
3 usando namespace std;

função getArea 4 5 getArea duplo ( raio duplo)


6{
se (raio < 0)
lançar exceção throw invalid_argument("O raio não pode ser negativo");

7 raio de retorno * raio * 3,14159;


8 9 10 11 }
12
13 int principal()
14 {
15 // Solicita ao usuário para inserir o raio
16 cout << "Insira o raio: ";
17 raio duplo ;
lê raio 18 cin >> raio;
19
tente bloquear 20 tentar
21 {
invocar função 22 resultado duplo = getArea(raio);
23 cout << "A área é " << resultado << endl;
24 }
pegar bloco 25 pegar (exceção e ex)
26 {
27 cout << ex.what() << endl;
28 }
29
30 cout << "A execução continua..." << endl;
31
retornar 0;
32 33 }

Insira o raio: 5
A área é 78,5397
A execução continua...
Machine Translated by Google

16.5 Classes de Exceção Personalizadas 627

Insira o raio: -5
O raio não pode ser negativo
A execução continua...

Na saída de amostra, o programa solicita que o usuário insira o raio 5 e -5. Invocar getArea(-5) (linha 22) faz com
que uma exceção invalid_argument seja lançada (linha 8). Esta exceção é capturada no bloco catch na linha
25. Observe que a exceção do tipo de parâmetro catch-block é uma classe base para invalid_argument.
Portanto, ele pode capturar um argumento_inválido.

16.5 Descreva a classe de exceção C++ e suas classes derivadas. Dê exemplos de uso de bad_alloc e
bad_cast. ÿVerificação de ponto

16.6 Mostre a saída do código a seguir com entradas 10, 60 e 120, respectivamente.

#include <iostream>
usando namespace std;

int principal()
{
cout << "Insira a temperatura: ";
temperatura dupla ; cin
>> temperatura;

tentar
{
cout << "Início do bloco try ..." << endl;

se (temperatura > 95)


throw runtime_error(" Temperatura excepcional");

cout << "Fim do bloco try ..." << endl; } catch

(runtime_error& ex) {

cout << ex.what() << endl; cout <<


"Está muito quente" << endl;
}

cout << "Continuar..." << endl;

retornar 0;
}

16.5 Classes de Exceção Personalizadas


Você pode definir classes de exceção customizadas para modelar exceções que não podem
Chave
ser representadas adequadamente usando classes de exceção padrão C++. Apontar

C++ fornece as classes de exceção listadas na Figura 16.1. Use-os sempre que possível em vez de criar suas
próprias classes de exceção. No entanto, se você se deparar com um problema que não pode ser descrito
adequadamente pelas classes de exceção padrão, poderá criar sua própria classe de exceção. Esta classe é
como qualquer classe C++, mas muitas vezes é desejável derivá-la de exceção ou de uma classe derivada de Nota de vídeo
exceção para que você possa utilizar os recursos comuns (por exemplo, a função what() ) na classe de exceção . Crie classes de exceção
personalizadas
Machine Translated by Google

628 Capítulo 16 Tratamento de exceções

Vamos considerar a classe Triangle para modelar triângulos. O diagrama UML de classes é
mostrado na Figura 16.2. A classe é derivada da classe GeometricObject , que é uma classe
abstrata introduzida na Seção 15.9, “Classes abstratas e funções virtuais puras”.

Objeto Geométrico

Triângulo

-lado1: duplo Os três lados deste triângulo.


-lado2: duplo
-lado3: duplo

+Triângulo() Constrói um Triângulo padrão com cada lado 1.


+Triângulo(lado1: duplo, lado2: Constrói um triângulo com lados especificados.
duplo, lado3: duplo)
+getSide1(): const duplo Retorna o lado1 deste triângulo.
+getSide2(): const dupla Retorna o lado2 deste triângulo.
+getSide3(): const dupla Retorna o lado3 deste triângulo.
+setSide1(lado1: duplo): vazio Define um novo lado1.

+setSide2(lado2: duplo): vazio Define um novo lado2.

+setSide3(lado3: duplo): vazio Define um novo lado3.

Figura 16.2 A classe Triangle modela triângulos.

Um triângulo é válido se a soma de quaisquer dois lados for maior que o terceiro lado. Ao
tentar criar um triângulo ou alterar um lado de um triângulo, você precisa garantir que essa
propriedade não seja violada. Caso contrário, uma exceção deverá ser lançada. Você pode
definir a classe TriangleException como na Listagem 16.9 para modelar esta exceção.

Listagem 16.9 TriangleException.h


1 #ifndef TRIANGLEEXCEPTION_H
2 #define TRIANGLEEXCEPTION_H
incluir stdexceto 3 #include <stdexcept>
4 usando namespace std;

estender logic_error 5 6 classe TriangleException: erro_lógico público


7{
8 público:
9 TriangleException( lado duplo1, lado duplo2 , lado duplo3 )
invocar o construtor base 10 : logic_error(" Triângulo inválido")
11 {
12 este->lado1 = lado1;
13 este->lado2 = lado2;
14 este->lado3 = lado3;
15 }
16
17 duplo getSide1() const
18 {
19 retornar lado1;
20 }
21
22 duplo getSide2() const
23 {
24 retornar lado2;
25 }
Machine Translated by Google

16.5 Classes de Exceção Personalizadas 629

26
27 getSide3() const duplo
28 {
29 retornar lado3;
30 }
31
32 privado:
33 lado duplo1 , lado2, lado3;
34}; //Ponto e vírgula obrigatório
35
36 #endif

A classe TriangleException descreve um erro lógico, portanto, é apropriado definir esta classe
para estender a classe logic_error padrão na linha 6. Como logic_error está no arquivo de
cabeçalho <stdexcept> , este cabeçalho está incluído na linha 3.
Lembre-se de que se um construtor base não for invocado explicitamente, o construtor no-arg
da classe base será invocado por padrão. No entanto, como a classe base logic_error não possui
um construtor sem argumento, você deve invocar o construtor de uma classe base para evitar erros
de compilação na linha 10. Invocar logic_error("Invalid Triangle") define uma mensagem de erro,
que pode ser retornada ao invocar what() em um objeto de exceção .

Observação

Uma classe de exceção personalizada é como uma classe normal. Não é necessário estender a
partir de uma classe base, mas é uma boa prática estender a partir da exceção padrão ou de uma
classe derivada de exceção para que sua classe de exceção personalizada possa usar as funções
das classes padrão.

Observação

O arquivo de cabeçalho TriangleException.h contém a implementação da classe. Lembre-se de


que esta é a implementação inline. Para funções curtas, usar a implementação inline é eficiente.

A classe Triangle pode ser implementada como segue na Listagem 16.10.

Listagem 16.10 Triângulo.h


1 #ifndef TRIÂNGULO_H cabeçalho para

2 #define TRIÂNGULO_H Objeto Geométrico


3 #include "AbstractGeometricObject.h" // Definido na Listagem 15.13 cabeçalho para

4 #include "TriangleException.h" TriânguloExceção


5 #incluir <cmath> cabeçalho para cmath

6 7 classes Triângulo: GeometricObject público estender GeometricObject


8{
9 público:
10 Triângulo() construtor sem argumento
11 {
12 lado1 = lado2 = lado3 = 1;
13 }
14
15 Triângulo ( lado duplo1, lado duplo2 , lado duplo3 ) construtor
16 {
17 if (!isValid(lado1, lado2, lado3))
18 lançar TriangleException(lado1, lado2, lado3); lançar TriânguloException
19
20 este->lado1 = lado1;
21 este->lado2 = lado2;
Machine Translated by Google

630 Capítulo 16 Tratamento de Exceções

22 este->lado3 = lado3;
23 }
24
25 duplo getSide1() const
26 {
27 retornar lado1;
28 }
29
30 duplo getSide2() const
31 {
32 retornar lado2;
33 }
34
35 getSide3() const duplo
36 {
37 retornar lado3;
38 }
39
40 vazio setSide1( lado duplo1)
41 {
42 if (!isValid(lado1, lado2, lado3))
lançar TriânguloException 43 lançar TriangleException(lado1, lado2, lado3);
44
45 este->lado1 = lado1;
46 }
47
48 vazio setSide2( lado duplo2)
49 {
50 if (!isValid(lado1, lado2, lado3))
lançar TriânguloException 51 lançar TriangleException(lado1, lado2, lado3);
52
53 este->lado2 = lado2;
54 }
55
56 vazio setSide3( lado duplo3)
57 {
58 if (!isValid(lado1, lado2, lado3))
lançar TriânguloException 59 lançar TriangleException(lado1, lado2, lado3);
60
61 este->lado3 = lado3;
62 }
63
substituir getPerimeter() 64 double getPerimeter() const
65 {
66 retornar lado1 + lado2 + lado3;
67 }
68
substituir getArea() 69 getArea duplo () const
70 {
71 double s = getPerimeter() / 2;
72 return sqrt(s * (s - lado1) * (s - lado2) * (s - lado3));
73 }
74
75 privado:
76 lado duplo1 , lado2, lado3;
77
verifique os lados 78 bool éValid( lado duplo1, lado duplo2 , lado duplo3 ) const
Machine Translated by Google

16.5 Classes de Exceção Personalizadas 631

79 {
80 return (lado1 < lado2 + lado3) && (lado2 < lado1 + lado3) &&
81} (lado3 < lado1 + lado2);
82
83};
84
85 #endif

A classe Triangle estende GeometricObject (linha 7) e substitui as funções virtuais puras


getPerimeter e getArea definidas na classe GeometricObject (linhas 64–73).
A função isValid (linhas 78–83) verifica se um triângulo é válido. Esta função é definida
como privada para uso dentro da classe Triangle .
Ao construir um objeto Triangle com três lados especificados, o construtor invoca a função
isValid (linha 17) para verificar a validade. Se não for válido, uma TriangleException
o objeto é criado e lançado na linha 18. A validade também é verificada quando as
funções setSide1, setSide2 e setSide3 são invocadas. Ao invocar setSide1(side1),
isValid(side1, side2, side3) é invocado. Aqui side1 é o novo side1 a ser definido, não
o side1 atual no objeto.
A Listagem 16.11 fornece um programa de teste que cria um objeto Triangle usando seu
construtor no-arg (linha 9), exibe seu perímetro e área (linhas 10 a 11) e altera seu lado 3 para 4.
(linha 13), o que faz com que uma TriangleException seja lançada. A exceção é capturada no
bloco catch (linhas 17–22).

Listagem 16.11 TestTriangle.cpp


1 #include <iostream>
2 #include "Triângulo.h" Cabeçalho triangular
3 usando namespace std;

4 5 int principal()
6{
tentar
{
Triângulo triângulo; criar objeto
7 cout << "Perímetro é " cout << << triângulo.getPerimeter() << endl;
8 "Área é " << triângulo.getArea() << endl;
9
10 triângulo.setSide3(4); definir um novo lado

11 cout << "Perímetro é " cout << << triângulo.getPerimeter() << endl;
12 "Área é " << triângulo.getArea() << endl;
13 }
14 pegar (TriangleException& ex) pegar bloco
15 {
16 cout << ex.what(); invocar ex.what()
" ""
17 conta << "três lados são << << ex.getSide1() << << invocar ex.getSide1()
""
18 ex.getSide2() << ex.getSide3() << endl;
19 }
20
21 retornar 0;
22 23 24 25 }

O perímetro é 3
A área é 0,433013
Triângulo inválido, três lados são 1 1 4
Machine Translated by Google

632 Capítulo 16 Tratamento de Exceções

A função what() é definida na classe de exceção . Desde TriangleException


é derivado de logic_error, que é derivado de exceção, você pode invocar what() (linha 19) para
exibir uma mensagem de erro em um objeto TriangleException . O objeto TriangleException
contém as informações pertinentes a um triângulo. Esta informação é útil para tratar a exceção.

16.7 Qual é a vantagem de definir uma classe de exceção personalizada a ser derivada da
ÿVerificação de ponto classe de exceção ?

16.6 Capturas Múltiplas


Um bloco try-catch pode conter múltiplas cláusulas catch para lidar com diferentes
Chave
Apontar exceções lançadas na cláusula try .

Normalmente, um bloco try deve ser executado sem exceções. Ocasionalmente, porém, pode
lançar uma exceção de um tipo ou de outro. Por exemplo, um valor não positivo para um lado de
um triângulo na Listagem 16.11 pode ser considerado um tipo de exceção diferente de TriangleException.
Portanto, o bloco try pode lançar uma exceção de lado não positivo ou uma TriangleException,
dependendo da ocasião. Um bloco catch pode capturar apenas um tipo de exceção. C++ permite
adicionar vários blocos catch após um bloco try para capturar vários tipos de exceções.

Vamos revisar o exemplo da seção anterior criando uma nova classe de exceção chamada
NonPositiveSideException e incorporando-a na classe Triangle . A classe
NonPositiveSideException é mostrada na Listagem 16.12 e a nova classe Triangle na Listagem
16.13.

Listagem 16.12 NonPositiveSideException.h


1 #ifndef NonPositiveSideException_H
2 #define NonPositiveSideException_H
incluir stdexceto 3 #include <stdexcept>
4 usando namespace std;
5
estender logic_error 6 classe NonPositiveSideException: erro_lógico público
7{
8 público:
9 NonPositiveSideException ( lado duplo)
invocar o construtor base 10: logic_error("Lado não positivo")
11 {
12 este->lado = lado;
13 }
14
15 getSide duplo ()
16 {
17 lado de retorno ;
18 }
19
20 privado:
21 lado duplo ;
22};
23
24 #endif

A classe NonPositiveSideException descreve um erro lógico, portanto é apropriado definir esta


classe para estender a classe logic_error padrão na linha 6.
Machine Translated by Google

16.6 Capturas Múltiplas 633

Listagem 16.13 NewTriangle.h


1 #ifndef TRIÂNGULO_H cabeçalho para GeometricObject
2 #define TRIÂNGULO_H cabeçalho para

3 #include "AbstractGeometricObject.h" TriânguloExceção


4 #include "TriangleException.h" Lado Não Positivo-
5 #include "NonPositiveSideException.h" Exceção
6 #incluir <cmath> cabeçalho para cmath

7 8 classes Triângulo: GeometricObject público estender GeometricObject


9{
10 público:
11 Triângulo() construtor sem argumento
12 {
13 lado1 = lado2 = lado3 = 1;
14 }
15
16 Triângulo ( lado duplo1, lado duplo2 , lado duplo3 ) construtor
17 {
18 verificar(lado1); verifique lado 1
19 verificar(lado2);
20 verificar(lado3);
21
22 if (!isValid(lado1, lado2, lado3))
23 lançar TriangleException(lado1, lado2, lado3); lançar TriânguloException
24
25 este->lado1 = lado1;
26 este->lado2 = lado2;
27 este->lado3 = lado3;
28 }
29
30 duplo getSide1() const
31 {
32 retornar lado1;
33 }
34
35 duplo getSide2() const
36 {
37 retornar lado2;
38 }
39
40 getSide3() const duplo
41 {
42 retornar lado3;
43 }
44
45 vazio setSide1( lado duplo1)
46 {
47 verificar(lado1); verifique lado 1
48 if (!isValid(lado1, lado2, lado3))
49 lançar TriangleException(lado1, lado2, lado3);
50
51 este->lado1 = lado1;
52 }
53
54 vazio setSide2( lado duplo2)
55 {
56 verificar(lado2);
57 if (!isValid(lado1, lado2, lado3))
Machine Translated by Google

634 Capítulo 16 Tratamento de Exceções

58 lançar TriangleException(lado1, lado2, lado3);


59
60 este->lado2 = lado2;
61 }
62
63 vazio setSide3( lado duplo3)
64 {
65 verificar(lado3);
66 if (!isValid(lado1, lado2, lado3))
67 lançar TriangleException(lado1, lado2, lado3);
68
69 este->lado3 = lado3;
70 }
71
72 double getPerimeter() const
73 {
74 retornar lado1 + lado2 + lado3;
75 }
76
77 getArea duplo () const
78 {
79 double s = getPerimeter() / 2;
80 return sqrt(s * (s - lado1) * (s - lado2) * (s - lado3));
81 }
82
83 privado:
84 lado duplo1 , lado2, lado3;
85
86 bool éValid( lado duplo1, lado duplo2 , lado duplo3 ) const
87 {
88 return (lado1 < lado2 + lado3) && (lado2 < lado1 + lado3) &&
89 (lado3 < lado1 + lado2);
90 }
91
92 verificação nula ( lado duplo) const
93 {
94 se (lado <= 0)
lançar NonPositiveSide- 95 lançar NonPositiveSideException(lado);
Exceção 96 }
97};
98
99 #endif

A nova classe Triangle é idêntica à da Listagem 16.10, exceto que também verifica os lados não positivos.
Quando um objeto Triângulo é criado, todos os seus lados são verificados invocando a função check (linhas
18–20). A função check verifica se um lado é não positivo (linha 94); lança uma NonPositiveSideException
(linha 95).
A Listagem 16.14 fornece um programa de teste que solicita ao usuário que insira três lados (linhas 9–11)
e cria um objeto Triângulo (linha 12).

Listagem 16.14 MultipleCatchDemo.cpp


1 #include <iostream>
nova classe Triângulo 2 #include "NovoTriangle.h"
3 usando namespace std;

4 5 int principal()
6{
7 tentar
Machine Translated by Google

16.6 Capturas Múltiplas 635

{
cout << "Insira três lados: ";
8 lado duplo1 , lado2, lado3;
9 cin >> lado1 >> lado2 >> lado3;
10 Triângulo triângulo(lado1, lado2, lado3); criar objeto
11 cout << "Perímetro é " cout << << triângulo.getPerimeter() << endl;
12 "Área é " << triângulo.getArea() << endl;
13 }
14 pegar (NonPositiveSideException&ex) pegar bloco
15 {
16 cout << ex.what();
17 conta << "o lado é" << ex.getSide() << endl;
18 }
19 pegar (TriangleException& ex) pegar bloco
20 {
21 cout << ex.what();
" ""
22 conta << "três lados são << << ex.getSide1() << <<
""
23 ex.getSide2() << ex.getSide3() << endl;
24 }
25
26 retornar 0;
27 28 29 }

Insira três lados: 2 2,5 2,5 Execução normal


O perímetro é 7
A área é 2,29129

Insira três lados: -1 1 1 Lado não positivo –1


Lado não positivo o lado é -1

Insira três lados: 1 2 1 Triângulo inválido


Triângulo inválido, três lados são 1 2 1

Conforme mostrado no exemplo de saída, se você inserir três lados 2, 2,5 e 2,5, será um triângulo jurídico.
O programa exibe o perímetro e a área do triângulo (linhas 13–14). Se você inserir -1, 1 e 1, o construtor
(linha 12) lançará uma NonPositiveSideException. Esta exceção é capturada pelo bloco catch na linha
16 e processada nas linhas 18–19. Se você inserir 1, 2 e 1, o construtor (linha 12) lançará uma
TriangleException. Esta exceção é capturada pelo catch
bloco na linha 21 e processado nas linhas 23–25.

Observação

Várias classes de exceção podem ser derivadas de uma classe base comum. Se um bloco catch pegar bloco
capturar objetos de exceção de uma classe base, ele poderá capturar todos os objetos de exceção
das classes derivadas dessa classe base.

Observação

A ordem em que as exceções são especificadas nos blocos catch é importante. Uma pegadinha ordem dos manipuladores de exceção
O bloco para um tipo de classe base deve aparecer após um bloco catch para um tipo de classe
derivado. Caso contrário, a exceção de uma classe derivada é sempre capturada pelo bloco catch
da classe base. Por exemplo, a ordem em (a) abaixo está errada, porque TriangleException é uma
classe derivada de logic_error. A ordem correta deve ser conforme mostrado em (b). Em (a), uma
TriangleException ocorrida no bloco try é capturada pelo bloco catch para logic_error.
Machine Translated by Google

636 Capítulo 16 Tratamento de Exceções

tentar tentar
{ {
... ...

} catch (logic_error& ex) { } catch (logic_error& ex) {

... ...

} catch (TriangleException& ex) { } catch (logic_error& ex) {

... ...
} }

(a) Ordem errada (b) Ordem correta

capturar todas as exceções Você pode usar reticências (...) como parâmetro de catch, que capturará qualquer exceção,
independentemente do tipo de exceção que foi lançada. Isso pode ser usado como um manipulador padrão
que captura todas as exceções não capturadas por outros manipuladores se for especificado por último,
conforme mostrado no exemplo a seguir:

tentar
{
Execute algum código aqui

} catch (Exceção1 e ex1) {

cout << "Tratar Exceção1" << endl;

} catch (Exceção2 e ex2) {

cout << "Tratar Exceção2" << endl;

} pegar (...) {

cout << "Tratar todas as outras exceções" << endl;


}

16.8 Você pode lançar múltiplas exceções em uma instrução throw ? Você pode ter vários blocos catch
ÿVerificação de ponto
em um bloco try-catch ?

16.9 Suponha que a instrução2 cause uma exceção no seguinte bloco try-catch :

tentar
{
declaração1;
declaração2;
declaração3;

} capturar (Exceção1 e ex1) { }

capturar (Exceção2 e ex2) { }

declaração4;
Machine Translated by Google

16.7 Propagação de Exceção 637

Responda as seguintes questões:


n A instrução3 será executada?

n Se a exceção não for detectada, a instrução4 será executada?


n Se a exceção for capturada no bloco catch , a instrução4 será executada?

16.7 Propagação de Exceções


Uma exceção é lançada através de uma cadeia de chamadas de funções até ser
Chave
capturada ou chegar à função principal. Apontar

Agora você sabe como declarar uma exceção e como lançar uma exceção. Quando uma exceção é
lançada, ela pode ser capturada e tratada em um bloco try-catch , como segue:

tentar
{
declarações; //Declarações que podem lançar exceções
}
pegar (Exceção1 e ex1) {

manipulador para exceção1;


}
catch (Exception2& ex2)

{manipulador para exceção2;


}
...
catch (ExceçãoN& exN) {

manipulador para exceçãoN;


}

Se nenhuma exceção surgir durante a execução do bloco try , os blocos catch serão ignorados.
Se uma das instruções dentro do bloco try lançar uma exceção, C++ ignora as instruções restantes
no bloco try e inicia o processo de localização do código para tratar a exceção. Esse código,
chamado manipulador de exceção, é encontrado propagando a exceção para trás por meio de uma manipulador de exceções
cadeia de chamadas de função, começando pela função atual. Cada bloco catch é examinado
sucessivamente, do primeiro ao último, para ver se o tipo do objeto de exceção é uma instância da
classe de exceção no bloco catch . Nesse caso, o objeto de exceção é atribuído à variável declarada
e o código no bloco catch é executado. Se nenhum manipulador for encontrado, o C++ sai desta
função, passa a exceção para a função que invocou a função e continua o mesmo processo para
encontrar um manipulador. Se nenhum manipulador for encontrado na cadeia de funções que está
sendo invocada, o programa imprime uma mensagem de erro no console e termina. O processo de
encontrar um manipulador é chamado de captura de exceção. capturando exceção
Suponha que a função principal invoque a função1, a função1 invoque a função2,
a função2 invoque a função3 e a função3 lance uma exceção, conforme mostrado na
Figura 16.3. Considere o seguinte cenário:
n Se o tipo de exceção for Exception3, ela será capturada pelo bloco catch para tratar a
exceção ex3 na função2. a instrução5 é ignorada e a instrução6 é executada.

n Se o tipo de exceção for Exception2, a função2 será abortada, o controle será


retornado para a função1 e a exceção será capturada pelo bloco catch para
tratar a exceção ex2 na função1. a instrução3 é ignorada e a instrução4 é
executada.
Machine Translated by Google

638 Capítulo 16 Tratamento de Exceções

int principal() função1 { função2 {


{
... ... ... Uma exceção
tentar tentar tentar é lançada
{ { { na function3
... ... ...
invocar função1; invocar função2; invocar função3;
declaração1; declaração3; declaração5;

} catch (Exceção1 e ex1) { } catch (Exceção2 e ex2) { } catch (Exceção3 e ex3) {

Processo ex1; Processo ex2; Processo ex3;

} instrução2; } declaração4; } declaração6;


} } }

Pilha de chamadas

função3

função2 função2

função1 função1 função1

principal principal principal principal

Figura 16.3 Se uma exceção não for detectada na função atual, ela será passada para seu chamador. O processo é repetido até que a exceção seja
capturada ou passada para a função principal .

n Se o tipo de exceção for Exception1, a função1 será abortada, o controle será retornado para a função principal
e a exceção será capturada pelo bloco catch para tratar a exceção ex1 na função principal . a instrução1 é
ignorada e a instrução2 é executada.

n Se a exceção não for detectada em function2, function1 e main, o programa será encerrado. instrução1 e
instrução2 não são executadas.

16.8 Relançando Exceções


Depois que uma exceção é detectada, ela pode ser lançada novamente para o chamador da função.
Chave
Apontar
C++ permite que um manipulador de exceção repita a exceção se não puder processá-la ou simplesmente quiser permitir
que seu chamador seja notificado. A sintaxe pode ser semelhante a esta:

tentar
{
declarações;
}
catch (TheException& ex) {

realizar operações antes das saídas; lançar;

A instrução throw relança a exceção para que outros manipuladores tenham a chance de processá-la.
exceção de relançamento A Listagem 16.15 dá um exemplo que demonstra como relançar exceções.

Listagem 16.15 RethrowExceptionDemo.cpp


1 #include <iostream> 2 #include
<stdexcept> 3 usando namespace
std; 4
Machine Translated by Google

16.8 Relançamento de Exceções 639

5 você f1()
6{
tentar
{
throw runtime_error("Exceção em f1"); lançar uma exceção
7 }
8 pegar (exceção e ex) pegar bloco
9 {
10 cout << "Exceção capturada na função f1" << endl;
11 cout << ex.what() << endl;
12 lançar; //Relança a exceção exceção de relançamento
13 }
14 15 16 17 }
18
19 int principal() 20
{
21 tentar
22 {
23 f1(); invocar f1
24 }
25 pegar (exceção e ex) pegar bloco
26 {
27 cout << "Exceção capturada na função main" << endl;
28 cout << ex.what() << endl;
29 }

30 retornar 0;
31 32 }

Exceção capturada na função f1 Manipulador na função f1


Exceção em f1

Exceção capturada na função main Manipulador na função principal


Exceção em f1

O programa invoca a função f1 na linha 23, que lança uma exceção na linha 9. Essa exceção é capturada no
bloco catch na linha 11 e é lançada novamente na função principal na linha 15. O bloco catch na função principal
captura o exceção relançada e a processa nas linhas 27–28.

16.10 Suponha que a instrução2 cause uma exceção na seguinte instrução:


ÿVerificação de ponto
tentar
{
declaração1;
declaração2;
declaração3;
}
pegar (Exceção1 e ex1) {

}
pegar (Exceção2 e ex2) {

}
pegar (Exceção3 e ex3) {

declaração4;
Machine Translated by Google

640 Capítulo 16 Tratamento de Exceções

lançar;
}
declaração5;

Responda as seguintes questões:


n A instrução5 será executada se a exceção não for detectada?
n Se a exceção for do tipo Exception3, a instrução4 será executada e a instrução5 será
executada?

16.9 Especificação de Exceção


Você pode declarar possíveis exceções que uma função pode lançar no cabeçalho da função.
Chave
Apontar
Uma especificação de exceção, também conhecida como lista de lançamento, lista as exceções que uma
especificação de exceção função pode lançar. Até agora, você viu a função definida sem uma lista de lançamento. Neste caso, a
lista de lançamento função pode lançar qualquer exceção. Portanto, é tentador omitir a especificação de exceções. No entanto,
esta não é uma boa prática. Uma função deve avisar sobre quaisquer exceções que possa lançar, para
que os programadores possam escrever um programa robusto para lidar com essas exceções potenciais
em um bloco try-catch .
A sintaxe para especificação de exceção é a seguinte:

returnType functionName (lista de parâmetros) lançar (lista de exceções)

As exceções são declaradas no cabeçalho da função. Por exemplo, você deve revisar a função check
e o construtor Triangle na Listagem 16.13 para especificar exceções apropriadas como segue:

lista de lançamento 1 lançamento de verificação nula ( lado duplo) (NonPositiveSideException)


2{
se (lado <= 0)
lançar NonPositiveSide- 34 lançar NonPositiveSideException(lado);
Exceção }
56

7 Triângulo (lado duplo 1, lado duplo 2, lado duplo 3)


lista de lançamento lançar (NonPositiveSideException, TriangleException)
89{
10 verificar(lado1);
11 verificar(lado2);
12 verificar(lado3);
13
14 if (!isValid(lado1, lado2, lado3))
lançar TriânguloException 15 lançar TriangleException(lado1, lado2, lado3);
16
17 este->lado1 = lado1;
18 este->lado2 = lado2;
19 este->lado3 = lado3;
20 }

A verificação da função declara que lança NonPositiveSideException e o construtor Triangle declara


que lança NonPositiveSideException e TriangleException.

Observação

especificação de exceção vazia Colocar throw() após um cabeçalho de função, conhecido como especificação de exceção vazia,
declara que a função não lança nenhuma exceção. Se uma função tentar lançar uma exceção,
uma função C++ padrão inesperada será invocada, o que normalmente encerra o programa. No
Visual C++, entretanto, a especificação de exceção vazia é tratada como se nenhuma lista de
exceções estivesse presente.
Machine Translated by Google

16.10 Quando Usar Exceções 641

Observação

Lançar uma exceção que não esteja declarada na lista de lançamento fará com que a exceção não declarada
função inesperada seja invocada. No entanto, uma função sem especificação de
exceção pode lançar qualquer exceção e não causará a invocação inesperada .

Observação

Se uma função especificar bad_exception em sua lista de lançamento, a função lançará exceção_ruim
uma bad_exception se uma exceção não especificada for lançada da função.

16.11 Qual é o propósito das especificações de exceção? Como você declara uma lista de lançamento?
ÿVerificação de ponto
Você pode declarar múltiplas exceções em uma declaração de função?

16.10 Quando usar exceções


Use exceções para circunstâncias excepcionais, não para erros lógicos simples que podem ser Chave
Apontar
detectados facilmente usando uma instrução if .

O bloco try contém o código que é executado em circunstâncias normais. O bloco catch contém o código que
é executado em circunstâncias excepcionais. O tratamento de exceções separa o código de tratamento de
erros das tarefas normais de programação, tornando os programas mais fáceis de ler e modificar. Esteja
ciente, entretanto, que o tratamento de exceções geralmente requer mais tempo e recursos, porque requer a
instanciação de um novo objeto de exceção, a reversão da pilha de chamadas e a propagação da exceção
por meio da cadeia de funções invocadas para procurar o manipulador.
Uma exceção ocorre em uma função. Se você deseja que a exceção seja processada pelo chamador,
você deve lançá-la. Se você puder tratar a exceção na função onde ela ocorre, não há necessidade de lançar
ou usar exceções.
Em geral, exceções comuns que podem ocorrer em diversas classes de um projeto são candidatas a
classes de exceção. Erros simples que podem ocorrer em funções individuais são melhor tratados localmente,
sem gerar exceções.
O tratamento de exceções serve para lidar com condições de erro inesperadas. Não use um bloco try-
catch para lidar com situações simples e esperadas. Às vezes é difícil decidir quais situações são excepcionais
e quais são esperadas. A questão não é abusar do tratamento de exceções como forma de lidar com um
simples teste lógico.
Um paradigma geral para o tratamento de exceções é declarar lançar uma exceção em uma função
conforme mostrado em (a) abaixo e usar a função em um bloco try-catch conforme mostrado em (b).

função returnType1 (lista de parâmetros) returnType function2(listadeparâmetros) {


lançar (lista de exceções)
{ tentar
... {
if (uma condição de exceção) ...
lançar AnException(argumentos); função1 (argumentos);
... ...
} }
pegar (AnException&ex)
{
Manipulador;
}
...
}

(a) (b)

ÿVerificação de ponto
16.12 Quais exceções devem ser usadas em um programa?
Machine Translated by Google

642 Capítulo 16 Tratamento de Exceções

Termos chave

exceção 620 exceção padrão 623


especificação de exceção 640 lançar exceção 623
relançar exceção 638 lista de lançamento 640

Resumo do capítulo

1. O tratamento de exceções torna os programas robustos. O tratamento de exceções separa o código


de tratamento de erros das tarefas normais de programação, tornando os programas mais fáceis
de ler e modificar. Outra vantagem importante do tratamento de exceções é que ele permite que
uma função lance uma exceção ao seu chamador.

2. C++ permite que você use a instrução throw para lançar um valor de qualquer tipo (tipo primitivo ou de classe)
quando ocorre uma exceção. Este valor é passado para um bloco catch como um argumento para que o
bloco catch possa utilizar esse valor para processar a exceção.

3. Quando uma exceção é lançada, o fluxo normal de execução é interrompido. Se o valor da exceção corresponder
a um tipo de parâmetro do bloco catch , o controle é transferido para um bloco catch.
bloquear. Caso contrário, a função será encerrada e a exceção será lançada para o chamador da função. Se a
exceção não for tratada na função principal , o programa será abortado.

4. C++ fornece diversas classes de exceção padrão que podem ser usadas para criar objetos de exceção. Você
pode usar a classe de exceção ou suas classes derivadas runtime_
error e logic_error para criar objetos de exceção.

5. Você também pode criar uma classe de exceção personalizada se as classes de exceção padrão não puderem
descrever exceções adequadamente. Esta classe é como qualquer classe C++, mas muitas vezes é desejável
derivá-la de uma exceção ou de uma classe derivada de exceção para que você possa utilizar os recursos
comuns (por exemplo, a função what() ) na classe de exceção .

6. Um bloco try pode ser seguido por vários blocos catch . A ordem em que as exceções são especificadas nos
blocos catch é importante. Um bloco catch para um tipo de classe base deve aparecer após um bloco catch
para um tipo de classe derivada.

7. Se uma função lança uma exceção, você deve declarar o tipo da exceção na função
cabeçalho de ação para alertar os programadores para lidar com possíveis exceções.

8. O tratamento de exceções não deve ser usado para substituir testes simples. Você deve testar exceções simples
sempre que possível e reservar o tratamento de exceções para lidar com situações que não podem ser
tratadas com instruções if .

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.


Machine Translated by Google

Exercícios de Programação 643

Exercícios de programação

Seções 16.2–16.4
*16.1 (argumento_inválido) A Listagem 6.18 fornece o hex2Dec(const string& hexString)
função que retorna um número decimal de uma string hexadecimal. Implemente a função hex2Dec
para lançar uma exceção invalid_argument se a string não for uma string hexadecimal. Escreva um
programa de teste que solicite ao usuário que insira um número hexadecimal como uma string e exiba
o número em decimal.

*16.2 (argumento_inválido) O Exercício de Programação 6.40 especifica a função bin2Octal(const string&


binaryString) que retorna um número octal de uma string binária. Implemente a função bin2Octal
para lançar uma exceção invalid_argument se a string não for uma string binária. Escreva um
programa de teste que solicite ao usuário que insira um número binário como uma string e exiba o
número octal.

*16.3 (Modificar a classe Course ) Reescreva a função addStudent na classe Course na Listagem 11.16,
Course.cpp para lançar um runtime_error se o número de alunos exceder a capacidade.

*16.4 (Modifique a classe Rational ) Reescreva a função do operador subscrito na classe Rational na Listagem
14.8, RationalWithOperators.cpp para lançar um runtime_
erro se o índice não for 0 ou 1.

Seções 16.5–16.10

*16.5 (HexFormatException) Implemente a função hex2Dec no Exercício de Programação 16.1 para lançar uma
HexFormatException se a string não for uma string hexadecimal.
Nota de vídeo
Defina uma classe de exceção personalizada chamada HexFormatException. Escreva um programa
A classe HexFormatException
de teste que solicite ao usuário que insira um número hexadecimal como uma string e exiba o número
em decimal.

*16.6 (BinaryFormatException) Implemente a função bin2Octal no Exercício de Programação 16.2 para lançar
uma BinaryFormatException se a string não for uma string binária. Defina uma classe de exceção
personalizada chamada BinaryFormatException. Escreva um programa de teste que leia um número
binário como uma string, invoque a função bin2Octal e exiba o número octal.

*16.7 (Modificar classe Rational ) A Seção 14.4, “Sobrecarregando o Operador Subscrito []”, introduziu como
sobrecarregar o operador subscrito [] na classe Rational . Se o subscrito não for 0 nem 1, a função
lança uma exceção runtime_error .
Defina uma exceção personalizada chamada IllegalSubscriptException e deixe o operador da
função lançar uma IllegalSubscriptException se o subscrito não for 0 nem 1. Escreva um programa
de teste com um bloco try-catch para lidar com esse tipo de exceção.

*16.8 (Modificar classe StackOfIntegers ) Na Seção 10.9, “Estudo de caso: a classe StackOfIntegers ”, você
definiu uma classe de pilha para números inteiros. Defina uma classe de exceção personalizada
chamada EmptyStackException e deixe as funções pop e peek lançarem uma EmptyStackException
se a pilha estiver vazia. Escreva um programa de teste com um bloco try-catch para lidar com esse
tipo de exceção.

*16.9 (Álgebra: resolver equações lineares 3 × 3 ) O Exercício de Programação 12.24 resolve uma equação 3 × 3
sistema de equação linear. Escreva a seguinte função para resolver a equação.
Machine Translated by Google

644 Capítulo 16 Tratamento de Exceções

vetor<duplo> resolverLinearEquation(
vetor<vetor<duplo>> a, vetor<duplo> b)

O parâmetro a armazena {{a11, a12, a13}, {a21, a22, a23}, {a31, a32, a33}}e b armazena
{b1, b2, b3}. A solução para {x, y, z} é retornada em um vetor de três elementos. A função
lança um runtime_error se | Um | é 0 e um argumento_inválido se o tamanho de a,
a[0], a[1], a[2] e b não for 3.
Escreva um programa que solicite ao usuário que digite a11, a12, a13, a21, a22, a23, a31, a32,
a33, b1, b2 e b3 e exibe o resultado. Se | Um | for 0, informe que “A equação não tem
solução”. As execuções de amostra são as mesmas do Exercício de Programação 12.24.
Machine Translated by Google

CAPÍTULO

17
Recursão
Objetivos
n Descrever o que é uma função recursiva e os benefícios de usar
recursão (§17.1).
n Desenvolver programas recursivos para funções matemáticas recursivas (§§17.2–17.3).

n Para explicar como as chamadas de função recursivas são tratadas em uma pilha de
chamadas (§§17.2–17.3).

n Pensar recursivamente (§17.4).

n Para usar uma função auxiliar sobrecarregada para derivar uma função recursiva
(§17.5).
n Para resolver a classificação por seleção usando recursão (§17.5.1).

n Para resolver a pesquisa binária usando recursão (§17.5.2).

n Resolver o problema das Torres de Hanói usando recursão (§17.6).

n Para resolver o problema das Oito Rainhas usando recursão (§17.7).

n Para entender a relação e a diferença entre recursão e


iteração (§17.8).
n Conhecer funções recursivas de cauda e porque são desejáveis (§17.9).
Machine Translated by Google

646 Capítulo 17 Recursão

17.1 Introdução
A recursão é uma técnica que leva a soluções elegantes para problemas difíceis de programar usando
Chave
Apontar loops simples.

permutações de strings Suponha que você queira imprimir todas as permutações de uma string. Por exemplo, para uma
string abc, suas permutações são abc, acb, bac, bca, cab e cba. Como você resolve este problema?
Existem várias maneiras de fazer isso. Uma solução intuitiva e eficaz é usar recursão.
Problema das Oito Rainhas O quebra-cabeça clássico das Oito Rainhas consiste em colocar oito rainhas em um tabuleiro de xadrez de
modo que duas não possam atacar uma à outra (ou seja, não há duas rainhas na mesma linha, na mesma
coluna ou na mesma diagonal), conforme mostrado na Figura 17.1. Como você escreve um programa para
resolver esse problema? Existem várias maneiras de resolver esse problema. Uma solução intuitiva e eficaz é usar recursão.

Figura 17.1 O problema das Oito Rainhas pode ser resolvido usando recursão.

função recursiva Usar recursão é programar usando funções recursivas – funções que se invocam. A recursão é uma técnica
de programação útil. Em alguns casos, permite desenvolver uma solução natural, direta e simples para um
problema que de outra forma seria difícil. Este capítulo apresenta os conceitos e técnicas da programação
recursiva e ilustra com exemplos como “pensar recursivamente”.

17.2 Exemplo: Fatoriais


Uma função recursiva é aquela que invoca a si mesma.
Chave
Apontar
Muitas funções matemáticas são definidas usando recursão. Começamos com um exemplo simples que ilustra
a recursão.
O fatorial de um número n pode ser definido recursivamente da seguinte forma:

0! = 1;
não! = n × (n - 1)!; n > 0

Como você encontra n! para um determinado n? É fácil encontrar 1! porque você sabe que 0! é 1 e 1! é 1 ×
0!. Supondo que você saiba que (n - 1)!, n! pode ser obtido imediatamente usando n × (n - 1)!. Assim, o
problema de calcular n! é reduzido à computação (n - 1)!.
Ao calcular (n - 1)!, você pode aplicar a mesma ideia recursivamente até que n seja reduzido a 0.
Seja fatorial(n) a função para calcular n!. Se você chamar a função com n = 0, ela retornará imediatamente
o resultado. A função sabe como resolver o caso mais simples, denominado caso base ou condição de parada.
caso base ou condição Se você chamar a função com n > 0, ela reduzirá o problema a um subproblema para calcular o fatorial de n - 1.
de parada
O subproblema é essencialmente o mesmo que o problema original, mas é mais simples ou menor que o
original.
Como o subproblema tem a mesma propriedade do original, você pode chamar a função com um argumento
chamada recursiva diferente, que é chamado de chamada recursiva.
Machine Translated by Google

17.2 Exemplo: Fatoriais 647

O algoritmo recursivo para calcular fatorial(n) pode ser simplesmente descrito como segue:

se (n == 0)
retornar 1;
outro
retornar m * fatorial(n - 1);

Uma chamada recursiva pode resultar em muito mais chamadas recursivas, porque a função está dividindo um
subproblema em novos subproblemas. Para que uma função recursiva termine, o problema deve eventualmente
ser reduzido a um caso de parada. Neste ponto, a função retorna um resultado ao seu chamador.
O chamador então realiza um cálculo e retorna o resultado para seu próprio chamador. Este processo continua
até que o resultado seja devolvido ao chamador original. O problema original agora pode ser resolvido
multiplicando n pelo resultado de fatorial (n - 1).
A Listagem 17.1 é um programa completo que solicita ao usuário que insira um número inteiro não negativo e
exibe o fatorial do número.

Listagem 17.1 ComputeFactorial.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // Retorna o fatorial para um índice especificado
5 int fatorial(int);

6 7 int principal()
8{
// Solicita ao usuário que insira um número inteiro
9 cout << "Por favor, insira um número inteiro não negativo: ";
10 11 intn ;
12 cin >> n;
13
14 //Exibe fatorial
15 cout << "Fator de " << n << "é" << fatorial(n);
16
17 retornar 0;
18 }
19
20 // Retorna o fatorial para um índice especificado
21 int fatorial (int n)
22 {
if (n == 0) // Caso base caso base
23 retornar 1;
outro
24 retornar m * fatorial(n - 1); //chamada recursiva recursão
25 26 27 }

Insira um número inteiro não negativo: 5


Fatorial de 5 é 120

A função fatorial (linhas 21–27) é essencialmente uma tradução direta da definição matemática recursiva
para o fatorial em código C++. A chamada para fatorial é recursiva porque chama a si mesmo. O parâmetro
passado para fatorial é decrementado até atingir o caso base 0.

Você verá como escrever uma função recursiva. Como funciona a recursão? A Figura 17.2 ilustra a execução como funciona?
das chamadas recursivas, começando com n = 4. O uso do espaço de pilha para chamadas recursivas é
mostrado na Figura 17.3.
Machine Translated by Google

648 Capítulo 17 Recursão

fatorial(4)

Passo 0: executa factorial(4)


Etapa 9: retornar 24

retorne 4 * fatorial (3)

Passo 1: executa factorial(3)


Etapa 8: retornar 6
retorne 3 * fatorial (2)

Etapa 2: executa factorial(2)


Etapa 7: retornar 2

retornar 2 * fatorial (1)

Etapa 3: executa factorial(1)


Etapa 6: retornar 1

retornar 1 * fatorial (0)

Etapa 4: executa factorial(0)


Etapa 5: retornar 1

retornar 1

Figura 17.2 Invocar factorial(4) gera chamadas recursivas para factorial.

5 Registro de ativação
para fatorial (0) n:
0

4 Registro de ativação Registro de ativação


para fatorial (1) n: para fatorial (1) n:
1 1

3 Registro de ativação Registro de ativação Registro de ativação


para fatorial (2) n: para fatorial (2) n: para fatorial (2) n:
2 2 2

2 Registro de ativação Registro de ativação Registro de ativação Registro de ativação


para fatorial (3) para fatorial (3) para fatorial (3) para fatorial (3)
n: 3 n: 3 n: 3 n: 3

1 Registro de ativação Registro de ativação Registro de ativação Registro de ativação Registro de ativação
para fatorial (4) n: para fatorial (4) n: para fatorial (4) n: para fatorial (4) n: para fatorial (4) n:
4 4 4 4 4

6 Registro de ativação
para fatorial(1)
n: 1

Registro de ativação 7 Registro de ativação


para fatorial (2) n: para fatorial (2) n:
2 2

Registro de ativação Registro de ativação 8 Registro de ativação


para fatorial (3) n: para fatorial (3) n: para fatorial (3) n:
3 3 3

Registro de ativação Registro de ativação Registro de ativação 9 Registro de ativação


para fatorial (4) para fatorial (4) para fatorial (4) para fatorial (4)
n: 4 n: 4 n: 4 n: 4

Figura 17.3 Quando factorial(4) está sendo executado, a função fatorial é chamada recursivamente, fazendo com que o espaço
de memória mude dinamicamente.
Machine Translated by Google

17.2 Exemplo: Fatoriais 649

Cuidado
A recursão infinita pode ocorrer se a recursão não reduzir o problema de uma maneira que permita recursão infinita
que ele eventualmente convirja para o caso base ou se um caso base não for especificado. Por
exemplo, suponha que você escreva erroneamente a função fatorial da seguinte forma:

int fatorial(int n) {

retornar m * fatorial(n - 1);


}

A função é executada infinitamente e causa estouro de pilha.

Nota Pedagógica
É mais simples e eficiente implementar a função fatorial usando um loop.
Porém, a função fatorial recursiva é um bom exemplo para demonstrar o conceito de recursão.

Observação

O exemplo discutido até agora mostra uma função recursiva que invoca a si mesma. Isso é
conhecido como recursão direta. Também é possível criar recursão indireta. Isso ocorre quando recursão direta
a função A invoca a função B, que por sua vez invoca a função A. Pode até haver várias outras recursão indireta
funções envolvidas na recursão. Por exemplo, a função A invoca a função B, que invoca a função
C, que invoca a função A.

17.1 O que é uma função recursiva? Descreva as características das funções recursivas.
O que é uma recursão infinita? ÿVerificação de ponto

17.2 Mostre a saída dos programas a seguir e identifique casos base e recursivos
chamadas.

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

intf (int n) vazio f(int n) {


{
se (n == 1) se (n > 0) {
retornar 1;
outro cout << n% 10;
retornar n + f(n - 1); f(n/ 10);
} }
}
int principal()
{ int principal()
cout << "A soma é " << f(5) << fim; {
f(1234567);
retornar 0;
} retornar 0;
}

17.3 Escreva uma definição matemática recursiva para calcular 2n para um número inteiro positivo n.
17.4 Escreva uma definição matemática recursiva para calcular xn para um inteiro positivo n
e um número real x.

17.5 Escreva uma definição matemática recursiva para calcular 1 + 2 + 3 + para um c +n


número inteiro positivo.

17.6 Quantas vezes a função fatorial da Listagem 17.1 é invocada para factorial(6)?
Machine Translated by Google

650 Capítulo 17 Recursão

17.3 Estudo de Caso: Números de Fibonacci


Em alguns casos, a recursão permite criar uma solução intuitiva, direta e simples para um problema.
Chave
Apontar

A função fatorial na seção anterior poderia ser facilmente reescrita sem usar recursão. Em alguns casos,
entretanto, o uso da recursão permite fornecer uma solução natural, direta e simples para um programa que de
outra forma seria difícil de resolver. Considere o conhecido problema da série de Fibonacci, como segue:

A série: 0 1 1 2 3 5 8 13 21 34 55 89 . 9 10 11 . .
Índices: 0 1 2 3 4 5 6 7 8

A série de Fibonacci começa com 0 e 1, e cada número subsequente é a soma dos


dois números anteriores da série. A série pode ser definida recursivamente da seguinte forma:

fib(0) = 0;
fib(1) = 1;
fib(índice) = fib(índice - 2) + fib(índice - 1); índice >= 2

A série Fibonacci foi nomeada em homenagem a Leonardo Fibonacci, um matemático medieval, que a criou
para modelar o crescimento da população de coelhos. Pode ser aplicado em otimização numérica e em diversas
outras áreas.
Como você encontra fib(index) para um determinado índice? É fácil encontrar fib(2) porque você conhece
fib(0) e fib(1). Supondo que você conheça fib(index - 2) e fib(index - 1), fib(index) pode ser obtido imediatamente.
Assim, o problema de calcular fib(índice) é reduzido a calcular fib(índice - 2) e fib(índice - 1). Ao calcular
fib(index - 2) e fib(index - 1), você aplica a ideia recursivamente até que o índice seja reduzido para 0 ou 1.

O caso base é índice = 0 ou índice = 1. Se você chamar a função com índice = 0 ou índice = 1, ela retornará
imediatamente o resultado. Se você chamar a função com índice >= 2, ela dividirá o problema em dois
subproblemas para calcular fib(index - 1) e fib(index - 2)
usando chamadas recursivas. O algoritmo recursivo para calcular fib(index) pode ser simplesmente descrito
como segue:

se (índice == 0)
retornar 0;
senão se (índice == 1)
retornar 1;
outro
return fib(índice - 1) + fib(índice - 2);

A Listagem 17.2 é um programa completo que solicita ao usuário que insira um índice e calcula
o número de Fibonacci para o índice.

Listagem 17.2 ComputeFibonacci.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // A função para encontrar o número de Fibonacci
5 int mentira(int);

6 7 int principal()
8{
// Solicita ao usuário que insira um número inteiro
9 cout << "Insira um índice para o número de Fibonacci: ";
10 11 índice interno ;
Machine Translated by Google

17.3 Estudo de caso: Números de Fibonacci 651

12 cin >> índice;


13
14 //Exibe fatorial
15 cout << "Número de Fibonacci no índice " << fib(index) << índice << "é"
16 << endl;
17
18 retornar 0;
19 }
20
21 // A função para encontrar o número de Fibonacci
22 int fib( índice int)
23 {
24 if (índice == 0) // Caso base caso base
25 retornar 0;
26 else if (index == 1) // Caso base caso base
27 retornar 1;
28 else // Redução e chamadas recursivas
29 return fib(índice - 1) + fib(índice - 2); recursão
30 }

Insira um índice para o número de Fibonacci: 7


O número de Fibonacci no índice 7 é 13

O programa não mostra a quantidade considerável de trabalho realizado nos bastidores pelo computador. A
Figura 17.4, entretanto, mostra chamadas recursivas sucessivas para avaliar fib(4).
A função original, fib(4), faz duas chamadas recursivas, fib(3) e fib(2), e então retorna fib(3) + fib(2). Mas em
que ordem essas funções são chamadas? Em C++, os operandos para o operador binário + podem ser avaliados
em qualquer ordem. Suponha que seja avaliado da esquerda para a direita. Os rótulos na Figura 17.4 mostram
a ordem em que as funções são chamadas.

mentira (4)

17: retornar mentira (4) 0: chamada de mentira (4)

retornar fib(3) + fib(2)


11: chamada de mentira (2)
10: retornar mentira (3)

16: retornar mentira (2)


1: chamada de mentira (3)

retornar fib(2) + fib(1)


retornar fib(1) + fib(0)

7: retornar mentira (2) 14: retornar mentira(0)


8: chamada de mentira (1)
13: retornar mentira (1)
2: chamada de mentira (2) 12: ligue para mentira (1)

15: retornar mentira(0)


9: retornar mentira (1)
retornar fib(1) + fib(0) retornar 1 retornar 1 retornar 0

4: retornar mentira(1) 5: ligue para mentira (0)

3: chamada de mentira (1)

6: retornar mentira(0)
retornar 1 retornar 0

Figura 17.4 Invocar fib(4) gera chamadas recursivas para fib.

Conforme mostrado na Figura 17.4, existem muitas chamadas recursivas duplicadas. Por exemplo, fib(2)
é chamado duas vezes, fib(1) três vezes e fib(0) duas vezes. Em geral, computando fib(index)
requer o dobro de chamadas recursivas necessárias para calcular fib (índice - 1). À medida que você tenta
valores de índice maiores, o número de chamadas aumenta substancialmente, conforme mostrado na Tabela 17.1.
Machine Translated by Google

652 Capítulo 17 Recursão

Tabela 17.1 Número de chamadas recursivas em fib (índice)


índice 2 3 4 10 20 30 40 50

Nº de chamadas 3 5 9 177 21891 2.692.537 331.160.281 2.075.316.483

Nota Pedagógica
A implementação recursiva da função fib é muito simples e direta, mas não eficiente.
Veja o Exercício de Programação 17.2 para uma solução eficiente usando loops.
A função recursiva fib é um bom exemplo para demonstrar como escrever funções
recursivas, embora não seja prática.

17.7 Quantas vezes a função fib na Listagem 17.2 é invocada para fib(6)?
ÿÿVerificação de ponto

17.8 Mostre a saída dos dois programas a seguir:

#include <iostream> #include <iostream>


usando namespace std; usando namespace std;

vazio f(int n) { vazio f(int n) {

se (n > 0) { se (n > 0) {

cout << n << f(n " "; f(n - 1);


- 1); cout << n << " ";
} }
} }

int principal() int principal()


{ {
f(5); f(5);

retornar 0; retornar 0;
} }

17.9 O que há de errado na função a seguir?

#include <iostream>
usando namespace std;

vazio f(duplo n) {

se (n! = 0) {

contagem << n;
f(n/ 10);
}
}

int principal()
{
f(1234567);

retornar 0;
}
Machine Translated by Google

17.4 Solução de problemas usando recursão 653

17.4 Solução de problemas usando recursão


Se você pensar recursivamente, poderá resolver muitos problemas usando recursão.
Chave
Apontar
As seções anteriores apresentaram dois exemplos clássicos de recursão. Todas as funções recursivas possuem as
seguintes características: características de recursão

n A função é implementada usando uma instrução if-else ou switch que leva a se-outro
casos diferentes.

n Um ou mais casos base (o caso mais simples) são usados para interromper a recursão. casos básicos

n Cada chamada recursiva reduz o problema original, aproximando-o cada vez mais de um caso base até se tornar redução
esse caso.

Em geral, para resolver um problema usando recursão, você o divide em subproblemas. Se um subproblema se assemelhar
ao problema original, você poderá aplicar a mesma abordagem para resolver o subproblema recursivamente. Este
subproblema é quase igual ao problema original na natureza, com um tamanho menor.

A recursão está em toda parte. É divertido pensar recursivamente. Considere beber café. Você pode pense recursivamente

descreva o procedimento recursivamente da seguinte forma:

bebida vaziaCafé (xícara e xícara)


{
if (!cup.isEmpty())
{
xícara.takeOneSip(); //Tome um gole
beberCafé(xícara);
}
}

Suponha que cup seja um objeto para uma xícara de café com as funções de instância isEmpty() e takeOneSip().
Você pode dividir o problema em dois subproblemas: um é beber um gole de café e o outro é beber o resto do café na
xícara. O segundo problema é igual ao problema original, mas menor em tamanho. O caso base para o problema é quando
o copo
está vazia.
Vamos considerar um problema simples de imprimir uma mensagem n vezes. Você pode dividir o problema em dois
subproblemas: um é imprimir a mensagem uma vez e o outro é imprimir a mensagem n - 1 vezes. O segundo problema é
igual ao problema original com um tamanho menor. O caso base do problema é n == 0. Você pode resolver esse problema
usando recursão da seguinte maneira:

void nPrintln(const string& mensagem, int times) {

if (vezes >= 1) {

cout << mensagem << endl;


nPrintln(mensagem, vezes - 1); chamada recursiva
} // O caso base é times == 0
}
Machine Translated by Google

654 Capítulo 17 Recursão

Observe que a função fib na Listagem 17.2 retorna um valor para seu chamador, mas a função nPrintln
é nula e não retorna um valor para seu chamador.
Muitos dos problemas apresentados nos primeiros capítulos podem ser resolvidos usando recursão se
pense recursivamente você pensar recursivamente. Considere o problema do palíndromo na Listagem 5.16, TestPalindrome.cpp.
Lembre-se de que uma string é um palíndromo se for lida da mesma forma da esquerda e da direita. Por
exemplo, mamãe e papai são palíndromos, mas tio e tia não. O problema para verificar se uma string é
um palíndromo pode ser dividido em dois subproblemas:

n Verifique se o primeiro caractere e o último caractere da string são iguais.

n Ignore esses dois caracteres finais e verifique se o restante da substring é um


palíndromo.

O segundo subproblema é igual ao problema original com tamanho menor. Existem dois casos básicos:
(1) os dois caracteres finais não são iguais; (2) o tamanho da string é 0 ou 1. No caso 1, a string não é um
palíndromo; e no caso 2, a string é um palíndromo. A função recursiva para este problema pode ser
implementada na Listagem 17.3.

Listagem 17.3 RecursivePalindrome.cpp


1 #include <iostream>
incluir arquivo de cabeçalho 2 #incluir <string>
3 usando namespace std;

cabeçalho de função 4 5 bool isPalindrome(const string& s)


6{
comprimento da corda 7 if (s.size() <= 1) // Caso base
retornar verdadeiro;
senão if (s[0] != s[s.size() - 1]) // Caso base
8 retorna falso;
9 10 11 outro
chamada recursiva 12 return isPalindrome(s.substr(1, s.size() - 2));
13}
14
15 int principal()
16 {
17 cout << "Digite uma string: ";
18 cordas;
sequência de entrada 19 getline(cin,s);
20
21 if (é Palíndromo(s))
cout << s << "é um palíndromo" << endl;
22 outro
23 cout << s << "não é um palíndromo" << endl;
24
25 retornar 0;
26 27 }

Digite uma string: aba aba


é um palíndromo

Digite uma string: abab abab


não é um palíndromo

A função isPalindrome verifica se o tamanho da string é menor ou igual a 1


(linha 7). Nesse caso, a string é um palíndromo. A função verifica se o primeiro e o último elemento da
string são iguais (linha 9). Caso contrário, a string não é um palíndromo. De outra forma,
Machine Translated by Google

17.5 Funções Auxiliares Recursivas 655

obtenha uma substring de s usando s.substr(1, s.size() - 2) e invoque recursivamente isPalindrome com a
nova string (linha 12).

17.5 Funções auxiliares recursivas


Às vezes você pode encontrar uma solução para o problema original definindo uma função
Chave
recursiva para um problema semelhante ao problema original. Esta nova função é chamada de Apontar

função auxiliar recursiva. O problema original pode ser resolvido invocando a função auxiliar
recursiva.

A função recursiva isPalindrome anterior não é eficiente porque cria uma nova string para cada chamada
recursiva. Para evitar a criação de novas cordas, você pode usar os graves e agudos
índices para indicar o intervalo da substring. Esses dois índices devem ser passados para a função
recursiva. Como a função original é isPalindrome(const string& s), você deve criar uma nova função
isPalindrome(const string& s, int low, int high) para aceitar informações adicionais na string, conforme
mostrado na Listagem 17.4.

Listagem 17.4 RecursivePalindromeUsingHelperFunction.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;
4
5 bool isPalindrome(const string& s, int baixo, int alto) função auxiliar
6{
if (alto <= baixo) // Caso base
retornar verdadeiro;
else if (s[low] != s[high]) // Caso base
7 retorna falso;
8 outro
9 return isPalindrome(s, baixo + 1, alto - 1); chamada recursiva

10 11 12 13 }
14
15 bool é Palíndromo (const string& s) cabeçalho de função

16 {
17 return isPalindrome(s, 0, s.size() - 1); invocar função auxiliar
18}
19
20 int principal()
21 {
22 cout << "Digite uma string: ";
23 cordas;
24 getline(cin,s); sequência de entrada

25
26 if (é Palíndromo(s))
27 cout << s << else "é um palíndromo" << endl;
28
29 cout << s << "não é um palíndromo" << endl;
30
31 retornar 0;
32}

Digite uma string: aba aba


é um palíndromo

Digite uma string: abab abab


não é um palíndromo
Machine Translated by Google

656 Capítulo 17 Recursão


Duas funções isPalindrome sobrecarregadas são definidas. A função éPalin
drome(const string& s) (linha 15) verifica se uma string é um palíndromo, e a segunda
função isPalindrome(const string& s, int low, int high) (linha 5) verifica se uma
substring s(low..high ) é um palíndromo. A primeira função passa a string s
com low = 0 e high = s.size() – 1 para a segunda função. A segunda função pode ser invocada recursivamente para
verificar um palíndromo em uma substring cada vez menor. É uma técnica de projeto comum em programação recursiva
definir uma segunda função que recebe parâmetros adicionais. Essa função é conhecida como função auxiliar recursiva.
função auxiliar recursiva
Funções auxiliares são muito úteis para projetar soluções recursivas para problemas envolvendo strings e arrays.
As seções a seguir apresentam mais dois exemplos.

17.5.1 Ordenação por seleção


A classificação por seleção foi introduzida na Seção 7.10, “Classificando matrizes”. Agora apresentamos uma
classificação de seleção recursiva para caracteres em uma string. Uma variação da classificação por seleção funciona
da seguinte maneira. Ele encontra o maior elemento da lista e o coloca por último. Em seguida, ele encontra o maior
elemento restante e o coloca próximo ao último, e assim por diante, até que a lista contenha apenas um único elemento.
O problema pode ser dividido em dois subproblemas:

n Encontre o maior elemento da lista e troque-o pelo último elemento.

n Ignore o último elemento e classifique a lista menor restante recursivamente.

O caso base é que a lista contém apenas um elemento.


A Listagem 17.5 fornece a função de classificação recursiva.

Listagem 17.5 RecursiveSelectionSort.cpp


1 #include <iostream>
2 #incluir <string>
3 usando namespace std;

função de classificação auxiliar 4 5 void sort(string& s, int high)


6{
se (alto > 0)
{
//Encontre o maior elemento e seu índice
7 int indexOfMax = 0;
8 char máx = s[0];
9 for (int i = 1; i <= alto; i++)
10 {
11 se (s[i] > máx.)
12 {
13 máximo = s[i];
14 indexOfMax = i;
15 }
16 }
17
18 //Troca o maior pelo último elemento da lista
19 s[indexOfMax] = s[alto];
20 s[alto] = máx;
21
22 //Ordena a lista restante
chamada recursiva 23 classificar(s, alto - 1);
24 }
25 26 27 28 }

função de classificação 29 30 classificação nula (string& s)


Machine Translated by Google

17.5 Funções Auxiliares Recursivas 657

31 {
classificar(s, s.size() - 1); invocar função auxiliar
32 33}

34 35 int principal()
36 {
37 cout << "Digite uma string: ";
38 cordas;
39 getline(cin,s); sequência de entrada

40
41 classificar(ões);
42
43 cout << "A string classificada é " << s << endl;
44
45 retornar 0;
46 }

Insira uma string: ghfdacb


A string classificada é abcdfgh

Duas funções de classificação sobrecarregadas são definidas. A função sort(string& s) classifica caracteres
em s[0..s.size() - 1] e a segunda função sort(string& s, int high)
classifica caracteres em s[0..high]. A função auxiliar pode ser invocada recursivamente para classificar uma
substring cada vez menor.

17.5.2 Pesquisa Binária


A pesquisa binária foi introduzida na Seção 7.9.2, “A abordagem da pesquisa binária”. Para que a pesquisa binária Nota de vídeo
funcione, os elementos do array já devem estar ordenados. A pesquisa binária primeiro compara a chave com o Pesquisa binária

elemento no meio do array. Considere os seguintes casos:

n Caso 1: Se a chave for menor que o elemento do meio, pesquise recursivamente a chave no
primeira metade da matriz.

n Caso 2: Se a chave for igual ao elemento do meio, a pesquisa termina com uma correspondência.

n Caso 3: Se a chave for maior que o elemento do meio, pesquise recursivamente a chave na segunda
metade do array.

O Caso 1 e o Caso 3 reduzem a pesquisa a uma lista menor. O caso 2 é um caso base quando há uma
correspondência. Outro caso básico é que a pesquisa se esgota sem correspondência. A Listagem 17.6 fornece uma
solução clara e simples para o problema de pesquisa binária usando recursão.

Listagem 17.6 RecursiveBinarySearch.cpp


1 #include <iostream>
2 usando namespace std;

3 4 int binarySearch(const int list[], int key, int low, int high) função auxiliar
5{
6 if (low > high) // A lista se esgotou sem correspondência
retornar -baixo - 1; //chave não encontrada, retorna o ponto de inserção caso base
7
8 int médio = (baixo + alto)/ 2;
9 if (chave <lista[meio])
10 retornar pesquisabinária(lista, chave, baixo, médio - 1); chamada recursiva
11 12 senão if (chave == lista[meio])
Machine Translated by Google

658 Capítulo 17 Recursão


caso base 13 retornar no meio;
14 outro
chamada recursiva 15 return pesquisabinária(lista, chave, médio + 1, alto);
16 }
17
Função de pesquisa binária 18 int binarySearch(const int list[], chave int , tamanho int )
19 {
20 int baixo = 0;
21 int alto = tamanho - 1;
chamar função auxiliar return pesquisabinária(lista, chave, baixo, alto);
22 23 }

24 25 int principal()
26 {
27 lista interna [] = { 2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79};
28 int i = buscabinária(lista, 2, 13); //Retorna 0
29 int j = buscabinária(lista, 11, 13); //Retorna 4
30 int k = buscabinária(lista, 12, 13); // Retorna –6
31
"
32 cout << "binarySearch(lista, 2, 13) retorna cout << << eu << endl;
"
33 "binarySearch(lista, 11, 13) retorna cout << << j << endl;
"
34 "binarySearch(lista, 12, 13) retorna << k << final;
35
36 retornar 0;
37 }

binarySearch(lista, 2, 13) retorna 0


binarySearch(lista, 11, 13) retorna 4
binarySearch(lista, 12, 13) retorna -6

A função binarySearch na linha 18 encontra uma chave em toda a lista. O ajudante binárioSearch
A função na linha 4 encontra uma chave na lista com índice de menor para maior.
A função binarySearch na linha 18 passa o array inicial com low = 0 e high = size - 1 para a função auxiliar
binarySearch . A função auxiliar é invocada recursivamente para encontrar a chave em um subarray cada vez
menor.

17.10 Mostre a pilha de chamadas para isPalindrome("abcba") usando as funções definidas nas Listagens
ÿVerificação de ponto 17.3 e 17.4, respectivamente.

17.11 Mostre a pilha de chamadas para selectionSort("abcba") usando a função definida em


Listagem 17.5.

17.12 O que é uma função auxiliar recursiva?

17.6 Torres de Hanói


Nota de vídeo O problema clássico das Torres de Hanói pode ser resolvido facilmente usando recursão, mas é
Chave
Torres de Hanói Apontar difícil resolvê-lo de outra forma.

O problema das Torres de Hanói é um exemplo clássico de recursão. Pode ser resolvido facilmente usando
recursão, mas é difícil de resolver de outra forma.
O problema envolve mover um número específico de discos de tamanhos distintos de uma torre para outra,
observando as seguintes regras:

n Existem n discos rotulados como 1, 2, 3, . . . , n e três torres identificadas como A, B e C.

n Nenhum disco pode ficar em cima de um disco menor em nenhum momento.


Machine Translated by Google

17.6 Torres de Hanói 659

n Todos os discos são inicialmente colocados na torre A.

n Apenas um disco pode ser movido por vez e deve ser o disco superior da torre.

O objetivo é mover todos os discos de A para B com a ajuda de C. Por exemplo, se você tiver três discos, as
etapas para mover todos os discos de A para B são mostradas na Figura 17.5.

0 4

A B C A B C

Posição original Etapa 4: mover o disco 3 de A para B

1 5

A B C A B C

Etapa 1: mover o disco 1 de A para B Etapa 5: mova o disco 1 de C para A

2 6

A B C A B C

Etapa 2: mover o disco 2 de A para C Etapa 6: mover o disco 2 de C para B

3 7

A B C A B C

Etapa 3: mover o disco 1 de B para C Etapa 7: mover o disco 1 de A para B

Figura 17.5 O objetivo do problema das Torres de Hanói é mover os discos da torre A para a torre B sem
quebrar as regras.

Observação

As Torres de Hanói são um problema clássico da ciência da computação. Muitos sites são
dedicados a esse problema. Vale a pena dar uma olhada no site www.cut-the-knot.com/recurrence/
hanoi.shtml .

No caso de três discos, você pode encontrar a solução manualmente. Entretanto, para um número maior de
discos – mesmo para quatro – o problema é bastante complexo. Felizmente, o problema tem uma natureza
inerentemente recursiva, o que leva a uma solução recursiva simples.
O caso base para o problema é n = 1. Se n == 1, você poderia simplesmente mover o disco de A para B.
Quando n > 1, você poderia dividir o problema original em três subproblemas e resolvê-los sequencialmente.

1. Mova os primeiros n - 1 discos de A para C recursivamente com a ajuda da torre B, como


mostrado na Etapa 1 da Figura 17.6.

2. Mova o disco n de A para B, conforme mostrado na Etapa 2 da Figura 17.6.


Machine Translated by Google

660 Capítulo 17 Recursão

3. Mova n - 1 discos de C para B recursivamente com a ajuda da torre A, conforme mostrado em


Etapa 3 na Figura 17.6.

02
discos n-1 discos n-1

.
. .
. .
.

A B C A B C

Posição original Etapa 2: mover o disco n de A para B

1 3
discos n-1 discos n-1

. .
. .
. .

A B C A B C

Etapa 1: mover os primeiros n-1 discos de A para C recursivamente Etapa 3: mover discos n-1 de C para B recursivamente

Figura 17.6 O problema das Torres de Hanói pode ser decomposto em três subproblemas.

A função a seguir move n discos de fromTower para toTower com o


assistência do auxTower:

void moveDisks(int n, char fromTower, char toTower, char auxTower)

O algoritmo para a função pode ser descrito da seguinte forma:

if (n == 1) // Condição de parada
Mova o disco 1 de fromTower para toTower;
outro
{
moveDisks(n - 1, fromTower, auxTower, toTower);
Mova o disco n de fromTower para toTower;
moveDisks(n - 1, auxTower, toTower, fromTower);
}

A Listagem 17.7 solicita que o usuário insira o número de discos e invoca a função recursiva
tion moveDisks para exibir a solução para mover os discos.

Listagem 17.7 TowersOfHanoi.cpp


1 #include <iostream>
2 usando namespace std;
3
4 // A função para encontrar a solução para mover n discos
5 // de fromTower para toTower com auxTower
Machine Translated by Google

17.6 Torres de Hanói 661

6 void moveDisks(int n, char fromTower, função recursiva


char toTower, char auxTower)
78{
if (n == 1) // Condição de parada
9 << n << << << toTower
cout << "Mover disco " " de " fromTower <<
" "
10 << endl; para

11 outro
12 {
13 moveDisks(n - 1, fromTower, auxTower, toTower); recursão
14 << n << << << toTower
cout << "Mover disco " " de " fromTower <<
" "
15 << endl; para

16 moveDisks(n - 1, auxTower, toTower, fromTower); recursão


17 }
18 19 }
20
21 int principal()
22 {
23 // Ler o número de discos, n
24 cout << "Insira o número de discos: ";
25 intn ;
26 cin >> n;
27
28 // Encontre a solução recursivamente
"
29 cout << "Os movimentos são: << endl;
30 moveDisks(n, 'A', 'B', 'C');
31
32 retornar 0;
33 }

Insira o número de discos: 4


Os movimentos são:
Mova o disco 1 de A para C
Mova o disco 2 de A para B
Mova o disco 1 de C para B
Mova o disco 3 de A para C
Mova o disco 1 de B para A
Mova o disco 2 de B para C
Mova o disco 1 de A para C
Mova o disco 4 de A para B
Mova o disco 1 de C para B
Mova o disco 2 de C para A
Mova o disco 1 de B para A
Mova o disco 3 de C para B
Mova o disco 1 de A para C
Mova o disco 2 de A para B
Mova o disco 1 de C para B

Este problema é inerentemente recursivo. O uso da recursão torna possível encontrar uma solução
natural e simples. Seria difícil resolver o problema sem usar recursão.
Considere rastrear o programa para n = 3. As chamadas recursivas sucessivas são mostradas na
Figura 17.7. Como você pode ver, escrever o programa é mais fácil do que rastrear as chamadas
recursivas. O sistema usa pilhas para rastrear as chamadas nos bastidores. Até certo ponto, a
recursão fornece um nível de abstração que oculta iterações e outros detalhes do usuário.
Machine Translated by Google

662 Capítulo 17 Recursão

moverDiscos(3,'A','B','C')

moveDisks(2,'A','C','B') move o
disco 3 de A para B
moveDisks(2,'C','B','A')

moverDiscos(2,'A','C','B') moveDiscos(2,'C','B','A')

moveDisks(1,'A','B','C') move o moverDiscos(1,'C','A','B')


disco 2 de A para C mova o disco 2 de C para B
moveDisks(1,'B','C','A') moverDiscos(1,'A','B','C')

moverDiscos(1,'A','B','C') moverDiscos(1,'B','C','A') moverDiscos(1,'C','A','B') moverDiscos(1,'A','B','C')

mova o disco 1 de A para B mova o disco 1 de B para C mova o disco 1 de C para A mova o disco 1 de A para B

Figura 17.7 Invocar moveDisks(3, 'A', 'B', 'C') gera chamadas para moveDisks recursivamente.

17.13 Quantas vezes a função moveDisks na Listagem 17.7 é invocada para


ÿVerificação de ponto moveDisks(5, 'A', 'B', 'C')?

17.7 Oito Rainhas


O problema das Oito Rainhas pode ser resolvido usando recursão.
Chave
Apontar
Esta seção fornece uma solução recursiva para o problema das Oito Rainhas apresentado anteriormente. A tarefa
é colocar uma rainha em cada linha de um tabuleiro de xadrez de forma que duas rainhas não possam atacar uma
à outra. Você pode usar uma matriz bidimensional para representar um tabuleiro de xadrez. Entretanto, como cada
linha pode ter apenas uma rainha, é suficiente usar um array unidimensional para denotar a posição da rainha na
linha. Então, vamos declarar array queens da seguinte forma:

int rainhas[8];

Atribua j a rainhas[i] para denotar que uma rainha é colocada na linha i e na coluna j. A Figura 17.8a mostra o
conteúdo do arranjo de rainhas para o tabuleiro de xadrez da Figura 17.8b.

rainhas[0] 0

rainhas[1] 6

rainhas[2] 4

rainhas[3] 7

rainhas[4] 1

rainhas[5] 3

rainhas[6] 5

rainhas[7] 2

(a) (b)

A Figura 17.8 rainhas[i] denota a posição da rainha na linha i.

A Listagem 17.8 é um programa que encontra uma solução para o problema das Oito Rainhas.

Listagem 17.8 EightQueen.cpp


1 #include <iostream> 2
usando namespace std;
Machine Translated by Google

17.7 Oito Rainhas 663

3 4 const int NUMBER_OF_QUEENS = 8; // Constante: oito rainhas


5 rainhas int [NUMBER_OF_QUEENS];
6
7 // Verifica se uma rainha pode ser colocada na linha i e na coluna j 8 bool isValid(int
row, int column) verifique se é válido

9{
10 for (int i = 1; i <= linha; i++)
11 if (rainhas[linha - i] == coluna //Verifica coluna
12 || queens[row - i] == column - i // Verifica a diagonal superior esquerda
13 || queens[row - i] == column + i) // Verifica a diagonal superior direita
14 retorna falso; //Há um conflito
15 retornar verdadeiro; // Sem conflito
16 }
17
18 // Exibe o tabuleiro de xadrez com oito rainhas 19 void
printResult()
20 {
21 cout << "\n---------------------------------\n";
22 for (int linha = 0; linha < NUMBER_OF_QUEENS; linha++)
23 {
24 for (int coluna = 0; coluna < NUMBER_OF_QUEENS; coluna++)
"
25 printf(coluna == rainhas[linha] ? "| Q : "| ");
26 cout << "|\n---------------------------------\n";
27 }
28 }
29
30 // Pesquisa para colocar uma rainha na linha especificada 31 bool
search(int row) pesquisar esta linha

32 {
33 if (row == NUMBER_OF_QUEENS) // Condição de parada
34 retornar verdadeiro; // Foi encontrada uma solução para colocar 8 rainhas em 8 linhas
35
36 for (int coluna = 0; coluna < NUMBER_OF_QUEENS; coluna++) colunas de pesquisa
37 {
38 rainhas[linha] = coluna; //Coloca uma rainha em (linha, coluna)
39 if (isValid(linha, coluna) && pesquisa(linha + 1)) pesquisar na próxima linha

40 retornar verdadeiro; // Encontrado, portanto retorne true para sair do loop for encontrado

41 }
42
43 // Nenhuma solução para uma rainha colocada em qualquer coluna desta linha
44 retorna falso; não encontrado

45 }
46
47 int principal()
48 {
49 pesquisa(0); // Inicia a pesquisa a partir da linha 0. Observe que os índices das linhas são de 0 a 7
printResult(); //Exibir resultado
50
retornar 0;
51 52 53 }

---------------------------------

|P| | | | | | | |
---------------------------------

| | | | |P| | | |
---------------------------------

| | | | | | | |P|
---------------------------------

(contínuo )
Machine Translated by Google

664 Capítulo 17 Recursão

| | | | | |P| | |
---------------------------------

| | |P| | | | | |
---------------------------------

| | | | | | |P| |
---------------------------------

| |P| | | | | | |
---------------------------------

| | | |P| | | | |
---------------------------------

O programa invoca search(0) (linha 49) para iniciar uma busca por uma solução na linha 0, que invoca
recursivamente search(1), search(2), . . . e search(7) (linha 39).
A função de pesquisa recursiva (linha) retorna verdadeiro se todas as linhas forem preenchidas (linhas
39–40). A função verifica se uma rainha pode ser colocada na coluna 0, 1, 2,. . e .7, em um loop for (linha
36). Coloque uma rainha na coluna (linha 38). Se o posicionamento for válido, pesquise recursivamente a
próxima linha invocando search(row + 1) (linha 39). Se a pesquisa for bem-sucedida, retorne verdadeiro
(linha 40) para sair do loop for . Nesse caso, não há necessidade de procurar a próxima coluna da linha.
Se não houver solução para colocar uma rainha em qualquer coluna desta linha, a função retorna falso
(linha 44).
Suponha que você invoque search(row) para row is 3, conforme mostrado na Figura 17.9a. A função
tenta preencher uma rainha nas colunas 0, 1, 2 e assim por diante, nesta ordem. Para cada tentativa, a
função isValid(row, column) (linha 39) é chamada para verificar se colocar uma rainha na posição
especificada causa um conflito com as rainhas colocadas antes desta linha. Ele garante que nenhuma
rainha seja colocada na mesma coluna (linha 11), nenhuma rainha seja colocada na diagonal superior
esquerda (linha 12) e nenhuma rainha seja colocada na diagonal superior direita (linha 13), conforme mostrado na Figura 17.9
Se isValid(row, column) retornar false, verifique a próxima coluna, conforme mostrado na Figura 17.9b.
Se isValid(linha, coluna) retornar verdadeiro, invoque recursivamente search(linha + 1), conforme
mostrado na Figura 17.9d. Se search(row + 1) retornar falso, verifique a próxima coluna na linha anterior,
conforme mostrado na Figura 17.9c.

01234567 01234567
0 verificar 0 verificar
coluna coluna
1 1 cima esquerda
diagonal vertical diagonal vertical
2 2
3 pesquisa (linha) 3 pesquisa (linha)

4 4
5 5
6 6
7 7

(a) (b)

01234567 01234567
0 verificar 0
coluna
1 1 verificar
diagonal vertical coluna
2 2
cima esquerda diagonal vertical
3 pesquisa (linha) 3
4 4 pesquisa (linha 1)

5 5
6 6
7 7

(c) (d)

Figura 17.9 A invocação de search(row) preenche uma rainha em uma coluna da linha.
Machine Translated by Google

17.9 Recursão de Cauda 665

17.8 Recursão versus Iteração


A recursão é uma forma alternativa de controle de programa. É essencialmente uma repetição sem loop.
Chave
Apontar

A recursão é uma forma alternativa de controle de programa. É essencialmente uma repetição sem controle de loop. Ao
usar loops, você especifica um corpo de loop. A repetição do corpo do loop é controlada pela estrutura de controle do
loop. Na recursão, a própria função é chamada repetidamente.
Uma instrução de seleção deve ser usada para controlar se a função deve ser chamada recursivamente ou não.
A recursão tem uma sobrecarga substancial. Cada vez que o programa chama uma função, o sistema deve atribuir sobrecarga de recursão

espaço para todas as variáveis e parâmetros locais da função. Isto pode consumir memória considerável e requer tempo
extra para gerenciar o espaço adicional.
Qualquer problema que possa ser resolvido recursivamente pode ser resolvido de forma não recursiva com iterações.
A recursão tem alguns aspectos negativos: Ela usa muito tempo e muita memória. Por que, então, você deveria usá-lo?
Em alguns casos, o uso da recursão permite especificar uma solução clara e simples para um problema recursivo inerente vantagens de recursão
que, de outra forma, seria difícil de obter. O problema das Torres de Hanói é um exemplo, bastante difícil de resolver sem
o uso de recursão.

A decisão de usar recursão ou iteração deve ser baseada na natureza do problema que você está tentando resolver recursão ou iteração?

e na sua compreensão dele. A regra prática é usar qualquer uma das duas abordagens que melhor possa desenvolver
uma solução intuitiva que espelhe naturalmente o problema. Se uma solução iterativa for óbvia, use-a. Geralmente será
mais eficiente que a opção recursiva.

Observação

Seu programa recursivo pode ficar sem memória, causando um tempo de execução de estouro de pilha estouro de pilha
erro.

Dica
Se você está preocupado com o desempenho do seu programa, evite usar recursão, pois ela leva mais preocupação com o desempenho

tempo e consome mais memória do que a iteração.

17.9 Recursão de Cauda


Uma função recursiva final é eficiente para reduzir o espaço da pilha.
Chave
Apontar
Uma função recursiva é considerada recursiva de cauda se não houver operações pendentes a serem executadas no
retorno de uma chamada recursiva, como ilustrado na Figura 17.10a. Entretanto, a função B na Figura 17.10b não é recursão de cauda

recursiva final porque há operações pendentes após o retorno de uma chamada de função.

Função recursiva A Função recursiva B


... ...
... ...
... Invocar a função B recursivamente
Invocar a função A recursivamente ...
...

(a) Recursão de cauda (b) Recursão não-cauda

Figura 17.10 Uma função recursiva final não possui operações pendentes após uma chamada recursiva.

Por exemplo, a função recursiva isPalindrome (linhas 5–13) na Listagem 17.4 é recursiva final porque não há operações
pendentes após invocar recursivamente isPalindrome em
Machine Translated by Google

666 Capítulo 17 Recursão

linha 12. Entretanto, a função fatorial recursiva (linhas 21–27) na Listagem 17.1 não é recursiva final, porque há uma
operação pendente, a saber, multiplicação, a ser executada no retorno de cada chamada recursiva.

A recursão final é desejável porque a função termina quando a última chamada recursiva termina. Portanto não
há necessidade de armazenar as chamadas intermediárias na pilha. Alguns compiladores podem otimizar a recursão
final para reduzir o espaço de pilha.
Uma função não recursiva de cauda geralmente pode ser convertida em uma função recursiva de cauda usando
parâmetros auxiliares. Esses parâmetros são usados para conter o resultado. A ideia é incorporar as operações
pendentes nos parâmetros auxiliares de tal forma que a chamada recursiva não tenha mais operação pendente.
Você pode definir uma nova função recursiva auxiliar com os parâmetros auxiliares. Esta função pode sobrecarregar
a função original com o mesmo nome, mas com uma assinatura diferente. Por exemplo, a função fatorial na Listagem
17.1 pode ser escrita de forma recursiva final como segue:

1 // Retorna o fatorial para um número especificado 2 int factorial(int


função original n) 3 {

invocar função auxiliar retornar fatorial(n, 1); //Chama função auxiliar


4 5}
6
7 // Função recursiva de cauda auxiliar para fatorial 8 int factorial(int n, int
função auxiliar result) 9 {

10 se (n == 1)
11 retornar resultado;
12 outro
chamada recursiva 13 retornar fatorial (n - 1, n *resultado); //chamada recursiva
14}

A primeira função fatorial simplesmente invoca a segunda função auxiliar (linha 4). A segunda função contém um
parâmetro auxiliar result que armazena o resultado do fatorial de n. Esta função é invocada recursivamente na linha
13. Não há operação pendente após o retorno de uma chamada. O resultado final é retornado na linha 11, que
também é o valor de retorno da invocação de factorial(n, 1) na linha 4.

17.14 Quais das seguintes afirmações são verdadeiras?


ÿVerificação de ponto
n Qualquer função recursiva pode ser convertida em uma função não recursiva.

n Uma função recursiva leva mais tempo e memória para ser executada do que uma função não recursiva
função.

n Funções recursivas são sempre mais simples que funções não recursivas.

n Sempre há uma instrução de seleção em uma função recursiva para verificar se um


o caso base é alcançado.

17.15 Qual é a causa da exceção de estouro de pilha?

17.16 Identifique funções recursivas de cauda neste capítulo.

17.17 Reescreva a função fib na Listagem 17.2 usando recursão tail.

Termos chave
caso básico 646 função auxiliar recursiva 656
recursão infinita 649 condição de parada 646
função recursiva 646 recursão de cauda 665
Machine Translated by Google

Exercícios de Programação 667

Resumo do capítulo

1. Uma função recursiva é aquela que se invoca direta ou indiretamente. Para uma recursividade
função terminar, deve haver um ou mais casos base.

2. A recursão é uma forma alternativa de controle de programa. É essencialmente uma repetição sem
controle de loop. Pode ser usado para escrever soluções simples e claras para problemas
inerentemente recursivos que de outra forma seriam difíceis de resolver.

3. Às vezes, a função original precisa ser modificada para receber parâmetros adicionais para ser
invocada recursivamente. Uma função auxiliar recursiva pode ser definida para esse propósito.

4. A recursão acarreta sobrecarga substancial. Cada vez que o programa chama uma função, o
sistema deve atribuir espaço para todas as variáveis e parâmetros locais da função. Isto
pode consumir memória considerável e requer tempo extra para gerenciar o espaço adicional.

5. Uma função recursiva é considerada recursiva final se não houver operações pendentes a serem
executadas no retorno de uma chamada recursiva. Alguns compiladores podem otimizar a recursão
final para reduzir o espaço de pilha.

Questionário

Responda ao questionário deste capítulo online em www.cs.armstrong.edu/liang/cpp3e/quiz.html.

Exercícios de programação

Seções 17.2–17.3

17.1 (Pesquisa linear) Reescreva a função de pesquisa linear na Listagem 7.9 usando recursão.
*17.2 (números de Fibonacci) Reescreva a função fib na Listagem 17.2 usando iterações.
Dica: para calcular fib(n) sem recursão, você precisa primeiro obter fib(n - 2) e fib(n - 1) . Sejam
f0 e f1 os dois números de Fibonacci anteriores. O número atual de Fibonacci seria então f0 +
f1. O algoritmo pode ser descrito da seguinte forma:

f0 = 0; //Para fib(0)
f1 = 1; //Para mentira(1)

for (int i = 2; i <= n; i++) {

atualFib = f0 + f1;
f0 = f1;
f1 = atualFib;
}

// Após o loop, currentFib é fib(n)

Escreva um programa de teste que solicite ao usuário que insira um índice e exiba seu número
Fibo-nacci.

*17.3 (Calcule o máximo divisor comum usando recursão) O mdc(m, n) também pode ser
definido recursivamente da seguinte forma:
Nota de vídeo

n Se m% n for 0, mdc (m, n) é n. O problema do GCD

n Caso contrário, mdc(m, n) é mdc(n, m % n).


Machine Translated by Google

668 Capítulo 17 Recursão

Escreva uma função recursiva para encontrar o GCD. Escreva um programa de teste que solicite
ao usuário que insira dois números inteiros e exiba seu GCD.

17.4 (Soma de séries) Escreva uma função recursiva para calcular as seguintes séries:
1 1 1
f(n) = 1 + + + c +
4 9 n2

Escreva um programa de teste que exiba f(n) para n = 1, 2, . . . , 15.


17.5 (Soma de séries) Escreva uma função recursiva para calcular as seguintes séries:
1 1 1 1
f(n) = 3 + + + c +
8 15 n(n + 2)

Escreva um programa de teste que exiba f(n) para n = 1, 2, . . . , 15.


**17.6 (Soma de séries) Escreva uma função recursiva para calcular as seguintes séries:
3 3 1 3
f(n) = 1 + + + + c +
4 5 2 n+2

Escreva um programa de teste que exiba f(n) para n = 1, 2, . . . , 15.


*17.7 (string Palindrome) Modifique a Listagem 17.3, RecursivePalindrome.cpp, para que o
programa encontre o número de vezes que a função isPalindrome é chamada.
(Dica: use uma variável global e aumente-a sempre que a função for chamada.)

Seção 17.4
**17.8 (Contar dígitos pares e ímpares) Escreva uma função recursiva que exiba o número
de dígitos pares e ímpares em um número inteiro usando o seguinte cabeçalho:

void EvenAndOddCount(valor int )

Escreva um programa de teste que solicite ao usuário que insira um número inteiro e exiba
o número de dígitos pares e ímpares nele.
**17.9 (Imprime os caracteres em uma string inversamente) Escreva uma função recursiva que
exibe uma string inversamente no console usando o seguinte cabeçalho:

void reverseDisplay(const string& s)

Por exemplo, reverseDisplay("abcd") exibe dcba. Escreva um programa de teste


que solicite ao usuário que insira uma string e exiba sua reversão.
*17.10 (Ocorrências de um caractere especificado em uma string) Escreva uma função recursiva que
encontre o número de ocorrências de uma letra especificada em uma string usando o
Nota de vídeo
seguinte cabeçalho de função.
Contar ocorrência
contagem int (const string& s, char a)

Por exemplo, count("Bem-vindo", 'e') retorna 2. Escreva um programa de teste que


solicite ao usuário que insira uma string e um caractere e exiba o número de
ocorrências do caractere na string.
**17.11 (Produto de dígitos em um número inteiro usando recursão) Escreva uma função recursiva que
calcule o produto dos dígitos em um número inteiro. Use o seguinte cabeçalho de função:

int dígitos do produto (int n)

Por exemplo, productDigits(912) retorna 9 * 1 * 2 = 18. Escreva um programa de


teste que solicite ao usuário que insira um número inteiro e exiba o produto dos dígitos.
Machine Translated by Google

Exercícios de Programação 669

Seção 17.5
**17.12 (Imprime os caracteres em uma string inversamente) Reescreva o Exercício de Programação
17.9 usando uma função auxiliar para passar o índice alto da substring para a função. O
cabeçalho da função auxiliar é o seguinte:

void reverseDisplay(const string& s, int high)

**17.13 (Encontre o menor número em um array) Escreva uma função recursiva que retorne o menor
número inteiro em um array. Escreva um programa de teste que solicite ao usuário que
insira uma lista de cinco inteiros e exiba o menor inteiro.
*17.14 (Encontre o número de letras minúsculas em uma string) Escreva uma função recursiva para
retornar o número de letras minúsculas em uma string. Você precisa definir as duas
funções a seguir. A segunda é uma função auxiliar recursiva.

int getNumberOfLowercaseLetters(const string& s) int


getNumberOfLowercaseLetters(const string& s, int low)

Escreva um programa de teste que solicite ao usuário que insira uma string e exiba o
número de letras minúsculas na string.
*17.15 (Ocorrências do caractere de espaço em uma string) Escreva uma função recursiva para
retornar o número total de caracteres de espaço em uma string. Você precisa definir as
duas funções a seguir. A segunda é uma função auxiliar recursiva.

int numberOfSpaces(const string& s) int


numberOfSpaces(const string& s, int i)

Escreva um programa de teste que solicite ao usuário que insira uma string, invoque a
função e exiba o número de espaços na string.

Seção 17.6
*17.16 (Torres de Hanói) Modifique a Listagem 17.7, TowersOfHanoi.cpp, para que o programa
encontre o número de movimentos necessários para mover n discos da torre A para a
torre B. (Dica: use uma variável global e aumente-a sempre a função é chamada.)

Compreensivo
***17.17 (Permutações de strings) Escreva uma função recursiva para imprimir todas as permutações de
uma string. Por exemplo, para uma string abc, a permutação é

abc
bateria

voltar
bca
táxi
cba

(Dica: defina as duas funções a seguir. A segunda é uma função auxiliar.)

void displayPermuation(const string& s) void


displayPermuation(const string& s1, const string& s2)

A primeira função simplesmente invoca displayPermuation("", s). A segunda função


usa um loop para mover um caractere de s2 para s1 e invocar recursivamente
Machine Translated by Google

670 Capítulo 17 Recursão

com um novo s1 e s2. O caso base é que s2 está vazio e imprime s1 no console.

Escreva um programa de teste que solicite ao usuário que insira uma string e exiba todas as suas
permutações.

***17.18 (Jogo: Sudoku) O Suplemento VI.A fornece um programa para encontrar uma solução para um problema de Sudoku.
Reescreva-o usando recursão.

***17.19 (Jogo: múltiplas soluções de Oito Rainhas) Reescreva a Listagem 17.8 usando recursão.

***17.20 (Jogo: múltiplas soluções de Sudoku) Modifique o Exercício de Programação 17.18 para
exibir todas as soluções possíveis para um quebra-cabeça Sudoku.

*17.21 (Decimal para binário) Escreva uma função recursiva que converte um número decimal em um número binário
como uma string. O cabeçalho da função é:

string decimalToBinary ( valor interno)

Escreva um programa de teste que solicite ao usuário que insira um número decimal e exiba seu
equivalente binário.

*17.22 (Decimal para hexadecimal) Escreva uma função recursiva que converte um número decimal
em um número hexadecimal como uma string. O cabeçalho da função é:

string decimalToHex( valor interno)

Escreva um programa de teste que solicite ao usuário que insira um número decimal e exiba seu
equivalente hexadecimal.

*17.23 (Binário para decimal) Escreva uma função recursiva que analisa um número binário como um
string em um inteiro decimal. O cabeçalho da função é:

int binárioToDecimal ( string const e string binária)

Escreva um programa de teste que solicite ao usuário que insira uma string binária e exiba seu equivalente
decimal.

*17.24 (Hex para decimal) Escreva uma função recursiva que analisa um número hexadecimal como uma string em um
número inteiro decimal. O cabeçalho da função é:

int hexToDecimal(const string e hexString)

Escreva um programa de teste que solicite ao usuário que insira uma string hexadecimal e exiba seu
equivalente decimal.
Machine Translated by Google

Apêndices

Apêndice A
Palavras-chave C++

Apêndice B
O conjunto de caracteres ASCII

apêndice C
Gráfico de precedência do operador

Apêndice D
Sistemas Numéricos

Apêndice E
Operações bit a bit

671
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Apêndice A
Palavras-chave C++
As palavras-chave a seguir são reservadas para uso pela linguagem C++. Eles não devem ser usados para
nada além de seus propósitos predefinidos em C++.

asm fazer em linha curto ID do tipo


auto dobro interno assinado Digite o nome
bool Dynamic_cast longo tamanho de União

quebrar outro mutável estático não assinado


caso enumeração espaço para nome static_cast usando
pegar explícito novo estrutura virtual

Caracteres externo operador trocar vazio

aula falso privado modelo volátil

const flutuador protegido esse wchar_t

const_cast para público lançar enquanto

continuar amigo registro verdadeiro

padrão Vá para reinterpretar_cast tente


excluir se retornar typedef

Observe que as onze palavras-chave C++ a seguir não são essenciais. Nem todos os compiladores C++ os
suportam. No entanto, eles fornecem alternativas mais legíveis para alguns operadores C++.

Palavra-chave Operador Equivalente

e &&

e_eq &=
bit e &
chegando |
~
completo

não !
não_eq !=
ou ||
ou_eq |=
livre ^

xor_eq
^=

673
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Apêndice B
O conjunto de caracteres ASCII
As Tabelas B.1 e B.2 apresentam caracteres ASCII e seus respectivos códigos decimais e hexadecimais.
O código decimal ou hexadecimal de um caractere é uma combinação de seu índice de linha e índice
de coluna. Por exemplo, na Tabela B.1, a letra A está na linha 6 e na coluna 5, portanto seu equivalente
decimal é 65; na Tabela B.2, a letra A está na linha 4 e na coluna 1, portanto seu equivalente
hexadecimal é 41.

675
Machine Translated by Google

67

Tabela B.1 Conjunto de caracteres ASCII no índice decimal

0 1 2 3 4 5 6 7 8 9

0 nulo então stx casa eot eq ack bel besteira ht

1 nl vt aff cr então e de acordo com dcl dc2 dc3

2 dc4 filho dele etb pode em sub esc fs gs


” '
3 rs nós sp ! # $ % &

4 * + – . / 0 1
( ) ,
5 2 3 4 5 6 7 8 9 : ;
6 6 = 7 ? @ A B C D E

7 F G H EU J. K eu M N O

8 P P R S T EM EM EM X E
^ - '
9 COM
[ \ ] a b c

10 d e f g h eu
j k eu eu

11 n o p q R é t em em Em

12 x { } ~ do
e Com

Tabela B.2 Conjunto de caracteres ASCII no índice hexadecimal

0 1 2 3 4 5 6 7 8 9 A B C D E F

0 nulo então stx casa eot eq ack bel besteira ht nl vt aff cr então e

1 de acordo com dcl dc2 dc3 dc4 filho dele etb pode em sub esc fs gs rs nós

” * -
2 sp ! # $ %&' ( ) + , . /

3 0 1 2 3 4 5 6 7 8 9 : ; 6 = 7 ?

4 @A B C D E F G H EU J. KLMN O

5 P R S T VWxyZ \ ^ -
P EM [ ]
'
6 a b c d e f g h eu
j k eu homem o

7 R é t x { } ~ do
p q em em Em e Com
Machine Translated by Google

apêndice C
Gráfico de precedência do operador
Os operadores são mostrados em ordem decrescente de precedência, de cima para baixo. Os operadores do mesmo grupo têm
a mesma precedência e sua associatividade é mostrada na tabela.

Operador Tipo Associatividade

:: resolução de escopo binário da esquerda para direita

:: resolução de escopo unário

. acesso de membro de objeto via objeto acesso da esquerda para direita

-> de membro de objeto via ponteiro

() chamada de função

[] subscrito de matriz

++ incremento pós-fixado
--
decremento pós-fixado

ID do tipo informações do tipo de tempo de execução

elenco_dinâmico elenco dinâmico (tempo de execução)

static_cast conversão estática (tempo de compilação)

reinterpretar_cast lançado para conversão fora do padrão

++ incremento de prefixo direita para esquerda

--
decremento de prefixo

+ unário mais
-
unário menos

! negação lógica unária


~
negação bit a bit

tamanho de tamanho de um tipo

& endereço de uma variável


*
ponteiro de uma variável

novo alocação dinâmica de memória

novo[] alocação dinâmica de array

excluir desalocação dinâmica de memória

excluir[] desalocação dinâmica de array

(tipo) Elenco estilo C direita para esquerda

*
multiplicação da esquerda para direita

/ divisão

% módulo

677
Machine Translated by Google

678 Apêndice C

Operador Tipo Associatividade

+ Adição da esquerda para direita

- subtração

<< saída ou deslocamento bit a bit para a esquerda da esquerda para direita

>> entrada ou deslocamento bit a direita para a direita

< menor que da esquerda para direita

<= menos que ou igual a

> Maior que

>= Melhor que ou igual a

== igual da esquerda para direita

!= não igual

& E bit a bit da esquerda para direita

^ OR exclusivo bit a bit da esquerda para direita

OU bit a bit inclusivo


| da esquerda para direita

&& Booleano E da esquerda para direita

Booleano OU
|| da esquerda para direita

?: operador ternário direita para esquerda

= atribuição direita para esquerda

+= atribuição de adição

-= atribuição de subtração

*= tarefa de multiplicação

/= atribuição de divisão

%= atribuição de módulo

&= atribuição AND bit a bit

^= atribuição OR exclusiva bit a bit

|= atribuição OR inclusiva bit a bit

<<= atribuição de deslocamento à esquerda bit a bit

>>= atribuição de deslocamento à direita bit a bit


Machine Translated by Google

Apêndice D
Sistemas Numéricos
D.1 Introdução
Os computadores usam números binários internamente, porque os computadores são feitos naturalmente para números binários
armazenar e processar 0s e 1s. O sistema numérico binário possui dois dígitos, 0 e 1. Um número ou caractere
é armazenado como uma sequência de 0 e 1. Cada 0 ou 1 é chamado de bit (dígito binário).
Em nossa vida diária usamos números decimais. Quando escrevemos um número como 20 em um números decimais

programa, presume-se que seja um número decimal. Internamente, o software de computador é usado
para converter números decimais em números binários e vice-versa.
Escrevemos programas de computador usando números decimais. No entanto, para lidar com um
sistema operacional, precisamos chegar ao “nível da máquina” usando números binários. Os
números binários tendem a ser muito longos e complicados. Freqüentemente, números hexadecimais
são usados para abreviá-los, com cada dígito hexadecimal representando quatro dígitos binários. O número hexadecimal

sistema numérico hexadecimal tem 16 dígitos: 0–9 e A–F. As letras A, B, C, D, E e F correspondem


aos números decimais 10, 11, 12, 13, 14 e 15.
Os dígitos no sistema numérico decimal são 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. Um número decimal é
representado por uma sequência de um ou mais desses dígitos. O valor que cada dígito
representa depende de sua posição, o que denota uma potência integral de 10. Por exemplo, os
dígitos 7, 4, 2 e 3 no número decimal 7423 representam 7.000, 400, 20 e 3, respectivamente,
conforme mostrado abaixo:

7 4 2 3 = 7*103 + 4*102 + 2*101 + 3*100

103 102 101 100 = 7.000 + 400 + 20 + 3 = 7.423

O sistema numérico decimal tem dez dígitos e os valores das posições são potências integrais de 10.
Dizemos que 10 é a base ou raiz do sistema de numeração decimal. Da mesma forma, como o sistema base

numérico binário tem dois dígitos, sua base é 2, e como o sistema numérico hexadecimal tem 16 dígitos, raiz
sua base é 16.
Se 1101 for um número binário, os dígitos 1, 1, 0 e 1 representam 1 * 23 , 1* 22 , 0* 21 , e
1* 20 , respectivamente:

1 1 0 1 = 1 * 23 + 1 * 22 + 0 * 21 + 1 * 20

23 22 21 20 = 8 + 4 + 0 + 1 = 13
Se 7423 for um número hexadecimal, os dígitos 7, 4, 2 e 3 representam 7 * 163 , 4*162 , 2*161 , e
3*160 , respectivamente:

7 4 2 3 = 7*163 +4*162 +2*161 +3*160

163 162 161 160 = 28672 + 1024 + 32 + 3 = 29731

679
Machine Translated by Google

680 Apêndice D

D.2 Conversões entre números binários e decimais


binário para decimal Dado um número binário bnbn-1bn-2 c b2b1b0, o valor decimal equivalente é
bn * 2n + bn-1 * 2n-1 + bn-2 * 2n-2 + c
+ b2 * 22 + b1 * 21 + b0 * 20

Aqui estão alguns exemplos de conversão de números binários em decimais:

Binário Fórmula de conversão Decimal

10 1* 21 +0* 20 2

1000 1* 23 +0* 22 +0* 21 +0* 20 8

10101011 1* 27 +0* 26 +1* 25 + 0* 24 +1* 23 +0* 22 + 171


1* 21 + 1* 20

decimal para binário Converter um número decimal d em um número binário é encontrar os bits bn, bn-1,
bn-2, . . . , b2, b1 e b0 tais que

d = bn * 2n + bn -1 * 2n-1 + bn-2 * 2n-2 + . . . + b2 * 22 + b1 * 21 + b0 * 20

Esses bits podem ser encontrados dividindo sucessivamente d por 2 até que o quociente seja 0. Os
restos são b0, b1, b2 ,. . . , bn-2, bn-1 e bn.
Por exemplo, o número decimal 123 é 1111011 em binário. A conversão é feita da
seguinte forma:

0 1 3 7 15 30 61 Quociente

2 1 2 3 27 2 15 2 30 2 61 2 123
0 2 6 14 30 60 122

1 1 1 1 0 1 1 Restante

b6 b5 b4 b3 b2 b1 b0

Dica
A Calculadora do Windows, conforme mostrado na Figura D.1, é uma ferramenta útil para
realizar conversões numéricas. Para executá-lo, pesquise Calculadora no botão Iniciar e inicie
Calculadora e, em Exibir, selecione Científico.

Decimal Binário

Feitiço

Figura D.1 Você pode realizar conversões numéricas usando a Calculadora do Windows.
Machine Translated by Google

Apêndice D 681

D.3 Conversões entre números


hexadecimais e decimais
Dado um número hexadecimal hnhn-1hn-2 . . . h2h1h0, o valor decimal equivalente é hexadecimal para decimal

hn * 16n + hn-1 * 16n-1 + hn-2 * 16n-2 + . . . + h2 * 162 + h1 * 161 + h0 * 160

Aqui estão alguns exemplos de conversão de números hexadecimais em decimais:

Hexadecimal Fórmula de conversão Decimal

7F 7*161 +15*160 127

FFFF 15*163 + 15*162 +15*161 +15*160 65535

431 4*162 +3*161 +1*160 1073

Converter um número decimal d em um número hexadecimal é encontrar os dígitos hexadecimais, h2, decimal para hexadecimal

hn, hn-1, hn-2, c h1 e h0 tais que

d = hn * 16n + hn-1 * 16n-1 + hn-2 * 16n-2 + . . . + h2 *162

+ h1 * 161 + h0 * 160

Esses números podem ser encontrados dividindo sucessivamente d por 16 até que o quociente
seja 0. Os restos são h0, h1, h2 ,. . . , hn-2, hn-1 e hn.
Por exemplo, o número decimal 123 é 7B em hexadecimal. A conversão é feita como
segue:

0 7 Quociente

16 7 16 123
0 112

7 11 Restante

h1 h0

D.4 Conversões entre números


binários e hexadecimais
Para converter um número hexadecimal em binário, basta converter cada dígito do número hexadecimal hexadecimal para binário

em um número binário de quatro dígitos, usando a Tabela D.1.


Por exemplo, o número hexadecimal 7B é 1111011, onde 7 é 111 em binário e B é 1011 em binário.

Para converter um número binário em hexadecimal, converta cada quatro dígitos binários da direita
para a esquerda no número binário em um número hexadecimal. binário para hexadecimal

Por exemplo, o número binário 1110001101 é 38D, já que 1101 é D, 1000 é 8 e 11 é


3, conforme mostrado abaixo.

1110001101

3 8D
Machine Translated by Google

682 Apêndice D
Tabela D.1 Conversão de Hexadecimal em Binário

Hexadecimal Binário Decimal

0 0000 0

1 0001 1

2 0010 2

3 0011 3

4 0100 4

5 0101 5

6 0110 6

7 0111 7

8 1000 8

9 1001 9

A 1010 10

B 1011 11

C 1100 12

D 1101 13

E 1110 14

F 1111 15

Observação

Os números octais também são úteis. O sistema numérico octal tem oito dígitos, de 0 a 7. Um
número decimal 8 é representado no sistema octal como 10.

Aqui estão alguns bons recursos online para praticar conversões de números:

n http://forums.cisco.com/CertCom/game/binary_game_page.htm

http://people.sinclair.edu/nickreeder/Flash/binDec.htm _

n http://people.sinclair.edu/nickreeder/Flash/binHex.htm

D.1 Converta os seguintes números decimais em números hexadecimais e binários:


ÿVerificação de ponto

100; 4340; 2000

D.2 Converta os seguintes números binários em números hexadecimais e decimais:

1000011001; 100000000; 100111

D.3 Converta os seguintes números hexadecimais em números binários e decimais:

FEFA9; 93; 2000


Machine Translated by Google

Apêndice E
Operações bit a bit
Para escrever programas em nível de máquina, muitas vezes você precisa lidar diretamente com números
binários e realizar operações em nível de bit. C++ fornece os operadores bit a bit e os operadores de
deslocamento definidos na tabela a seguir.

Nome do operador Exemplo Descrição

& E bit a bit 10101110 e 10010010 O AND de dois bits correspondentes


rende 10000010 produz 1 se ambos os bits forem 1.

Bit a bit 10101110 | 10010010 O OR de dois bits correspondentes


|
inclusivo OU produz 10111110 produz 1 se um dos bits for 1.

^ Bit a bit 10101110 ^ 10010010 O XOR de dois bits correspondentes


exclusivo ou rende 00111100 produz 1 somente se dois bits forem
diferentes.

~ Complemento de alguém ~10101110 produz O operador alterna cada bit de 0


01010001 para 1 e de 1 para 0.

<< Desvio à esquerda 10101110 << 2 produz Desloca os bits do primeiro


10111000 operando para a esquerda pelo
número de bits especificado no
segundo operando, preenchendo com 0s à direita.

>> Deslocamento para a 1010111010101110 >> 4 Desloca o bit do primeiro operando


direita para número inteiro sem sinal rende 0000101011101010 para a direita pelo número de bits
especificado no segundo operando,
preenchendo com zeros à esquerda.

>> Deslocamento para a O comportamento depende da


direita para inteiro assinado plataforma. Portanto, você deve evitar
números inteiros com sinal de deslocamento
para a direita.

Todos os operadores bit a bit podem formar operadores de atribuição bit a bit, como ^=, |= <<= e
>>=.

683
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Índice
Símbolos –– chaves faltando causando erro, 43 uso de
caracteres especiais em C++, 36
(operador de decremento). consulte Operador de decremento (––)
"" ~ (til), use com destruidores, 456 < (operador
(aspas) falta de aspas
menor que), 397, 544 << (operador de
causando erro, 43 uso de caracteres especiais em C++,
inserção de fluxo). consulte Operador de inserção de fluxo (<<) <=
36
(menor ou igual a)
– operador (subtração), 63, 555 / operador
operador, 397 <> (colchetes angulares), uso de
(divisão), 63 // (barras duplas), uso
caracteres especiais em C++, 36 > (maior que) operador, 397 >= (maior que
de caracteres especiais em C++, 36 /= (operador de atribuição de divisão), 69,
ou igual to) operador, 397 >> (operador
553 :: (operador de resolução de escopo binário) , 372 ;
de extração de fluxo). consulte Operador de extração de
(ponto e vírgula) erros comuns em instruções de seleção,
fluxo (>>)
100 ponto e
vírgula ausente causando erro, 43 uso de caracteres
especiais em C++, 36 (caractere de escape), 143
Números
|| (ou) operador, 111, 113, 131 + operador
(adição). consulte Operador de Codificação de 8 bits, ASCII, 142

adição (+) + (operador de concatenação), jogo de cartas de 24 pontos, 505–506


1000BaseT NADA, 28
156, 397 ++ (operador de incremento). consulte Operador de
incremento (++) += (operador de atribuição de
adição). consulte Operador de atribuição de adição (+=) = (operador de
A
atribuição). consulte Operador de atribuição (=) –= (operador de função abs() , 140, 549
atribuição de Nomes de arquivos absolutos, 512

subtração), 69, Aulas abstratas

553 == (operador de igualdade), 100, 397 –> (operador de seta), AbstractGeometricObject.cpp, 603–604
acessando membros de objetos a partir de ponteiros, 454 ! DerivedCircleFromAbstractGeometricObject .cpp,
(não) operador, 111 != (diferente de) 604–605
operador, 397 # (sinal de cerquilha), uso de caracteres especiais em C+ DerivedRectangleFromAbstractGeometric
+, 36 % operador Object.cpp, 606–607
(módulo), 63 %= (operador visão geral de, 601–602

de atribuição de módulo), 69, 553 & TestGeometricObject.cpp, 607–609

( operador de endereço), endereços variáveis, operador 432 && (e). Benefícios de funções

consulte Operador And (&&) () abstratas, 608

(parênteses), uso de caracteres especiais em C++, implementação em classes derivadas concretas, 601–602
precedência do operador 36 * (operador de desreferência) e, Aceleração

440 valores de referência com ponteiros, operador 434 * computando o comprimento da pista para o aeródromo,
(multiplicação), 37, 63, 434 *= ( operador de atribuição de multiplicação) 87 relação com a velocidade, 86
operadores de atribuição Palavras-chave de acessibilidade, 600
aumentados, sobrecarga 69, 553 [] Os

(operador subscrito). consulte Operador subscrito acessadores obtêm função,

([]) {} (chaves) erros comuns em instruções de 376–378 tornando campos de dados privados acessíveis fora da classe,
seleção, 99–100 587 operador subscrito como, 553
ação após cada iteração, em loops for , 192–193

Ações (comportamento), objeto, 362


Registro de ativação (ou quadro), invocando funções e, 231–232

685
Machine Translated by Google

Índice 686

Parâmetros reais, na definição de funções, 229 Declarador de tamanho de array,


Linguagem Ada, 31 287 arrays, elementos de array de acesso geral
Operador de atribuição de adição (+=) e unidimensional, 288–289 E/S de objeto
operadores de atribuição aumentada, 69 binário e 529 pesquisas binárias,
concatenação de strings e 156 exemplo de seleção de cartão
sobrecarga, 554 tipos 307–310, 296–298 strings C. veja
de operadores de string, 397 declaração de strings C,
Exercício de adição, seleção e, 128 exercícios 287–288, 470
Operador de adição (+) funções para
concatenando strings e, 156 manipulação, inicializador 447–449, 289
sobrecarga, 555 tipos inicializando
de operadores numéricos, 63 usos de, matrizes de caracteres com string C, exemplo de contagem
544 de 312 letras, pesquisas lineares 304–306,
Operador de endereço (&), endereços variáveis, 432 exemplo de número de loteria
Relacionamento de agregação, composição como caso especial de, 306–307, 293– 296 de objetos, 404–406
418–419 visão geral de, 286–287
Exercícios passagem para funções,
de tratamento de exceções de álgebra, 643–644 298–300 ponteiros e, 439–442
exercícios de função, 279 prevenção de alterações em
exercícios de array multidimensional, 347–349, 351–352, 358 exercícios argumentos de array em funções,
de objeto e classe, 388–390 exercícios de 300–301
seleção, 126–127, 135 exercícios de vetor, processamento, 290–293
506, 510 referência com ponteiros, 432
Algoritmos substituição de matrizes por vetores, 494–497
algoritmo bubble-sort, 321 retorno de funções, 301–303 pesquisa, 306
problema de validação de cartão de crédito, 280 classificação, 310–
Algoritmo de Horner, 260 em 312 resumo, 317–
resolução de problemas, 50 326 exercícios de
Invocação ambígua, de funções, 240–241 Amortização modelo, 503 classe de vetor
de empréstimos, exercícios de loop, 217 Anagramas, funcionando como redimensionável matriz, 492 vetores em
exercícios OOP, 425 Operador And comparação com matrizes, 494
(&&) Operadores Matrizes, exemplo de adivinhação de aniversário
booleanos, 111, 113 exercícios de bidimensional e multidimensional, exemplo de
seleção, 131 colchetes par de pontos mais próximo 345–346, 337–338
angulares (<>). uso de caracteres especiais em C++, 36 ângulos, declarando matrizes bidimensionais, 330–331
cálculo de ângulos de triângulo, 140–142 objetos anônimos, exercícios, 346–360
370 argumentos padrão, 243– multidimensional, 342–343 exemplo
244 funções de teste de múltipla escolha, 335– 337 visão
de definição e, 229 geral de, 329–330 passagem
passagem por referência, 250–251 de matrizes bidimensionais para funções, 334–335 processamento
passagem por valor, 235–236 de matrizes bidimensionais, 331–334
passagem de argumentos de Exemplo de Sudoku, resumo 339–
ponteiro em chamadas de função, 442–446 evitando alterações em 342, exemplo 346
argumentos de array em funções, 300–301 de temperatura e umidade, exercícios vetoriais 343–344,
Expressões de avaliação 507
aritmética Teclas de seta, em teclados, 27
com classe Stack , 497–498 exercícios de loop, 218 operadores, Operador de seta (->), acessando membros de objetos a partir de
63 aritmética de ponteiro, ponteiros, 454
439 ASCII (Código Padrão Americano para Informações
Interchange)
Unidade lógica aritmética, componente CPU, 23 codificação de 8 bits,
Índice de matriz, 288, 320 conjunto de 142 caracteres em índices decimais e hexadecimais,
Inicializador de matriz, 289 código 675 para caracteres maiúsculos e minúsculos, 144–145
Machine Translated by Google

Índice 687

caracteres de comparação/teste, esquema de Classes básicas

codificação 145–146, 24 AbstractGeometricObject.cpp, 603–604 chamando


sequências de escape e, 143 construtores de classe base, 589–590 chamando
exercícios, 171 funções de classe base, 595 design de
geração de caracteres aleatórios, 146–148 classe e, 601–602 encadeamento
exercícios de loop, 215 de construtor/destruidor e, 590–594 programação genérica
terminador nulo, 312 e, 588–589 GeometricObject.cpp, 582 in
Assembler, traduzindo linguagem assembly em máquina herança, 580–581 exercícios de herança,
código, 30 616 redefinindo funções de
Linguagem assembly, 29–30 classe base em classes derivadas,
Expressões de atribuição, 58
Operador de atribuição (=) 594–595

aumentando. consulte Operadores de atribuição aumentada, supertipos e, 595


operadores de atribuição bit a bit, 682 TestGeometricObject.cpp, 586–587
copiando conteúdo entre objetos, 369 herança e, Linguagem de programação BASIC, 31
592 Lvalue (valor esquerdo) BCPL (linguagem de programação básica combinada), 33
e Rvalue (valor direito), 553 sobrecarga, 571–574 visão geral Máquina de feijão (quincunce), exercícios de matriz,
de, 58 regra de três e, 574 321–322
tipos de string Comportamento (ações), objeto, 362
operadores, 397 instruções função bin2Hex , conversão de números binários em
de atribuição, 57–58 função at(index) hexadecimais, 327
acessando caracteres em strings, 155– E/S de matriz
156 recuperando caracteres binária de arquivos binários,
de string, 392, 394 função atof convertendo strings 529 E/S de objeto binário, 529–533
C em números de ponto flutuante, 316 visão geral de, 526
convertendo leitura de, 527–528
números em strings, 398 função atoi convertendo strings C para atualização, 536–537
tipo int , 316 convertendo números em gravação em, 526–527
strings, 398 função Números binários no
atol , convertendo strings C para tipo longo , jogo de adivinhação de aniversário, 151
316 Atributos, objeto, 362 Operadores de conversão entre números binários e decimais, 281,
atribuição aumentada 327, 679
conversão entre números binários e hexadecimais;
171–172, 281, 327, 680–681
sobrecarga, 554 visão Operadores binários,
geral de, 69–70 operadores de adição e subtração, 64 operador
Conversão automática de tipo de resolução de escopo binário (::), 372
definindo funções não membros para operadores de Pesquisas binárias
sobrecarga, 562–563 do aplicando recursão a, 657 matrizes
tipo primitivo para o tipo Rational , 563 do tipo Rational de pesquisa, 307–310 exercícios
para o tipo de objeto, 561–562 do tipo Rational para o tipo de modelo, 503
primitivo, 561 BinaryFormatException, 643
Variáveis automáticas, 248 Exercícios de adivinhação de
Média, retornando a média de uma matriz, 319 aniversário, função matemática aplicada a, 148–151
matriz multidimensional aplicada a, 345–346
Bits, unidades de armazenamento, 24–25
B Operadores bit a bit, 682
Linguagem B, 33 Bloquear comentários, em programas C++, 35
bad_exception, 641 Blocos, em programas C++, 35
Caso base, em recursão IMC (Índice de Massa Corporal)
aplicando recursão a números de Fibonacci, 651 visão geral Aula de IMC , 413–415
de, 646 resolução de exercícios de programação, 87
problemas usando recursão, 653 exercícios de seleção, 104–105, 127–128
Machine Translated by Google

Índice 688

tipo de dados bool Exercício de loop de calendário, 218–


visão geral de 92 219 Pilhas de chamadas, invocando funções e, 231–
seleções e 92–93 232 Chamando funções, 230–232
escrevendo um programa simples, 51 Chamando objetos, 369
Expressões booleanas Erros de cancelamento, 217
criando expressões compostas com operadores lógicos, função capacidade() , aplicando a strings, 394–395 Caso,
111 convertendo entre superior e inferior case, 144–145 Sensibilidade de
fluxogramas e, 94 maiúsculas e
exercícios de ano bissexto, 114– minúsculas em programas
115 condição de continuação de loop, 176–178 C++, 35 identificadores para elementos de
visão geral de, 92 programa e, 55 erros de
instruções if-else bidirecionais e, 96–97
Exercício de números programação e, 43 Casting
aleatórios de operadores booleanos, testes DynamicCastingDemo.cpp , 611–614 static_cast vs.
115–117, 112–114 74 blocos de captura. veja blocos try-catch Capturando uma
tipos de, 111 exceção, exercício de loop de 637 CD
Valores booleanos (certificado de depósito), 219 CD-
visão geral de, 92 Rs, 26 CD-RWs, 26 CDs, 26 Celsius, conversão de/para
testes redundantes de, 101 Fahrenheit,
tratando valores inteiros como, 103–104 66–67, 84–85,
variável booleana, 92, 102 274
implementação de baixo para cima, funções, 264–269 Unidade central de processamento (CPU), 23–24 Exercício de loop de
colchetes. consulte {} certificado de depósito (CD), 219 Encadeamento,
(chaves) palavra-chave break , loops de controle, construtor, 590
instrução break 205–208 , instruções switch finais , 119 pontos
de interrupção, configuração na depuração, 124 Tipo de dados de caractere (char)
algoritmo de classificação de Código ASCII, 142
bolhas, 321 bugs, erros conversão de/para tipos numéricos, matriz
lógicos, 124 barramento, conexão de componentes de 144 caracteres ,
do computador, 22 bytes , de memória, 24–25 304 caracteres de comparação usando operadores relacionais, 145–
146
conversão entre maiúsculas e minúsculas, 144–145 conversão
C de dígito hexadecimal em valor decimal, 153–154 contagem de
C ocorrências usando array, 326 sequências de
como linguagem de alto nível, escape, 143 exercícios, 171
31 relação com C++, 33 localização de
C# ocorrências de caracteres especificados, 282 exercícios
como linguagem de alto nível, de função, 274 funções para
31 relação com C++, 33 trabalhar com caracteres, 151–153 gerando caracteres
função c_str() , 394 aleatórios, 146–148 inicializando matrizes de
introdução ao C++ caracteres com string C, 312 visão geral de, 51, 142
Exemplo ComputeExpression.cpp , 37–38 como lendo do teclado, 143
linguagem de alto nível, 31 classificando caracteres em uma
história de, 33–34 string, 474 strings de. consulte Resumo
ciclo de desenvolvimento do programa, 38– e exercícios de strings,
40 estilo e documentação do programa, 40–41 167–174 Literais de caracteres, 142
erros de programação, 41–44 Classes filhas. consulte
resumo e exercícios, 45–46 exemplo Classes derivadas cin (entrada do
Welcome.cpp , 34–36 exemplo console) em
WelcomeWithThreeMessages.cpp , 36–37 padrão ISO C++ 11, ComputeAreaWithConsoleInput.cpp, 52 entrada de
33–34 padrão ISO C++ 98, 33 strings C, visão geral 313 de,
modems a cabo, 28 34–35 leitura do teclado,
157–158, 512
Machine Translated by Google

Índice 689

Círculos, exercícios e exemplos Exemplo de GCD (máximo divisor comum), modularização 236–237
área de computação e perímetro de, 47 criando e, 236–238 números primos e, 237–238
objeto circular, 364–365 classes derivadas
e, 604–605 destruidores e, 456–459 Coesão, diretrizes de design de classe, 422
ponto determinante em, 170 ponto Comentários, em programas C++, 35, 40
de descoberta dentro, 129–130 Dispositivos de comunicação, 28–29 função
separando definição de classe da compare() , comparação de strings, 395 função compareTo() ,
implementação de classe, comparação de números racionais, 549
371–374

Clareza, diretrizes de design de classe, 423 Erros de compilação, 41


Abstração de classe, 381–385. veja também Classes abstratas Compiladores
Diagramas de classes, UML (Unified Modeling Language), 363 Encapsulamento compilando o programa principal a partir da linha de comando, 373
de classes, 381–385 Variáveis de funções inline e, 245 traduzindo
classes, 406 Classes arquivo intermediário em arquivo de código de máquina, 38 traduzindo o
abstratas. programa fonte em código de máquina, 30
consulte Abstração e encapsulamento Completude, diretrizes de design de classe, 423
de classes abstratas, 381–385 funções constantes e, Classe complexa , 576-577
410–412 construtores de cópia, 462–468 Números complexos, 576–577
classes de exceção personalizadas, Composição, composição de objetos, 418–419
627–628 definição, 362–363 diretrizes de Expressões compostas, avaliando com classe Stack , 497–498
design, 422–424
desenvolvimento de classe genérica Valor composto, exercícios de loop, 219
com modelos de classe, 482– 484 classes de exceção, 623–624 exercícios, Arquitetura de computadores, exercícios de loop, 221
387–390 funções inline em, 375–376 Noções básicas de
instâncias e membros computador, bits e bytes de armazenamento,
estáticos, 406–409 para cursos de 24–25 dispositivos de comunicação, 28–29
modelagem, 459–462 para pilhas de modelagem, CPU (unidade central de processamento), 23–24
420–422 convenções de nomenclatura, dispositivos de entrada e saída, 27–28
369 visão geral de, 362 separação memória, 25
definição de classe a partir da sistemas operacionais, 32–33
implementação de programação definida, 22
classe, linguagens de programação, 29–31
371–374 dispositivos de armazenamento,
Função clear() , limpando strings, 394 25–27 resumos e exercícios, 45–48 o que
Cliente, da classe, 372 são computadores, 22–23
Velocidade do clock, da CPU, Concatenação
ângulos de triângulo operador de concatenação (+). 397 de
de computação de cabeçalho de 23 cmath , 140– strings C, 315 de
142 funções de expoente, 139 strings, 156
funções matemáticas e 166–167 funções min, max Expressões condicionais, 121–122 Visão geral
e abs , 140 funções matemáticas de sobrecarga, de operadores condicionais
241 funções de arredondamento, 139–140 funções de, 113 operador
trigonométricas , 138–139 ternário como, 122 jogo Connect
Four
Linguagem de programação COBOL, 31 exercícios de matriz multidimensional, 355–356 exercícios
de vetores, 510
Matrizes de código usadas para Consistência, diretrizes de design de classe, 423
simplificar, 292 evitando duplicação, 102– Console

103 codificação (implementação) no ciclo de vida de desenvolvimento de exibindo saída formatada ativada, 160–161 entrada/
software, 75, 77–78 saída, 34–35 visão geral
Benefícios da de, 34 Entrada do
reutilização de código do refinamento gradual, console. veja cin (entrada do console)
269 funções que definem código reutilizável, 228 Saída do console. veja cout (saída do console)
Machine Translated by Google

Índice 690

Consoantes, contagem de exercícios, palavra- caracteres de saída, 512 strings C


chave 222 const . consulte Constantes de saída, 312–313 visão geral de, 34–35
Constantes manipulador
ponteiros constantes, 438–439, 441 setprecision(n) , 161 manipulador set(width) , 163
parâmetros de referência constantes, 259 manipulador showpoint , 162–163
declarando, 59
globais, 247 Extensão .cpp, para arquivos de origem, 38
constantes nomeadas para valores permanentes, 59–60 CPU (unidade central de processamento), 23–24
especificando função de membro constante, 410–412 regra de Cramer, 131
Encadeamento de construtor jogo de dados, exercícios de função, 277 função
ConstructorDestructorCallDemo.cpp, 590–594 visão geral createArray , 306 cartão de crédito,
de, 590 validação de exercício, 280 comparação de
Lista de inicializadores strings C,
de construtores, 368 Construtores 315–316 concatenação ,
315 conversão para
chamando construtores de classe base, 589–590 strings, 392 conversão de/para
encadeamento, números, 316–317 cópia, 314 exercícios, 326–328
590 para modelos de classe, funções, 313–314
485 ConstructorDestructorCallDemo.cpp, 590–594 construtores de entrada e saída de, 312–
cópia, 462–468 criando objetos, 364 313 visão geral de, 312
criando strings, 392 definição resumo, 318
de, 362–363 visão geral de,
367–368, 589
Elenco estilo C, conversões de tipo numérico, 73
palavra-chave continue , controlando loops, 205–208 Exercício de câmbio, 133
Contrato, classes como, 362, 381 Cilindro, volume de cálculo de, 85
Unidade de controle, componente CPU, 23
Variáveis de controle, for loop e, 192, 247
Coordenar exercícios D
determinação do ponto de canto, 170 Pendurando outra ambigüidade, 101
loops e, 224 Copiar Ponteiros pendurados, 452
classes de construtores, Dados, armazenamento em matrizes, 286
462–468 Encapsulamento

CopyConstructorDemo.cpp, 463 customização, de campos de dados, 376–379, 423


465–468 herança e, 592 visão inicializando com construtores, 368 tornando
geral de, 462 regra de três campos privados acessíveis fora da classe, 587 representando o
e, 574 estado do objeto, 362 em diagramas de
ShallowCopyDemo.cpp, classes UML, 363
463–465 resumo, 469 Copiando matrizes, 290 C- Função data() , retorna string C de string, 394 tipos de dados. veja
strings, 314 Core, também por conversão automática de tipo de tipo
CPUs de individual, 561–563 benefícios de tipos genéricos,
núcleo único e 476–477 classe como um tipo, 369 definindo
multicore, 24 modelo de função com
Exercício de comparação de custos, 133 Loops tipos genéricos, 477 definindo tipos sinônimos, 437–438 parâmetros
controlados por contador, 177 Contagem não-tipo, 487 conversões de tipo numérico, 72 –74
de unidades monetárias, estudo de caso, tipos numéricos, 60–62 notação
matriz de 79–81 contagens , 305 Aula do curso , 459–462 científica, 63 parâmetros de tipo, 477–478,
cout (saída do console) 485 variáveis usadas com,
56 escrevendo um programa
simples, 51
exibindo saída formatada no console, 160–161 manipulador fixo ,
162
Machine Translated by Google

Índice 691

Lei de De Morgan, 113 Classes derivadas

Benefícios de acessando membros protegidos de, design de classe 600–


depuração do refinamento gradual, 269 601 e, 601–602 encadeamento
seleções e 124 de construtor/destruidor, 590–594 DerivedCircle.cpp,
Números decimais 583–584
Conjunto de caracteres ASCII, DerivedCircleFromAbstractGeometricObject .cpp, 604–605
675 no jogo de adivinhação de
aniversário, 151 conversão para DerivedRectangleFromAbstractGeometric Object.cpp, 606–
frações, 578 conversão para/de binário, 281, 327, 607 exercícios, 616
679 conversão para/de hexadecimal, 153–154, 172, 203–205, programação
259–261, 281, 327 , 680 divisão de, 67 genérica e, 588–589 implementando funções
abstratas em derivadas concretas
Declarando matrizes classes, 601–602 em
multidimensionais, 342 herança, 580–581 redefinindo
unidimensionais, 287–288 funções de classe base em classes derivadas,
bidimensionais, 330–331 594–595

Declarando constantes, 59, 438 subtipos e, 595


Declarando funções, 241–243 Identificadores descritivos, 55
Declarando objetos, 486 Diretrizes de design, aulas, 422–424
Exemplos de declaração Encadeamento de destruidor, 590–594
de variáveis, 56 destruidores

variáveis de ponteiro, 432 CircleWithDestructor.cpp, 456–457


escopo de variáveis em um loop for , 247–248 CircleWithDestructor.h, 456 objetos e, 469
variáveis estáticas, 248, 407 visão geral de, 456–
escrevendo um programa simples, 51–52 459 regra de três e, 574
Sobrecarga do operador de TestCircleWithDestructor.cpp,
decremento (--) , 555 457–459 Exercício de desvio, cálculo de desvio usando array,
visão geral de, 70–72 320 Recursão direta, 649 Caminho do diretório , 512 “Dividir e conquistar”,
usando com variáveis char , 142 na resolução de problemas,
Cópia profunda, construtores de cópia e, 463 262 Operador de divisão
Construtor padrão, 368 (/) , 63 Operador de atribuição de divisão (/=), 69, 553
Programação defensiva, funções constantes em, 411 Documentação, de programas
Definindo classes, 362–363 em C++, 40–41 Operador ponto (.), 369 , 454 Dot pitch,
Definindo funções resolução da tela e, 28 barras duplas (//), uso de
funções amigas , 557–558 funções caracteres especiais em C++, 36
não membros, 562–563 operadores tipo duplo , 63 conversão de loop do-while
como, 550–551 visão geral para loop, 194 decisão de qual tipo de loop usar, 194–196 visão geral de
de, 229 redefinindo 188–190 Downcasting,
funções de classe base em classes derivadas, 594–595 611 Drivers,
funções programa de teste na abordagem
estáticas, 407 bottom-up, 265 DSL (linha de assinante digital), 28
Definindo tipos sinônimos, 437–438 elementos duplicados,
Tecla Delete, em teclados, 27 remoção de exercício
operador delete , ponteiros e, 452 vetorial, 509 DVDs, 26 matrizes dinâmicas, 450 ligação dinâmica
Denominadores definida, 599 herança e , 596–600
acessando com operador subscrito ([]), 552 em números TestGeometricObject.cpp, 607
racionais, 544–545 retorno por
referência, 553
Implantação, no ciclo de vida de desenvolvimento de software, 76
Operador de desreferência (*)
precedência do operador e, 440
referenciando valores com ponteiros, 434
Machine Translated by Google

Índice 692

Fundição dinâmica BadCastExceptionDemo.cpp, 625–626 classes de


operador dynamic_cast , 609–614 visão exceção customizadas, 627–628 classes
geral de, 626 de exceção, 623–624 propagação
Alocação dinâmica de memória de exceção, 637–638 especificação de
CorrectReverse.cpp, 451–453 criando e exceção, 640–641 exercícios, 643–644
acessando objetos dinâmicos, 453–455 visão geral de, 449
resumo, 469 InvalidArgumentExceptionDemo.cpp, 626–627 capturas
múltiplas, 632–637 visão geral
WrongReverse.cpp, 449–451 operador de, 617–618 Quotient.cpp,
dynamic_cast , 609–614 618
QuotientThrowRuntimeError.cpp, 624–625
QuotientWithException.cpp, 619–621
E QuotientWithIf.cpp, 618–619 exceções
Quebra-cabeça das Oito de relançamento, 638–640 resumo, 642
Rainhas aplicando recursão a, 646, 662–664 exceções de
exercícios de array, 322–323 lançamento e captura, 618 classe
elementType, declarando arrays, 287 função TriangleException , 628–632 quando usar
vazia() , testando strings vazias, 394 exceções, 641 Propagação de
Criação de exceções, 637–638 Especificação de
strings vazias, exceções, 640–641 Funções de
visão geral 392 de, expoentes,
155 testes, 394 139 operações,
Benefícios de 64–65 Expressões
encapsulamento de, booleanas.
classe 575–576, veja expressões booleanas exemplo
381–385 de campos de ComputeExpression.cpp , 37–38 condicional, 121–
dados, 376–379 diretrizes de 122 EvaluateExpression.cpp,
design e, 423 abstração de função e, 262 499–502 avaliação, 65–67 avaliação com classe
Esquema de codificação, ASCII, 24, 142 Stack , 497–498, 504
Linha final, em programas C++, 35 exercícios, 47 visão geral de, 58
Exercício energético, calculando a energia necessária para aquecer
água, 86 função eof() , testando o final do arquivo, 515–517
Operador de igualdade (==), 100, 397
função equals() , comparando números racionais, 549
F
Exemplos de erros comuns, 43–44 função fatorial , 647-648
erros lógicos, 42–43 Fatoriais
erros de minimização em números de ponto flutuante na aplicando recursão a, 646–650
continuação do loop, 198– recursão de cauda e, 666
199 erros fora dos limites, 289 Fahrenheit, conversão de/para Celsius, 66–67, 84–85, 274 função fail() ,
programação, 81–82 erros teste de existência de arquivo, 515 comportamento
de tempo de execução, de queda, instruções switch e, 119 pés, conversão de/para
42 erros de sintaxe, 41– 42 metros, 85, 273 números de Fibonacci , aplicando
Caractere de escape (\), 143 recursão a, 650–652 E/S de matriz binária de entrada/saída de
Sequências de escape, tipo de dados caractere (char) , 143 arquivo, 529 E/S
função avaliarExpression , 501 binária, 526 E/S de objeto
Personalização de binário, 529–533
classes de exceção, visão CopyFile.cpp, 520–522 criando
geral de 627–628, 623–624 objetos de arquivo usando a
Vantagens de tratamento classe fstream , 522– 523 exercícios, 538–541 formatação de
de exceções de, 621–623 saída com manipuladores
BadAllocExceptionDemo.cpp, 625 de fluxo, 518–519
Machine Translated by Google

Índice 693

funções get e put , 520 função Fluxogramas

getline , 519–520 permitindo ao de instruções if , 94


usuário inserir nomes de arquivos, 517–518 instruções switch , 118 loops
visão geral de, 164, 511–512 for decidindo
arquivos de acesso aleatório, 533– quando usar, 194–196 exemplo aninhado,
536 leitura de um arquivo binário, 527–528 196–198 visão geral de, 191–194
leitura de um arquivo, 166–167, 514–515 processamento de matrizes,
resumo, 537–538 teste 290–293 processamento de matrizes
de fim de arquivo, 515–517 teste bidimensionais, 331 escopo de variáveis em, 247 –
de existência de arquivo, 515 248 Parâmetros formais, definição de
teste de estados de fluxo, 524–525 funções e, 229 Formatos, saída, 160–161, 518–519 Linguagem
E/S de texto, de programação FORTRAN, 31 Frações,
512 atualização de arquivos, conversão de números decimais em, 578 Memória
536–537 gravação em um arquivo, 165 – Freestore, 451 classes de amigos , 557–558 funções de
166, 512–514 gravando em arquivo binário, 526–527 amigos , 557–558 palavra-
Modo de abertura de arquivo, 522–523 chave amigo , 557 E/S de matriz
Ponteiros de arquivo, 533–534 binária de classe fstream , 529 E/S
de objeto binário, 529–533
Arquivos contando letras, 222 criação de objetos
modos de arquivo, de arquivo usando, 522–523
522 permitindo que os usuários insiram nomes de leitura de um arquivo binário, 527–
arquivos, 517–518 arquivos de acesso 528 leitura de um arquivo, 165–166, 186– 188,
aleatório, 533–534 leitura de, 166–167, 186–188, 514– 512–514 testando estados de fluxo, 524–525
515 leitura de um arquivo binário, 527–528 para trabalhar com arquivos, 512 gravando em um arquivo, 165–
testes fim de, 515–517 testando 166, 512–514 gravando em arquivo
a existência de, 515 atualizando binário, 526–527 Abstração de
arquivos binários, 536–537 gravando em, função, 262 Chamadas de função, 124
165–166, 512–514 gravando em Declaração de função, 241 –243
arquivos binários, 526–527 Cabeçalho de função, 229 teclas
Exercícios de matriz de de função, em teclados,
aplicações financeiras, 27 Protótipos de função declarando
321 exercícios de função, 273–275, 280 variáveis estáticas, 248–250
exercícios de loop, 201–202, 215–217, 219–221 exercícios visão geral de, 241–243 passando matrizes
de matriz multidimensional, 348, 353–354 para funções e, 299 no
Exercícios OOP, 427 exemplo PrintCalendar.cpp , 266 escopo de
exercícios de programação, 85, 87, 89 variável e, 246
exercícios de seleção, 128, 133–135
encontrar funções

de matrizes de funções, 447–448


pesquisa em strings, 396 Assinatura de função, 229
manipulador fixo , 162 tipo Abstração

flutuante , 63 de funções e refinamento gradual, 262 para anexar a


Literais de ponto flutuante, 63 strings, 393 para manipulação de
Números de ponto flutuante array, 447–449 benefícios do refinamento
convertendo para inteiros, 72–73 gradual, 269 chamadas, 230–232 para caracteres,
convertendo de/para strings C, 316, 327 tipos de 151–153 diretrizes de
dados para, 51, 61 design de classe e 423 para
minimizando erros devido à continuação do loop, 198–199 não modelos de classe, 485 para conversão
confiabilidade de testes de igualdade, 101–102 numérico números, 259–261 para strings
erros e, 185 manipulador C, 313–314 argumentos padrão em, 243–
setprecision (n) , 161 erro de underflow, 82 244
Machine Translated by Google

Índice 694

Definição de funções Funções genéricas


(continuação) , benefícios de tipos genéricos, 476–477
229 definição de funções não membros, 562–563 definindo classificação genérica, 480–482
definição de comportamentos de objetos, desenvolvendo classe genérica com modelos de classe, 482–484 para
362 definição de operadores como. consulte Exemplos de uso de versão genérica da classe Stack , 484–488
sobrecarga de operador, 250–253 GenericMaxValue.cpp, 477–478
exercícios, 272–283 GenericMaxValuePassByReference.cpp, 478–480 Genérico programação,
funções de expoente, 139 588–589 exercícios de função geométrica,
formatação, 160–161 276, 279
genérico. veja Funções genéricas exercícios de função matemática, 168–
implementando funções abstratas em derivadas concretas 170 exercícios de array multidimensional, 351, 359–360
classes, 601–602 inline, exercícios de objeto e classe, 390 exercícios OOP, 428 exercícios
244–245 variáveis de ponteiros, 471–473 exercícios de
locais e globais e, 245–247 modularização de código programação, 87–88
e, 236–238 sobrecarga, 238–241 visão geral exercícios de seleção, 129–134
de, 227–228 passagem de exercícios vetoriais, 503 função get , 376–
argumentos por referência, 378, 520 função getline , 519–520
250–251 passagem de argumentos por valor, 235–236 GHz (gigahertz), medição
passagem de arrays para, 298–300 passagem de da velocidade do clock do computador
arrays bidimensionais para, 334–335 em, 23 Gigabyte (GB), unidades de
prevenção de alterações nos argumentos de array em armazenamento, 24 Gigahertz (GHz) , medindo a velocidade do relógio do
funções, computador em, 23 Variáveis globais, 245–247
300–301 GMT (Horário de Greenwich), exercício exibindo a hora atual, 67–69 Google,
protótipos. consulte Protótipos de função avaliando expressões de, 497–498
recursivos, 646 Grande distância do círculo, determinação do exercício, 169 Maior ou igual
redefinindo funções de classe base em classes derivadas, 594–595 variáveis operador para
de referência e, 254–259 retornando matrizes (>=) , 397 Operador maior que (>) , 397 Máximo divisor comum.
de, 301–303 retornando ponteiros de, 446– veja GCD (maior comum
447 funções de arredondamento, 139–140
funções de serviço, 140 para string
atribuição, 393–394 para
strings, 154–155 resumo, 270–271 design divisor)
de cima para baixo, 262– Horário de Greenwich (GMT), exercício exibindo a hora atual, 67–69
264 implementação de
cima para baixo ou de baixo para Grades, matrizes bidimensionais representadas como 339–340, 342
cima, 264–269 funções trigonométricas, 138–139 escopo variável e, Exercícios
247–250 virtual , 596–600 void, 232–235 de loop de adivinhação, exercício
Tipos de dados fundamentais. consulte de função matemática 179–181, exercício de matriz
Tipos de dados multidimensional 148–151, 345–346
primitivos Exercício OOP, 427
(fundamentais)

H
Jogo da forca, exercício OOP, 429
G Discos rígidos, 26
Caixa de Galton, exercícios de matriz, 321–322 Dispositivos

GB (gigabyte), unidades de armazenamento, 24 de comunicação de hardware, 28–29


Exercícios de loop GCD (máximo divisor componentes de computador, 22
comum), 199–201, 215 modularizando CPU (unidade central de processamento), 23–24
código e, 236–237 reduzindo números dispositivos de entrada e saída, 27–28
racionais a termos mais baixos, 545–546 função gcd() , 549 memória, 25
dispositivos de armazenamento, 25–27
Machine Translated by Google

Índice 695

Tem um relacionamento, modelos de agregação e, 418 modos de arquivo de


Arquivo de cabeçalho classe ifstream e, 522
erro de tempo de compilação, leitura de um arquivo, 166–167, 514–515 para
82 impedindo múltiplas inclusões de, 374–375 em trabalhar com arquivos, 512
programa C++ simples, 34 Implementação (codificação), no ciclo de vida de desenvolvimento de software,
Exercícios de loop de 75, 77-78
jogo cara ou coroa, 220 Proteção de inclusão, evitando múltiplas inclusões de arquivo de cabeçalho,
exercícios de seleção, 128 Função
Exercícios de de incremento 374–375
programação de aplicações de passando argumentos por referência, 250–251 usado
saúde, 87 exercícios de seleção, 127–128 com variável de referência, 254
Memória heap, 451 Sobrecarga do operador de
Funções auxiliares, recursivas, 655–656 incremento (++), 555
Hertz (Hz), medindo a velocidade do clock do computador, função visão geral de, 70–72
23 hex2Dec , 261, 643 usando com variáveis char , 142
Literais hexadecimais, 62 Desenvolvimento e testes incrementais, 269
Números hexadecimais Recuo, estilo de programação em C++, 40
Conjunto de caracteres ASCII, Matrizes

675 convertendo de/para binário, 171–172, 281, 327, 680– de índices,


681 288 strings, 155–156
conversão de/para decimal, 153–154, 203–205, vetores, 492
259–261.281, 327, 680 Recursão indireta, 649
Exercícios Indireção, referenciando valores com ponteiros, 434
de função matemática hexágono, 169 exercícios Operador indireto (*), 434
de programação, 88 função Loops infinitos, 178
hexCharToDecimal , 261 Recursão infinita, 649
Linguagens de alto nível, 30–31 Ocultação de informações, abstração de funções e, 262
Algoritmo de Horner, 260 Herança
Umidade, exercício de matriz multidimensional, 343–344 AbstractGeometricObject.cpp, 603–604
Hz (hertz), medindo a velocidade do clock do computador em, 23 acessando membros protegidos de classe derivada, 600–
601 classes base e classes derivadas,
580–581 chamando construtores de
EU
classe base, 589–590 lançando
IDE (ambiente de desenvolvimento integrado) objeto em tempo de execução, 609–611
compilando o programa principal de, 373 encadeamento de construtor/
depuração e 124 visão destruidor, 590–594 DerivedCircle.cpp, 583–584
geral de, 39
identificadores, para elementos do programa,
55 erros comuns DerivedCircleFromAbstractGeometricObject. cpp, 604–605 Derive
de instruções if , 99–101 cpp, 606–607
cálculo de IMC (Índice de Massa Corporal), 104–105 DynamicCastingDemo.cpp, 611–614 exercícios,
impostos de cálculo, 106 –108 615–616 programação
determinação do ano bissexto, genérica e, 588–589 GeometricObject.cpp,
115 tratamento de exceções e, 641 582 implementando funções abstratas
aninhados, 97– em derivadas concretas
99 visão geral de, 93–95 classes, 601–602
SimpleIfDemo.cpp, 95–96 instruções visão geral de, 580, 587
if-else polimorfismo e, 595–596 redefinindo
pendente, else ambiguidade, 101 funções de classe base em classes derivadas, 594–595 resumo, 614–615
multidirecional, 97–99 TestGeometricObject.cpp,
resolução de problemas usando recursão, 653 586–587, 607–609 funções virtuais e ligação dinâmica, 596–600
bidirecional, 96–97
Machine Translated by Google

Índice 696

ação inicial, em loops for , 192–193 Taxa de juros


Inicializando matrizes, 289–290, 331–332 exercícios de loop, 216–217
Inicializando ponteiros, 435 exercícios de programação, 89
Inicializando variáveis, 56–57 Organização Internacional de Normalização (ISO), 33–34
Definição embutida, 375–376 Intérpretes
Funções embutidas encontrando o máximo divisor comum, exercícios de loop
em classes, 375–376 199–201, 215 traduzindo
especificando, 244–245 o programa fonte em código de máquina, 30
palavra-chave embutida , 244–245 Interseção, de dois vetores, 509
Etapas do programa de entrada, processo Exercício de investimento, cálculo do valor futuro do investimento, 89
e saída (IPO), 54 análise Invocar construtor, 367
e design do sistema e, 75 Invocar funções
Redirecionamento de entrada, loops e, 186 invocação ambígua, 240–241 definição de
Fluxo de entrada, 512 modelos de função e, 478 funções inline, 244–
Entrada/saída. consulte função de inserção de 245 visão geral de, 230 passagem
E/S (entrada/saída) , 396–397 Tecla de arrays para funções
de inserção, em teclados, 27 Inserindo/ e, 299
substituindo strings, 396–397 Instância, classe, E/S (entrada/saída)
362, 406–407 Diretrizes de design de entrada do console. veja saída do console cin
classe de funções de (entrada do console). veja cout (saída do console)
instância, 423–424 associação de classe e, Strings C e entrada/saída de
406 objetos e, 369 visão geral de, arquivo 312–313. consulte Dispositivos de entrada
168 vs. funções e saída de entrada/saída de arquivo, 27–
estáticas, 409 classe 28 redirecionamento e, 186
de string e, 155 membros de cabeçalho iomanip , 161
instância, classes, 406–409 etapas do programa IPO (entrada, processo
classes de variáveis de instância e, 406–407 e saída), 54 análise e
objetos e, 369 escopo design do sistema e, 75 relacionamentos Is-a,
inclui classe inteira, 379 herança e, 587 ISO (International Standard
Instanciação, de Organization), 33–34 Iteração de loops, 176 vs. recursão, 665
instâncias de classe, 362 tipo int , 56 função itoa

operadores aritméticos inteiros e, 63 estudo de convertendo


caso de classe números inteiros em
para strings C, 316

modelagem de pilha de, 420–422 convertendo números em strings, 398


conversão em tipo char , 144 conversão de strings C de/para, 316
conversão de números de ponto
flutuante para, 72 –73 tipos de dados para, J.
51, 61 definição de tipos sinônimos, 437–438 exibição de Java

números primos, 210–212 como linguagem de alto nível, 31


divisão, 67 exercícios de função, 272, 280 geração relação com C++, 33
de números aleatórios, 109–111 erro de estouro,
81 números K
perfeitos, 220 exercícios de seleção, KB (kilobyte), unidades de armazenamento, 24
128 somando os dígitos, 85 tratando valores Teclados
booleanos como, 103– como dispositivo de
104 erro de divisão não entrada, 27 entradas de leitura de, 53–55, 143
intencional, 82 Chaves
pesquisas binárias e, 307–310 pesquisas
lineares e, 306–307
Palavras-chave (palavra reservada). veja também por tipo individual em
Ambiente de desenvolvimento integrado. veja IDE (integrado C++, 673 visão
ambiente de desenvolvimento) geral de, 35
Machine Translated by Google

Índice 697

Kilobyte (KB), unidades de armazenamento, 24 Loops


Quilogramas, convertendo de/para libras, 85, 214 aplicados para adivinhar números, 179–181 aplicados
Quilômetros, convertendo de/para milhas, 214 à previsão de mensalidades futuras, 201–202 aplicados ao
questionário de subtração, 178–179, 182–184 quebram e
continuam palavras-chave em, 205–208 verificando
eu palíndromos, 208–210 controlando com valores
Ilustração de LAN (rede local), 29 sentinela , 184–185 controlando com confirmação do
usuário, 184 convertendo números decimais em
NIC (placa de interface de rede) e, 28 hexadecimais, 203–205 decidindo qual tipo usar, 194–196 estratégias de
Quadrado latino, exercícios vetoriais, 508 design, 182 exibindo números primos, 210–212
Ano bissexto, exercício de seleção, 114–115 loop do-while , 188–190
manipuladores esquerdos , manipuladores de fluxo, função length() encontrando o maior comum divisor, 199–201
163–164 , strings, 394–395 redirecionamento de entrada/saída
Operador menor que (<), 397, 544 e, 186 para loop, 191–194 minimizando erros em
Operador menor ou igual a (<=) , 397 números de ponto flutuante na continuação
Exercícios de matriz do loop, 198–199
de contagem de letras, exercícios
de cordas 304–306, 326–327, 427
Biblioteca, código predefinido na biblioteca C++, 34 Simulação de Monte Carlo, 202–203
Comentário de linha, em programas C++, 35 aninhados, 196–198
Inclinação da linha, exercícios de programação, 89 visão geral de, 176
Pesquisas lineares lendo todos os dados de um arquivo, 186–188
estimativa do tempo de execução, 320 resumo e exercícios, 212–225 loop while ,
matrizes de pesquisa, 306–307 176–178
exercícios de modelo, 503 Loteria
Linker, conectando arquivo de código de máquina com biblioteca, 38 aplicando matrizes a, 293–296
Listas, particionamento com pivô, 325 aplicando números aleatórios a, 115–117 aplicando
Literais, 62-63 strings a, 158–160 exercícios de loop,
Exemplo 220 exercícios de seleção,
de abstração e encapsulamento de classe de empréstimos, 128
382–385 Caracteres minúsculos, convertendo para maiúsculos, 144–145
exercícios de loop, 216–217 Termos mais baixos, números racionais em, 545–546
função pow (a, b) em ComputeLoan.cpp, 77 ilustração de rede Linguagem de baixo nível, 30
local (LAN) de, 29 NIC (placa de Cheque Luhn, aplicando-se à validação de cartão de crédito, 280
interface de rede) e, 28 Operadores de atribuição
Lvalue (valor esquerdo) e, 553–554 lista de
Variáveis locais, 245–247 bugs operadores Lvalue, 563 operadores
de erros lógicos, de pré-incremento e pré-decremento e, 555
124 classe
logic_error , 623
NonPositiveSideException.cpp, 632 tipos de
M
erros de programação, 42–43 operadores Linguagem de máquina, 29
lógicos, 111–114 tipo longo funções principais

convertendo executando programas C++, 34–35


strings C em, 316 declarando invocando, 231
variáveis, 56 tipo duplo Manutenção, no ciclo de vida de desenvolvimento de software, 76
longo , 60 Corpo do loop, 176 Conjunto Mandelbrot, exercícios de sobrecarga do operador, 577
Condição de Matriz de Markov, exercícios de array multidimensional, 356–357
continuação do loop
loops do-while e, design de loop 188–189 Exercícios de matriz de aplicações
e, 182 loops for e, visão matemáticas, 323
geral de 191–193 de, 176–178 exercícios de função, 272–279
exercícios de loop, 221, 223–224
Machine Translated by Google

Índice 698

Funções matemáticas Monitores, 28


calculando ângulos de triângulo, 140–142 simulação de Monte Carlo
funções de expoente, 139 estimando p, 202–203
exercícios de geometria, 168–170 exercícios de loop, 221
exercícios de adivinhação, 148– placas-mãe, 23–24
151 funções min, max e abs , 140 matrizes multidimensionais. veja Matrizes, bidimensionais e
sobrecarga, 241 multidimensional
visão geral de, 138 Herança múltipla, 587
funções de arredondamento, 139– Teste de múltipla escolha, 335–337
140 resumo e exercícios, 167–174 Operador de multiplicação (*) , 37, 63, 434
funções trigonométricas, 138–139 Operador de atribuição de multiplicação (*=)
Matrizes operadores de atribuição aumentados, 69
matrizes multidimensionais, 346–348 sobrecarga, 553
matrizes bidimensionais, 330 Tabela de multiplicação, exemplo de loop for aninhado , 196–198
vetores, 509 Multiplicidade, composição de objetos e, 418
teste de função Multiprocessamento, 32–33
max , 230–232 Multiprogramação, 32–33
funções trigonométricas, 140 Multithreading, 32–33
função max_element , 447–448 função Declarações if-else multidirecionais , 98–99
maxValue , 477–480 MB (megabyte), Mutadores
unidades de armazenamento, 24 Megabyte que tornam campos de dados privados acessíveis fora da classe,
(MB), unidades de armazenamento, 24 587 definir função, 376–
Megahertz (MHz), computador de medição velocidade 378 operador subscrito como, 553
de clock de
entrada, 23 Operador de acesso de membro,
invocando N
funções de objeto, 369 Constantes nomeadas, para valores permanentes, 59–60
Objetos de funções Namespace, use no programa C++ para evitar conflitos de
de membro e, 369 sobrecarga de nomenclatura, 34
operador e, 563 Cópia Memberwize, copiando conteúdo Convenções de
entre objetos, nomenclatura para classes e
369 objetos, 369 consistência
Memória RAM (memória de acesso e, 423 para
aleatório), 25 dispositivos constantes, 59 identificadores para
de armazenamento, 25–27 elementos de
unidades de armazenamento, 24–25 Endereço programa, 55 para ponteiros, 435 para variáveis, 56
de memória, retenção Estreitamento (de tipos), conversões de tipo numérico, 73
de ponteiros, 432 Instruções if aninhadas
Vazamentos de memória, computando IMC (Índice de Massa Corporal), 104–105
452 Merge sorts, 324–325 Mersenne prime, computando impostos, 106–108
277 Metros, conversão de/para, 85, 273 MHz (megahertz), medição da visão geral de, 97–98
velocidade do clock do Loops aninhados, 196–198
computador em, 23 Ratos, como dispositivo de Placa de interface de rede (NIC), 28
entrada, 28 milhas, novos operadores, criando memória persistente com, 449–450
conversão de/para quilômetros, função NIC (placa de interface de rede), 28
214 min , função min_element de 140 , 447–448 Construtores sem
Mnemônico, em instruções de linguagem de máquina, argumentos, objetos anônimos e,
30 Modem, tipos de dispositivos de 370 diretrizes de design de classe,
comunicação, 28 teclas modificadoras, em 423 visão geral de, 368
teclados, 27 Modularizando Funções não-membros, 562–563
código. consulte Operador de módulo de reutilização Parâmetros não-tipo, na classe de modelo, 487
de código (%) , 63 Operador de atribuição de módulo (%=), 69.553 UnidadesOperador Não
monetárias, (!) , 111 79–81
contagem,
Machine Translated by Google

Índice 699

Operador diferente de (!=) , 397 NULL, destruidores, 456–459


435 encapsulando campos de dados, 376–379
Terminador nulo (\0), C-string, 312, 392 Números exercícios, 387–390
binários. funções inline em classes, 375–376
consulte Números binários convenções de nomenclatura,
complexos, 576–577 369 visão geral de, 361–
convertendo strings C de/para, 316–317 362 passagem para funções, 401–
convertendo strings de/para, 398 404 evitando múltiplas inclusões de arquivo de cabeçalho,
contando ocorrências usando array, 318–319 decimal. 374 –375
consulte Números decimais em ponto referenciando com ponteiros, 432
flutuante. consulte Números de ponto flutuante retornando arrays de funções, 301 escopo de
hexadecimais. veja Números hexadecimais inteiros. variáveis e, 379–381 separando a
consulte exercícios de definição de classe da implementação de classe,
loop de inteiros, visão geral 371–374
215–216 de, 678 armazenamento em vetores,
números perfeitos, 220 491 tipo de string
primos. veja Números primos como, 154
aleatórios. veja Números aleatórios resumo, 385 este ponteiro e, 455–456
racionais. veja números racionais Literais octais, 62
Numeradores Erros off-by-one, loops e 178 modos de
acessando com operador subscrito ([]), 552 em arquivo de classe
números racionais, 544–545 retorno ofstream e 522 para
por referência, 553 trabalhar com arquivos, 512
Tipos de dados numéricos, 60, 144 gravação em um arquivo, 165–166, 512–514
Erros numéricos, 198–199 OOP (programação orientada a objetos)
Teclado numérico, em teclados, 27 anexando a strings, 393
Literais numéricos, 62–63 aplicando funções de comprimento, tamanho e capacidade a strings,
Operadores numéricos 394–395
aplicados a caracteres, visão geral matriz de objetos, 404–406
144 de, 63–64 atribuição de strings, 393–394
Conversões de tipo numérico, 72–74 diretrizes de design de classe, 422–424
comparação de strings, 395
funções de membro constante, 410–412
O
construção de strings, 392
Composição de objetos, 418–419 conversão de números em strings, 398
Arquivo de objeto, arquivo de código de criação de classe para modelagem de pilhas, 420–422
máquina como, 38 Programação orientada a objetos. veja OOP exercícios, 425–430
(programação orientada a objetos) história de C++ e, 33–34 inserção/
Matrizes de objetos/tipos substituição de strings, 396–397 instâncias e
de objetos, 404–406 E/ membros de classe estáticos, 406–409 lista de
S de objeto binário, 529–533 operadores de string, 397
abstração e encapsulamento de classe, 381–385 composição de objetos, 418–419
composição, 418–419 obtenção de substrings, 395– 396
construção e uso, 368–371 construtores, visão geral de, 362
367–368 conversão de passagem de objetos para funções, 401–404
funções para, 561– 562 criando e substituição de strings, 399–401
acessando objetos dinâmicos, 453–455 criando objeto círculo, pesquisa em strings, 396
364–365 criando objetos de arquivo, divisão de strings, 398
522–523 criando objeto TV, 365–367 classe de string , 392
declarando a partir de classe de resumo, 424–425
modelo, 486 definindo classes para, 362– processo de pensamento para, 412–
363 418 transição de programação processual para, 392
Machine Translated by Google

Índice 700

Sobrecarga Saída. veja também E/S (entrada/saída)


de operandos e, 551 exibindo saída formatada no console, 160–161 formatação com
armazenamento na classe Stack , 497– manipuladores de fluxo, 518–519
498 valores operados por operadores, 63 Redirecionamento de saída,
Sistemas operacionais (SOs), 32–33 186 Fluxo de saída, 512
Gráfico de associatividade Overflow, erros inteiros, 81 Funções
do operador de de sobrecarga, 238–241 Operadores de
sobrecarga 676–677 e sobrecarga. consulte Sobrecarga do operador Substituindo uma
visão geral 551 da palavra- função, 597
chave do operador 123–124 , 544, 550
Sobrecarga do operador
conversão automática para tipo de dados primitivo, 561 P
definindo funções amigas e classes amigas , 557–558 definindo Teclas Page Down/Page Up, em teclados, 27 palíndromos
funções verificando, 208–

não membros para, 562–563 definindo operadores como 210 exercícios de função,
funções, 550–551 exercícios, 575–578 funções de 272, 277 exercícios OOP, 426
operador, 561–562 RecursivePalindrome.cpp,
operadores que podem ser 654–655 RecursivePalindromeUsingHelperFunction.
sobrecarregados , 551–552 operadores de atribuição cpp, 655–656 exercícios de seleção, 136 recursão de cauda e, 665
de sobrecarga, 571–574 operadores de atribuição
aumentada de sobrecarga, 554 operadores de sobrecarga pré e
pós-incremento e decremento, 555–557 operadores de sobrecarga
de extração e inserção Comentário de parágrafo, em programas C++, 35
de fluxo, Lista de parâmetros, definição de funções e, 229
559–560 Associação de ordem de parâmetro, 235
sobrecarregando operador subscrito, 552–554 Parâmetros

sobrecarregando operadores unários, 555 definir modelos de função usando parâmetros de tipo;
visão geral de, 543–544 477–478

Classe racional para modelagem de números racionais, 544–546 definindo funções e, 229 uso de
Classe Rational com operadores de função sobrecarregados, parâmetros não-tipo na classe de modelo, 487 classes pai.
563–571 veja Classes base Parênteses (()), uso
RationalClass.cpp, 547–550 resumo, 575 de caracteres especiais em C++, 36 Verificação de paridade, exercícios
de array multidimensional, 360 Linguagem de programação Pascal, 31
TestRationalClass.cpp, 546–547 Gráfico de Argumentos de passagem por referência,
precedência do operador 253–254 comparação
de, 676–677 operador com passagem por valor,
de desreferência e, 440 sobrecarga e, 259 GenericMaxValuePassByReference .cpp,
551 visão geral de, 122– 478–480 parâmetros de objeto, 414 objetos para funções, 402–404
123 Operadores aritméticos, argumentos de ponteiro em
63 chamadas de função, 442–443
operadores de argumentos de passagem por valor, 235–236, 250–251
atribuição aumentada , 69–70 bit a bit e shift, 682 matrizes para
definindo como funções, funções, 299–300 comparação com
550–551 avaliando a precedência do passagem -por referência, 259
operador, 65–67 alternativas de palavras-chave, objetos para funções, 402 argumentos de ponteiro
673 gráfico de precedência, 676– em chamadas de função, 442–
677 armazenando na classe 443 VirtualFunctionDemoPassByValue.cpp, 598–599
Stack , 497–498 string, 397 que pode/não
pode ser
sobrecarregado , 551 Senhas, exercícios de loop, 225
Ou operador (||) , 111, 113, 131 Exercícios de matriz de
SOs (sistemas operacionais), 32–33 reconhecimento de
Erros fora dos limites, índices de array e, 289 padrões/padrões, 324 exercícios de função, 273
Machine Translated by Google

Índice 701

exercícios de loop, 216 Sobrecarga do operador pré-


exercícios de array multidimensional, 355 decremento, visão geral
exercícios de vetor, 505–506 555–557 de, 70
Pentágono, determinando a área de, 168-169 Exercícios de previsão, função de
Números perfeitos, 220 prefixo 201–202 , 282, 326
Problemas de desempenho, devido à recursão, 665 Sobrecarga do operador
Exercícios de física pré-incremento, visão geral
calculando o comprimento da pista para aeródromo, 555–557 de, 70
87 fórmula para aceleração e velocidade, 86 Pré-processador, 38
Elemento pivô, lista de particionamento com, 325 Diretiva de pré-processador
pixels, em resolução de tela, 28 em programas C++, 35
variáveis de ponteiro. consulte para compilador, 34
Ponteiros Strings baseadas em Loops de pré-teste, loops while e loops for as, 194
ponteiros, Fatores primos, exercícios OOP, 429
442 Matrizes de ponteiros Exibição de
e, 439–442 ponteiros constantes, números primos,
438–439 exercícios, exercícios de função 210–
469–474 funções para manipulação de matrizes e, 447– 212, exercícios de loop
449 visão geral de, 432– 277, código de modularização 216 e 237–238
433 passagem de argumentos de ponteiro em Exercícios OOP, 429
chamadas de Tipos de dados primitivos (fundamentais)
função, 442– 446 retornando ponteiros de funções, convertendo funções para, 561
446–447 resumo, 468– visão geral de, 51
469 TestPointer.cpp, 433–437 este retornando matrizes de funções, 301
ponteiro, 455–456 declaração Impressão de matrizes,
typedef e, 437–438 290 funções printStack , adição à classe Stack , 503
VirtualFunctionDemoUsingPointer.cpp, 597–598 encapsulamento de
Ponteiros, campo de dados de palavras-chave privadas
arquivo, 533–534 Localização e 376–379 palavras-chave de visibilidade, 600–601
de Resolução de problemas, recursão em, 653–655
pontos distância entre dois pontos, 337–338 encontrando Programação processual, comparada com OOP, 416
o ponto mais próximo, 350, 504 Programação de
Polígonos instruções e expressões de atribuição, 57–59 operadores
determinando a área de, 170, 325, 509 de atribuição aumentados, 69–70 contando
exercícios OOP, 428–429 unidades monetárias, 79–81 exibindo
Tipos polimórficos, 599 Visão a hora atual, 67–69 erros, 41–44, 81–
geral do 82 avaliando expressões
polimorfismo de, 595–596 e precedência de operadores, 65–67 operações com expoentes ,
PolymorphismDemo.cpp, 596 64–65 genéricos, 588–589
Exercício de projeção populacional, 86 identificadores em,
Sobrecarga de operador pós- 55 operadores de
decremento, 555–557 incremento e decremento, 70–72 constantes
visão geral de, 70 nomeadas em, 59–60 tipos de
Notação pós-fixada, 504–505 dados numéricos, 60–62 literais
Sobrecarga de operador pós- numéricos, 62–63 operadores
incremento, 555–557 visão numéricos, 63–64 conversões
geral de, 70 Pós- de tipo numérico , 72–74 visão geral de,
teste loops, loops do-while como, 194 Libra (#), 49–50 programa
uso de caracteres especiais em C++, 36 Pounds, conversão de/ definido, 22 ciclo de
para quilogramas, 85, 214 função pow(a, b) desenvolvimento de programa em C++, 38–40 leitura
de entrada do teclado, 53–55 processo de
ComputeLoan.cpp, 77 desenvolvimento de software, 75–79 estilo e
operações de expoente, 64–65 documentação em C++, 40–41
Machine Translated by Google

Índice 702

Resumo e exercícios de exercícios de tratamento de exceções, 643


programação (continuação) , 82–89 exercícios, 575
variáveis, 55–57 modelagem de números racionais, 544–546 com
escrevendo um programa simples, 50–52 operadores de função sobrecarregados, 563–571
Linguagens de programação sobrecarga de operadores de atribuição aumentada, 553
linguagem assembly, 29–30 RationalClass.cpp, 547–550
linguagens de alto nível, 30–31 TestRationalClass.cpp, 546–547
linguagem de máquina, 29 Números racionais

uso por desenvolvedores de software, acessando com operador subscrito ([]), modelagem
22 Prompt, para entrada do 552, função de leitura
usuário, 53 544–546 , E/S binária, 527 Leitura de um
diretrizes de design de classe de arquivo
propriedades, binário, 527–528 caracteres do
423 objetos, 362 palavras-chave teclado, 143 strings, 157 de um arquivo
protegidas , 600–601 protótipos. veja de texto, 166–
Protótipos de função Pseudocódigo, descrevendo algoritmos 167, 186–188, 514–515 Área de computação de retângulos
em linguagem de, 47 classes
natural, 50 Números pseudoaleatórios, derivadas, 606–607 pontos
109 palavra-chave pública , 365, 600–601 de descoberta dentro, 130
Funções virtuais puras, 601–602 função exemplos de herança, 585–586
put , 520 Linguagem pesquisas binárias de recursão e, 657
de programação Python, 31 quebra-

cabeça de oito rainhas, 662–664


exercícios, 667–670 fatoriais, 646–650
Q números de Fibonacci,
Equações quadráticas, resolução, 126 650–652 vs. iteração,
Quincunx (máquina de feijão), exercícios de array, 321–322 Aspas 665 visão geral de, 645–646 resolução
(."") faltando aspas causando de problemas, 653–
erro, 43 uso de caracteres especiais em C++, 36 655 funções auxiliares
recursivas, 655–656 classificação
de seleção recursiva, 656–657 resumo, 667
recursão de cauda, 665–666 Torres do
R problema de Hanói
RAM (memória de acesso aleatório), função e, 658-662
25 rand() , 109–111, 146–148
Arquivos de acesso aleatório
Chamada recursiva, 646
visão geral de, 533–534 Funções recursivas
RandomAccessFile.cpp, 534–536 caracteres características de, 653
aleatórios, gerando, 146–148 números aleatórios funções auxiliares, 655–656 visão
geral de, 646
estudo de caso aplicado à loteria, geração 115– Classificação de seleção recursiva, 656–657
117, 109–111 na simulação Variáveis de referência
de Monte Carlo, 202 Ponto aleatório, exemplo usando, 253
determinação de coordenada aleatória, 136 Embaralhamento função de incremento usada com, 254 visão
aleatório. consulte Embaralhando valores geral de, 252 função
aleatórios, inicializando matrizes com, 290 função de troca usada com, 254–259
random_shuffle , 447–448 memória de acesso reinterpret_cast, 527
aleatório (RAM), 25 classe Rational Operadores relacionais
comparando caracteres, 145–146
convertendo objeto Rational em tipo de dados de objeto, 561 comparando ponteiros para, 441
convertendo objeto Rational em tipo de dados primitivo, 561 definindo comparando strings, 156–158 lista
funções não membros para operadores de sobrecarga, de, 92
562–563
operadores de string e, 397
Machine Translated by Google

Índice 703

Nomes de arquivos relativos, Busca base, movendo ponteiros de arquivo em arquivos de acesso
512 Operador restante (%) , 63 aleatório,
função de substituição 514 função seekg() , 513–514 função
ReplaceString.cpp, 399–401 sequências seekp() , 513–514
de substituição, 396–397 Exercícios de matriz

Especificação de requisitos, no ciclo de vida de desenvolvimento de software, de classificação de


75–76 Palavra seleção, 321 genérico,
reservada (palavras-chave). veja também por tipo individual Palavra 480–482 recursivo, 656–
reservada (palavras-chave) em C++, 657 matrizes de classificação, 310–312
673 visão geral Seleções
de, 35 Relançamento aplicação de números aleatórios à loteria, tipo de dados bool
de exceções, 638–640 funções de retorno 115–117 e, 92–93 erros comuns em
de valor de palavra- declarações de seleção, 99–104 cálculo de IMC (Índice de
chave de retorno e, 229 funções void e, 234 Massa Corporal), 104–105 cálculo de impostos, 106–108
expressões condicionais, 121–122
Retorno por referência, função retorna aliases para variáveis, depuração e, 124 determinando ano bissexto,
553 Código 114–115 gerando números
reutilizável. consulte Função reversa de aleatórios, 109–111 instruções if , 93–96
reutilização de código operadores lógicos e, 111–114 instruções if
retornando matrizes de funções, 302–303 retornando aninhadas e if-else
ponteiros de funções, 446–447, 449, 451 multidirecionais , 97–99 precedência de
manipuladores certos , manipuladores de fluxo, 163-164 operador e associatividade e, 122–124 visão geral de, 92 instruções de
Erros de arredondamento, 82 seleção, 92 instruções switch , 117–121 instruções if-else bidirecionais,
Funções de arredondamento, 139–140 96–97
Regra de três, 574
Erros de tempo de execução

QuotientThrowRuntimeError.cpp, 624 classe


runtime_error , 623 stack overflow Função SelectionSort , 482 ponto e
devido à recursão, 665 tipos de erros de vírgula (;). ver ; (ponto e vírgula)
programação, 42 Rvalue (valor correto), Loops controlados por sentinela, 184–185
operadores de atribuição e, 553 Separação de responsabilidade, diretrizes de design de classe, 422 Arquivos
de acesso sequencial, 534 Funções
de serviço, 140 funções de
S conjunto , 376–378
Exercícios de ciências, manipulador setprecision(n) , 161 manipulador
exercícios de programação, 86, 88 set(width) , 163 Operadores de atribuição
exercícios de seleção, 129, 135 de cópia superficial
Notação científica, de literais de ponto flutuante, 63 e, 571 construtores de cópia e, 463
Exercícios de loop de jogo com operadores Shift, 682 tipo curto , 60
tesoura, pedra e papel, operador Curto-circuito,
220 exercícios de seleção, 128–129 113 manipulador
Escopo de variáveis showpoint , 162–163
inicializando variáveis e, 57 variáveis Embaralhamento de matrizes multidimensionais,
locais e globais, 245–247 for loop e, 247–248 349 matrizes
objetos e, 379–381 visão geral simples, 291 matrizes bidimensionais,
de, 245 333 vetores, 505–506

Resolução da tela, 28
Pesquisando matrizes Inteiros assinados, vs. não assinados, 61
de pesquisas binárias, 307–310 Simulação
pesquisas lineares, 306–307 exercícios de array, 323
visão geral de, 306 exercícios de vetor, 503
Pesquisando strings, 396 função size() , aplicação a strings, 394–395
Machine Translated by Google

Índice 704
sizeof tipo de dados se declarações. veja instruções if
de função tamanho e, 61 instruções if-else . consulte o terminador de instrução
tamanho do objeto e, de instruções if-else , 35
369 size_t tipo, 313 instruções switch . veja instruções switch
Inclinação, determinação da inclinação de uma instruções throw . veja declarações de lançamento
linha, 89 Software, componentes de Ligação estática, 598
computador, 22 Implantação do ciclo de Funções estáticas
vida de acessando sem criar objetos, 409 diretrizes
desenvolvimento de software, 76 de design de classe, 423–424
implementação, 75, associação de classe e, 406–407
77–78 manutenção, vs. funções de instância, 409
76 visão geral de, 75 especificação de palavra-chave estática , 248
requisitos, 75–76 no ciclo de vida de desenvolvimento membros de classe
de software, 78–79 análise de membros estáticos, 406–409
de sistema, 75–76 design na classe de modelo, 488
de sistema, membros de classe
75–77 teste, 76 função de de variáveis estáticas e, 406
declarando, 407
classificação , 447–448, 482 variáveis locais, 248–250, 379
Classifica algoritmo de classificação operador static_cast , 610–611 exercícios
por bolha, 321 caracteres de matriz
em string, 426, 474 de estatísticas, 320, 323
classificação genérica, 480–482 classificações de exercícios de loop , 222
mesclagem, 324–325 exercícios de array Benefícios de refinamento
multidimensional, 350, 357 passo a passo de,
classificação de seleção recursiva, 656–657 269 exemplo PrintCalendar.cpp , 266–269 na
classificações de seleção, 310–312 Código- solução de problemas, 262
fonte, em linguagens de alto nível, 30 Arquivos-fonte, design de cima para baixo, 262–
264 implementação de cima para baixo ou de baixo para
Extensão .cpp para, 38 cima, 264–265 Condições de parada, em
Programa fonte, em linguagens recursão, 646
de alto nível, 30 Espaçamento em programas C+ Dispositivos de
+, 35 estilo de programação e, 40 Caracteres especiais, em programas C++, 36 armazenamento
Classe Stack
desenvolvimento de classe genérica com modelos de classe, 482– CDs/DVDs, 26 discos
484 versão dinâmica, 489–491 rígidos, 26 visão geral de, 25–
EvaluateExpression.cpp, 499–502 avaliação de 26 unidades flash USB, 26–27 função strcat/strncat , concatenação de
expressões com, 497–498 exercícios de strings C, 315 função strcmp , comparação de strings C,
tratamento de exceções, 643 versão 315–316 função strcopy/strncopy , cópia Strings C, 313–314
genérica, 484–485 exercícios Operador
de herança, 616 exercícios de de extração de fluxo (>>)
modelo, 503 em ComputeAreaWithConsoleInput.cpp, 52
TestGenericStack .cpp, 485–486 implementação como funções não-membros amigas , 557
TestGenericStackWithTemplateFunction.cpp, sobrecarga, 559–560
487–488 leitura de strings que incluem espaços em branco,
Estouro de pilha, erros de tempo de execução, 665 519 operadores de
Classe string , 397 Operador de inserção
Stacks para modelagem, 420– de fluxo (<<) em
422 invocando funções e, 231–232 programas C++, 35 implementação como funções não-
exercícios de sobrecarga de operador, 576 membros amigas , 557
Estado, de um objeto, 362 sobrecarga, 559–560 operadores de string , 397
Declarações Manipuladores de fluxo
declarações de atribuição, 57–58 exibindo saída formatada no console, manipulador fixo
em linguagens de alto nível, 30 160–161, 162
Machine Translated by Google

Índice 705

formatação de saída de arquivo com, 518– Subclasses. consulte Classes


519 manipuladores esquerdo e direito , 163– derivadas Operador de
164 manipulador setprecision(n) , 161 subscrito ([]) acessando caracteres em strings, 155–
manipulador set(width) , 163 156, 392 exercícios de sobrecarga de
manipulador showpoint , 162–163 operador, 576 sobrecarga,
Stream states, 524–525 552–554 operadores
Streams de string, 397 usos de, 544
file. veja entrada da classe função substr , obtendo substrings, 395–396
fstream . veja a saída da classe Substrings, obtenção, 395–396
ifstream . consulte estados de Operador de subtração (-) , 63, 555
fluxo de teste de classe ofstream , Subtração, exercícios vetoriais, 510
524–525 Matrizes estritamente idênticas, Operador de atribuição de subtração (-=) 69, 553
323–324, 358 Subtipos, 595–596
funções de classe de string para comparar Exercícios
strings, 395 funções para encontrar de matriz multidimensional Sudoku, 356
substrings, 396 funções para inserir ou substituir strings, exemplos de matrizes bidimensionais, 339–342
396–397
implementação, Exercícios de loop de
474 visão geral de, 392 soma, 218 variável total e, 290, 332
Índice de string, 155–156 Superclasses. consulte Supertipos
Strings. veja também de classes base, 595–
strings C anexadas a, 393 aplicando funções de comprimento, 596 função de
tamanho e capacidade a, 394–395 aplicando recursão para troca passagem por valor e,
determinar permutações de, 646 aplicando 251–252 exercícios de
a programa de loteria, modelo, 503 usados com variável de
158–160 atribuindo, 393– referência, 254–259 função swapCase ,
394 verificando substrings, exercícios de array,
282 comparando, 156 327 instruções switch
–158, 395 ChineseZodiac.cpp,
concatenação, 156 construção, 392 conversão 119–121 visão geral de , 117–118 resolução
de números de ponto flutuante de
para, 327 conversão de números problemas
para, 398 strings C usando recursão, 653 regras, 119 sintaxe,
comparadas com, 312 118
exercícios, 172–173 tipos sinônimos, definição,
exercícios de função, 278 437–438 sintaxe de
inserção, 396–397 lista de modelos de classe, 485 de
operadores de string, 397 funções, 229 regras em
obtenção de substrings, 395–396 programas C++, 36 de instruções
exercícios OOP, 425– switch , 118 de
427 exercícios de matrizes bidimensionais , 330 Erro de sintaxe, 41 Análise de sistema,
sobrecarga de no ciclo de vida de desenvolvimento de software, 75–76 Design de sistema, no cic
operador, 576 visão
geral de, 154–
155 baseado em T
ponteiro, 442 leitura, 157 Tabelas, representação de dados com matriz
substituição, bidimensional, 330
399–401 reversão, Função recursiva de cauda, 665–666
222 pesquisa, 396 classificação de caracteres, 474 divisão, Cálculo de impostos, exercícios de seleção, 106–108, 128
398 classe de string , 392 índices de string e operador TB (terabyte), unidades de armazenamento, 24
de subscrito e, 155–156 Trabalho em equipe, facilitação de,
usando loops para verificar palíndromos, 208–210 classe 269 função Tellg() , 534
stringstream , 398 função strlen , localização do comprimento da string função
C, 313–314 funções
Tellp() , 534 stub, 264
Machine Translated by Google

Índice 706

Exemplo de Implementação de cima para baixo, variável


matriz multidimensional de temperatura, exercícios total 264–269 , somando matrizes, 290, 332
de seleção 343–344, classe problema das Torres de Hanói, aplicando recursão a, 658–662
de modelo 127, função de programas de
modelo 482–484, palavra- rastreamento, 52 transistores,
chave de modelo 487, prefixo CPUs
de modelo 477, 478, 485 construídas, 23 ângulos de
modelos computação de triângulos,
para classe Stack dinâmica , exercícios 140–142 área de computação de,
489–491, 503–510 88 perímetro de computação
para classificação genérica, de, 129 pontos de descoberta
480–482 para classe Stack genérica , dentro, 131 herança e, 615–616 classe
484–488 GenericMaxValue.cpp, 477– TriangleException , 628–632 funções
478 GenericMaxValuePassByReference.cpp, 478–480 visão trigonométricas, 138–139, 214–215 valores
geral de, 476–477 resumo, verdadeiros/falso. consulte tabelas verdade de
502 Classe de tipo de dados bool , para operadores booleanos, 111 blocos try-catch
modelo, 482–484 Terabyte BadAllocExceptionDemo.cpp, 625
(TB), unidades de armazenamento, 24 BadCastExceptionDemo.cpp, 625–626 captura
Operador ternário, como operador condicional, 122 de exceções, 618 classes de
Testando exceção e, 623 propagação de
benefícios do refinamento gradual, 269 no exceção, 637–638 especificação de
ciclo de vida de desenvolvimento de software, 76, 78– exceção e, 640–641
79 Arquivos InvalidArgumentExceptionDemo.cpp, 626–627 capturas
de texto comparando arquivos de texto com múltiplas, 632–637
arquivos binários, 526 QuotientThrowRuntimeError.cpp , 624
CopyFile.cpp, 520–522 criando objetos de arquivo usando QuotientWithException.cpp, 619–620
a classe fstream , 522–523 formatação de saída com QuotientWithFunction.cpp, 622 quando
manipuladores de fluxo, 518–519 usar exceções, 641 Parâmetros
funções get e put , 520 função de tipo para
getline , 519–520 permissão ao usuário modelos de classe, 485
para inserir nomes definindo modelos de função, 477–478
de arquivos, 517–518 visão declaração typedef , 437–438
geral de, 512 leitura de, 514– palavra-chave typedef , 437–438
515 teste de final de arquivo,
515– 517 testando a existência de
EM
arquivo, 515 testando
estados de fluxo, 524–525 Diagramas de classes UML (Unified
escrevendo para, 512– Modeling Language),
514 pensando 363 criando diagrama UML para classe,
recursivamente, 653–654 esta palavra-chave, 455 429 diagrama de classe Rational , 545
este ponteiro, 455–456 listas de lançamento, tratamento de exceções e, 640–641 instruções de lançamento
Operadores
BadAllocExceptionDemo.cpp, 625 unários, operadores de adição e subtração,
InvalidArgumentExceptionDemo.cpp, 626–627 sobrecarga 64, erro
QuotientThrowRuntimeError.cpp, 624 de underflow 555, números de ponto flutuante,
QuotientWithException.cpp, 620 linguagem de modelagem unificada 82. veja UML (linguagem de
QuotientWithFunction.cpp, 622 exceções modelagem unificada)
de relançamento, 638–640 exceções Barramento serial universal (USB),
de lançamento, 618 jogo 26 épocas UNIX,
TicTacToe, exercícios de matriz multidimensional, 354 Tilde ( ~) 67 inteiros não
usar com destruidores, função de tempo assinados vs.
(0) 456 , 67–69 Design de assinados, 61 tipo não
cima para baixo, 262–264 assinado , 437 Upcasting, 611
Machine Translated by Google

Índice 707

Atualizando arquivos, 536–537 Exercícios de velocidade


Caracteres maiúsculos calculando o comprimento da pista para aeródromo, 87
convertidos para minúsculos, 144–145 contando, relação com aceleração, 86 Funções virtuais
222 fundição dinâmica e,

USB (barramento serial universal), 26 611 substituindo uma função, 597


Unidades flash USB, 26–27 visão geral de, 596–597 funções
Usuários virtuais puras, 601–602

controlando loops através da confirmação do usuário, 184 VirtualFunctionDemoPassByValue.cpp,


permitindo que o usuário insira nomes de arquivos, 517–518 598–599

VirtualFunctionDemoUsingPointer.cpp, 597–598
EM

Invocação de funções de retorno de palavra-chave virtual , 598


valor, 230 visão Palavras-chave de visibilidade, 600
geral de, 229 funções Linguagem de programação Visual Basic, 31
void comparadas com, 232–235 Valores, pesquisas Funções nulas, 232–235
binárias baseadas na lista de, 307–310 Operador de endereço de Exercício de contagem de vogais, 222
variáveis (&)

e, 432 Booleano, 92 classes usando, 362


EM
declarando, 51 –

52 exibição/modificação loops while


na depuração, 124 aplicados a números de adivinhação, 179–181 aplicados
inicialização, 56–57 local e global, 245–247 ponteiro. ao teste de subtração, 178–179, 182–184 controlando com valores
consulte Referência de sentinela, 184–185 controlando com confirmação do
ponteiros, 432 escopo de, 57, 247– usuário, 184 convertendo loop for para, 194 convertendo
248 variáveis locais estáticas, para loop do -while , 190 decidir qual
248–250 para tipo de loop usar, 194–196 estratégias de design,
armazenar valores alteráveis, 182 variação do-while , 188–190 redirecionamento de entrada/
55–56 não declarados, não inicializados e saída, 186 visão geral de, 176–
não utilizados, 81 usando com tipos de dados, 56 178 lendo todos os dados de um arquivo,
classe de vetor DeckOfCardsUsingVector.cpp, 494– 186–188
496 exercícios, 503–510 visão geral
de, 491–492

substituindo matrizes por, 494 resumo, 502 TestVector.cpp, Caracteres de espaço em branco, 143, 519
492–494 Ampliação (de tipos), 73
TwoDArraysUsingVector.cpp, Sensação térmica, no cálculo da temperatura, 88, 129 função de
496–497 gravação , E/S binária, 526–527
Gravando
em arquivo binário, 526–527 em
um arquivo, 165–166, 512–514
Machine Translated by Google

Esta página foi intencionalmente deixada em branco


Machine Translated by Google

Créditos
Capa © Tetra Images/Glow Images
Figura 1.1a © Studio 37/Shutterstock
Figura 1.1b © Arno van Dulmen/Shutterstock
Figura 1.1ci © Peter Gudella/Shutterstock
Figura 1.1cii © Vasilius/Shutterstock
Figura 1.1ciii © Nata-Lia/Shutterstock
Figura 1.1di © Dmitry Rukhlenko/Shutterstock
Figura 1.1dii © Andrey Khrobostov/Shutterstock
Figura 1.1diii © George Dolgikh/Shutterstock
Figura 1.1ei © Nikola Spasenoski/Shutterstock
Figura 1.1eii © restyler/shutterstock
Figura 1.1fi © prism68/Shutterstock
Figura 1.1fii © moritorus/Shutterstock
Figura 1.1fiii © tuanyick/Shutterstock
Figura 1.2 © Xavier P/Shutterstock
Figura 1.4 © Peter Gudella/Shutterstock
Figura 1.5a © Vasilius/Shutterstock
Figura 1.5b © xj/Shutterstock
Figura 1.6 © Dmitry Rukhlenko/Shutterstock
Figura 1.7a © Madlen/Shutterstock
Figura 1.7b © Dmitry Melnikov/Shutterstock
Figura 1.7c © moritorus/Shutterstock
Figura 9.5 Captura de tela © Microsoft Visual C++ © 2012 pela Microsoft Corporation. Reimpresso com permissão.
Figuras 1.8–1.10, 4.1, 18.1, 18.2, 18.4, 18.5, 19.3–19.18 INTRODUÇÃO À PROGRAMAÇÃO JAVA por Daniel Liang, 9ª Ed. copyright © 2013 da Pearson
Education, Inc. Reimpresso e reproduzido eletronicamente com permissão da Pearson Education, Inc., Upper Saddle River, Nova Jersey.

Copyright © 2012 da Microsoft Corporation. Usado com permissão da Microsoft.


A MICROSOFT E/OU SEUS RESPECTIVOS FORNECEDORES NÃO FAZEM REPRESENTAÇÕES SOBRE A ADEQUAÇÃO
IDADE DAS INFORMAÇÕES CONTIDAS NOS DOCUMENTOS E GRÁFICOS RELACIONADOS PUBLICADOS COMO

PARTE DOS SERVIÇOS PARA QUALQUER FINALIDADE. TODOS TAIS DOCUMENTOS E GRÁFICOS RELACIONADOS SÃO

FORNECIDO "COMO ESTÁ" SEM GARANTIA DE QUALQUER TIPO. A MICROSOFT E/OU SEUS RESPECTIVOS SUPORTES
ALICATE POR MEIO DESTE ISENTA-SE DE TODAS AS GARANTIAS E CONDIÇÕES COM RELAÇÃO A ESTA INFORMAÇÃO-

ÇÃO, INCLUINDO TODAS AS GARANTIAS E CONDIÇÕES DE COMERCIALIZAÇÃO, SEJAM EXPRESSAS, IMPLÍCITAS OU ESTATUTÁRIAS,
ADEQUAÇÃO A UM DETERMINADO FIM, TÍTULO E NÃO VIOLAÇÃO. EM HIPÓTESE ALGUMA A MICROSOFT E/OU SEUS RESPECTIVOS
FORNECEDORES SERÃO RESPONSÁVEIS POR QUAISQUER DANOS ESPECIAIS, INDIRETOS OU CONSEQUENCIAIS OU QUAISQUER DANOS
RESULTANTES DE PERDA DE USO, DADOS OU LUCROS, SEJA EM UMA AÇÃO DE CONTRATO, NEGLIGÊNCIA OU OUTRA AÇÃO ILÍCITA,
DECORRENTE OU RELACIONADO COM O USO OU DESEMPENHO DAS INFORMAÇÕES DISPONÍVEIS NOS SERVIÇOS. OS DOCUMENTOS E
GRÁFICOS RELACIONADOS AQUI CONTIDOS

PODE INCLUIR IMPRECISÕES TÉCNICAS OU ERROS TIPOGRÁFICOS. AS MUDANÇAS SÃO PERIODI-

ADICIONADO ÀS INFORMAÇÕES AQUI. A MICROSOFT E/OU SEUS RESPECTIVOS FORNECEDORES PODEM

FAÇA MELHORIAS E/OU ALTERAÇÕES NO(S) PRODUTO(S) E/OU NO(S) PROGRAMA(S) AQUI DESCRITO(S) A QUALQUER MOMENTO. CAPTURAS
DE TELA PARCIAIS PODEM SER VISUALIZADAS COMPLETAMENTE NO SOFTWARE
VERSÃO ESPECIFICADA.

709

Você também pode gostar