Escolar Documentos
Profissional Documentos
Cultura Documentos
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.
if (condição1) {
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.
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++!
*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
if (condição1) {
acompanhe a programação
Através do poder da prática e da personalização imediata
MyProgrammingLab™
Saiba mais em www.myprogramminglab.com
Introdução a
Programação
Com
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
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.
10 9 8 7 6 5 4 3 2 1 14
13 12 11 10
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
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 A introdução do tipo string no Capítulo 4 permite que os alunos escrevam programas usando
cordas cedo
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?
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 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.
8
Machine Translated by Google
Prefácio 9
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
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.
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
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++.
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).
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).
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.
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.
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).
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 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.
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
13
Machine Translated by Google
Conteúdo
Capítulo 1 Introdução aos Computadores,
Programas e C++ 21
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
16 Conteúdo
Conteúdo 17
18 Conteúdo
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.
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).
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.
Um computador consiste nos seguintes componentes principais de hardware (veja a Figura 1.1):
ô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
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.
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
CPU é colocada
sob o ventilador
Memória
Placa-mãe
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.
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.
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
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.
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).
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
Figura 1.5 As unidades flash USB são portáteis e podem armazenar muitos dados.
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
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
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.
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
Figura 1.7 Uma rede local conecta computadores próximos uns dos outros.
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
foi criado nos primórdios da computação como uma alternativa às linguagens de máquina. Conjunto
Machine Translated by Google
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.
... ...
Montador 1101101010011010
adicione 2, 3, resultado
... ...
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.
á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.
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.
COBOL Linguagem comum orientada para negócios. É usado para aplicativos de negócios.
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.
... Saída
área = 5 * 5 * 3,1415; Intérprete
...
(a)
...
... 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
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.
n Agendamento de operações
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
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.
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
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
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.
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
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
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
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
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
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.
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.
Programar é divertido!
Fundamentos orientados
para o primeiro problema
Machine Translated by Google
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.
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.
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?
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
int principal()
{
"
contagem << "3,5 * 4/2 – 2,5 = << ( 3,5 * 4/2 – 2,5 ) << fim;
retornar 0;
}
int principal()
{
cout << "C++" << "Java" << endl;
cout << "C++" << endl << "Java" << endl;
cout << "C++, " << "Java, " << "e C#" << endl;
retornar 0;
}
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.
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
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
exemplo, Bem-vindo
Resultado
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
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++?
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.
Um espaço de linha única deve ser usado para separar segmentos do código para facilitar a leitura do programa.
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
#include <iostream>
usando namespace std;
int principal()
{
cout << "2 + 3 = "<<2+3;
retornar 0;
}
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.
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
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.
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.
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}
3 4 int principal()
5 {
6 cout << "Celsius 35 é grau Fahrenheit " cout << (9/5 ) * 35 << fim;
+ 32 << endl;
retornar 0;
7 8 9 10}
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.
int principal()
{
int principal()
{
cout << "Programar é divertido!" << fim;
cout << "Fundamentos Primeiro" << endl;
cout << "Orientado pelo problema" << endl
}
Falta um ponto e vírgula
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
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 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
4. Os programas de computador , conhecidos como software, são as instruções invisíveis que controlam o hardware
e o fazem executar tarefas.
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.
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.
14. Programas e dados são armazenados permanentemente em dispositivos de armazenamento e movidos para a
memória quando o computador realmente os utiliza.
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.
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.
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.
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
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.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 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).
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 escrever literais inteiros, literais de ponto flutuante e literais em linguagem científica
notação (§2.8.2).
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).
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.
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:
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() {
O programa precisa ler o raio inserido pelo usuário no teclado. Isso aumenta
duas questões importantes:
n Lendo o raio
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 ;
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.
3 4 int principal()
5{
raio duplo ; declarar variável
6 7 área dupla ;
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.
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.
3 4 int principal()
{
56 // Etapa 1: leitura no raio
7 raio duplo ;
8 cout << "Insira o raio: ";
Machine Translated by Google
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
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:
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
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.
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 }
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
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
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).
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 interno Esses exemplos usam os tipos de dados int e double. Posteriormente, você conhecerá tipos de dados adicionais,
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
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
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:
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.
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}
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:
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;
1 =x; // Errado
Observação
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;
3 4 int principal()
5{
6 int i = j = k = 1;
7
retornar 0;
8 9}
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.
3 4 int principal()
5 {
6 const duplo PI = 3,14159; PI constante
7
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
2.7 Quais são os benefícios de usar constantes nomeadas? Declarar uma constante int SIZE
ÿVerificação de ponto com valor 20.
curto não assinado int curto não assinado 216-1 (65535) -231 to 16 bits não assinado
não assinado int não assinado (4294967295) -231 32 bits não assinado
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
-1,7976931348623157E+308 a -4,9E-324
Faixa positiva:
4.9E-324 a 1.7976931348623157E+308
-1,18E+4932 a -3,37E-4932
Faixa positiva:
3,37E-4932 a 1,18E+4932
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:
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
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.
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}
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
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.
+ Adição 34 + 1 35
- Subtração 34,0 - 0,1 33,9
* 300*30 9.000
Multiplicação
% 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
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:
(6 + 10)% 7 é 2
O segundo dia da semana é terça-feira
Depois de 10 dias
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}
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.
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
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?
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 ;
(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.
3 + 4 * 4 + 5 * (4 + 3) - 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).
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
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++.
4 3 + d(2 + uma)
a. - 9 (uma + aC) +
3(r + 34) a + bd
2,5+t
b. 5,5 * (r + 2,5)
Decorrido
tempo
Tempo
Figura 2.1 Invocar time(0) retorna o número de segundos desde a época Unix.
Machine Translated by Google
Você pode usar esta função para obter a hora atual e então calcular o segundo atual,
minuto e hora da seguinte forma:
2. Calcule o segundo atual a partir de totalSeconds % 60 (por exemplo, 1203183086 segundos % 60 = 26, que
é o segundo atual).
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).
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}
Linha# 8 11 14 17 20 23
Variáveis
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.
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.
+= 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
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
intuma = 6;
uma -= uma + 1;
cout << a << endl;
uma *= 6;
cout << a << endl;
uma /= 2;
cout << a << endl;
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
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;
int eu = 10;
Mesmo efeito que eu = eu + 1;
int novoNum = 10 * (++i);
cout << "i é " << i << ", int novoNum = 10 * i;
newNum é " << newNum;
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
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.
d. A afirmação x = y = x = 0 é ilegal.
intuma = 6; int
b = a++; cout << a
<< endl; cout << b << endl; uma
= 6; b = ++a; cout << a << endl;
cout << b
<< endl;
intuma = 6; int
b = uma--; cout << a
<< endl; cout << b << endl; uma
= 6; b = --uma; cout << a <<
endl; cout
<< b << endl;
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
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
exibe 1. Quando um valor duplo é convertido em um valor int , a parte fracionária é truncada.
A seguinte declaração
exibe 0,5, porque 1 é convertido em 1,0 primeiro e depois 1,0 é dividido por 2. No entanto, a instrução
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;
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.
3 4 int principal()
{
56 //Insira o valor da compra
Machine Translated by Google
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
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;
valor duplo = 5;
cout << quantidade / 2 << endl;
cout << 5/2 << endl ;
Machine Translated by Google
manutenção.
Requisitos
Especificação
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
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.
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
A saída é o pagamento mensal e o pagamento total, que podem ser obtidos por meio das seguintes fórmulas:
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.
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 4. Calcule o pagamento total, que é o pagamento mensal multiplicado por 12 e multiplicado
pelo número de anos.
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.
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
Linha# 10 13 18 23 26 28 30 31
Variáveis
número de anos 5
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.
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
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
-b + 2b2 - 4ac
2a
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
1. Solicite ao usuário que insira o valor como um número decimal, como 11,56.
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.
8. Exiba o resultado.
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
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
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).
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.
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.
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 .
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.
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.
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
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.
4. A declaração de uma variável informa ao compilador que tipo de dados uma variável pode conter.
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.
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.
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
13. C++ fornece operadores que realizam operações numéricas: + (adição), – (subtração),
* (multiplicação), / (divisão) e % (módulo).
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.
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
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:
Exercícios de Programação 85
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:
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:
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:
* 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:
** 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.
* 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:
* 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:
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:
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 é
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:
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:
** 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
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.)
* 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:
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
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:
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
* 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 é:
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:
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
é
Exercícios de programação 89
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:
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:
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:
* 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
CAPÍTULO
3
Seleções
Objetivos
n Para declarar variáveis bool e escrever expressões booleanas usando relações
operadores nacionais (§3.2).
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.
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:
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,
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.
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)
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
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
(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) {
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.
(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.
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
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).
96 Capítulo 3 Seleções
se raio >= 0
{
área = raio * raio * PI;
cout << "A área do círculo de " " raio " << raio <<
<< "é" <<área;
}
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;
}
verdadeiro falso
expressão booleana
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:
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?
(a) (b)
se (eu > k) {
98 Capítulo 3 Seleções
(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
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;
se (x > 2)
se (y > 2) {
int z = x + y;
cout << "z é" << z << endl;
}
outro
cout << "x é" << x << endl;
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).
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;
(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.
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
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.
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";
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
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.
(a) (b)
Isto não é um erro, mas deveria ser melhor escrito como mostrado em (b).
if (no estado) {
mensalidade = 5.000;
cout << "A mensalidade é " << mensalidade << endl;
}
outro
{
mensalidade = 15.000;
cout << "A mensalidade é " << mensalidade << endl;
}
if (no estado) {
mensalidade = 5.000;
}
outro
Machine Translated by Google
{
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).
(a) (b)
(a) (b)
(c) (d)
3.13 Quais das seguintes afirmações são equivalentes? Quais estão corretos
recuado?
se (contagem% 10 == 0)
novaLinha = verdadeiro;
outro
novaLinha = falso;
(a) (b)
(a) (b)
IMC Interpretação
IMC 6 18,5 Abaixo do peso
18,5… IMC 6 25,0 Normal
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.
3 4 int principal()
5{
6 // Solicita ao usuário que insira o peso em libras
Machine Translated by Google
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
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) {
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
A Listagem 3.3 fornece a solução para calcular impostos para arquivadores únicos. A solução completa é
deixado como exercício.
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
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.
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;
rand()% 10
5 6 int principal()
7{
// 1. Gere dois números inteiros aleatórios de um dígito
srand(tempo(0)); definir uma semente
Quanto é 5 – 2? 3
Você está certo!
O que é 4 – 2? 1
Sua resposta está errada.
4 – 2 deveria ser 2
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.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.
À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.
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:
3 4 int principal()
5{
6 número interno ;
7 cout << "Digite um número inteiro: ";
entrada 8 cin >> número;
9
Cuidado
Em matemática, a expressão
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:
Por exemplo,
(número% 2 != 0 || número% 3 != 0)
!(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
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
3.24 Para testar se x está entre 10 e 100, quais das seguintes expressões são
correto?
3.26 Qual é o valor da expressão x >= 50 && x <= 100 se x for 45, 67,
ou 101?
#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.
// Um ano bissexto é divisível por 4, mas não por 100 ou divisível por 400
isLeapYear = isLeapYear || (ano % 400 == 0);
A Listagem 3.6 fornece um programa que permite ao usuário inserir um ano e verificar se é um ano
bissexto.
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}
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
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
Linha# 10 15 18 19 22 23 37
Variável
loteria 34
adivinhar 23
loteriaDigit1 3
loteriaDigit2 4
palpiteDigit1 2
palpiteDigit2 3
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:
4. Caso contrário, nada corresponde e exibe "Desculpe, não corresponde" (linhas 38–39).
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
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) {
status é 0
Calcular imposto para arquivadores únicos quebrar
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 :
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.
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) {
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.
3 4 int principal()
5 {6
cout << "Insira o ano: ";
Machine Translated by Google
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
ano interno ;
insira o ano cin >> ano;
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
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;
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:
Como outro exemplo, a instrução a seguir exibe a mensagem “num é par” se num for par e, caso contrário, exibe
“num é ímpar”.
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;
}
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:
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
Precedência Operador
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
é 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
3.39 Verdadeiro ou falso? Todos os operadores binários, exceto =, são associativos à esquerda.
2 * 2 - 3 > 2 || 4 – 2 > 5
Machine Translated by Google
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
Termos chave
Resumo do capítulo
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.
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.
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
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
Exercícios de programação
Observação
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:
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.
Digite a, b, c: 1 2 3
A equação não tem raízes reais
Machine Translated by Google
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:
*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:
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:
*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
*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.
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
**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)
(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
**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.
**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:
*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.
(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:
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)
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:
**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
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.
**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
(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
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:
*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
*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):
(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: 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:
*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)
*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:
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: 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
26(m + 1) k j
+k+ +
h = ¢q + 10 4 4 + 5jÿ % 7
onde
(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:
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:
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 converter um valor numérico em um caractere e converter um caractere em um número inteiro (§4.3.4).
n Para usar o operador subscrito para acessar e modificar caracteres em uma string
(§4.8.1).
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.
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.
Atlanta
(33.7489954, –84.3879824)
Savana (32.0835407, –81.0998342)
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.
Função Descrição
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:
Função Descrição
x
exp(x) Retorna e elevado à potência de x (e ).
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:
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
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
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.
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 }
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:
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
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.
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.
'0' a '9' 48 a 57
'A a Z' 65 a 90
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
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.
Sequência de fuga Nome Código ASCII Sequência de fuga Nome Código ASCII
\f Formfeed 12
Então, agora você pode imprimir a mensagem citada usando a seguinte instrução:
A saída é
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:
Entretanto, usar endl garante que a saída seja exibida imediatamente em todas as plataformas. \n vs. fim
Machine Translated by Google
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.
Quando um char é convertido em um tipo numérico, o ASCII do caractere é convertido no tipo especificado
tipo numérico. Por exemplo:
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
mostrar
eu tenho 101
j é 99
99 é o código ASCII para o caractere c
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.
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
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
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 .
'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.
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.
flutuar f = 1000,34f;
intj =f;
duplo d = 1000,34;
intk =d;
intl = 97;
char ch = eu;
int principal()
{
caractere x = 'a';
char y = 'c';
retornar 0;
}
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
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,
Em geral,
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')
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:
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:
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.
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
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á?
= 19
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).
3 4 int principal()
Machine Translated by Google
5{
dia interno = 0; // Dia a definir dia a definir
resposta de caractere ;
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
19 1
28 E
31 3
40 N
52 N
Machine Translated by Google
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.
(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
Função Descrição
(contínuo)
Machine Translated by Google
é inferior (ch) Retorna verdadeiro se o caractere especificado for uma letra minúscula.
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
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 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 }
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?
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.
Função Descrição
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:
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.
Índices 0 1 2 3 4 5 6 7 8 9 10 11 12 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
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.
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.
strings = "ABC";
s+= 'D';
Cuidado
É ilegal concatenar duas strings literais. Por exemplo, o código a seguir está incorreto:
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".
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
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').
Como o valor padrão para o terceiro argumento na função getline é '\n', a linha 3 pode ser substituída
por
A Listagem 4.7 fornece um programa que solicita ao usuário que insira duas cidades e as exibe em
ordem alfabética.
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
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:
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.
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?
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
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:
4. Caso contrário, nada corresponde e exibe “Desculpe, não corresponde” (linhas 31–32).
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:
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:
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
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
Operador Descrição
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
exibições
exibições
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,
exibições
23456
Machine Translated by Google
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,
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,
exibições
345,46
78676,89
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
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.
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,
exibições
ÿÿÿÿÿC++101
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.
exibições
ÿÿÿÿ1.23
ÿÿ351.34
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;
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:
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
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.
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
incluindo cabeçalho <fstream> Como ofstream é definido no arquivo de cabeçalho fstream , a linha 2 inclui esse arquivo de cabeçalho.
entrada ifstream;
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,
ou
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.
arquivo
95 56 34
pontuações.txt
input.close();
4 5 int principal()
Machine Translated by Google
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 }
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:
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.
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.
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
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
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:
10691,79183231593 km
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:
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:
*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)).)
60
R
65 R
a
(0, 0)
55
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:
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:
*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:
*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:
*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:
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
*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:
Escreva um programa que solicite ao usuário que digite uma letra e exiba o número correspondente.
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
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:
*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:
*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
*4.22 (Aplicação financeira: folha de pagamento) Escreva um programa que leia as seguintes informações e imprima um extrato da folha de
*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
CAPÍTULO
5
rotações
Objetivos
n Para escrever programas que executam instruções repetidamente usando um while
laço (§5.2).
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:
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 .
//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
contagem = 0;
falso falso
Condição de (contagem <100)?
continuação do loop?
verdadeiro verdadeiro
(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.
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
Aqui está outro exemplo para ajudar a entender como funciona um loop:
soma interna = 0, i = 1;
enquanto (eu <
10) {
soma = soma + i;
eu++;
}
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;
}
Observação
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
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
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.
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:
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.
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:
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
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 }
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
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.
Declarações;
Instruções adicionais para controlar o loop;
}
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.
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
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
Quanto é 7 - 5? 4 Sua
A contagem correta é 3
O tempo de teste é de 201 segundos
Machine Translated by Google
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:
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.
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
22 cout << "A soma é " << soma << endl; exibir resultado
23
24 retornar 0;
25 26 }
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
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
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:
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:
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:
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
20
21 cout << "\nEu sou " << soma << endl;
22
23 retornar 0;
24 }
95 56 34
A pontuação total é 185
Feito
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?
#include <iostream>
usando namespace std;
int main()
retornar 0;
}
interno x = 80000000;
enquanto (x > 0)
x++;
loop do-while
fazer {
//Corpo do loop;
Declarações);
} while (condição de continuação do loop);
Declarações)
(corpo em loop)
verdadeiro
Condição de
continuação do loop?
falso
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}
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 .
int main()
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;
}
fazer {
total += num; }
enquanto (num! = 0);
//Corpo do loop
...
eu++; // Ajusta a variável de controle do loop
}
//Corpo do loop
...
}
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);
}
Ação inicial eu = 0
falso falso
Condição de (eu <100)?
continuação do loop?
verdadeiro verdadeiro
(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
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++) {
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:
omitindo chaves Se houver apenas uma instrução no corpo do loop, como neste exemplo, os colchetes podem ser omitidos
conforme mostrado abaixo:
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
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:
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
Equivalente Equivalente {
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; i++) {
(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.
#include <iostream>
usando namespace std;
int principal()
{
número interno , soma = 0, contagem;
retornar 0;
}
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;
(a) (b)
(c) (d)
(a) (b)
Machine Translated by Google
(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.
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).
int eu = 0; int eu = 0;
enquanto (eu < 10); enquanto (eu < 10) { };
{ {
cout << "eu é" i++; << eu << endl; cout << "eu é" i++; << eu << endl;
} }
(c) (d)
int eu = 0;
fazer
{
cout << "eu é" i++; << eu << endl;
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++;
}
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}
1int principal ( )
2{
3 for (int i = 0; i < 10; i++);
4 cout << i + 4 << endl;
5 }
5 int principal()
6{
"
cout << Tabela de Multiplicação\n"; título da tabela
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:
5.22 Mostre a saída dos seguintes programas. (Dica: desenhe uma tabela e liste as variáveis
nas colunas para rastrear esses programas.)
(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;
} }
(c) (d)
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.
3 4 int principal()
5{
// Inicializa a soma
6 7 soma dupla = 0;
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:
soma += ValorAtual;
ValorAtual += 0,01;
}
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.
A Listagem 5.10 apresenta o programa que solicita ao usuário que insira dois números inteiros positivos e
encontre seu MDC.
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 }
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:
}
Machine Translated by Google
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
Esta revisão está errada. Você consegue encontrar o motivo? Consulte o Ponto de Verificação 5.23 para obter a resposta.
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:
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
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.
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.
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
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).
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.
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 }
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
(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.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?
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.
3 4 int principal()
5{
soma interna = 0;
número interno = 0;
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.
3 4 int principal()
5{
soma interna = 0;
número interno = 0;
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
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;
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
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
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.
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.
se (i * j > 2) se (i * j > 2)
quebrar; continuar;
(a) (b)
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.
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 }
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
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.
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:
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:
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:
if (número% divisor == 0) {
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}
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:
{
//Se verdadeiro, o número não é primo
if (número% divisor == 0) {
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.
5. Ao projetar loops, você precisa considerar tanto a estrutura de controle do loop quanto a estrutura do loop.
corpo.
Machine Translated by Google
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.
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.
15. A palavra-chave break encerra imediatamente o loop mais interno, que contém o break.
Questionário
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
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):
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
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
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:
*5.20 (Exibir quatro padrões usando loops) Use loops aninhados que exibem os seguintes padrões em quatro
programas separados:
**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:
Para obter a fórmula para calcular o pagamento mensal, consulte a Listagem 2.11, ComputeLoan.cpp.
Machine Translated by Google
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:
*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
*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
1 1 1 1 1
+ + + + c + 2
p = A6 * 11 + 4 9 16 25 n2
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)!
**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
-----------------------------------------------
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
dezembro de 2013
------------------------------------------------------------
*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
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
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.
Valor do CD do mês
1 10047,91
2 10096.06
...
17 10846,56
18 10898,54
Machine Translated by Google
*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:
1001–5000 US$ 2
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
*5.42 (Aplicação financeira: encontre o lucro por item) Exercício de programação de reescrita
5.39 da seguinte forma:
*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:
**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
...
...
*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:
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:
*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:
*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:
*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.
*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:
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:
*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:
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.
(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
(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:
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
CAPÍTULO
6
Funções
Objetivos
n Definir funções com parâmetros formais (§6.2).
n Desenvolver código reutilizável que seja modular, fácil de ler, fácil de depurar e fácil de
manter (§6.6).
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:
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
// 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.
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
chama max(3, 4) e atribui o resultado da função à variável maior. Outro exemplo de tal chamada é
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 ;
O máximo entre 5 e 2 é 5
Linha# eu
j k num1 num2 resultado
19 5
20 2
5 5 2
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
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
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.
(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.
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.
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
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.
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.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?
a. Retorne uma comissão de vendas, considerando o valor das vendas e a taxa de comissão.
int função1(int n) {
contagem << n;
}
função2(int n, m) {
n+=m;
função1(3,4);
}
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
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.
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}
Funções podem ser usadas para reduzir código redundante e permitir a reutilização de código. As funções também
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 }
Ao encapsular o código para obtenção do GCD em uma função, este programa apresenta diversas
vantagens:
2. Se houver erros no cálculo do GCD, eles ficarão confinados na função gcd , que
restringe o escopo da depuração.
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.
retornar 0;
52 53 54 55 }
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
é criar outra função com o mesmo nome, mas com parâmetros diferentes, conforme mostrado no
código a seguir:
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.
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 principal()
{
cout << maxNumber(1, 2) << endl;
retornar 0;
}
Machine Translated by Google
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:
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?
vazio p (int i)
{
cout << i << endl;
}
int p(int j)
{
cout << j << endl;
}
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
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.
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
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);
ÿVerificação de ponto
mês é 10
o ano é 2008
mês é 9
o ano é 2010
Machine Translated by Google
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.
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 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.
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
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.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.
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
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:
A última instrução causaria um erro de sintaxe, porque a variável i não está definida fora
do loop for .
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
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).
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
{
int eu = 5;
estático int j = 5;
eu++;
j++;
int principal()
{
p();
p();
}
vazio p (int i)
{
int eu = 5;
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.
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.
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
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.
temperatura: n2: 2
n1:1
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.
ou equivalente,
int& r = contagem;
Observação
notação equivalente As seguintes notações para declarar variáveis de referência são equivalentes:
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
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)
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
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
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
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 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.
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
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;
n++;
}
int principal()
{
duplo x = 1; int y
= 1;
incremento(x);
incremento(y); // Não é possível invocar increment(y) com um argumento int
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:
void maxValue(int valor1, int valor2, int max) { if (valor1 > void maxValue(int valor1, int valor2, int& max) { if (valor1 >
} }
retornar 0; retornar 0;
} }
(a) (b)
Machine Translated by Google
cout << num << " "; cout << num << " ";
num *= 2; num *= 2;
} }
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;
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
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;
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.
vazio f(duplo& p) {
p+= 2;
}
int principal()
{
duplo x = 10; int
y = 10;
f(x);
f(s);
retornar 0;
}
Machine Translated by Google
vazio p(int& i)
{
cout << i << endl;
}
int p(int j)
{
cout << j << endl;
}
int principal()
{
int k = 5;
p(k);
retornar 0;
}
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.
resultado interno ;
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.
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:
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
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:
antes do ciclo 0
após a 1ª iteração 0 A 10 10
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
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 }
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
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.
Cabeçalho
agosto de 2013
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.
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
(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
(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
getTotalNumberOfDays
getStartDay
getNumberOfDaysInMonth
(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)
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.
#include <iostream>
#include <iomanip>
usando namespace std;
Machine Translated by Google
int principal()
{
// Solicita ao usuário para inserir o ano
cout << "Insira o ano completo (por exemplo, 2001): ";
ano interno ;
cin >> ano;
retornar 0;
}
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.
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
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.
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
165 {
166 ano de retorno % 400 == 0 || (ano % 4 == 0 && ano % 100 != 0);
167}
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.
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.
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.
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
Termos chave
Resumo do capítulo
3. Uma função pode retornar um valor. O returnValueType é o tipo de dados do valor que
a função retorna.
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.
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
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.
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.
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
Questionário
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:
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:
*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:
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
*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.
double futureInvestmentValue(
valor de investimento duplo , taxa de juros mensal dupla , anos inteiros )
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
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 é:
6.11 (Exibir valores ASCII) Escreva uma função que imprima os valores ASCII dos caracteres
atores usando o seguinte cabeçalho:
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)
n f(n)
0,458333
24 0,566667
...
12 0,675824
14 0,685417
Machine Translated by Google
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:
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:
...
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:
void printMatrix(int n)
Digite n: 3 0 1 0
000
111
Machine Translated by Google
/** 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)
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 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
**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
**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
**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.
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:
*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)
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:
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
*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:
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:
**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
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
6.36 (Formatar um inteiro) Escreva uma função com o seguinte cabeçalho para formatar um inteiro positivo com a
largura especificada:
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:
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
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
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:
*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:
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:
**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:
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:
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
*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:
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.
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:
*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:
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:
***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:
**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
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:
**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:
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:
CAPÍTULO
7
Unidimensional
Matrizes
e cordas C
Objetivos
n Descrever porque um array é necessário na programação (§7.1).
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).
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.
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
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.
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;
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;
Observação
O tamanho do array usado para declarar um array deve ser uma expressão constante no C++ padrão. tamanho constante
tamanho interno = 4;
double minhaLista[tamanho]; // Errado
Mas está tudo bem se SIZE for uma constante como segue:
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:
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[0]++;
O código a seguir invoca a função max para retornar o número maior entre
minhaLista[1] e minhaLista[2]:
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.
Por exemplo,
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:
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.
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
n Todos os elementos de um array são do mesmo tipo. Eles são processados uniformemente da mesma maneira
usando um loop.
1. Inicializando arrays com valores de entrada: O loop a seguir inicializa o array myList com
valores de entrada do usuário:
2. Inicializando arrays com valores aleatórios: O loop a seguir inicializa o array myList
com valores aleatórios entre 0 e 99:
3. Imprimindo arrays: Para imprimir um array, você deve imprimir cada elemento nele, usando um loop como o
seguindo:
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:
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. 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.
max = minhaLista[i];
indexOfMax = i;
}
}
Qual será a consequência se (myList[i] > max) for substituído por (myList[i] >= max)?
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
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:
minhaLista[i - 1] = minhaLista[i];
}
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:
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:
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?
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?
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?
1 int principal() {
duplo[100] r;
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
11 48 51 42 8 74 1 41 36 53 52 82 16 72 19
70 44 56 29 33 0
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
[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
Figura 7.2 Se o número i aparecer em um bilhete de loteria, isCovered[i-1] será definido como verdadeiro.
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:
Linha# [1] [2] [3] [4] [5] [22] [42] número tudo coberto
17 verdadeiro
18 5
17 verdadeiro
(contínuo )
Machine Translated by Google
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);
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.
Todas as cartas podem ser representadas usando um array denominado baralho, preenchido com valores iniciais de 0 a
51, como segue:
// Inicializa os cartões
para (int i = 0; i < NUMBER_OF_CARDS; i++)
baralho[i] = i;
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
15 16 17 18 19
srand(time(0));
Machine Translated by Google
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.
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
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:
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.
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.
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.
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++
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:
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
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:
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
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
(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.
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
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.
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
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:
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.
matriz que corresponde à chave. Caso contrário, a pesquisa retornará -1. A função linearSearch na Listagem 7.9
fornece a solução:
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, ...
}
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.
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
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
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.
retornar -1;
} }
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
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.
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.
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.
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
trocar
trocar
trocar
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
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 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:
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.
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:
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.
cout << s;
Machine Translated by Google
Você pode ler uma string C no teclado da mesma forma que lê um número. Por exemplo, considere
o seguinte código:
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:
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:
Como o valor padrão para o terceiro argumento na função cin.getline é '\n', a linha 3 pode ser substituída
por
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
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.
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.
void itoa(int valor, char s[], int Obtém um valor inteiro para uma string com base na base
raiz) especificada.
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.
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
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.
No entanto, o código a seguir não funciona porque não há espaço para adicionar s2 em s1.
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:
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.
Ele exibe 0.
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
Observe que alguns compiladores C++ podem não suportar a função itoa .
a. s1 = "bom"
b. s1 <s2
Machine Translated by Google
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
Resumo do capítulo
1. Um array armazena uma lista de valores do mesmo tipo.
elementType arrayName[tamanho]
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:
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
13. Uma matriz de caracteres que termina com um terminador nulo é chamada de string C.
17. Você pode copiar uma string C para outra string C usando a função strcpy .
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
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:
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:
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
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:
*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:
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:
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:
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:
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:
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:
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.
{
alterado = falso;
for (int j = 0; j <listSize - 1; j++)
if (lista[j] > lista[j + 1]) {
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
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:
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
***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:
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:
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
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.
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:
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.
**7.27 (Classificado?) Escreva a seguinte função que retorna verdadeiro se a lista já estiver classificada
em ordem crescente:
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ô:
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
*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:
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.
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:
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:
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:
**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:
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:
. .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:
* 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:
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:
*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:
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
*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:
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:
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:
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:
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).
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.
elementType nomeArray[ROW_SIZE][COLUMN_SIZE];
Como exemplo, aqui está como declarar uma matriz bidimensional de valores int :
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]
Figura 8.1 O índice de cada subscrito de um array bidimensional é um valor int começando em 0.
Machine Translated by Google
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).
(a) (b)
ÿ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];
int r[2];
intx [];
int y[3][];
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
2. (Inicializando arrays com valores aleatórios) O loop a seguir inicializa o array com
valores aleatórios entre 0 e 99:
3. (Exibindo matrizes) Para exibir uma matriz bidimensional, você deve exibir cada
elemento na matriz usando um loop como o seguinte:
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++) {
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:
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;
{
int totalOfThisRow = 0;
for (int coluna = 0; coluna < COLUMN_SIZE; coluna++) totalOfThisRow
+= matriz[linha][coluna];
maxRow = totalOfThisRow;
indexOfMaxRow = linha;
}
}
srand(tempo(0));
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;
retornar 0;
}
Machine Translated by Google
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;
retornar 0;
}
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
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
29
30 cout << "\nA soma de todos os elementos é " << soma(m, ROW_SIZE) << endl; matriz de passagem
31
retornar 0;
32 33 }
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.
Aluno 0 ABACCDEEAD
Aluno 1 DBABCAEEAD
Aluno 2 EDDACBEAD
Aluno 3 CBAEDCEEAD
Aluno 4 ABDCCDEEAD
Aluno 5 BBECCDEEAD
Aluno 6 BBACCDEEAD
Aluno 7 EBECCDEEAAD
chave DBDCCDAEAD
Machine Translated by Google
3 4 int principal()
5{
const int NUMBER_OF_STUDENTS = 8;
6 7 const int NUMBER_OF_QUESTIONS = 10;
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
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 }
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:
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
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
(a) (b)
Figura 8.4 Uma grade pode ser representada usando uma matriz bidimensional.
Machine Translated by Google
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.
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.
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
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
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 }
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
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]
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:
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:
Você também pode usar a notação abreviada para criar e inicializar o array da seguinte maneira:
{{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[ eu ] [ j ] [ k ]
... ...
(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]
3 4 int principal()
5{
Machine Translated by Google
4 5 int principal()
6{
7 dia interno = 0; // Dia a definir
8 resposta de caractere ;
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
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).
ÿVerificação de ponto
Resumo do capítulo
1. Um array bidimensional pode ser usado para armazenar uma tabela.
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
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:
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:
*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:
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:
*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.
O cabeçalho da função é
const int N = 3;
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:
Os colchetes de cada taxa para todos os status de depósito podem ser representados em uma matriz
bidimensional da seguinte forma:
Suponha que o lucro tributável seja de $ 400.000 para arquivadores únicos. O imposto pode ser calculado da
seguinte forma:
**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
***8.8 (Embaralhar colunas) Escreva uma função que embaralha as colunas em um array int bidimensional
usando o seguinte cabeçalho:
**8.9 ( Álgebra : multiplicar duas matrizes) Escreva uma função para multiplicar duas matrizes aeb e salve o
resultado em c .
O cabeçalho da função é
const int N = 3;
void multiplicarMatriz(const duplo a[][N], const duplo b[][N],
duplo c[][N]);
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:
**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:
Cada estado também pode ser representado usando um número binário. Por exemplo, as
matrizes anteriores correspondem aos números
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:
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:
110101
110111
100010
111110
110101
100100
O menor índice da linha: 2
O menor índice da coluna: 2
Machine Translated by Google
* J1,5
-2 -0,5
1 R=J1001R
1
A = J abcd
A-1R= ad - bc J d -b -ca R
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:
*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.
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
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
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
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
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
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
(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:
-------------
| | | |
-------------
| | | |
-------------
| | | |
-------------
-------------
| | | |
-------------
| |X| |
-------------
| | | |
-------------
-------------
| | | |
-------------
| |X|Ó|
-------------
| | | |
-------------
...
-------------
|X| | |
-------------
| Ó | X| Ó |
-------------
| | |X|
-------------
X jogador venceu
Machine Translated by Google
**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.
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:
***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:
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
----------------------
||||||||||||||| | | | | |
| | | | |
| | | | |
| | | | |
| | | | |
|R| | | | | | |
----------------------
|||||||||||||||||||||||||||||| | |
| |
| |
| |
| |
|R| | |S| | | |
...
...
...
||||||||
|| | |R| | | |
|| | |S|R|S| |
| | |R|S|S|S|S|
|R|S|R|S|R|R|R|
----------------------
*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.
*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:
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:
*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.
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:
*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.
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:
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:
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:
O cabeçalho da função é
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
*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 é
Insira x1, y1, x2, y2, x3, y3: 2,5 2 5 -1,0 4,0 2,0
A área do triângulo é 2,25
v2 (x2, y2)
v1 (x1, y1)
v3 (x3, y3)
v4 (x4, y4)
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
*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.
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:
*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:
*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 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).
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.
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.
Campos de dados:
raio é
Funções:
Getaria
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
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
raio = 1;
}
Construtores
//Constrói um objeto circular
Círculo(duplo novoRaio) {
raio = novoRaio;
}
} };
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
dataFieldName: dataFieldType
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
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
+setVolume(novoNívelVolume: int): void Define um novo nível de volume para esta TV.
A Listagem 9.2 fornece um programa que define a classe TV e utiliza a classe TV para criar dois objetos.
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
68 tv1.turnOn(); ligar
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 }
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 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
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
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:
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.
construir objetos Um construtor é invocado quando um objeto é criado. A sintaxe para criar um objeto usando o construtor no-arg é
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;
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
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
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;
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);
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()
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.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
(a) (b)
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
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 .
6
7 //Constrói um objeto círculo padrão
8 Círculo(); construtor sem argumento
Machine Translated by Google
Cuidado
não omita ponto e vírgula É um erro comum omitir o ponto e vírgula (;) no final da definição da classe.
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.
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
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 é
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
(a) (b)
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.
3 4 classe Círculo
5{
6 público:
Machine Translated by Google
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 _
#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
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
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:
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
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
returnType getPropertyName()
Se returnType for bool, por convenção a função get deverá ser definida da seguinte forma: acessador bool
bool éPropertyName()
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
Figura 9.6 A classe Circle encapsula propriedades do círculo e fornece get/set e outras funções.
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.
4 5 int principal()
6{
construir objeto Círculo círculo1;
construir objeto 7 8 Círculo círculo2(5,0);
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?
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:
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.
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
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
n x é declarado como um campo de dados na classe Foo , mas também é definido como uma variável local na função p()
Dica
Conforme demonstrado no exemplo, é fácil cometer erros. Para evitar confusão, não declare o mesmo nome de variável duas vezes
9.18 Os campos de dados e funções podem ser colocados em qualquer ordem em uma classe? ÿVerificação de ponto
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
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
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).
+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.
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.
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
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.
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
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
Resumo do capítulo
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.
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.
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.
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).
returnType getPropertyName()
19. Se o returnType for bool, a função get deve ser definida como
bool éPropertyName().
Questionário
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;
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 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.
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:
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:
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:
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.8 (A classe Date ) Projete uma classe chamada Date. A classe contém:
A classe contém:
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.
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).
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.
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.
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:
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:
corda
+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 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
corda
+assign(s[]: char): string +assign(s: Atribui uma matriz de caracteres ou uma string s a 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 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
+ apagar (índice: int, n: int): string Remove n caracteres desta string começando no índice de posição.
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:
corda
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
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.
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).
Por exemplo:
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.
Por exemplo:
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, í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.
Por exemplo:
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.
strings2 ("AA");
s2.inserir(1, 4, 'B'); cout <<
s2 << endl; // s2 se torna ABBBBA
Observação
Operador Descrição
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
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 }
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
A função retorna verdadeiro se a string s for alterada e, caso contrário, retorna falso.
A Listagem 10.2 fornece o programa.
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
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).
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:
Suponha que cada expressão seja independente. Quais são os resultados das seguintes
expressões?
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)
Suponha que cada expressão seja independente. Quais são os resultados das seguintes
expressões?
10.5 Suponha que você tenha entrado em Nova York ao executar os programas a seguir. O que
seria a impressão?
cout << cidade << endl; cout << cidade << endl;
retornar 0; retornar 0;
} }
(a) (b)
10.7 Se a função replaceString for retornada da linha 44 da Listagem 10.2, o valor retornado é
sempre falso?
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
(a) (b)
Figura 10.9 Você pode passar um objeto para uma função (a) por valor ou (b) por referência.
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;
}
};
int principal()
{
Conte minha contagem;
int vezes = 0;
retornar 0;
}
Machine Translated by Google
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,
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.
1 #include <iostream>
2 #include <iomanip>
incluir arquivo de cabeçalho 3 #include "CircleWithPrivateDataFields.h"
4 usando namespace std;
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
--------------------------------------------
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
ÿ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
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);
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.
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
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:
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
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}
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
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
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.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
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.
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
2 3 int Circle::numberOfObjects = 0;
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
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
contagem de classe
{
público:
contagem interna ;
Contagem (int c)
{
contagem = c;
}
Machine Translated by Google
Contar()
{
contagem = 0;
}
contagem de retorno ;
}
contar++;
} };
#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 ;
}
int principal()
{
Um meuObject;
printA(meuObjeto);
retornar 0;
}
Nota de vídeo
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.
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.
IMC
+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.
3 4 #incluir <string>
5 usando namespace std;
6 7 classe IMC
8{
9 público:
Machine Translated by Google
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.
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 }
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.
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.
int principal()
{
string nome("John Doe");
IMC imc1(nome, 18, 145, 70);
nome[0] = 'P';
retornar 0;
}
#include <iostream>
#incluir <string>
usando namespace std;
Machine Translated by Google
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;
}
#include <iostream>
#include <string>
usando namespace std;
classe A
{ público:
A() { };
strings ("abc"); };
int principal()
{
Uma;
cout << as << endl;
retornar 0;
}
#include <iostream>
#include <string>
usando namespace std;
classe A
Machine Translated by Google
{
público:
A() { };
privado:
cordas;
};
int principal()
{
Uma;
cout << as << endl;
retornar 0;
}
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
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
...
}
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
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];
}
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.28 Por que tanto a agregação quanto a composição são chamadas juntas de composição?
Machine Translated by Google
Dados3
Dados2 Dados2
Dados1 Dados1 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
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).
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
4 5 int principal()
6{
Pilha StackOfIntegers; criar uma pilha
9876543210
elementos[capacidade - 1]
.
.
.
elementos[tamanho – 1] principal
. capacidade 100
.
tamanho
elementos[1]
elementos[0] fundo
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.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.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
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.
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.
Termos chave
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
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.
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
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:
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:
*10.2 (Caracteres comuns) Escreva uma função que retorne os caracteres comuns de dois
strings usando o seguinte cabeçalho:
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
**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
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:
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
*10.5 (Verificar palíndromo) Escreva a seguinte função para verificar se uma string é um palíndromo, assumindo que as letras não
Escreva um programa de teste que leia uma string e mostre se ela é um palíndromo.
*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:
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:
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.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:
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
... ...
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
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:
Seções 10.8–10.11
10.12 (A classe Stock ) Projete uma classe chamada Stock que contenha o seguinte:
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
4 * seu p nb
***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:
*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
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.
CAPÍTULO
11
Ponteiros e
Memória Dinâmica
Gerenciamento
Objetivos
n Descrever o que é um ponteiro (§11.1).
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.
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:
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
.
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
.
.
principal() 5 { 6 7
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á
em vez de
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
ou
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++:
(*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
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:
px x
Endereço de x Endereço de x 5
pY e
px x px x
pY e pY e
(a) (b)
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:
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
interno x =
30; int* pX =
x; cout << "x é " << x << endl; <<
cout << "x é " pX << endl;
interno x =
30; int* p = &x;
cout << *p << endl;
int y = 40; p
= &y;
cout << *p << endl;
duplo x = 3,5;
duplo* p1 = &x;
duplo y = 4,5;
duplo* p2 = &y;
strings = "ABCD";
string* p = &s;
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:
Uma variável de ponteiro inteiro agora pode ser declarada da seguinte forma:
intPointerp;
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:
Uma boa maneira de evitar esse erro é usar o tipo sinônimo intPointer da seguinte forma:
11.8 Como você define um novo tipo chamado doublePointer que é sinônimo de
ÿVerificação de ponto dobro*?
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:
Neste caso, o ponteiro é uma constante e os dados apontados pelo ponteiro também são uma constante.
Machine Translated by Google
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
interno x;
const int* p = &x;
interno ;
p = &y;
*p = 5;
cout << "O endereço inicial do array é " << list << endl;
11 12 13 14 15 16 11 12 13 14 15 16
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.
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 }
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
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.
3 4 int principal()
5{
lista interna [6] = {11, 12, 13, 14, 15, 16}; declarar matriz
int* p = lista; declarar ponteiro
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];
Na verdade, um nome de array é tratado como um ponteiro constante em C++. ponteiro constante
Machine Translated by Google
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:
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
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]?
caractere*
p; cin >> p;
que é equivalente a
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.
35 36 int principal()
Machine Translated by Google
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}
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
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.
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
int principal()
{
int i = 1, j = 1, k = 1;
f1(i, j, &k);
retornar 0;
}
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
retornar 0;
27 28 29 30 }
654321
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
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;
}
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.
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
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:
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
O tipo de valor de retorno é um ponteiro int . Como você declara um novo array na Etapa 2? Você pode tentar declará-lo
como
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
Agora você pode implementar o código da Listagem 11.8, mas logo descobrirá que ele não está funcionando corretamente.
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 }
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,
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,
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,
Ao criar um array dinâmico, seu tamanho é determinado em tempo de execução. Pode ser um número inteiro
variável. Por exemplo,
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.
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
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:
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;
(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
0013FF60 45
p novo interno;
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.23 Suponha que você crie um array dinâmico e posteriormente precise liberá-lo. Identifique dois erros no
código a seguir:
duplo d = 5,4;
duplo* p1 = d;
duplo d = 5,4;
duplo* p1 = &d;
excluir p1;
duplo* p1;
p1* = 5,4;
Você também pode criar objetos dinamicamente no heap usando a sintaxe mostrada abaixo. criar objeto dinâmico
cria um objeto usando o construtor com argumentos e atribui o endereço do objeto ao ponteiro.
Machine Translated by Google
Por exemplo,
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,
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
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?
(a) (b)
Machine Translated by Google
4 5 int principal()
6{
7 cout << Círculo(5).getArea() << endl;
cout << (new Circle(5))->getArea() << endl;
8 retornar 0;
9 10 11}
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.
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.
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.
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}
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
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?
#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
classe Pessoa
{ público:
Pessoa()
{ númeroDeCrianças = 0; filhos
= nova string[20]; }
{ 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; };
Curso
+Curso(nomedocurso: string&, capacidade: int) Cria um curso com o nome especificado e máximo
número de alunos permitido.
+~Curso() Destruidor
4 5 int principal()
6{
criar curso1 7 Curso curso1(" Estruturas de Dados", 10);
criar curso2 Curso curso2("Sistemas de Banco de Dados", 15);
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}
24 {
alunos[númeroDeAlunos] = nome;
25 númeroDeAlunos++; aumentar o número de alunos
26 27}
36 37}
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 ?
NomeDaClasse(constNomeDaClasse &)
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
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
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.
5 int principal()
6{
criar curso1 Curso curso1("C++", 10);
criar curso2 Curso curso2(curso1);
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.
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
int principal()
{
strings1 ("ABC");
string s2("DEFG");
s1 = string(s2);
cout << s1 << endl;
cout << s2 << endl;
retornar 0;
}
Qual é melhor?
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
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.
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.
4 5 int principal()
6{
Curso curso1(" Programação C++", 10);
Curso curso2(curso1); usar construtor de cópia
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.
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
ÿ
ÿ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
Resumo do capítulo
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).
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
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.
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.
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
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
*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:
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:
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".
**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
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:
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
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.
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.
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.
Esta função retorna as contagens como um array de 10 elementos. Por exemplo, depois de invocar
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:
*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
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
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;
CAPÍTULO
12
Modelos, vetores e
pilhas
Objetivos
n Conhecer a motivação e os benefícios dos modelos (§12.2).
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.
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:
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:
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.
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
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 .
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
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.
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?
#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
retornar 0;
}
É 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
55 retornar 0;
56 57}
Machine Translated by Google
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.
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;
}
StackOfIntegers Pilha<T>
+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)
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}
38 39 int StackOfIntegers::pop()
40 {
41 retornar elementos[--tamanho];
42}
Machine Translated by Google
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.
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.
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
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
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.
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;
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
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:
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:
{
...
privado:
Elementos T[capacidade];
tamanho
interno ; };
Portanto, ao criar uma pilha, você pode especificar a capacidade do array. Por exemplo,
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<nome do tipo
T> class
Foo { público:
Foo();
T f1(valor T);
Tf2(); };
Foo::Foo() {
...
}
Pilha T::f1(valor T) {
...
}
Pilha T::f2() {
...
};
Pilha de pilha;
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
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 é
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;
vetor<elementType> nomedovetor;
Por exemplo,
vetor<int> intVetor;
vetor<string> stringVetor;
vetor<elementType>
+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.
+at(índice: int): elementType const Retorna o elemento no índice especificado neste vetor.
+swap(v2: vetor): vazio Troca o conteúdo deste vetor pelo vetor especificado.
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,
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.
5 6 int principal()
7{
criar um vetor vetor<int> intVetor;
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
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
12.15 Por que o código em (a) está errado, mas o código em (b) está correto?
(a) (b)
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
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:
// 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.
9 classificações de string [13] = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", classificações
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:
Observação
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
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:
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
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:
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.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.
(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
132 }
133
134 // Processa um operador: Pegue um operador do operadorStack e 135 // aplique-o nos operandos
do operandoStack 136 void processAnOperator(
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.
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).
Termos chave
Resumo do capítulo
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
Questionário
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
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
**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:
*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
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:
**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.
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:
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
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:
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
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.
+Transação(tipo: char, Construa uma transação com a data, tipo, saldo e data especificados.
valor: duplo, saldo: e descrição.
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
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
**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:
Seu programa deve implementar e usar a seguinte função para encontrar a submatriz
quadrada máxima:
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
**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:
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:
**12.32 (Remover duplicatas) Escreva uma função que remova os elementos duplicados de um vetor usando
o seguinte cabeçalho:
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:
*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:
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:
***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 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 usar as funções seekp e seekg para mover os ponteiros de arquivo para
acesso aleatório a arquivos (§13.8).
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.
4 5 int principal()
6{
Machine Translated by Google
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;
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 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
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++.
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
John T Smith 90
arquivo
pontuações.txt
Eric K Jones 85
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.
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");
retornar 0;
6 7 8 9 10 }
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.
12 3 . 3 12 .9 85 .6
Caractere em branco
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
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 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.
4 5 int principal()
6{
7 //Abre um arquivo
criar objeto de entrada 8 ifstream input("pontuação.txt");
9
Machine Translated by Google
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 }
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?
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.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 ?
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
20
21 cout << "Concluído" << endl;
22
23 retornar 0;
24 }
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
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 é
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
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
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:
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 é
5 6 int principal()
7{
Machine Translated by Google
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
36
37 cout << "\nCópia concluída" << endl;
38
39 retornar 0;
40 41 42 }
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:
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
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.
Modo Descrição
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.
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:
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
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 }
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
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.
Pedaço
Descrição
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.
Função Descrição
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
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 }
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 }
Status da transmissão:
eof(): 1
falha(): 0
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
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.
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
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.
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 }
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.
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}
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 .
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
4 5 int principal()
6{
7 const int TAMANHO = 5; //Tamanho da matriz tamanho de matriz constante
8
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 }
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).
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
Estudante
+Aluno(nome: string, mi: char, sobrenome: string, pontuação: int) Constrói um aluno com nome especificado, mi, sobrenome
nome e pontuação
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).
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
2 #incluir <cstring>
20 {
21 strcpy(primeiroNome, s.c_str());
22}
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.
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.
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.
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.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?
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
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);
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.
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.
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
67 cout << "A posição atual é " << binaryio.tellg() << endl;
68
69 binárioio.close();
70
71 retornar 0;
72 }
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.
Muitas vezes você precisa atualizar o conteúdo do arquivo. Você pode abrir um arquivo para entrada e saída.
Por exemplo,
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
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
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.
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.
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.
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
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
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:
*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:
*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
*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:
*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:
*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
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.
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
CAPÍTULO
14
Operador
Sobrecarga
Objetivos
n Compreender a sobrecarga do operador e seus benefícios (§14.1).
n Para sobrecarregar os operadores relacionais (<, <=, ==, !=, >=, >)
e operadores aritméticos (+, -, *, /) (§14.3).
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).
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;
Racional
+igual(segundoRacional: Retorna verdadeiro se este número racional for igual ao número especificado.
Racional): bool const
-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.
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.
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;
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
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
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
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.
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
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:
O seguinte código
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
+ - * / % ^ & ~ ! =
|
< > += -= *= /= %= ^= &= |= <<
>> >>= <<= == != <= >= && ++ --
||
->* , -> [] () novo excluir
?: . .* ::
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.
O seguinte código
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.
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:
O seguinte código
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
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:
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
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
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.
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
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
Aqui está um exemplo que sobrecarrega o operador - . Adicione o cabeçalho da função na Listagem 14.1,
Rational.h.
Operador racional- ()
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
exibições
r2 é 2/3
r3 é -2/3
Racional Racional::operador-()
{
numerador *= -1;
retorne *isto;
}
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
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:
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?
Esta implementação está correta? Em caso afirmativo, compare-o com a implementação no texto; qual é o
melhor?
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
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.
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.
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
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
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).
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 <<?
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 <<
Isto é equivalente a
Para que isso funcione, cout << r1 deve retornar uma referência de ostream. Portanto, a função << pode ser
implementada da seguinte forma:
Da mesma forma, para sobrecarregar o operador >> , defina o seguinte cabeçalho de função no arquivo de
cabeçalho Rational.h:
O código a seguir fornece um programa de teste que usa os operadores de funções << e >> sobrecarregados .
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?
na classe Racional ?
Machine Translated by Google
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.
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
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.
Desde que o operador + também esteja sobrecarregado (consulte a Seção 14.3), o código a seguir
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?
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
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
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:
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)
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
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
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.
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
53 denominador interno ;
estático int gcd(int n, int d);
54 55};
30 int n1 = abs(n);
31 int n2 = abs(d);
Machine Translated by Google
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 }
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
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 }
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.25 A função mdc na classe Rational pode ser definida como uma função constante?
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.
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 .
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
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 }
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.
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
Por que o tipo de retorno Curso não é nulo? C++ permite expressões com múltiplas atribuições, como:
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
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.
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 }
Observação
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
Resumo do capítulo
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.
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
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
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
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:
+c
zn+1 = z2 n
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
**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:
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 acessar membros protegidos de uma classe base a partir de classes derivadas
(§15.8).
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.
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.
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
#endif
Objeto Geométrico
Círculo Retângulo
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
informa ao compilador que a classe é derivada da classe base. Portanto, todos os membros
públicos em GeometricObject são herdados em Circle.
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
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
informa ao compilador que a classe é derivada da classe base. Portanto, todos os membros públicos em
GeometricObject são herdados em Rectangle.
A Listagem 15.7 fornece um programa de teste que usa estas três classes – GeometricObject,
Círculo e Retângulos.
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
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
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
ÿVerificação de ponto
15.2 Uma classe pode ser derivada de múltiplas classes base em C++?
classe Círculo
{ público:
Círculo( raio duplo) {
raio = raio;
}
double getRadius() {
raio de retorno ;
}
double getArea() {
privado:
raio duplo ; };
classe B: Círculo
{ público:
B( raio duplo, comprimento duplo ) {
raio = raio;
comprimento =
comprimento; }
privado:
comprimento
duplo ; };
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:
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 .
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.
ou
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,
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:
ou
Este último também inicializa o raio do campo de dados no inicializador do construtor. raio é um
campo de dados definido na classe Circle .
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}
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:
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) { } };
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:
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 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
{ público: { público:
Pai() { Pai(int x) { } };
conta <<
"O construtor sem argumento dos pais é invocado";
int principal()
{
int principal() Criança c;
{
Criança c; retornar 0;
}
retornar 0;
}
(a) (b)
#include <iostream>
usando namespace std;
classe Pai
{ público:
Parent()
{ cout << "O construtor sem argumento do pai é invocado" << endl; }
~Parente() {
Criança()
Machine Translated by Google
~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?
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:
A função toString() é redefinida na classe Rectangle (linhas 63–66 na Listagem 15.6) como segue:
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
Observação
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
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
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.
Objeto geométrico
Objeto geométrico
Objeto geométrico
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
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:
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 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:
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.
Objeto geométrico
Objeto geométrico
Objeto geométrico
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
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
3 4 classe Pai
{
5 6 público:
7 vazio f()
8{
{ público: { público:
void printInfo() { void printInfo() {
cout << getInfo() << endl; } cout << getInfo() << endl; }
} }; } };
class Aluno: public Person { public: classe Aluno: public Person { public:
string string
virtual getInfo() { getInfo() {
} }; } };
(a) (b)
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
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 }
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?
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
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:
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.
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
O nome da classe
Objeto Geométrico
abstrata está em itálico
#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
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 .
GeometricObject::GeometricObject() 4 { 5 6 7 } 8 9
cor = "branco";
preenchido = falso;
setColor(cor);
setFilled(preenchido);
GeometricObject::getColor( ) const
Machine Translated by Google
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.
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}
23 24 #endif
2 #include "DerivedCircleFromAbstractGeometricObject.h"
3 #include "DerivedRectangleFromAbstractGeometricObject.h"
4 #include <iostream>
5 usando namespace std;
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
classe A
{
público:
vazio virtual f() = 0;
};
int principal()
{
Uma;
retornar 0;
}
Machine Translated by Google
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() {
classe C: público B
{ público:
vazio virtual m() = 0; };
classe D: público C
{ público:
virtual void m() { cout
} };
void p(A& a)
{ af();
}
int principal()
{
Dd;
p(d);
dm();
retornar 0;
}
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;
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;
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,
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
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:
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.
2 #include "DerivedCircleFromAbstractGeometricObject.h"
3 #include "DerivedRectangleFromAbstractGeometricObject.h"
4 #include <iostream>
5 usando namespace std;
29 30 int principal()
31 {
32 Círculo círculo(5);
Machine Translated by Google
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
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
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:
#include <iostream>
usando namespace std;
classe Pai { };
void m()
{ cout <<
} };
int principal()
{
Pai* p = new Filho();
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
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();
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();?
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
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.
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
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
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 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).
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.
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 }
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.
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
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 }
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
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
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
pegar (int) {
int principal()
{
cout << "Insira a temperatura: "; temperatura
dupla ; cin >> temperatura;
tentar
{
cout << "Início do bloco try ..." << endl;
temperatura é " << temperatura << endl; cout << "Está muito quente" <<
endl; }
retornar 0;
}
cout << "A temperatura é " << temperatura << endl; cout << "Está muito
quente" << endl;
}
pegar (duplo) {
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
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
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).
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
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
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.
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
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}
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 .
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
15 {
invocar ex.what() 16 cout << "Exceção: " << ex.what() << endl;
17 }
18
19 retornar 0;
20 }
Insira o raio: 5
A área é 78,5397
A execução continua...
Machine Translated by Google
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;
(runtime_error& ex) {
retornar 0;
}
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
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
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.
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
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
79 {
80 return (lado1 < lado2 + lado3) && (lado2 < lado1 + lado3) &&
81} (lado3 < lado1 + lado2);
82
83};
84
85 #endif
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
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 ?
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.
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).
4 5 int principal()
6{
7 tentar
Machine Translated by Google
{
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 }
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
tentar tentar
{ {
... ...
... ...
... ...
} }
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
} pegar (...) {
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;
declaração4;
Machine Translated by Google
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) {
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.
Pilha de chamadas
função3
função2 função2
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.
tentar
{
declarações;
}
catch (TheException& ex) {
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.
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 }
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.
}
pegar (Exceção2 e ex2) {
}
pegar (Exceção3 e ex3) {
declaração4;
Machine Translated by Google
lançar;
}
declaração5;
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:
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
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?
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).
(a) (b)
ÿVerificação de ponto
16.12 Quais exceções devem ser usadas em um programa?
Machine Translated by Google
Termos chave
Resumo do capítulo
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
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.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
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 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).
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”.
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
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.
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 }
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
fatorial(4)
retornar 1
5 Registro de ativação
para fatorial (0) n:
0
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
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
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) {
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.
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.6 Quantas vezes a função fatorial da Listagem 17.1 é invocada para factorial(6)?
Machine Translated by Google
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
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.
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
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)
6: retornar mentira(0)
retornar 1 retornar 0
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
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
se (n > 0) { se (n > 0) {
retornar 0; retornar 0;
} }
#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
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
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:
if (vezes >= 1) {
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:
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.
obtenha uma substring de s usando s.substr(1, s.size() - 2) e invoque recursivamente isPalindrome com a
nova string (linha 12).
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.
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}
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 }
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.
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.
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
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 }
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.
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 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
1 5
A B C A B C
2 6
A B C A B C
3 7
A B C A B C
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.
02
discos n-1 discos n-1
.
. .
. .
.
A B C A B C
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.
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.
11 outro
12 {
13 moveDisks(n - 1, fromTower, auxTower, toTower); recursão
14 << n << << << toTower
cout << "Mover disco " " de " fromTower <<
" "
15 << endl; para
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
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')
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.
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 Listagem 17.8 é um programa que encontra uma solução para o problema das Oito Rainhas.
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
| | | | | |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
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
recursiva final porque há operações pendentes após o retorno de uma chamada de função.
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
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:
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.
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.
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
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
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)
atualFib = f0 + f1;
f0 = f1;
f1 = atualFib;
}
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
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
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:
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:
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:
**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.
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.
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
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 é:
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 é:
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 é:
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 é:
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
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++.
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++.
e &&
e_eq &=
bit e &
chegando |
~
completo
não !
não_eq !=
ou ||
ou_eq |=
livre ^
xor_eq
^=
673
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
0 1 2 3 4 5 6 7 8 9
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
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.
() chamada de função
[] subscrito de matriz
++ incremento pós-fixado
--
decremento pós-fixado
--
decremento de prefixo
+ unário mais
-
unário menos
*
multiplicação da esquerda para direita
/ divisão
% módulo
677
Machine Translated by Google
678 Apêndice C
- subtração
<< saída ou deslocamento bit a bit para a esquerda da esquerda para direita
!= não igual
Booleano OU
|| da esquerda para direita
+= atribuição de adição
-= atribuição de subtração
*= tarefa de multiplicação
/= atribuição de divisão
%= atribuição de módulo
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
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:
679
Machine Translated by Google
680 Apêndice D
10 1* 21 +0* 20 2
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
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
Converter um número decimal d em um número hexadecimal é encontrar os dígitos hexadecimais, h2, decimal para hexadecimal
+ 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
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
1110001101
3 8D
Machine Translated by Google
682 Apêndice D
Tabela D.1 Conversão de Hexadecimal em Binário
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
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.
Todos os operadores bit a bit podem formar operadores de atribuição bit a bit, como ^=, |= <<= e
>>=.
683
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
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
( operador de endereço), endereços variáveis, operador 432 && (e). Benefícios de funções
(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
([]) {} (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
685
Machine Translated by Google
Índice 686
Índice 687
Índice 688
Í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
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
Índice 691
Índice 692
Índice 693
Índice 694
H
Jogo da forca, exercício OOP, 429
G Discos rígidos, 26
Caixa de Galton, exercícios de matriz, 321–322 Dispositivos
Índice 695
Índice 696
Índice 697
Índice 698
Índice 699
Índice 700
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
Índice 702
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-
Í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
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
Índice 706
Índice 707
VirtualFunctionDemoUsingPointer.cpp, 597–598
EM
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
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.
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
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