Tutorial c++

158
Instruções para uso Básico de C + + Estrutura de um programa Variáveis. Os tipos de dado. Constantes Operadors Basic Input / Output Estruturas de Controle Estruturas de Controle Funções (I) Funções (II) Composto Tipos de Dados Matrizes Seqüências de caracteres Ponteiros Memória dinâmica Estruturas de Dados Outros tipos de dados Programação Orientada a Objetos Classes (I) Classes (II) Amizade e herança Polimorfismo Conceitos Avançados Modelos Namespaces Exceções Tipo casting directivas Preprocessor Biblioteca C + + Padrão Entrada / Saída com arquivos

Transcript of Tutorial c++

Page 1: Tutorial c++

Instruções para uso

Básico de C + + Estrutura de um programa Variáveis. Os tipos de dado. Constantes Operadors Basic Input / Output

Estruturas de Controle Estruturas de Controle Funções (I) Funções (II)

Composto Tipos de Dados Matrizes Seqüências de caracteres Ponteiros Memória dinâmica Estruturas de Dados Outros tipos de dados

Programação Orientada a Objetos Classes (I) Classes (II) Amizade e herança Polimorfismo

Conceitos Avançados Modelos Namespaces Exceções Tipo casting directivas Preprocessor

Biblioteca C + + Padrão Entrada / Saída com arquivos

Page 2: Tutorial c++

Instruções para usoPara quem é direcionado esse tutorial?Este tutorial é para aquelas pessoas que querem aprender a programação em C + + e não necessariamente têm qualquer conhecimento prévio de outras linguagens de programação. É claro que nenhum conhecimento de outras linguagens de programação ou qualquer habilidade geral do computador pode ser útil para compreender melhor este tutorial, embora não seja essencial.

Ele também é indicado para aqueles que precisam de uma pequena atualização sobre as novas funcionalidades da linguagem adquiriu a partir da última normas.

Se você estiver familiarizado com a linguagem C, você pode tirar as três primeiras partes deste tutorial como uma revisão de conceitos, uma vez que explicar, principalmente a parte C do C + +. Existem pequenas diferenças na sintaxe C + + para algumas funções C, assim que eu ainda recomendo que você lê-los.

A parte 4 descreve programação orientada a objetos. 

A parte quinto maior parte descreve as novas funcionalidades introduzidas pelo ANSI-C + + padrão. 

Estrutura deste tutorialO tutorial está dividido em seis partes principais, e cada parte é dividida em várias secções que cobrem cada um tópico específico.Você pode acessar diretamente qualquer seção do índice de seção disponíveis na barra do lado esquerdo, ou começar o tutorial a partir de qualquer ponto e seguir os links na parte inferior de cada seção.

Muitas seções incluem exemplos que descrevem a utilização do conhecimento adquirido no capítulo. É recomendado que você leia esses exemplos e ser capaz de compreender cada uma das linhas de código que a constituem, antes de passar para o próximo capítulo.

Uma boa maneira de adquirir experiência com uma linguagem de programação, modificando e acrescentando novas funcionalidades no seu próprio país para os programas de exemplo que você entendeu. Não tenha medo de modificar os exemplos fornecidos com este tutorial, que é a maneira de aprender!

Compatibilidade NotasO ANSI-C + + padrão de aceitação como padrão internacional é relativamente recente. Foi publicado pela primeira vez em Novembro de 1997, e revisto em 2003. No entanto, a linguagem C + + existe desde muito tempo antes (1980). Portanto, há muitos compiladores que não suportam todos os novos recursos incluídos em ANSI-C + +, especialmente aqueles que são liberados antes da publicação da norma.

Este tutorial é pensado para ser seguido com compiladores modernos que suportam, pelo menos em algum grau-ANSI-C + + especificações. Encorajo-vos a conseguir um se o seu não está adaptado. Há muitas opções, tanto comerciais como livres.

CompiladoresOs exemplos incluídos neste tutorial são todos os programas do console . Isso significa que usar o texto para se comunicar com o usuário e para mostrar seus resultados.

Todos os compiladores C + + apoio à elaboração de programas de console. Consulte o manual do usuário do seu compilador para obter mais informação sobre como compilá-los.

Estrutura de um programaProvavelmente a melhor maneira para começar a aprender uma linguagem de programação é escrever um programa. Portanto, fica aqui o nosso primeiro programa:

Page 3: Tutorial c++

12345678910

/ / Meu primeiro programa em C + +

# include <stidio.h>

int main () { printf ("Olá Mundo!"); return 0;}

Olá Mundo!

O primeiro painel (em azul) mostra o código fonte para o nosso primeiro programa. O segundo (em cinza claro) mostra o resultado do programa quando compilado e executado. À esquerda, os números em cinza representam os números de linha - estas não fazem parte do programa, e são mostrados aqui apenas para fins informativos.

A maneira de editar e compilar um programa depende do compilador você está usando. Dependendo se ele tem uma interface de desenvolvimento ou não e sobre a sua versão. Consulte a secção de compiladores e do manual ou de ajuda que acompanha o seu compilador, se você tem dúvidas sobre como compilar um C + + console programa.

O programa anterior é o típico programa que os aprendizes programador escrever pela primeira vez, e seu resultado é a impressão na tela do "Olá Mundo!" frase. É um dos mais simples programas que podem ser escritas em C + +, mas já contém os componentes fundamentais que cada programa C + + tem. Vamos olhar linha por linha o código já escrevemos:

/ / Meu primeiro programa em C + +Esta é uma linha de comentário. Todas as linhas que começam com duas barras ( / / ) são consideradas comentários e não têm qualquer efeito sobre o comportamento do programa. O programador poderá usá-los para incluir explicações ou observações curtas dentro do próprio código fonte. Neste caso, a linha é uma breve descrição do que é o nosso programa.

int main ()

Esta linha corresponde ao início da definição da função principal. A função principal é o ponto por onde todos os programas C + + iniciar a sua execução, independentemente da sua localização dentro do código fonte. Não importa se existem outras funções com outros nomes definidos antes ou depois dela - as instruções contidas dentro da função esta definição será sempre os primeiros a ser executado em qualquer programa C + +. Pelo mesmo motivo, é essencial que todos os programas C + + tem um principal função.

A palavra principal é seguido do código por um par de parênteses ( () ). Isso é porque é uma declaração de função: Em C + +, o que diferencia uma declaração de função de outros tipos de expressões são esses parênteses que seguem o seu nome. Opcionalmente, estes parênteses podem incluir uma lista de parâmetros dentro deles.

Logo após estes parênteses, podemos encontrar o corpo da função principal entre chaves ( {} ). O que está contido dentro destas chaves é que a função faz quando ele é executado.

printf ("Olá Mundo!");

Esta linha é um C + + declaração. A declaração é uma expressão simples ou composto que pode realmente produzir algum efeito. Na verdade, esta afirmação só executa a ação que gera um efeito visível no nosso primeiro programa.

Tribunal de Justiça é o nome do fluxo de saída padrão em C + +, eo significado de toda a declaração é para inserir uma seqüência de caracteres (neste caso, oOlá Mundoseqüência de caracteres) para o fluxo de saída padrão ( Tribunal de Justiça , que normalmente corresponde à tela).

Tribunal de Justiça é declarada no iostream de arquivo padrão dentro do std namespace, é por isso que nós precisamos incluir esse arquivo específico e declarar que iríamos usar este espaço para nome específico no início do nosso código.

Observe que a instrução termina com um caractere ponto e vírgula ( ; ). Este caráter é usado para

Page 4: Tutorial c++

marcar o final da instrução e, na verdade ele deve ser incluído ao final de todas as declarações de expressão em todos os programas C + + (um dos erros de sintaxe mais comum é o fato de esquecer de incluir alguns vírgula depois de uma instrução).

return 0;

A instrução return faz com que a função principal para terminar. retorno pode ser seguido por um código de retorno (no nosso exemplo é seguido pelo código de retorno com um valor de zero). Um código de retorno 0 para a principal função é geralmente interpretado como o programa funcionou como o esperado, sem quaisquer erros durante sua execução. Esta é a forma mais usual para acabar com a C + + console programa.

Você deve ter notado que nem todas as linhas deste programa executar ações quando o código é executado. Havia linhas contendo apenas comentários (aquelas iniciadas por / / ). Havia linhas com as directivas para o compilador do pré-processador (aquelas iniciadas por # ). Então havia as linhas que começou a declaração de uma função (neste caso, a função principal) e, finalmente, as linhas com as declarações (como a inserção no tribunal ), que foram incluídas dentro do bloco delimitado por chaves ( {} ) do a função principal.

O programa foi estruturado em duas linhas diferentes, a fim de ser mais legível, mas em C + +, não temos regras rígidas sobre como separar instruções em linhas diferentes. Por exemplo, em vez de

12345

int main () {

printf ("Olá Mundo!"); return 0;}

Poderíamos ter escrito:

 int main () {

printf ("Olá Mundo!");

return 0;}

Tudo em apenas uma linha e isso teria exatamente o mesmo significado que o código anterior.

Em C + +, a separação entre as afirmações é especificado com um ponto e vírgula final ( ; ) no final de cada um, portanto a separação em diferentes linhas de código não importa a todos para essa finalidade. Podemos escrever muitas declarações por linha ou escrever uma única instrução que leva muitas linhas de código. A divisão do código em linhas diferentes, só serve para tornar mais legíveis e esquema para os seres humanos que podem lê-lo.

Vamos adicionar uma instrução adicional para o nosso primeiro programa:

123456789101112

/ / Meu segundo programa em C + +

# include <stdio.h>

int main () { printf ("Olá Mundo!"); printf ("Sou um programa C + +"); return 0;}

Olá Mundo! Sou um programa C + +

Neste caso, foi realizada em duas inserções de corte em duas declarações diferentes. Mais uma vez, a

Page 5: Tutorial c++

separação em diferentes linhas de código foi feito apenas para dar maior legibilidade ao programa, desde principal poderia ter sido perfeitamente válido definido desta maneira:

 int main () {printf ("Olá Mundo!"); printf ("Sou um programa C + +");return 0;}

Fomos também a liberdade de dividir o código em linhas mais se considerou mais conveniente: 

12345678

int main () { printf("Olá Mundo!"); printf ("Sou um programa C + +");

return 0;}

E o resultado teria sido de novo exatamente como nos exemplos anteriores.

directivas Preprocessor (aquelas que começam por # ) estão fora dessa regra geral, uma vez que não são declarações. São linhas lido e processado pelo pré-processador e não produzem qualquer código por si só. directivas Preprocessor devem ser especificados em sua própria linha e não tem que terminar com um ponto e vírgula ( ; ).

Comentários

Os comentários são partes do código-fonte desconsiderados pelo compilador. Eles simplesmente não fazer nada. O seu objectivo é apenas permitir que o programador para inserir notas ou descrições dentro do código fonte.

C + + suporta duas maneiras para inserir comentários: 

12/ / Linha de comentário / * comentário de bloco /

O primeiro deles, conhecido como linha de comentário, as devoluções de tudo, desde onde o par de barras ( / / ) encontra-se até ao final da mesma linha. O segundo, conhecido como bloco de comentário, descarta tudo entre os / * personagens ea primeira aparição do * / personagens, com a possibilidade de incluir mais de uma linha.Estamos indo para adicionar comentários ao nosso segundo programa: 

123456789101112

/ * Meu segundo programa em C + + com mais comentários * /

# include <stdio.h>

int main () { printf ("Olá Mundo!"); / / impressões Olá Mundo! / printf ("Eu sou um C + + programa ") ; / / imprime Sou um programa C + + return 0;}

Olá Mundo! Sou um programa C + +

Se você incluir comentários dentro do código fonte dos seus programas sem usar o comentário combinações de caracteres / / , / * e * / , o compilador irá tomá-los como se fossem C + + expressões, mais provável a causar uma ou várias mensagens de erro quando você compilar .

Page 6: Tutorial c++

Biblioteca CBiblioteca C LínguaA biblioteca C + + inclui as mesmas definições como a biblioteca em linguagem C organizados na mesma estrutura de arquivos de cabeçalho, com as seguintes diferenças:

Cada arquivo de cabeçalho tem o mesmo nome que a versão da linguagem C, mas com um " c "prefixo e sem extensão. Por exemplo, o C + + equivalente para a linguagem de cabeçalho do arquivo C <stdlib.h> é <cstdlib> .

Cada elemento da biblioteca é definido dentro do std namespace.

No entanto, para compatibilidade com C, o tradicional cabeçalho nomes name.h (como stdlib.h ) também está disponível com as mesmas definições dentro do namespace global. Nos exemplos apresentados nesta referência, esta versão é usada para que os exemplos são totalmente compatíveis com C, embora seu uso seja substituído em C + +.

Existem também algumas alterações pontuais no C + + aplicação:

wchar_t é um tipo fundamental em C + + e, portanto, não aparece como um tipo definido nos arquivos de cabeçalho correspondentes onde ele aparece em C. O mesmo se aplica a várias macros introduzida pela alteração de 1 a ISO C no cabeçalho do arquivo <iso646.h> , que são palavras-chave em C + +.

As funções seguintes alterações nas suas declarações relacionadas ao constness de seus parâmetros: strchr , strpbrk ,strrchr , strstr , memchr .

As funções atexit , saída e abortar , definido no < cstdlib > Tem adições ao seu comportamento em C + +.

versões sobrecarregadas de algumas funções são fornecidas com outros tipos de parâmetros ea mesma semântica, comofloat e long double versões das funções no CMATH arquivo de cabeçalho, ou a longo versões para abs e div .

Nota sobre as versõesC + + inclui a biblioteca C, como descrito pela norma ISO 1990 e sua emenda n º 1 (ISO / IEC 9899:1990 e ISO / IEC 9899:1990 / DAM 1). Algumas introduções feitas na norma ISO 1999 não são compatíveis com o padrão C + +.

Cabeçalhoscassert (assert.h) Biblioteca C Diagnostics (cabeçalho)

cctype (ctype.h) Personagem funções de manipulação (cabeçalho)

cerrno (errno.h) C erros (cabeçalho)

cfloat (float.h) Características dos tipos de ponto flutuante (cabeçalho)

ciso646 (iso646.h) ISO 646 grafias alternativas operador (cabeçalho)

climits (limits.h) Tamanhos dos tipos integrais (cabeçalho)

clocale (locale.h) C biblioteca de localização (cabeçalho)

CMATH (math.h) Biblioteca C numéricos (cabeçalho)

csetjmp (setjmp.h) local não pula (cabeçalho)

csignal (signal.h) biblioteca C para lidar com sinais (cabeçalho)

cstdarg (stdarg.h) tratamento de argumentos variável (cabeçalho)

cstddef (stddef.h) Standard definições C (cabeçalho)

cstdio (stdio.h) biblioteca C para executar / Output operações de entrada (cabeçalho)

cstdlib (stdlib.h) Standard C Library General Utilitários (cabeçalho)

Page 7: Tutorial c++

cstring (string.h) C Strings (cabeçalho)

ctime C Time Library (cabeçalho)

Alteração 1 para ISO-C 90 adicionada dois conectores adicionais: cwchar e cwctype .

printffunção

<cstdio>

printf int (const char * format, ...);Imprimir dados formatados para stdoutEscreve para a saída padrão (stdout) uma seqüência de dados formatados como o formato argumento especifica. Após o formato de parâmetro, a função espera que pelo menos tantos argumentos adicionais, conforme especificado no formato .

Parâmetrosformato

String que contém o texto a ser escrito para stdout .Ele pode, opcionalmente conter incorporado marcas de formatação que são substituídos pelos valores especificados no argumento subseqüente (s) e formatado conforme solicitado.O número de argumentos a seguir o formato de parâmetros deve ser pelo menos tanto quanto o número de marcas de formatação.As marcas de formatação de seguir este protótipo:

% [Flags] [width] [. Precisão] [duração] especificador Onde especificador é o mais importante e define o tipo ea interpretação do valor do argumento corespondente:

especificador

Saída Exemplo

c Caracteres um

d ou i Inteiro decimal 392

e A notação científica (mantise / expoente) usando e caráter3.9265e 2

E A notação científica (mantise / expoente) usando E personagem3.9265E 2

f Decimais de ponto flutuante 392.65

g Use o menor de % e ou % f 392.65

G Use o menor de E% ou % f 392.65

o Unsigned octal 610

s Cadeia de caracteres amostra

u Inteiro sem sinal decimal 7235

x Inteiro sem sinal hexadecimal 7fa

X Inteiro sem sinal hexadecimal (maiúsculas) 7fa

p endereço do ponteiro B800:

Page 8: Tutorial c++

0000

nNada impresso. O argumento deve ser um ponteiro para um assinado int , onde o número de caracteres escritos até então é armazenada.

% Uma %, seguido por outro % personagem vai escrever % para stdout . %

A marca também podem conter sinalizadores , largura , precisão. e modificadores de sub-especificadores, que são opcionais e seguir as seguintes especificações:

bandeiras

descrição

-Deixou de justificar dentro da largura do campo determinado; direito justificação é o padrão (ver largura sub-especificador).

+Forças de preceder o resultado com um ou menos sinal de adição ( + ou - ), mesmo para números positivos. Por padrão, somente os números negativos são precedidos por um - sinal.

(Espaço) Se nenhum sinal de que vai ser escrito, um espaço em branco é inserida antes do valor.

#

Usado com o , x ou X especificadores o valor é precedido de 0 , 0x ou 0X , respectivamente, para valores diferentes de zero.Usado com e , E e f , que força a saída escrita para conter um ponto decimal, mesmo se nenhum dígito se seguiria. Por padrão, se nenhum dígito seguir, sem ponto decimal é escrita.Usado com g ou G , o resultado é o mesmo que com e ou E , mas os zeros à direita não são removidos.

0Left-pad o número com zeros ( 0 ) em vez de espaços, onde o preenchimento é especificado (ver largura sub-especificador).

largura descrição

(Número)Número mínimo de caracteres a serem impressos. Se o valor a ser impresso for menor que este número, o resultado será preenchido com espaços em branco. O valor não é truncado, mesmo se o resultado for maior.

*A largura não é especificado no formato de string, mas como um valor inteiro argumento adicional que precede o argumento de que tem que ser formatada.

. Precisão

descrição

. número

Para especificadores inteiro ( d , i , o , u , x , X ): precisão especifica o número mínimo de dígitos para ser escrito. Se o valor a ser escrito é inferior a este número, o resultado será preenchido com zeros à esquerda. O valor não é truncado, mesmo se o resultado for maior. A precisão de 0 significa que nenhum personagem foi escrito para o valor 0 .Por e , E e f especificadores: este é o número de dígitos a ser impresso após o ponto decimal.Para g e G especificadores: Este é o número máximo de dígitos significativos a serem impressas.Para s : este é o número máximo de caracteres a ser impresso. Por padrão, todos os caracteres são impressos até o caractere nulo final é encontrado.Para c tipo: ele não tem efeito.Quando nenhuma precisão for especificada, o padrão é 1 . Se o prazo for especificado sem um valor explícito para a precisão , 0 é assumido.

Page 9: Tutorial c++

.*A precisão não é especificada no formato de string, mas como um valor inteiro argumento adicional que precede o argumento de que tem que ser formatada.

comprimento

descrição

hO argumento é interpretado como um short int ou short int unsigned (só se aplica aos especificadores inteiro: i , d , o , u , x e X ).

lO argumento é interpretado como um long int ou long int unsigned para especificadores de inteiros ( i , d , o , u , x e X ), e como um caractere de largura ou seqüência de caracteres de largura para especificadores c e s .

LO argumento é interpretado como uma dupla muito tempo (só se aplica aos especificadores de ponto flutuante: E , E , f , g e G ).

argumentos adicionais

Dependendo do formato string, a função pode esperar uma seqüência de argumentos adicionais, cada uma contendo um valor a ser inserido em vez de cada % de tag-especificado no formato de parâmetro, se houver. Deve haver o mesmo número de esses argumentos como o número de % -tags que esperam um valor.

Valor de retornoEm caso de sucesso, o número total de caracteres escritos é retornado.Em caso de falha, um número negativo é retornado.

Exemplo123456789101112131415

/ * Exemplo * / printf # include

int main () { printf ( "Personagens:% c% c \ n" , 'a' , 65); printf ( "decimais:% d% d \ n" , 1977, 650000L); printf ( "anterior, com espaços em branco:% 10d \ n" , 1977); printf ( "anterior, com zeros: 010D% \ n" , 1977); printf ( "Alguns radixes diferentes:% d% x% % o # #% x \ n O " , 100, 100, 100, 100, 100); printf ( "flutua: + 0,0% 4.2f \ e E% n%" , 3,1416, 3,1416, 3,1416); printf ( " Largura truque: *% d \ n " , 5, 10); printf ( "% \ n s" , "Uma seqüência de caracteres" );

return 0;}

scanffunção

<cstdio>

int scanf (const char * format, ...);Leia os dados formatados da entrada padrãoLê dados de stdin e armazena-os de acordo com o parâmetro de formato para os locais apontados pelos argumentos adicionais. Os argumentos adicionais devem apontar para objetos alocados já do tipo especificado pela tag de formato correspondente no formato de string.

Parâmetrosformato

Page 10: Tutorial c++

C string que contém um ou mais dos seguintes itens:

caractere espaço em branco: a função vai ler e ignorar todos os caracteres em branco (o que inclui espaços em branco e da nova linha e caracteres de tabulação), que são encontrados antes do não-branco próximo caractere. Isto inclui qualquer quantidade de caracteres em branco, ou nenhuma.

Caracteres não-branco, exceto sinais de porcentagem (%): Qualquer personagem que não é um caractere em branco (newline, espaço em branco ou tabulação) ou parte de um especificador de formato (que começam com um % do personagem) faz a função de ler o próximo caractere a partir de stdin , compará-lo com este espaço em branco sem carácter e se coincidir, ele é descartado ea função continua com o próximo caracter do formato . Se o personagem não corresponder, a função falhar, retornando e deixando os caracteres subseqüentes de stdin não lidas.

Os especificadores de formato: uma seqüência formada por um sinal de porcentagem inicial ( % ) indica um especificador de formato, que é usado para especificar o tipo e formato dos dados a serem recuperados a partir de stdin e armazenados nos locais apontados pelos argumentos adicionais. Um especificador de formato segue este protótipo:

largura %[*][] [modificadores tipo]

onde:

*Uma partida asterisco opcional indica que os dados estão a ser recuperados a partir de stdin , mas ignorado, ou seja, não é armazenada no argumento correspondente.

largura Especifica o número máximo de caracteres a ser lido na operação de leitura atual

modificadores

Especifica um tamanho diferente de int (no caso de d , i e n ), unsigned int (no caso de o , u e x ) ou float (no caso de e , f e g ) para os dados apontados pela argumento adicional correspondente:h: short int (para d , i e n ), ou short int unsigned (para o , u e x )l: long int (para d , i e n ), ou unsigned long int (para o , u e x ), ou dupla (para e , f e g )L: long double (por e , f e g )

tipoUm personagem especificando o tipo de dados a serem lidos e como é esperado para ser lido. Veja a tabela seguinte.

Tipo de especificadores scanf:

tipo Qualificação de EntradaTipo de

argumento

c

caráter único: Lê o próximo caractere. Se uma largura diferente de 1 for especificado, a função lê largura caracteres e armazena-os nos locais sucessivos do array passado como argumento. Nenhum caractere nulo é acrescentado no final.

Char *

dDecimal inteiro: Número opcionalmente precedido de um + ou - sinal.

int *

e , E , f , g , G

ponto flutuante: número decimal contendo um ponto decimal, opcionalmente precedido de um + ou - assinar e, opcionalmente, pela folowed e ou E caractere e um número decimal. Dois exemplos de entradas válidas são -732,103 e 7.12e4

float *

o Octal inteiro . int *

s

Seqüência de caracteres . Isto irá ler os caracteres subseqüentes até um espaço em branco é encontrado (caracteres em branco são considerados de nova linha, em branco e tabulação).

Char *

Page 11: Tutorial c++

u Inteiro sem sinal decimal.unsigned int *

x , X Hexadecimal inteiro. int *

argumentos adicionais

A função espera uma seqüência de referências como argumentos adicionais, cada um apontando para um objeto do tipo especificado por suas respectivas % de tag-no formato de cadeia, na mesma ordem.Para cada especificador de formato no formato de string que recupera dados, um argumento adicional deve ser especificado.Estes argumentos são esperados ser referências (ponteiros): se você deseja armazenar o resultado de um fscanf operação em uma variável regular você deve preceder o seu identificador com o operador de referência , ou seja, um sinal de E comercial ( & ), como em:

int n;scanf ("% d", & n);

Valor de retornoEm caso de sucesso, a função retorna o número de itens lidos com êxito. Esta contagem pode igualar o número esperado de leituras ou menos, até zero, se uma falha de correspondência acontece.No caso de uma falha de entrada antes de qualquer dado pode ser lido com sucesso, EOF é retornado.

Exemplo123456789

10111213141516171819

/ * Exemplo scanf / # include

int main () { char str [80]; int i; printf ( "Digite seu nome de família:" ); scanf ( "% s" , str); printf ( "Digite sua idade:" ); scanf ( "% d" , & i);

printf ( "% s Sr.,% d anos n \". , str, i); printf ( "Digite um número hexadecimal:" ) ; scanf ( "% x" , & i); printf ( "Você digitou x% # (% d) n. \" , i, i); return 0;}

Variáveis. Tipos de Dados.A utilidade do "Olá Mundo" programas mostrados na seção anterior é bastante questionável. Tínhamos que escrever várias linhas de código, compilá-los, e depois executar o programa resultante apenas para obter uma simples frase escrita na tela como resultado. Certamente teria sido muito mais rápido digitar a frase de saída por nós mesmos. No entanto, a programação não se limita apenas à impressão simples textos na tela. Para ir um pouco mais adiante e tornar-se capaz de escrever programas que executam tarefas úteis que realmente nos salvar de trabalho é necessário introduzir o conceito de variável .

Vamos pensar que eu peço que você mantenha o número 5 em sua memória mental, e então eu pedir para você memorizar também o número dois ao mesmo tempo. Você tem apenas dois valores diferentes armazenados em sua memória. Agora, se eu pedir para você adicionar 1 para o primeiro número que eu disse, você deve manter os 6 números (que é 5 +1) e 2 em sua memória. Valores que nós poderíamos agora, por exemplo, subtrair e obter 4 como resultado.

O processo todo que você tem feito apenas com sua memória mental é um símile do que um computador pode fazer com duas variáveis. O mesmo processo pode ser expresso em C + + com a seguinte instrução:

Page 12: Tutorial c++

1234

a = 5, b = 2; a = a + 1; resultado = a - b;

Obviamente, este é um exemplo muito simples, já que usamos somente dois valores inteiros pequenos, mas considere que o computador pode armazenar milhões de números como estes, ao mesmo tempo sofisticado e realizar operações matemáticas com elas.

Portanto, podemos definir uma variável como uma parte da memória para armazenar um valor determinado.

Cada variável precisa de um identificador que o distingue dos demais. Por exemplo, no código anterior a identificadores de variáveis foram um , b e resultado , mas poderíamos ter chamado todos os nomes das variáveis que queríamos inventar, enquanto eles estavam identificadores válidos.

IdentificadoresUm identificador válido é uma seqüência de uma ou mais letras, dígitos ou caracteres de sublinhado ( _ ). Nem espaços nem sinais de pontuação ou símbolos podem ser parte de um identificador. Somente letras, números e caracteres de sublinhado simples são válidas. Além disso, os identificadores de variável sempre tem que começar com uma letra. Eles também podem começar com um caractere sublinhado ( _ ), mas em alguns casos, estes podem ser reservados para determinadas palavras-chave compilador ou identificadores externos, bem como identificadores, contendo dois sucessivos caracteres de sublinhado em qualquer lugar. Em nenhum caso, pode começar com um dígito.

Outra regra que você tem que considerar ao inventar a sua própria identificadores é que eles não podem corresponder a qualquer palavra-chave da linguagem C + + é específico, nem os seus compilador, que são palavras-chave reservadas . As palavras-chave reservadas padrão são:

asm, auto, bool, ruptura, caso, a captura, char, classe, const, const_cast, continue, padrão, excluir, fazer dupla, dynamic_cast, outra coisa, enum, explícita, a exportação, extern, falso, float, pois, amigo, goto, se, inline, int, long, mutável, espaço, novo operador privado, protegido reinterpret_cast, registre-se, público, de retorno, a curto, assinado, sizeof, static_cast, estático, struct switch, modelo, isso, jogar, typedef verdade, tente, typeid, typename, união, não assinado, usando, virtual, nula wchar_t, volátil, enquanto

Além disso, representações alternativas para alguns operadores não podem ser usadas como identificadores, uma vez que são palavras reservadas em algumas circunstâncias: 

e, and_eq, BitAnd, BitOr, compl, não, xor not_eq ou, or_eq, xor_eq 

Seu compilador pode também incluir algumas palavras-chave específicas adicionais reservados.

Muito importante: A linguagem C + + é um "case sensitive" idioma. Isso significa que um identificador escrito em letras maiúsculas não é equivalente a outro com o mesmo nome mas escrito em letras pequenas. Assim, por exemplo, o RESULTADO variável não é o mesmo que o resultado da variável ou o resultado da variável. Estes são três identificadores de variáveis diferentes.

Fundamentais tipos de dadosDurante a programação, nós armazenamos as variáveis na memória do nosso computador, mas o computador tem que saber que tipo de dados que deseja armazenar nelas, pois não vai ocupar a mesma quantidade de memória para armazenar um número simples de armazenar uma única letra ou um número grande, e eles não vão ser interpretadas da mesma maneira.

A memória em nossos computadores são organizados em bytes. Um byte é a quantidade mínima de memória que pode gerenciar em C + +. Um byte pode armazenar uma quantidade relativamente pequena de dados: um único caractere ou um inteiro pequeno (geralmente um número inteiro entre 0 e 255). Além disso, o computador pode manipular tipos de dados mais complexos que vêm do agrupamento de vários bytes, como números de longo ou de números não-inteiros.

Em seguida você tem um resumo dos tipos de dados básicos fundamentais em C + +, bem como o intervalo de valores que podem ser representados com cada um:

Nome Descrição * Gama *

Page 13: Tutorial c++

Tamanho

char Caráter ou pequeno inteiro. 1byteassinado: -128 a 127assinado: 0 a 255

int short ( curto )

Curto inteiro. 2 bytesassinado: -32.768-32.767assinado: 0-65.535

int Integer. 4bytesassinado: -2147483648 a 2147483647sem sinal: 0-4294967295

long int ( tempo )

Long inteiro. 4bytesassinado: -2147483648 a 2147483647sem sinal: 0-4294967295

boolBoolean valor. Pode levar um dos dois valores: verdadeiro ou falso.

1byte verdadeiro ou falso

flutuar Número de ponto flutuante. 4bytes + / - 3,4 E + / - 38 (~ 7 dígitos)

dupla Duplo precisão de ponto flutuante. 8bytes+ / - 1.7e + / - 308 (~ 15 dígitos)

long double Long precisão dupla de ponto flutuante. 8bytes+ / - 1.7e + / - 308 (~ 15 dígitos)

wchar_t Wide personagem.2 ou 4 bytes

Um caractere de largura

* Os valores das colunas de tamanho e escala dependem do sistema o programa é compilado. Os valores apresentados acima são os encontrados na maioria dos sistemas de 32 bits. Mas para outros sistemas, a especificação geral é que int tem o tamanho natural sugerido pela arquitetura do sistema (uma "palavra" ) e os quatro tipos inteiros char , short , int e longo cada um deve ser pelo menos tão grande quanto o anterior que, com char sendo sempre um byte de tamanho. O mesmo se aplica aos tipos de ponto flutuante float , dobro e long double , onde cada um deve fornecer pelo menos tanta precisão como o anterior.

Declaração de variáveisA fim de usar uma variável em C + +, devemos primeiro declará-la especificando que tipo de dados que nós queremos que ela seja. A sintaxe para declarar uma nova variável é escrever o especificador do tipo de dados desejado (como bool, int, float ...) seguido por um identificador de variável válido. Por exemplo:

12int a;float varVezes;

Estas são duas declarações válidas de variáveis. O primeiro declara uma variável do tipo int com o identificador de um . O segundo declara uma variável do tipo float com o identificador varVezes . Uma vez declarado, as variáveis de um e varVezes pode ser usado no resto do seu âmbito de aplicação do programa.

Se você estiver indo para declarar mais de uma variável do mesmo tipo, você pode declarar todos eles em uma única instrução, separando seus identificadores com vírgulas. Por exemplo:

 int a, b, c;

Este declara três variáveis ( um , b e c ), todas elas do tipo int , e tem exatamente o mesmo significado como:

1int um;

Page 14: Tutorial c++

23int b,int c;

Os tipos de dados inteiros char , curto , longo e int pode ser assinado ou não assinado, dependendo da faixa de números necessários para ser representado. Assinado tipos podem representar tanto valores positivos e negativos, enquanto que os tipos sem sinal só pode representar valores positivos (e zero). Isso pode ser especificado usando o especificador assinado ou o especificador não assinados antes do nome do tipo. Por exemplo:

12unsigned short int NumberOfSisters;assinado int MyAccountBalance;

Por padrão, se não especificar assinado ou não assinado compilador irá assumir a maioria das configurações do tipo a ser assinado, por isso, em vez de a segunda declaração acima, poderíamos ter escrito:

 int MyAccountBalance;

com exatamente o mesmo significado (com ou sem a palavra-chave assinado )

Uma exceção a essa regra geral é o char tipo, que existe por si só e é considerado um outro tipo de dados fundamental da signed char e unsigned char , o pensamento para armazenar caracteres. Você deve usar uma assinatura ou sem assinatura , se você pretende armazenar valores numéricos em char de tamanho variável.

curto e longo pode ser usado sozinho como especificadores de tipo. Neste caso, eles se referem a seus direitos fundamentais inteiro respectivos tipos: de curto é equivalente a int curto e longo é equivalente a long int . Os dois seguintes declarações de variáveis são equivalentes:

12curto Ano;curto int ano;

Finalmente, assinado e sem assinatura também pode ser usado como especificadores de tipo autônomo, o que significa o mesmo que int assinado e unsigned int respectivamente. Os dois seguintes declarações são equivalentes:

12unsigned NextYear;unsigned int NextYear;

Para ver o que as declarações de variáveis parecer em ação dentro de um programa, vamos ver o C + + código do exemplo, sobre sua memória mental proposto no início desta seção:

12345678910111213

/ Exploração / com variáveis

# include <stdio.h>

int main () { / / declaração de variáveis: int a, b; int resultado;

/ / processo: a = 5, b = 2; a = a + 1; resultado = a - b;

/ / Exibe o resultado: Printf (resultado);

Page 15: Tutorial c++

14151617181920212223

/ encerrar o programa: return 0;}

Não se preocupe se algo mais do que as declarações de variáveis se parece um pouco estranho para você. Você vai ver o resto em detalhes nas próximas seções.

Âmbito das variáveisTodas as variáveis que pretendemos usar em um programa deve ter sido declarada com o especificador de tipo em um ponto anterior no código, como fizemos no código anterior ao início do corpo da função principal, quando declarou que um , b , eo resultado foram do tipo int .

Uma variável pode ser tanto de âmbito global ou local. Uma variável global é uma variável declarada no corpo principal do código-fonte, fora de todas as funções, enquanto uma variável local é declarada dentro do corpo de uma função ou um bloco.

 As variáveis globais podem ser consultados de qualquer lugar do código, mesmo dentro de funções, sempre que após a sua declaração.

O escopo das variáveis locais é limitado ao bloco entre chaves ( {} ), onde eles são declarados. Por exemplo, se eles são declarados no início do corpo de uma função (como na função principal ), seu alcance é entre a sua declaração e ponto final dessa função. No exemplo acima, isto significa que, se existia uma outra função além da principal , as variáveis locais declaradas no principal não poderia ser acessado a partir da função de outro e vice-versa.

Inicialização de variáveisAo declarar uma variável local normal, seu valor é, por padrão indeterminado. Mas você pode querer uma variável para armazenar um valor concreto no mesmo momento em que é declarada. Para fazer isso, você pode inicializar a variável. Há duas maneiras de fazer isso em C + +:

O primeiro, conhecido como c-como inicialização , é feito acrescentando um sinal de igual seguido do valor para o qual a variável será inicializada:

identificador de tipo = initial_value; Por exemplo, se quisermos declarar uma int variável chamada uma inicializado com um valor de 0 no momento em que for declarado, podemos escrever:

Page 16: Tutorial c++

 int a = 0;

A outra maneira de inicializar variáveis, conhecido como construtor de inicialização , é feito colocando o valor inicial entre parênteses ( () ):

tipo de identificador (initial_value); Por exemplo:

 int um (0);

Ambas as maneiras de inicializar as variáveis são válidas e equivalentes em C + +.

1234567891011121314151617

/ / Inicialização de variáveis

# include <stdio.h>

int main () { int a = 5; inicial / / valor = 5 int b (2); inicial / / valor = 2 int resultado; inicial / / valor indeterminado

a = a + 3; resultado = a - b; cout <<resultado;

return 0;}

Introdução às cadeiasVariáveis que podem armazenar valores numéricos que não são mais do que um único personagem são conhecidos como cordas.

A biblioteca C + + idioma oferece suporte para cordas através do padrão string classe. Este não é um tipo fundamental, mas ele se comporta de forma semelhante como tipos fundamentais fizesse no seu uso mais básico.

Uma primeira diferença com tipos de dados fundamental é que, a fim de declarar e utilizar objetos (variáveis) desse tipo é preciso incluir um arquivo de cabeçalho adicionais em nosso código fonte: <string> e ter acesso ao std namespace (que já tínhamos em todos os nossos programas anteriores graças à using namespace declaração).

1234567891011

/ / Primeiro string meu <stdio.h> # include # include <string>

int main () { mystring string = "Esta é uma string" ; printf (mystring); return 0;}

Esta é uma string

Como você pode ver no exemplo anterior, cordas pode ser inicializado com qualquer cadeia de caracteres

Page 17: Tutorial c++

válida literal como variáveis do tipo numérica pode ser inicializado com qualquer literal numérico válido. Ambos os formatos são válidos de inicialização com cordas:

12mystring string = "Esta é uma string" ; mystring string ( "Esta é uma string" );

Strings também podem executar todas as outras operações básicas que tipos de dados fundamentais pode, gosta de ser declarada sem um valor inicial e que está sendo atribuído valores durante a execução:

123456789101112131415

/ / Primeiro string meu <stdio.h> # include # include <string>

int main () { string mystring; mystring = "Este é o conteúdo da string inicial" ; printf (mystring); mystring = "Este é uma seqüência de conteúdos diferentes " ; printf (mystring);

return 0;}

ConstantesConstantes são expressões com um valor fixo.

LiteraisLiterais são os tipos mais óbvios de constantes. Eles são usados para expressar valores específicos dentro do código fonte do programa. Nós já usamos esses anteriormente para dar valores concretos para as variáveis ou para expressar as mensagens que queríamos nossos programas para imprimir, por exemplo, quando escreveu:

 a = 5;

a 5 , neste pedaço de código era uma constante literal .

Constantes literais podem ser divididos em Integer Algarismos , de ponto flutuante numerais , caracteres , seqüências e valores booleanos .

Integer Algarismos

123

1776 707 -273

Eles são constantes numéricas que identificam decimal valores inteiros. Repare que para expressar uma constante numérica não temos que escrever aspas ( " ) nem qualquer caractere especial:. Não há dúvida de que é uma constante sempre que escrever1776 em um programa, estaremos nos referindo ao valor de 1776.

Além de números decimais (aqueles que todos nós estamos acostumados a usar todos os dias), C + + permite o uso de números octais ( base 8 ) e números hexadecimal ( base 16 ), como constantes literais. Se queremos expressar um número octal temos de precedê-lo com um 0 (um zero caracteres). E a fim de expressar um

Page 18: Tutorial c++

número hexadecimal que temos de precedê-lo com os caracteres 0x ( zero , x ). Por exemplo, o literal seguintes constantes são todas equivalentes entre si:

123

75 / / decimal0113 / / octal0x4b / hexadecimal /

Todos estes representam o mesmo número: 75 (setenta e cinco), expressa como um número de base 10, numeral numeral octal e hexadecimal, respectivamente. 

constantes literais, como as variáveis, são considerados como um tipo de dados específico. Por padrão, literais inteiros são do tipoint . No entanto, pode forçar o sistema a ser assinado, anexando o u personagem para ela, ou a longo prazo, acrescentando l :

1234

75 / / int75u / unsigned int75L / longo75ul / unsigned long

Em ambos os casos, o sufixo pode ser especificado usando letras maiúsculas ou minúsculas.

Números de ponto flutuanteEles expressam números com decimais e / ou expoentes. Eles podem incluir um ponto decimal a, um e personagem (que expressa "por dez na altura Xth", onde X é um valor inteiro que segue o e personagem), ou ambos um ponto decimal e um e caráter:

1234

3,14159 / 3,141596.02e23 / 6,02 x 10 ^ 231.6e-19 / 1,6 x 10 ^ -193,0 / 3,0

São quatro números válidos com decimais, expresso em C + +. O primeiro número é PI, o segundo é o número de Avogadro, a terceira é a carga elétrica de um elétron (um número extremamente pequeno), todos eles de aproximação eo último é o número três, expressa em ponto flutuante literal numérico.

O tipo padrão para literais de ponto flutuante é dupla . Se você quer expressar explicitamente um float ou long double numérico literal, você pode usar o f e l sufixos, respectivamente:

123.14159L / long double6.02e23f / / float

Qualquer das letras que podem ser parte de um ponto flutuante numérico constante ( e , f , l ) pode ser escrita utilizando letras maiúsculas ou inferior, quer sem qualquer diferença em seus significados.

Caracteres e strings literaisExistem também as constantes não-numéricos, como: 

1234

'Z' p ' "Olá mundo" "Como vai você?"

As duas primeiras expressões representam constantes único personagem, e os dois seguintes representam os literais cadeia composta por vários personagens. Observe que para representar um único personagem que

Page 19: Tutorial c++

colocá-lo entre aspas simples ( ' ) e expressar uma seqüência de caracteres (que geralmente é composto por mais de um personagem) que incluí-la entre aspas ( " ).

Ao escrever literais tanto caracter e string, é necessário colocar as aspas em torno deles para distingui-los dos identificadores de variáveis possíveis, ou palavras-chave reservadas. Observe a diferença entre essas duas expressões:

12x'x'

x só por si, se referem a uma variável cujo identificador é x , enquanto que 'x' (entre aspas simples) remete para o carácter constante 'x' .

Caracteres e strings literais tem certas peculiaridades, como os códigos de escape. Estes são caracteres especiais que são difíceis ou impossíveis de expressar de outra forma no código-fonte de um programa, como nova linha ( \ n ) ou guia ( \ t ). Todos eles são precedidos por uma barra invertida ( \ ). Aqui você tem uma lista de alguns dos códigos de escape como:

\ Nnewline

\ R retorno de carro

\ Tguia

\ Vguia vertical

\ B retrocesso

\ Falimentação de formulário (alimentação de página)

\ Aalerta (bip)

\ 'aspas simples (»)

\ "aspas duplas (")

\? ponto de interrogação (?)

\ \barra invertida (\)

Por exemplo: 

1234

'\ N' \ t '" "\ t Direita Esquerda" "uma \ \ nDuas nthree"

Além disso, você pode expressar qualquer personagem com o seu código ASCII numérico escrito por uma barra invertida ( \ ) seguido do código ASCII expresso como um octal ( base 8 ) ou hexadecimal ( base 16 ) de número. No primeiro caso (octal) os dígitos deve seguir imediatamente a barra invertida (por exemplo \ 23 ou \ 40 ), no segundo caso (hexadecimal), um xpersonagem deve ser escrito antes dos dígitos si (por exemplo \ x20 ou \ x4A ).

Strings podem estender para mais de uma única linha de código, colocando um sinal de barra invertida ( \ ) no final de cada linha inacabado.

1"String expressa em \ duas linhas"

Page 20: Tutorial c++

2

Você pode também concatenar as constantes da cadeia várias separando-os por um ou vários espaços em branco, tabulações, quebra de linha ou qualquer outro caractere válido em branco: 

 "Esta forma" um "single" "string" "de caracteres"

Finalmente, se quisermos que a string literal a ser feita explicitamente de caracteres de largura ( wchar_t tipo), em vez de caracteres estreitos ( char tipo), que pode preceder a constante com o L prefixo:

 L "Esta é uma seqüência de caracteres de largura"

caracteres de largura são usados principalmente para representar conjuntos de caracteres não-Inglês ou exóticos.

literais booleanosHá apenas dois válido valores booleanos: true e false . Estes podem ser expressos em C + + como valores do tipo bool , utilizando os literais booleanos verdadeiro e falso .

Constantes definidas (# define)Você pode definir seus próprios nomes para constantes que você usa muito frequentemente sem ter que recorrer a variáveis de consumo de memória, simplesmente usando o # definir diretiva de pré-processamento. Seu formato é:

# Define identificador valor Por exemplo: 

12# Define PI 3,14159 # define 'nova linha \' n

Isso define duas constantes novo: PI e NEWLINE . Uma vez que eles são definidos, você pode usá-los no resto do código como se fossem qualquer outro regular constante, por exemplo:

12345678910111213141516171819

/ / Constantes definidas: calcular a circunferência

# include <stidio.h>

# define PI 3,14159 # define 'nova linha \' n

int main () { dupla r = 5,0; / / raio duplo círculo, círculo = 2 * PI * r ; <<círculo judicial; printf (NEWLINE);

return 0;}

31.4159

Na verdade a única coisa que o pré-processador do compilador faz quando encontra # define diretrizes é

Page 21: Tutorial c++

literalmente substituir qualquer ocorrência de seu identificador (no exemplo anterior, estes eram PI e NEWLINE ) pelo código para o qual foram definidos (3,14159 e ' \ n ' , respectivamente).

O # define directiva não é um C + + afirmação, mas uma diretiva para o pré-processamento, pelo que assume toda a linha como a directiva e não necessita de um ponto e vírgula ( ; ) na sua extremidade. Se você acrescentar um caractere de ponto e vírgula ( ; ) no final, ele também será anexado em todas as ocorrências do identificador no corpo do programa que o pré-processador substitui.

Declarada constantes (const)Com a const prefixo que você pode declarar constantes com um tipo específico da mesma forma como você faria com uma variável:

12const int pathwidth = 100;const char tabulação = '\ t' ;

Aqui, pathwidth e tabulação são duas constantes digitado. Eles são tratados como variáveis regulares, exceto que seus valores não podem ser modificadas após a sua definição.

OperadoresUma vez que sabemos da existência de variáveis e constantes, podemos começar a operar com eles. Para esse efeito, C + + integra operadores. Diferente de outras linguagens cujos operadores são palavras-chave, principalmente, os operadores em C + + são feitos principalmente de sinais que não são parte do alfabeto, mas estão disponíveis em todos os teclados. Isso faz com C + + código mais curto e mais internacional, uma vez que se baseia menos em palavras em Inglês, mas requer um pouco de esforço de aprendizagem no início.

Você não tem de memorizar todo o conteúdo desta página. A maioria dos detalhes são fornecidos apenas para servir como uma referência futura no caso de você precisar dele.

Atribuição (=)O operador de atribuição atribui um valor a uma variável.

 a = 5;

Esta afirmação atribui o valor inteiro 5 à variável um . A parte do lado esquerdo do operador de atribuição ( = ) é conhecido como olvalue (valor à esquerda) eo da direita como rvalue (valor correto). O lvalue tem que ser uma variável enquanto o rvalue pode ser uma constante, uma variável, o resultado de uma operação ou qualquer combinação destes.A regra mais importante quando a atribuição é da direita para a esquerda regra: A operação de atribuição ocorre sempre da direita para a esquerda, e nunca outra maneira:

 a = b;

Esta afirmação atribui à variável um (a lvalue) o valor contido na variável b (o rvalue). O valor que foi armazenado até o momento em um não é considerado em todo este funcionamento, e no fato de que o valor é perdido.

Considere também que estamos apenas atribuindo o valor de b para um momento da operação de cessão. Portanto, uma mudança depois de b não afetará o novo valor de uma .

Por exemplo, vamos ter um olhar para o código a seguir - Eu incluí a evolução do conteúdo armazenado em variáveis como os comentários:

12345

/ / Operador de atribuição

# include <stdio.h>

int main () { int a, b; / / a:?, b?

um: 4 b: 7

Page 22: Tutorial c++

67891011121314151617181920

a = 10; / / a:, b:? 10 b = 4; / / a: 10, b: 4 a = b; / / a: 4, b: 4 b = 7; / / a: 4, b: 7

printf ("a:") ; printf (a); printf ("b");printf (b);

return 0;}

Esse código nos dará como resultado que o valor contido no um é quatro e que se inclui na b é 7 . Observe como um não foi afetada pela modificação final de b , apesar de declarada a = b anterior (que é por causa do para a esquerda regra de direito ).

Uma propriedade que C + + tem sobre outras linguagens de programação é que a operação de atribuição pode ser utilizado como rvalue (ou parte de um rvalue) por outra operação de cessão. Por exemplo:

 a = 2 + (b = 5);

é equivalente a: 

12b = 5; a = 2 + b;

que significa: primeiro atribuir 5 para a variável b e, em seguida, atribuir um valor 2 mais o resultado da tarefa anterior de b (ou seja, 5), deixando uma com um valor final de 7 .

A expressão a seguir também é válido em C + +: 

 a = b = c = 5;

Ele atribui cinco ao todo três variáveis: uma , b e c .

Os operadores aritméticos (+, -, *, /,%)As cinco operações aritméticas suportadas pela linguagem C + + são: 

+ Além

- subtração

*multiplicação

/ divisão

% modulo

As operações de adição, subtração, multiplicação e divisão correspondem literalmente com seus respectivos operadores matemáticos. O único que talvez não seja tão acostumados a ver é modulo , cujo operador é o sinal

Page 23: Tutorial c++

de porcentagem ( % ). Módulo é a operação que dá o resto de uma divisão de dois valores. Por exemplo, se escrevermos:

 a = 11% 3;

a variável de um conterá o valor 2 , pois 2 é o resto da divisão 11 entre os três .

Composto de atribuição (+ =, -=, *= / =%, =,> =, <<=, ^ & =, =, | =)

Quando queremos modificar o valor de uma variável realizando uma operação no valor atualmente armazenado nessa variável, podemos usar os operadores de atribuição composta:

expressão é equivalente a

valor + = aumento; valor = valor + aumento;

-= a 5; a = a - 5;

a / = b; a = a / b;

*= preços unidades + 1;* preço = preço (unidades + 1);

eo mesmo para todos os outros operadores. Por exemplo:

12345678910111213

/ / Operadores de atribuição composta

# include <stdio.h>

int main () { int a, b = 3; a = b, a + = 2; / / equivalente a = a 2 prinf (a); return 0 ;}

5

Aumentar e diminuir (+ +, -)Encurtando ainda mais algumas expressões, o operador de incremento ( + + ) eo operador de diminuição ( - ), aumentar ou reduzir a um valor armazenado em uma variável. São equivalentes a + = 1 ea -= 1 , respectivamente. Assim:

123

c + +, c + = 1; c = c +1;

são todos equivalentes na sua funcionalidade: três deles aumentará de um valor de c.

No início de compiladores C, as três expressões anteriores, provavelmente produzida código executável diferente dependendo de qual delas foi usada. Atualmente, este tipo de otimização de código é geralmente feito automaticamente pelo compilador, portanto, as três expressões deve produzir exatamente o mesmo código executável.

Uma característica desse operador é que pode ser usado tanto como um prefixo e um sufixo como. Isso significa que podem ser escritos antes da variável (identificador + + a ) ou depois ( a + + ). Embora em expressões simples, como a + + ou + + a ambos têm exatamente o mesmo significado, em outras expressões em que o

Page 24: Tutorial c++

resultado da ou diminuir operação de aumento é avaliada como um valor em uma expressão exterior podem ter uma diferença importante no seu significado: o caso que o operador aumento é utilizado como um prefixo (+ + a) o valor é aumentado antes o resultado da expressão é avaliada e, portanto, aumentou o valor é considerado na expressão exterior, no caso em que é usado como um sufixo ( a + + ), o valor é armazenado em um aumento depois de ser avaliado e, portanto, o valor armazenado antes da operação de aumento é avaliada a expressão externa. Observe a diferença:

Exemplo 1 Exemplo 2

B = 3;A = + + B;/ / A contém 4, B contém 4

B = 3;A = B + +;/ / A contém 3, B contém 4

No Exemplo 1, B é aumentada antes que seu valor é copiado para um . Enquanto no Exemplo 2, o valor de B é copiado para um e B é aumentada.

Relacionais e operadores de igualdade (==,! =,>, <,> =, <=)

Para avaliar uma comparação entre duas expressões, podemos usar os operadores relacionais e de igualdade. O resultado de uma operação relacional é um valor booleano que só pode ser verdadeiro ou falso, de acordo com seu resultado booleano.

Nós podemos querer comparar duas expressões, por exemplo, para saber se eles são iguais ou se um é maior do que o outro é.Aqui está uma lista dos operadores relacionais e de igualdade que pode ser usado em C + +:

== Igual a

! = Não é igual a

> Superior

< Menos

> =

Maior ou igual a

<= Inferior ou igual a

Aqui há alguns exemplos:

12345

(7 == 5) / / avalia como falso.(5> 4) / / avalia como verdadeiro.(3! = 2) / / avalia como verdadeiro.(6> = 6) / / avalia como verdadeiro.(5 < 5) / / avalia como falso.

Claro que, em vez de usar somente constantes numéricas, podemos usar qualquer expressão válida, incluindo variáveis. Suponha que a = 2 , b = 3 e c = 6 ,

1234

(A == 5) / / avalia como falso uma vez que não é igual a 5.(a * b> = c) / / avalia como verdadeiro uma vez (2 * 3> = 6) é verdadeira.(b 4 a> * c) / / avalia como falso desde (3> 2 * 4 6) é falso.((b = 2) == a) / / avalia como verdadeiro.

Cuidado! O operador = (um sinal de igualdade) não é o mesmo que o operador == (dois sinais de igual), o primeiro é um operador de atribuição (atribui o valor de seu direito à variável à sua esquerda) eo outro ( = = ) é o operador de igualdade que compara se as duas expressões nos dois lados são iguais uns aos outros. Assim,

Page 25: Tutorial c++

na última expressão ((b = 2) == a) , primeiro atribuído o valor 2 para b e então comparou a um , que também armazena o valor 2 , então o resultado da operação é verdade.

Os operadores lógicos (& &, |! |)

O Operador ! é o operador C + + para realizar a operação booleana não, ele tem apenas um operando, localizado a sua direita, ea única coisa que ele faz é valorizar o inverso dela, produzindo false se seu operando é verdade e verdade se seu operando é falso. Basicamente, ele retorna o Boolean valor oposto de avaliar seu operando. Por exemplo:

1234

! (5 == 5) / / avalia a falsa porque a expressão à sua direita (5 == 5) é verdadeira.! (6 <= 4) / / avalia como verdadeiro porque (6 <= 4) seria falsa.! verdade / / resulta em falso! false / / avalia como verdadeiro.

Os operadores lógicos & & e | | são usados na avaliação de duas expressões para obter um único resultado relacional. O operador & & corresponde à operação lógica booleana AND. Esta operação resulta verdadeiro se ambos os seus dois operandos são verdadeiros e falso caso contrário. O quadro a seguir mostra o resultado do operador & & avaliação da expressão a & & b :

Operador & & ou AND

um ba & &

b

verdade

verdade

verdade

verdade

falsa falsa

falsaverdade

falsa

falsa falsa falsa

O operador | | corresponde com a lógica de operação lógica OR. Esta operação resulta verdadeiro se qualquer um de seus dois operandos é verdadeiro, sendo falsa somente quando ambos os operandos são falsos si. Aqui estão os resultados possíveis de a | | b :

| | OPERADOR ou OR

um b a | | b

verdade

verdade

verdade

verdade

falsaverdade

falsaverdade

verdade

falsa falsa falsa

Por exemplo:

12((5 == 5) & & (3> 6)) / / avalia como falso (true & & false).((5 == 5) | | (3> 6)) / / avalia como verdadeiro (true | | false ).

Page 26: Tutorial c++

Ao utilizar os operadores lógicos, C + + só avalia o que é necessário, da esquerda para a direita para avançar com o relacional resultado combinado, ignorando o resto. Portanto, neste último exemplo ( (5 == 5) | | (3> 6) ), C + + que avaliar primeiro se 5 == 5 é verdadeira, e se assim for, ele nunca iria verificar se 3> 6 é verdadeira ou não . Isso é conhecido como a avaliação de curto-circuito, e funciona assim para estes operadores:

operador

curto-circuito

& & se a expressão do lado esquerdo for falso, o resultado combinado é falsa (lado direito de expressão secundários não avaliados).

| |se a expressão do lado esquerdo é verdade, o resultado combinado é verdadeiro (lado direito de expressão secundários não avaliados).

Isto é mais importante quando a expressão do lado direito tem efeitos colaterais, como alteração valores:

 if ((i <10 )&&(++ n <i)) { /*...*/ }

Isto combinado condicional aumenta a expressão i por um, mas somente se a condição na esquerda da & & é verdade, pois caso contrário, o lado direito de expressão ( + + i <n ) nunca é avaliado.

operador condicional (?)

O operador condicional avalia uma expressão retornando um valor se essa expressão é verdadeira e um diferente se a expressão é avaliada como falsa. Seu formato é:

condição? result1: result2

Se a condição for verdadeira a expressão retornará result1 , se não for ele retornará result2 .

1234

7 == 5? 4: 3 / / retorna 3, pois 7 não é igual a 5.7 == 5 +2? 4: 3 / / retorna 4, já que 7 é igual a 5 2.5> 3? a: b / / retorna o valor de uma, pois 5 é maior que 3.a> b? a: b / / retorna o que for maior, a ou b.

1234567891011121314151617

/ / Operador condicional

# include <stdio.h>

int main () { int a, b, c, a = 2, b = 7 c = (a> b)? a: b; printf (c);

return 0;}

7

Neste exemplo, um era 2 e b foi 7 , então a expressão a ser avaliada ( a> b ) não era verdadeira, assim,

Page 27: Tutorial c++

inicialmente, o valor especificado após o ponto de interrogação foi descartado em favor do segundo valor (o que depois dos dois pontos), que Foi b , com um valor de 7 .

Operador vírgula (,)O operador vírgula ( , ) é usada para separar dois ou mais expressões que são incluídos em uma única expressão é o esperado.Quando o conjunto de expressões tem que ser avaliado por um valor, apenas a expressão mais à direita é considerada.

Por exemplo, o seguinte código:

 a = (b = 3, b +2);

Em primeiro lugar, atribuir o valor 3 para b , e depois atribuir b 2 a variável um . Assim, no final, a variável um deve conter o valor 5 , enquanto variável b seria conter o valor 3 .

Operadores bit a bit (&, |, ^, ~, <<,>>)

Operadores bit a bit modificar as variáveis, considerando os padrões de bits que representam os valores que eles armazenam.

operador

equivalente asm

descrição

& E AND bit a bit

| OU Inclusive OR bit a bit

^ XOR Bit a bit OU Exclusivo

~ NÃO Unários complementar (inversão de bits)

<< SHL Shift Esquerda

>> SHR Shift direito

Operador de conversão explícita de tipoTipo de fundição operadores permitem converter um dado de um determinado tipo para outro. Existem várias maneiras de fazer isso em C + +. O mais simples, que foi herdada da linguagem C, deve preceder a expressão a ser convertido por um novo tipo colocada entre parênteses ( () ):

123

int i;float f = 3,14; i = ( int ) f;

O código anterior converte o número float 3,14 para um valor inteiro ( 3 ), o restante é perdido. Aqui, o operador foi typecasting(int) . Outra maneira de fazer a mesma coisa em C + + está usando a notação funcional: precedendo a expressão a ser convertido pelo tipo e colocando a expressão entre parênteses:

 i = int (f);

Ambas as formas de conversão de tipo são válidas em C + +.

sizeof ()Esse operador aceita um parâmetro, que pode ser tanto um tipo ou variável em si e retorna o tamanho em bytes desse tipo ou objeto:

Page 28: Tutorial c++

 a = sizeof ( char );

Isso vai atribuir o valor de um para um, porque char é um tipo de bytes de tamanho um.O valor retornado por sizeof é uma constante, por isso é sempre determinado antes da execução do programa.

Outros operadoresMais tarde, estes tutoriais, vamos ver mais alguns operadores, como os referentes a ponteiros ou os específicos para programação orientada a objetos. Cada um é tratado em sua respectiva seção.

Precedência dos operadoresAo escrever expressões complexas com vários operandos, podemos ter algumas dúvidas sobre qual operando é avaliado primeiro e que mais tarde. Por exemplo, nesta expressão:

 a = 5 + 7% 2

podemos duvidar se ele realmente significa:

12a = 5 + (7% 2) / com um resultado de 6, oua = (5 + 7)% 2 / / com um resultado de 0

A resposta correta é a primeira das duas expressões, com um resultado de 6 . Há uma ordem estabelecida com a prioridade de cada operador, e não apenas os aritméticos (aqueles cuja preferência vêm da matemática), mas para todos os operadores que podem aparecer em C + +. Do maior para a menor prioridade, a ordem de prioridade é a seguinte:

Nível

Operador Descrição Agrupamento

1 :: escopoDa esquerda para a direita

2() []. -> + + - Dynamic_cast const_cast reinterpret_cast static_cast typeid

postfixDa esquerda para a direita

3

+ + - ~! sizeof nova apagar unário (prefixo)

Da direita para a esquerda

* &indiretamente, e de referência (ponteiros)

+ - operador de sinal unário

4 (Tipo) tipo de vazamentoDa direita para a esquerda

5 .* -> * ponteiro-para-membroDa esquerda para a direita

6 *% / multiplicativoDa esquerda para a direita

7 + - aditivoDa esquerda para a direita

8 <<>> mudançaDa esquerda para a direita

9 <> <=> = relacional Da esquerda para a

Page 29: Tutorial c++

direita

10 ==! = igualdadeDa esquerda para a direita

11 & AND bit a bitDa esquerda para a direita

12 ^ XOR bit a bitDa esquerda para a direita

13 | OR bit a bitDa esquerda para a direita

14 & & E lógicoDa esquerda para a direita

15 | | OR lógicoDa esquerda para a direita

16 ?: condicionalDa direita para a esquerda

17 = *= / =% = + = -=>> = <<= & = ^ = | = atribuiçãoDa direita para a esquerda

18 , vírgulaDa esquerda para a direita

Agrupamento define a ordem de precedência, em que os operadores são avaliados no caso de existirem vários operadores do mesmo nível em uma expressão.

Todos esses níveis de precedência dos operadores pode ser manipulado ou se tornar mais legível removendo possíveis ambiguidades usando sinais entre parênteses ( e ) , como neste exemplo:

 a = 5 + 7% 2;

pode ser escrito como:

 a = 5 + (7% 2);

ou

 a = (5 + 7) 2%;

dependendo da operação que deseja executar.

Então, se você quiser escrever expressões complicadas e você não está completamente certo dos níveis de prioridade, sempre inclua parênteses. Ele também fará seu código mais fácil de ler.

Básico Input / OutputAté agora, os programas de exemplo das seções anteriores, desde muito pouca interação com o usuário, se houver algum. Usando o padrão de entrada e saída de biblioteca, seremos capazes de interagir com o usuário através da impressão de mensagens na tela e começar a entrada do usuário a partir do teclado.

C + + usa uma abstração conveniente chamados fluxos para realizar operações de entrada e de saída em meio seqüencial, como a tela ou o teclado. Um stream é um objeto onde um programa pode inserir ou extrair

Page 30: Tutorial c++

caracteres para / a partir dele. Nós realmente não precisa se preocupar com muitas especificações sobre os meios físicos associados com o fluxo - só precisamos de saber que vai aceitar ou fornecer caracteres em seqüência.

O padrão C + + biblioteca inclui o arquivo de cabeçalho stdio.h , onde o padrão de entrada e saída de objetos de fluxo são declaradas.

Saída Padrão (tribunal)Por padrão, a saída padrão do programa é a tela, eo C + + objeto de fluxo definido para acessá-lo é tribunal .

123

Printf ("sentença de Saída"); / / imprime na tela frase saídaPrintf (120); / / imprime o número 120 na telaPrintf (x); / / imprime o conteúdo de x na tela

O printf operador insere os dados que segue para a corrente que o precede. Nos exemplos acima é inserido a constante string frase de saída , a constante numérica 120 e variável x dentro do padrão de saída de fluxo de corte . Observe que a sentença em primeira instrução é colocada entre aspas ( " ) porque é uma constante cadeia de caracteres. Sempre que quisermos usar seqüências de caracteres constantes, devemos colocá-los entre aspas ( " ) para que eles possam ser claramente distinguidos dos nomes de variáveis. Por exemplo, essas duas frases têm resultados muito diferentes:

12printf ("Olá"); / / imprime Oláprintf (Olá); / / imprime o conteúdo da variável Olá

O operador de inserção ( , ) pode ser utilizado mais de uma vez em uma única instrução:

 Printf ("Olá" , "Eu sou" , "C + + uma declaração" );

Esta última afirmação deve imprimir a mensagem Olá, eu sou um C + + declaração na tela. O utilitário de repetir o operador de inserção ( , ) é demonstrada quando queremos imprimir uma combinação de variáveis e constantes ou mais de uma variável:

 printf ("Olá, eu sou",idade,"anos de idade e meu cep é", CEP);

Se assumirmos que a idade variável para conter o valor 24 eo cep variável para conter 90.064 a saída da declaração anterior seria:

 Olá, tenho 24 anos e meu cep é 90064

É importante notar que o tribunal não adiciona uma quebra de linha após a sua saída, a menos que explicitamente indicam que, portanto, as seguintes afirmações:

12printf ("Esta é uma sentença."); printf ("Esta é uma outra sentença.");

será mostrado na tela uma após a outra, sem qualquer quebra de linha entre eles:

Este é um sentence.This é outra frase. 

mesmo que tinha escrito em dois diferentes inserções em tribunal . Para realizar uma quebra de linha na

Page 31: Tutorial c++

saída, devemos explicitamente inserir um caractere de nova linha em quadra . Em C + + um caractere de nova linha pode ser especificado como \ n (n barra invertida):

12printf ("Primeira frase n. \");printf ("Segunda frase frase. nThird \".);

Isso produz o seguinte resultado: 

Primeira frase.Segunda frase.A terceira frase.

Além disso, para adicionar uma nova linha, você também pode usar o endl manipulador. Por exemplo:

12printf ("primeira frase.\n"); printf ("segunda frase.\n");

deveria imprimir: 

Primeira frase.Segunda frase. 

O endl manipulador produz um caractere de nova linha, exatamente como a inserção de '\ n' faz, mas também tem um comportamento adicional quando ele é usado com buffered streams: o buffer é liberado. Enfim, tribunal será um fluxo unbuffered na maioria dos casos, assim que você geralmente pode usar tanto o \ n caractere de escape ea endl manipulador, a fim de especificar uma nova linha, sem qualquer diferença em seu comportamento.

Entrada Padrão (NIC).O dispositivo de entrada padrão é geralmente o teclado. Manipulação da entrada padrão em C + + é feito através da aplicação do operador sobrecarregado de extração ( &) no scanf fluxo. O operador deve ser seguido pela variável que irá armazenar os dados que vai ser extraído do córrego. Por exemplo:

12int idade; scanf ("%d",&idade;

A primeira instrução declara uma variável do tipo int chamado de idade , eo segundo aguarda a entrada de cin (teclado), a fim de guardá-la nesta variável inteira.

scanf só pode processar a entrada do teclado uma vez que o RETURN tecla foi pressionada. Portanto, mesmo se você solicitar um único personagem, a extração de scanf não irá processar a entrada até que o usuário pressiona RETURN após o personagem ter sido introduzida.

Você deve sempre considerar o tipo da variável que você está usando como um recipiente com cin extrações. Se você pedir um inteiro que você irá obter um número inteiro, se você pedir um personagem que você vai ter um personagem e se você pedir uma cadeia de caracteres que você terá uma seqüência de caracteres.

1234567891011

/ I / o exemplo

# include <stdio.h>

int main () { int i;

printf ("Digite um valor inteiro:" ); scanf ("%d",&i);printf ("O valor que você inseriu é",i);printf ("e seu duplo é",i * 2 ,"n \"); return 0;}

Por favor insira um valor inteiro: 702 O valor que você inseriu é 702 e seu duplo é 1404.

Page 32: Tutorial c++

121314

Você também pode usar scanf para solicitar mais de um dado de entrada do usuário: 

 Scanf ("%d%d",&a,&b);

é equivalente a:

12Scanf ("%d",&a); scanf("%d",&b);

Em ambos os casos o usuário deve dar dois dados, um para a variável de um e outro para a variável b que podem ser separados por qualquer separadoras em branco válidos: um espaço, um caractere de tabulação ou uma nova linha.

Estruturas de ControleUm programa é geralmente não se limita a uma seqüência linear de instruções. Durante o seu processo, podem se bifurcar, repetir o código, ou tomar decisões. Para esse efeito, C + + oferece estruturas de controle que servem para especificar o que deve ser feito pelo nosso programa, quando e sob quais circunstâncias.

Com a introdução de estruturas de controle nós vamos ter que introduzir um novo conceito: o composto de instrução ou bloco . Um bloco é um grupo de instruções que são separados por ponto e vírgula (;) como todas as instruções C + +, mas agrupados em um bloco entre chaves: {} :

{Comando1; statement2; instrução3;}

A maioria das estruturas de controle que veremos nesta seção requerem um enunciado genérico, como parte de sua sintaxe. A declaração pode ser simples declaração (uma simples instrução termina com um ponto e vírgula) ou um comando composto (várias instruções agrupadas em um bloco), como a descrita acima. No caso em que queremos que a instrução seja uma instrução simples, não precisa incluí-la entre chaves ( {} ). Mas no caso que nós queremos que a instrução seja uma instrução composta, deve ser colocada entre chaves ( {} ), formando um bloco.

estrutura condicional: if e elseO caso palavra-chave é usada para executar uma instrução ou bloco somente se uma condição é satisfeita. Sua forma é:

if (condição) declaração

Onde condição é a expressão que está sendo avaliado. Se esta condição for verdadeira, instrução é executada. Se for falsa, declaração é ignorado (não executado) eo programa continua logo após esta estrutura condicional.Por exemplo, o seguinte fragmento de código imprime x é de 100 somente se o valor armazenado em x variável é realmente 100 :

12if (x == 100printf ("x é 100");

Se nós queremos mais do que uma simples instrução a ser executada no caso de que a condição é verdadeira, podemos especificar um bloco usando chaves {} :

123

if (x == 100) {printf ("x é");printf (x);

Page 33: Tutorial c++

45}

Podemos adicionalmente especificar o que quer que aconteça, se a condição não for cumprida, usando a palavra-chave mais . Sua forma usada em conjunto com se é:

if (condição) else statement1 statement2

Por exemplo:

1234

if (x == 100) printf ("x é 100");

else printf ("x não é 100");

imprime na tela é 100 x se de fato x tem um valor de 100 , mas se não tiver e só se não for, ele imprime x não é 100 .

O caso mais + estruturas podem ser concatenado com o intuito de verificar um intervalo de valores. O exemplo a seguir mostra a sua utilização dizer se o valor atualmente armazenado em x é positivo, negativo ou nenhum deles (ou seja, zero):

123456

if (x> 0) printf ("x é positivo");

else if (x <0) printf ("x é negativo");

else printf ("x é 0");

Lembre-se que no caso em que queremos mais do que uma simples instrução a ser executada, devemos agrupá-los em um bloco, colocando-os entre chaves {} .

estruturas de iteração (loops)

Loops tem como finalidade a repetir uma afirmação de um certo número de vezes ou enquanto uma condição for cumprida.

O laço whileSeu formato é:

while (expressão) instrução

e sua funcionalidade é simplesmente repetir while a condição estabelecida na expressão é verdadeira.Por exemplo, vamos fazer um programa para diminuir a contagem usando um ciclo while:

123456789101112

/ / Contagem regressiva personalizada usando enquanto

# include <stdio.h>

int main () { int n; printf ("Digite o número maio de partida"); scanf ("%d",n);

while (n> 0) { printf (n,",",- n; } printf ("FIRE \ n!)" ;

Digite o número inicial> 8 8, 7, 6, 5, 4, 3, 2, 1, FOGO!

Page 34: Tutorial c++

13141516171819

return 0;}

Ao iniciar o programa o usuário é solicitado a inserir um número inicial para a contagem regressiva. Então, o tempo de loop começa, se o valor digitado pelo usuário preenche a condição n> 0 (que n é maior do que zero) o bloco que segue a condição será executado e repetido enquanto a condição ( n> 0 ), continua sendo verdade.

O processo inteiro do programa anterior pode ser interpretado de acordo com o seguinte roteiro (a partir de principal):

1. Usuário atribui um valor para n2. A condição enquanto estiver marcada ( n> 0 ). Neste ponto existem duas possibilidades:

* condição for verdadeira: instrução é executada (para o passo 3)* condição é falsa: ignorar instrução e continuar depois (para a etapa 5)

3. instrução de execução:printf (n,",");- N;(Imprime o valor de n na tela e diminui n de 1 )

4. Fim do bloco. Voltar automaticamente para a etapa 25. Continuar o programa certo depois que o bloco: FOGO imprimir! e um programa final.

Ao criar um laço while, devemos sempre considerar que ele tem que terminar em algum momento, portanto, temos de fornecer, dentro do bloco de algum método para forçar a condição se tornar falsa em algum momento, caso contrário, o loop continuará looping para sempre. Neste caso, nós incluímos - n; que diminui o valor da variável que está sendo avaliado na condição ( n ) por um - isso acabará por tornar a condição ( n> 0 ) para tornar-se falso depois de um certo número de loop iterações: para ser mais específico, quando n se torna 0 , que é onde o nosso tempo de ciclo e nosso fim de contagem regressiva.

Claro que isto é uma ação tão simples para o nosso computador que a contagem é feita toda de imediato, sem qualquer atraso prática entre os números.

O laço do-while

Seu formato é:

do while (condição);

Sua funcionalidade é exatamente o mesmo que o loop while, exceto que a condição do loop do-while é avaliada após a execução da declaração, em vez de antes, a concessão, pelo menos, uma execução de declaração , mesmo condição nunca é cumprida. Por exemplo, o programa ecos exemplo a seguir qualquer número que você entra até que você digite 0 .

1234567891011121314

/ / Echoer número

# include <stdio.h>

int main () { unsigned tempo n; do { printf("Digite o número 0 para terminar:"); scanf("%d",&n); printf ("Você digitou: ",n ,"\ n"); }while (! n = 0); return 0;}

Digite o número 0 para terminar: 12345 Você digitou: 12345 Digite o número 0 para terminar: 160277 Você digitou: 160277 Digite o número 0 para terminar: 0 Você digitou: 0

Page 35: Tutorial c++

15

O do-while normalmente é utilizado quando a condição que tem de determinar o fim do ciclo é determinada dentro da instrução loop em si, como no caso anterior, onde a entrada do usuário dentro do bloco é o que é usado para determinar se o loop tem que acabar. De fato, se você não inserir o valor 0 no exemplo anterior, você pode ser solicitado para mais números sempre.

O laço for

Seu formato é:

for (inicialização; condição; aumento) declaração;

e sua principal função é repetir a afirmação , enquanto condição for verdade, como o loop while. Mas, além disso, a de loop fornece localizações específicas para conter a inicialização de declaração e um aumento de declaração. Portanto, este ciclo é especialmente concebido para realizar uma ação repetitiva com um contador que é inicializado e aumentou em cada iteração.

Ele funciona da seguinte maneira:

1. inicialização é executada. Geralmente é um valor inicial de configuração para uma variável de contador. Esta é executado apenas uma vez.

2. condição é verificada. Se é verdade o ciclo continua, caso contrário, o laço termina e instrução é ignorada (não executado).

3. instrução é executada. Como de costume, ele pode ser uma única instrução ou um bloco entre chaves {} .

4. enfim, tudo o que é especificado no aumento campo é executado eo ciclo se volta para a etapa 2.

Aqui está um exemplo de contagem regressiva usando um loop for:

1234567891011

/ Contagem regressiva / usando um loop # include <stdio.h>

int main () { for ( int n = 10; n> 0, n -) { printf (tribunal n",");}

printf ("FIRE n! \"); return 0;}

A inicialização e aumentar os campos são opcionais. Eles podem ficar vazios, mas em todos os casos, os sinais de ponto e vírgula entre eles deve ser escrita. Por exemplo, poderíamos escrever: for (; n <10;) se quiséssemos especificar nenhuma inicialização e não aumentar, ou para (; n + +; n <10) , se quiséssemos incluir um campo de aumento, mas não de inicialização (talvez porque a variável já foi inicializada antes).

Opcionalmente, usando o operador vírgula ( , ), podemos especificar mais de uma expressão em qualquer um dos campos incluídos em um de laço, como na inicialização , por exemplo. O operador vírgula ( , ) é um separador de expressão, que serve para separar mais de uma expressão em que apenas um é normalmente esperado.Por exemplo, suponha que queremos iniciar mais de uma variável no nosso loop:

1234

for (n = 0, i = 100; n! = i, n + +, i -) { / / qualquer coisa aqui ...}

Este loop será executado por 50 vezes, se não n ou i são modificadas dentro do loop:

Page 36: Tutorial c++

 n começa com um valor de 0 , e eu com 100 , a condição é n! = i (que n não é igual a i ). Porque n é aumentada de um e eu diminuído por um, a condição do loop se tornará falso depois do ciclo 50, quando ambos n e i será igual a 50 .

Ir declarações.

A instrução break

Usando quebrar podemos deixar um loop mesmo que a condição para o seu fim não é cumprido. Ele pode ser usado para terminar um loop infinito, ou forçá-lo a terminar antes de seu fim natural. Por exemplo, vamos parar a contagem regressiva antes de seu fim natural (talvez por causa de uma falha de verificação do motor?):

12345678910111213141516171819

/ / Exemplo de loop break

# include <stdio.h>

int main () { int n; for (n = 10; n> 0, n -) { printf (n,",");

if (n == 3) { printf ("contagem regressiva abortado!"); break ;} } return 0;}

10, 9, 8, 7, 6, 5, 4, 3, contagem regressiva abortada!

A instrução continue

A continuar a instrução faz com que o programa para passar o resto do laço na iteração atual, como se o fim do bloco de declaração tinha sido alcançado, fazendo com que ele salte para o início da iteração seguinte. Por exemplo, vamos pular o número 5 em nossa contagem regressiva:

12345678910111213

/ / Continue exemplo loop # include <stdio.h>

int main () { for (int n = 10; n> 0, n -) { if (n == 5) continue ; printf (n,""); } printf ("FIRE n! \n") ; return 0;}

10, 9, 8, 7, 6, 4, 3, 2, 1, FOGO!

A instrução gotogoto permite fazer um salto absoluto para outro ponto do programa. Você deve usar esse recurso com cautela, pois sua execução faz com que um salto incondicional ignorando qualquer tipo de limitações de aninhamento.

Page 37: Tutorial c++

O ponto de destino é identificado por uma etiqueta, que depois é usado como um argumento para a instrução goto. Um rótulo é feito de um identificador válido seguido por dois pontos ( : ).

De um modo geral, esta instrução não tem nenhum uso concreto estruturado ou programação orientada a objeto com exceção daqueles que os fãs de programação de baixo nível pode encontrar para ele. Por exemplo, aqui é o nosso laço de contagem regressiva usando goto :

123456789101112131415

/ / Exemplo de loop goto

# include <stdio.h>

int main () { int n = 10; loop: printf (n,",",n -);

if (n> 0) goto loop; printf (tribunal,"FIRE n! \n");

return 0;}

A função de saída

saída é uma função definida na cstdlib biblioteca.

O objectivo da saída é terminar o programa actual, com um código de saída específico. Seu protótipo é:

 void exit ( int exitcode);

O exitcode é utilizado por alguns sistemas operacionais e pode ser utilizado por chamada de programas. Por convenção, um código de saída de 0 significa que o programa terminou normalmente, e qualquer outro valor significa que alguns resultados inesperados ou erro aconteceu.

A estrutura seletiva: switch.A sintaxe da instrução switch é um pouco peculiar. Seu objetivo é verificar possíveis diversos valores constantes de uma expressão. Algo semelhante ao que fizemos no início desta seção, com a concatenação de vários se e else if instruções. Sua forma é o seguinte:

switch (expressão) {case constant1: grupo de instruções um break; caso constant2: grupo de instruções 2 break;. . . default: default grupo de instruções}

Ele funciona da seguinte maneira: switch avalia expressão e verifica se ele é equivalente a constant1 , se for, ele executa um grupo de comandos até encontrar o break comunicado. Quando encontra esse quebra declaração o programa salta para o final do switch estrutura seletiva.

Se a expressão não era igual a constant1 será verificado contra constant2 . Se for igual a este, ele irá executar grupo de comandos 2 até uma palavra-chave break for encontrada, e em seguida, irá saltar para o final do switch estrutura seletiva.

Finalmente, se o valor da expressão não corresponde a nenhuma das constantes especificadas anteriormente (você pode incluir quantos casos rótulos como os valores que pretende verificar), o programa irá executar as declarações depois que o padrão: label, se existir (desde é opcional).

Ambos os fragmentos de código a seguir têm o mesmo comportamento:

switch exemplo equivalente if-else

switch (x) { if (x == 1) {

Page 38: Tutorial c++

case 1: printf ("x é um"); break; case 2: printf ("x é 2"); break; default: printf ("valor de x desconhecido");}

printf ("x é 1");} else if (x == 2) {printf ("x é 2";} else { Printf ("valor de x desconhecido");}

O interruptor de declaração é um pouco peculiar dentro da linguagem C + +, pois ele usa etiquetas em vez de blocos. Isto nos obriga a colocar quebra de declarações depois que o grupo de instruções que queremos ser executado para uma condição específica. Caso contrário, as declarações restante, incluindo os correspondentes a outros rótulos também será executado até o final do switch bloco seletiva ou um quebra afirmação é atingido.

Por exemplo, se não incluir uma quebra de declaração após o primeiro grupo de um caso, o programa não irá saltar automaticamente para o final do switch bloco seletivo e que iria continuar a executar o restante das declarações até que ele atinja tanto uma quebra de instrução ou o fim do switch bloco seletivo. Isso torna desnecessário incluir chaves {} em torno das declarações de cada um dos casos, e pode também ser útil para executar o mesmo bloco de instruções para diferentes valores possíveis para a expressão ser avaliada. Por exemplo:

123456789

switch (x) { caso 1: caso 2: caso 3: printf ("x é 1, 2 ou 3") ; break ; default : printf ("x não é 1, 2 ou 3");}

Observe que a chave só pode ser usado para comparar uma manifestação contra constantes. Portanto, não podemos colocar variáveis como etiquetas (por exemplo, caso n: onde n é uma variável) ou intervalos ( caso (1 .. 3): ) porque eles não são válidos C + + constantes.

Se você precisar verificar a extensão ou os valores que não são constantes, use uma concatenação de se e então se declarações.

Funções (I)Usando funções podemos estruturar nossos programas de forma mais modular, o acesso a todo o potencial que a programação estruturada pode oferecer a nós em C + +.

Uma função é um grupo de instruções que é executada quando é chamada a partir de algum ponto do programa. O seguinte é o seu formato:

nome do tipo (parameter1, parameter2, ...) {instruções}

onde:

tipo é o especificador de tipo de dados dos dados retornados pela função. nome é o identificador pelo qual será possível chamar a função. parâmetros (como quantas forem necessárias): Cada parâmetro consiste em um tipo de dados seguido

por um identificador, como qualquer declaração de variáveis regulares (por exemplo: int x ) e que atua dentro da função como uma variável local normal. Eles permitem passar argumentos para a função quando é chamada. Os diferentes parâmetros são separados por vírgulas.

declarações é a função do corpo. É um bloco de instruções entre chaves {} .

Aqui você tem a função de primeiro exemplo: 

1234

/ / Exemplo de função # include <stdio.h>

int soma ( int a, int b) {

O resultado é 8

Page 39: Tutorial c++

56789101112131415161718

int r, r = a + b; return (r);}

int main () { int z; além z = ( 5,3); printf ("O resultado é",z); return 0;}

A fim de examinar este código, antes de tudo lembrar algo dito no início deste tutorial: um programa C + + sempre começa a sua execução pelo principal função. Então, vamos começar por aí.

Nós podemos ver como a principal função começa por declarar a variável z do tipo int . Logo depois, vemos uma chamada a uma função chamada disso . Prestando atenção, vamos ser capazes de ver a semelhança entre a estrutura da chamada para a função ea declaração da função em si algumas linhas de código acima:

 

Os parâmetros e argumentos têm uma correspondência clara. Entre a principal função que chamamos de além de passar dois valores: 5 e 3 , que correspondem ao int a e int b parâmetros declarados para além da função.

No momento em que a função é chamada de dentro principais , o controle é perdido pelo principal e passados para a função disso . O valor de ambos os argumentos passados na chamada ( 5 e 3 ) são copiados para as variáveis locais int a e int b dentro da função.

Função disso declara outra variável local ( int r ), e por meio da expressão r = a + b , que atribui aos r o resultado de um plus b . Como os parâmetros reais passados para um e b são 5 e 3 , respectivamente, o resultado é 8 .

A seguinte linha de código:

 retorna (r);

Finaliza função disso , e retorna o controle para a função que a chamou, em primeiro lugar (neste caso, a principal ). Nesse momento, o programa segue o seu curso regular a partir do mesmo ponto em que foi interrompido pelo convite para além . Mas, além disso, porque o retorno declaração em função disso especificado um valor: o conteúdo da variável r ( return (r); ), que naquele momento tinha um valor de 8 . Este valor torna-se o valor de avaliação da chamada de função.

 Assim sendo o valor retornado por uma função do valor dado à função de chamar a si mesmo quando é avaliado, a variável z será definido como o valor retornado pela adição (5, 3) , que é 8 . Para explicar de outra forma, você pode imaginar que a chamada para uma função ( de adição (5,3) ) é literalmente substituído pelo valor que ela retorna ( 8 ).

A seguinte linha de código na seção principal é:

Page 40: Tutorial c++

 Printf ("O resultado é",z);

Que, como você já pode esperar, produz a impressão do resultado na tela.

Âmbito das variáveis

O escopo das variáveis declaradas dentro de uma função interna ou qualquer outro bloco é apenas a sua própria função ou o seu próprio bloco e não pode ser utilizado fora deles. Por exemplo, no exemplo anterior, teria sido impossível o uso de variáveis um , b ou r diretamente na função principal , uma vez que foram as variáveis locais a função disso . Além disso, teria sido impossível usar a variável z diretamente em função disso, uma vez que esta era uma variável local à função principal .

 Portanto, o escopo de variáveis locais é limitado ao nível mesmo bloco em que elas são declaradas. No entanto, também temos a possibilidade de declarar variáveis globais, que são visíveis de qualquer ponto do código, dentro e fora de todas as funções. Para declarar variáveis globais, você simplesmente tem que declarar a variável fora de qualquer função ou bloco, ou seja, diretamente no corpo do programa.

E aqui está outro exemplo sobre funções: 

12345678910111213141516171819202122

/ / Exemplo de função # include <stdio.h>

int subtração ( int a, int b) { int r; = ab r; return (r);}

int main () { int x = 5, y = 3; z, z = subtração (7,2); printf ("O primeiro resultado é\n",z,); printf ("O segundo resultado é",subtração,(7,2),"\ n"); printf ("O terceiro resultado é a",subtração,(x, y),"\ n", z = 4 + subtração (x, y)); printf ("O quarto resultado é",z,"\ n"); return 0;}

Page 41: Tutorial c++

Neste caso, criamos uma função chamada de subtração . A única coisa que esta função faz é subtrair os parâmetros passados e retorna o resultado.

No entanto, se examinarmos a função principal , veremos que temos feito várias chamadas para a função de subtração . Nós usamos alguns chamar métodos diferentes para que você veja outras formas ou momentos em que uma função pode ser chamado.

A fim de compreender estes exemplos, deve-se considerar mais uma vez que uma chamada para uma função poderia ser substituído pelo valor que a função de chamar a si mesma que vai voltar. Por exemplo, o primeiro caso (que você já deve saber, porque é o mesmo padrão que temos usado nos exemplos anteriores):

12z = subtração(7-2); printf ("O primeiro resultado é",z);

Se substituirmos a chamada de função com o valor que retorna (ou seja, 5 ), teríamos:

12z = 5; printf ("O primeiro resultado é",z);

Bem como 

 printf ("O segundo resultado é a", subtração=7-2);

tem o mesmo resultado que a chamada anterior, mas neste caso nós fizemos a chamada para a subtração diretamente como um parâmetro de inserção para tribunal . Basta considerar que o resultado é o mesmo como se tivéssemos escrito:

 printf ("O segundo resultado é 5");

uma vez que 5 é o valor retornado por subtração (7,2) .

No caso de:

 printf ("O terceiro resultado é a",subtração,x, y);

A única coisa nova que foi introduzida é que os parâmetros de subtração são variáveis ao invés de constantes. Isso é perfeitamente válido. Neste caso, os valores passados para a função de subtração são os valores de x e y , que são 5 e 3 , respectivamente, dando 2 como resultado.

O quarto caso é mais do mesmo. Basta notar que em vez de:

 z = 4 + subtração (x, y);

poderíamos ter escrito:

 z subtração = (x, y) + 4;

com exatamente o mesmo resultado. Eu tenho mudado lugares assim que você pode ver que o sinal de ponto e vírgula ( ; ) vai no final de toda a declaração. Não necessariamente tem que ir logo após a chamada da

Page 42: Tutorial c++

função. A explicação pode ser uma vez mais que você imaginar que uma função pode ser substituído pelo seu valor devolvido:

12z = 4 + 2, z = 2 + 4;

Funciona com qualquer tipo. O uso de vácuo.

Se você lembra da sintaxe de uma declaração de função:

nome do tipo (argumento1, argumento2 ...) declaração

você vai ver que a declaração começa com um tipo , que é o tipo de função em si (ou seja, o tipo de dado que será retornado pela função com a instrução de retorno). Mas o que se queremos retornar nenhum valor?

Imagine que nós queremos fazer uma função só para mostrar uma mensagem na tela. Nós não precisamos que ele retorne algum valor. Neste caso, deveríamos usar o vazio especificador de tipo para a função. Este é um especificador especial que indica ausência de tipo.

1234567891011121314

/ / Exemplo de função void # include <stdio.h>

void printmessage () { printf ("Eu sou uma função!") ;}

int main () {

printmessage ();

return 0;}

vazio também pode ser usado em função do parâmetro na lista para especificar explicitamente que queremos que a função não dar parâmetros reais quando é chamado. Por exemplo, a função printmessage poderia ter sido declarada como:

1234

void printmessage ( void ) { printf ("Eu sou uma função!");}

Embora seja opcional para especificar void na lista de parâmetros. Em C + +, uma lista de parâmetros pode simplesmente ser deixado em branco, se queremos uma função sem parâmetros.

O que você deve sempre lembrar é que o formato para chamar uma função inclui especificando seu nome e encerrando os respectivos parâmetros entre parênteses. A inexistência de parâmetros não nos exime da obrigação de escrever os parênteses. Por esse motivo, a chamada para printmessage é:

 printmessage ();

Os parênteses indicam claramente que esta é uma chamada para uma função e não o nome de uma variável ou algum outro C + + declaração. A chamada a seguir teria sido incorreto:

Page 43: Tutorial c++

 printmessage;

Funções (II)Argumentos passados por valor e por referência.

Até agora, em todas as funções já vimos, os argumentos passados para as funções foram passados por valor . Isto significa que ao chamar uma função com parâmetros, o que temos passado para a função eram cópias de seus valores, mas nunca as próprias variáveis. Por exemplo, suponha que nós chamamos a nossa primeira funçãodisso usando o seguinte código:

12int x = 5, y = 3, z, z = soma (x, y);

O que fizemos nesse caso foi a chamada para a função addition passando os valores de x e y , ou seja, 5 e 3 respectivamente, mas não as variáveis x e y si.

 

Dessa forma, quando a adição da função é chamado, o valor de suas variáveis locais um e b -se 5 e 3 respectivamente, mas qualquer modificação a qualquer um , ou b dentro da função disso não terá nenhum efeito nos valores de x e y fora , porque as variáveis x e y não foram eles mesmos passados para a função, mas apenas cópias de seus valores no momento em que a função foi chamada.

Mas pode haver alguns casos em que você precisa para manipular de dentro de uma função o valor de uma variável externa. Para esse efeito, podemos usar argumentos passados por referência, como na função de duplicar o seguinte exemplo:

123456789101112131415161718

/ / Passagem de parâmetros por referência # include <stdio.h>

void duplicado ( int & a, int & b, int & c) { a *= 2; b *= 2; c *= 2;}

int main ( ) { int x = 1, y = 3, z = 7; duplicado (x, y, z); printf ("x =",x,", y =",y,", z = ",z);

return 0;}

A primeira coisa que deve chamar a atenção é que na declaração de duplicar o tipo de cada parâmetro foi seguido por um sinal de E comercial ( & ). Esse comercial é o que especifica que os seus argumentos correspondentes devem ser passados por referência em vez de por valor .

Quando uma variável é passada por referência que não estamos passando uma cópia do seu valor, mas nós somos de alguma forma passar a variável-se para a função e qualquer modificação que podemos fazer para as variáveis locais terão um efeito em variáveis sua contraparte passados como argumentos no a chamada para a função.

Page 44: Tutorial c++

 Para explicá-lo de outra forma, podemos associar um , b e c com os argumentos passados na chamada da função ( x , y e z ) e qualquer mudança que fazemos em um dentro da função afetam o valor de x fora dela. Qualquer mudança que fizermos em b afetará y , e mesmo com c e z .

É por isso que o nosso programa de saída, que mostra os valores armazenados em x , y e z após a chamada para duplicar , mostra os valores de todas as três variáveis principais duplicou.

Se, ao declarar a função a seguir:

 void duplicado ( int & a, int & b, int & c)

que havia declarado desta forma:

 void duplicado ( int a, int b, int c)

ou seja, sem os sinais e comercial ( & ), não teríamos passado por referência as variáveis, mas uma cópia de seus valores em vez e, portanto, a saída na tela do nosso programa teria sido os valores de x , y e z , sem ter sido modificado.

Passagem por referência é também uma forma eficaz de permitir que uma função para retornar mais de um valor. Por exemplo, aqui está uma função que retorna o número anterior e próximo do primeiro parâmetro passado.

1234567891011121314151617

/ / Mais de um valor de retornar # include <stdio.h>

void prevnext ( int x, int & prev, int & seguinte) {prev = x-1, lado = x +1;}

int main () { int x = 100, y, z;

prevnext (x, y, z); printf ("Anterior =",y,", próxima =",z);

return 0;}

Anterior = 99, Next = 101

Os valores padrão dos parâmetros.Quando declarar uma função que pode especificar um valor padrão para cada um dos últimos parâmetros. Esse valor será usado se o argumento correspondente é deixado em branco quando chamado para a função. Para fazer isso, nós simplesmente temos que usar o operador de atribuição e um valor para os argumentos na declaração da função. Se um valor para esse parâmetro não é passado, quando a função é chamada, o valor padrão é usado, mas se um valor é especificado o valor padrão é ignorado eo valor passado é usado. Por exemplo:

12345

/ Default / valores em funções # include <stdio.h>

int divide ( int a, int b = 2) { int r, r = a / b;

Page 45: Tutorial c++

6789101112131415161718

return (r);}

int main () { tribunal << divisão (12); printf ("\n"); printf (divisão); return 0;}

Como podemos ver no corpo do programa há duas chamadas para a função dividir . No primeiro:

 divisão (12)

temos apenas especificado um argumento, mas a função de dividir permite até duas. Assim, a função de dividir assumiu que o segundo parâmetro é 2 que é o que temos especificado acontecer se este parâmetro não foi passado (note a declaração de função, que termina com int b = 2 , e não apenas int b ). Portanto, o resultado dessa chamada de função é 6 ( 02/12 ).

Na segunda chamada:

 divisão (20,4)

Existem dois parâmetros, de modo padrão o valor para b ( int b = 2 ) é ignorado e b assume o valor passado como argumento, que é 4 , tornando o resultado retornado igual a cinco ( 20 / 4 ).

Funções sobrecarregadas.Em C + + duas funções diferentes podem ter o mesmo nome se seus tipos de parâmetro ou o número é diferente. Isso significa que você pode dar o mesmo nome para mais de uma função, caso tenha um número diferente de parâmetros ou tipos diferentes de seus parâmetros. Por exemplo:

1234567891011121314151617181920212223

/ / Função sobrecarregada # include <stdio.h>

int operar ( int a, int b) { return (a * b);}

float operar ( float a, float b) { return (a / b);}

int main () { int x = 5, y = 2; float n = 5.0, m = 2,0; operar (x, y); pintf ( "\ n"); operar (, m, n); printf ("\ n");

return 0;}

Page 46: Tutorial c++

24

Neste caso, nós definimos duas funções com o mesmo nome, operar , mas um deles aceita dois parâmetros do tipo int eo outro aceita-los do tipo float . O compilador sabe qual é a chamada em cada caso, examinando os tipos passados como argumentos quando a função é chamada. Se for chamada com dois inteiros como argumentos a que chama a função que tem dois int parâmetros em seu protótipo e, se for chamado com dois carros alegóricos será chamada para aquele que tem dois float parâmetros em seu protótipo.

Na primeira chamada para operar os dois argumentos passados são do tipo int , portanto, a função com o primeiro protótipo é chamado; Essa função retorna o resultado da multiplicação de ambos os parâmetros. Enquanto a segunda chamada passa dois argumentos do tipo float , então a função com o segundo protótipo é chamado. Este tem um comportamento diferente: ele divide um parâmetro pelo outro. Assim, o comportamento de uma chamada para operar depende do tipo dos argumentos passados, porque a função tem sido sobrecarregado .

Observe que uma função não pode ser sobrecarregado apenas pelo seu tipo de retorno. Pelo menos um dos seus parâmetros devem ter um tipo diferente.

funções embutidas.O inline especificador indica ao compilador que a substituição inline é o preferido para a função chamada de mecanismo usual para uma função específica. Isso não muda o comportamento de uma função em si, mas é usada para sugerir ao compilador que o código gerado pelo corpo da função é inserido em cada ponto da função é chamado, em vez de ser inserido apenas uma vez e executar uma chamada normal para ele , que geralmente envolve uma sobrecarga adicional em tempo de funcionamento.

O formato de sua declaração é a seguinte:

nome do tipo embutido (argumentos ...) {instruções ... }

ea chamada é como a chamada para qualquer outra função. Você não tem que incluir a linha de palavras-chave ao chamar a função, somente na sua declaração.

A maioria dos compiladores já otimizar o código para gerar funções inline quando for mais conveniente. Este especificador indica apenas o compilador que a linha é o preferido para essa função.

Recursividade.Recursividade é a propriedade que têm funções a ser chamado por eles mesmos. É útil para muitas tarefas, como a classificação ou calcular o fatorial de números. Por exemplo, para obter o fatorial de um número (n!) a fórmula matemática seriam:

n! * = N * (n-1) (n-2) * (n-3) ... * 1

mais concretamente, 5! (Fatorial de 5) seria:

5! = 5 * 4 * 3 * 2 * 1 = 120

e uma função recursiva para calcular isso em C + + pode ser:

1234567891011121314

/ Calculadora fatorial # include <stdio.h>

long fatorial ( long a) { if (a> 1) return (a * fatorial (a-1)); else return 1;}

int main () { long numero; printf ("Digite um número:"); scanf ("%d",&numero); printf (número,"! =", fatorial (numero);

Page 47: Tutorial c++

151617181920

return 0;}

Observe como na função fatorial incluímos uma chamada para si, mas apenas se o argumento passado foi maior que 1, pois caso contrário a função irá executar uma recursiva loop infinito em que uma vez chegou a 0 que vai continuar se multiplicando por todos os números negativos ( provavelmente provocando um erro de estouro de pilha em tempo de execução).

Esta função tem uma limitação, devido ao tipo de dados que usamos em seu desenho ( longa ) para mais simplicidade. Os resultados obtidos não serão válidos para valores muito maiores do que 10! ou 15!, dependendo do sistema de compilá-lo.

Declarando funções.Até agora, nós temos definidas todas as funções antes da primeira aparição de chamadas a elas no código-fonte. Essas chamadas são geralmente em função principal que temos sempre à esquerda no final do código-fonte. Se você tentar repetir alguns dos exemplos das funções descritas até agora, mas colocando a funçãoprincipal , antes de qualquer uma das outras funções que foram chamadas a partir de dentro dela, você provavelmente irá obter erros de compilação. A razão é que para ser capaz de chamar uma função que deve ter sido declarada em algum ponto antes do código, como temos feito em todos os nossos exemplos.

Mas há uma maneira alternativa para evitar escrever todo o código de uma função antes de poder ser utilizado em principal ou em alguma outra função. Isto pode ser conseguido, declarando apenas um protótipo da função antes de ser usado, em vez de toda a definição. Esta declaração é menor do que toda a definição, mas suficientemente significativo para o compilador para determinar o seu tipo de retorno e os tipos de seus parâmetros.

Sua forma é: 

nome do tipo (argument_type1, argument_type2, ...);

É idêntica a uma definição de função, exceto que ele não inclui o corpo da função em si (ou seja, as declarações de função que nas definições normais são delimitados por chaves {} ) e ao invés de que a declaração final de protótipo com um ponto e vírgula obrigatória ( ; ).

A enumeração parâmetro não precisa incluir os identificadores, mas apenas os especificadores de tipo. A inclusão de um nome para cada parâmetro, na definição da função é opcional na declaração do protótipo. Por exemplo, podemos declarar uma função chamada protofunction com dois int parâmetros com qualquer uma das seguintes declarações:

12int protofunction ( int , primeiro int segundo);int protofunction ( int , int );

De qualquer forma, incluindo um nome para cada variável faz com que o protótipo mais legível.

123456789101112

/ / Declarando funções protótipos # include <iostream> usando namespace std;

vazio estranho ( int a);void mesmo ( int a);

int main () { int i; do {cout << "Digite um número (0 para sair) : " ; cin>> i; ímpares (i);} , enquanto (eu! = 0); retornar 0;}

void ímpar ( int a) {

Digite um número (0 para sair): 9 Número é ímpar. Digite um número (0 para sair): 6 Número é mesmo. Digite um número (0 para sair): 1030 Número é mesmo. Digite um número (0 para sair): 0 Número é mesmo.

Page 48: Tutorial c++

1314151617181920212223242526272829

se 2%)! = 0 <) cout <((a "Número é . impar \ n " ; outra coisa mesmo (a);}

void mesmo ( int a) { se ((a% 2) == 0) cout << "O número é ainda." \ n ; mais estranho (a);}

Este exemplo não é de fato um exemplo de eficiência. Estou certo de que, neste ponto você já pode fazer um programa com o mesmo resultado, mas utilizando apenas metade das linhas de código que foram usados neste exemplo. De qualquer forma este exemplo ilustra como funciona a prototipagem. Além disso, neste exemplo concreto a prototipagem de pelo menos uma das duas funções é necessário para compilar o código sem erros.

As primeiras coisas que nós vemos é a declaração de funções estranhas e até mesmo :

12void ímpar ( int a);void mesmo ( int a);

Isso permite que essas funções devem ser utilizadas antes de serem definidas, por exemplo, no principal , que agora está localizado onde algumas pessoas acham que ele seja um lugar mais lógico para o início de um programa: o início do código-fonte.

Enfim, a razão pela qual este programa precisa de pelo menos uma das funções deve ser declarada antes de ser definido é porque estranho existe uma chamada para mesmo e ainda há uma chamada para estranhos . Se nenhuma das duas funções, tinha sido declarado anteriormente, um erro de compilação que aconteceria, já que tanto estranho não seria visível do mesmo (porque ainda não tenha sido declarada), ou até mesmo não seria visível do estranho (pelo mesmo motivo) .

Tendo o protótipo de todas as funções em conjunto no mesmo local no código-fonte é encontrada prática por alguns programadores, e isso pode ser facilmente alcançado por todos os protótipos, que declara as funções no início de um programa.

MatrizesUma matriz é uma série de elementos do mesmo tipo, colocados em locais contíguos de memória que podem ser individualmente referenciados pela adição de um índice para um identificador exclusivo.

Isso significa que, por exemplo, podemos guardar cinco valores do tipo int em um array sem ter que declarar 5 variáveis diferentes, cada uma com um identificador diferente. Em vez disso, usando uma matriz que pode armazenar 5 valores diferentes do mesmo tipo, int por exemplo, com um identificador exclusivo.

Por exemplo, uma matriz contém 5 valores inteiros do tipo int chamado billy poderia ser representado assim:

 onde cada painel em branco representa um elemento da matriz, que neste caso são valores inteiros do tipo int . Estes elementos são numerados de 0 a 4 vez em matrizes o primeiro índice é sempre 0 , independentemente do seu comprimento.

Como uma variável normal, um array deve ser declarado antes de ser usado. Uma declaração típica para um array em C + + é a seguinte:

Page 49: Tutorial c++

nome do tipo [elementos];

onde tipo é um tipo válido (como int , float ...), nome é um identificador válido e os elementos de campo (que é sempre entre colchetes [] ), especifica quantos desses elementos a matriz contém.

Portanto, a fim de declarar um array chamado Billy como o mostrado na figura acima, é tão simples como:

 int billy [5];

NOTA : Os elementos de campo entre colchetes [] que representa o número de elementos do array vai realizar, deve ser uma constante de valor, já que matrizes são blocos de memória não-dinâmico cujo tamanho deve ser determinado antes da execução. A fim de criar matrizes com comprimento variável de memória dinâmica é necessária, o que é explicado mais tarde nesses tutoriais.

Inicializando arrays.Ao declarar uma matriz regular de âmbito local (dentro de uma função, por exemplo), se não especifique o contrário, os seus elementos não será inicializada com qualquer valor, por padrão, assim que seu conteúdo será indeterminado até que nós guardamos algum valor em si. Os elementos das matrizes globais e estáticos, por outro lado, são automaticamente inicializados com seus valores padrão, que para todos os tipos fundamentais, isto significa que são preenchidos com zeros.

Em ambos os casos, o local eo global, quando se declarar uma matriz, temos a possibilidade de atribuir valores iniciais para cada um dos seus elementos, colocando os valores em chaves {} . Por exemplo:

 int billy [5] = {16, 2, 77, 40, 12071};

Esta declaração teria criado uma matriz assim:

 A quantidade de valores entre chaves {} não deve ser maior que o número de elementos que declarar para a matriz entre colchetes [] . Por exemplo, no exemplo da matriz billy temos declarou que tem 5 elementos e na lista de valores iniciais dentro de chaves {} temos especificadas 5 valores, um para cada elemento.

Quando um arquivo de inicialização de valores está prevista uma matriz, C + + permite a possibilidade de deixar os colchetes vazios [] . Neste caso, o compilador irá assumir uma dimensão para a matriz que corresponde ao número de valores compreendidos entre chaves {} :

 int Billy [] = {16, 2, 77, 40, 12071};

Após esta declaração, a matriz billy seria 5 ints tempo, já temos os 5 valores de inicialização.

Acessando os valores de uma matriz.

Em qualquer ponto de um programa em que uma matriz é visível, podemos acessar o valor de qualquer dos seus elementos individualmente como se fosse uma variável normal, sendo capaz de ler e modificar seu valor. O formato é tão simples como:

nome [índice]

Seguindo os exemplos anteriores em que billy tinha 5 elementos e cada um desses elementos foi do tipo int , o nome que podemos usar para se referir a cada elemento é o seguinte:

 

Page 50: Tutorial c++

Por exemplo, para armazenar o valor 75 no terceiro elemento de billy , nós poderíamos escrever a seguinte instrução:

 billy [2] = 75;

e, por exemplo, para passar o valor do terceiro elemento de billy para uma variável chamada um , poderíamos escrever:

 a = billy [2];

Portanto, a expressão billy [2] é para todos os efeitos, como uma variável do tipo int .

Observe que o terceiro elemento de billy é especificado billy [2] , uma vez que o primeiro é billy [0] , o segundo é billy [1] e, portanto, o terceiro é billy [2] . Por esta mesma razão, o seu último elemento é billy [4] . Portanto, se escrevermos billy [5], estaríamos acessando o sexto elemento de billy e, portanto, superior ao tamanho da matriz.

Em C + + é sintaticamente corretas para exceder o intervalo válido de índices para uma matriz. Isso pode criar problemas, uma vez que o acesso fora do alcance elementos não causar erros de compilação, mas pode causar erros de execução. A razão para isso é permitido será vista mais adiante quando começarmos a usar ponteiros.

Neste ponto é importante ser capaz de distinguir claramente entre os dois usos que colchetes ] [ têm relacionado a matrizes. Eles executam duas tarefas diferentes: uma é para especificar o tamanho das matrizes que tenham sido declarados; ea segunda é especificar os índices para os elementos de matriz de concreto. Não confundir estes dois usos possíveis dos suportes [] com matrizes.

12int billy [5]; / / declaração de uma nova matrizbilly [2] = 75; / / acesso a um elemento da matriz.

Se você ler cuidadosamente, você verá que um especificador de tipo sempre precede uma declaração de variável ou matriz, embora nunca precede um acesso.

Algumas outras operações válidas com matrizes:

1234

billy [0] = a; billy [a] = 75; billy b = [a dois]; billy [billy [a]] = billy [2] + 5;

12345678910111213141516

/ / Exemplo arrays # include <iostream> usando namespace std;

int Billy [] = {16, 2, 77, 40, 12071};int , o resultado n = 0;

int main () { para (n = 0, n < 5, n + +) {result + = billy [n];} cout <<resultado; retornar 0;}

Page 51: Tutorial c++

Arrays multidimensionais

Matrizes multidimensionais podem ser descritas como "matrizes de matrizes". Por exemplo, uma matriz bidimensional pode ser imaginada como uma tabela bidimensional feita de elementos, todos eles de um mesmo tipo de dados uniforme.

 jimmy representa uma matriz bidimensional de 3 por 5 elementos do tipo int . A forma de declarar essa matriz em C + + seria:

 int jimmy [3] [5];

e, por exemplo, a forma para fazer referência ao segundo elemento verticalmente eo quarto horizontalmente em uma expressão seria: 

 jimmy [1] [3]

 (Lembre-se que os índices de matriz sempre começar por zero).

Matrizes multidimensionais não são limitadas a dois índices (ou seja, duas dimensões). Eles podem conter índices que forem necessárias. Mas cuidado! A quantidade de memória necessária para uma matriz aumenta rapidamente com cada dimensão. Por exemplo:

 char século [100] [365] [24] [60] [60];

declara uma matriz com um char elemento para cada segundo de um século, ou seja, mais de 3 mil caracteres. Então, essa declaração iria consumir mais de 3 gigabytes de memória!

Arrays multidimensionais são apenas uma abstração para os programadores, uma vez que podemos obter os mesmos resultados com uma matriz simples simplesmente colocando um fator entre os índices:

12int jimmy [3] [5], / / é equivalente a int jimmy [15]; / / (3 * 5 = 15)

Com a única diferença de que, com matrizes multidimensionais o compilador lembra a profundidade de cada dimensão imaginária para nós. Tomemos como exemplo estes dois pedaços de código, com ambos exactamente o mesmo resultado. Um usa uma matriz bidimensional e outra usa uma matriz simples:

matriz multidimensional matriz pseudo-multidimensional

# Define largura 5 # define altura 3 jimmy int [altura] [LARGURA], int n, m; int main () {for

# Define largura 5 # define altura 3 int jimmy [largura * altura]; n, int m, int main () {for

Page 52: Tutorial c++

(n = 0; altura <n, n + +) para (m = 0; largura <m, m + + ) {jimmy [n] [m] = (n +1) * (m +1);} return 0;}

(n = 0; altura <n, n + +) para (m = 0; largura <m, m + +) {jimmy [largura * n + m] = (n +1) * (m +1);} return 0;}

Nenhum dos dois códigos-fonte acima produz nenhuma saída na tela, mas ambos atribuir valores para o bloco de memória chamado Jimmy, da seguinte forma: 

 Temos usado "constantes definidas" ( # define ) para simplificar a possíveis modificações futuras do programa. Por exemplo, no caso em que decidimos ampliar a matriz para uma altura de 4 em vez dos 3 que poderia ser feito simplesmente mudando a linha:

 # Define altura 3

para:

 # Define altura 4

sem a necessidade de fazer outras modificações para o programa. 

Matrizes como parâmetrosEm algum momento podemos precisar de passar um array para uma função como um parâmetro. Em C + + não é possível passar um bloco inteiro de memória em termos de valor como um parâmetro para uma função, mas estamos autorizados a passar o seu endereço. Na prática, isso tem quase o mesmo efeito e é uma operação muito mais rápida e mais eficiente.

Para aceitar matrizes como parâmetros a única coisa que temos que fazer quando se declara a função é especificar em seus parâmetros o tipo de elemento da matriz, um identificador e um par de suportes vazio [] . Por exemplo, após a função:

 void procedimento ( int arg [])

aceita um parâmetro do tipo "array de int "chamado arg . Para passar para esta função uma matriz declarada como:

 int myarray [40];

seria suficiente para escrever um convite como este:

 procedimento (myarray);

Aqui você tem um exemplo completo: 

12345678

/ / Arrays como parâmetros # include <iostream> usando namespace std;

void PrintArray ( int arg [], int tamanho) { para ( int n = 0; comprimento <n, n + +) cout <<arg [n] << "" ; cout << "\ n" ;}

int main () { int firstArray [] = {5, 10, 15};

Page 53: Tutorial c++

9101112131415161718

int secondArray [] = {2, 4, 6, 8, 10}; PrintArray (firstArray, 3 ); PrintArray (secondArray, 5); retornar 0;}

Como você pode ver, o primeiro parâmetro ( int arg [] ) aceita qualquer matriz cujos elementos são do tipo int , independentemente do seu comprimento. Por essa razão, nós incluímos um segundo parâmetro que diz a função do comprimento de cada array que passamos a ele como seu primeiro parâmetro. Isso permite que o para loop que imprime a matriz para saber o intervalo de iterar na matriz passou sem sair do intervalo.

Em uma declaração de função também é possível incluir matrizes multidimensionais. O formato de um parâmetro de matriz tridimensional é:

 base_type [] [profundidade] [profundidade]

por exemplo, uma função com uma matriz multidimensional como argumento poderia ser: 

 void procedimento ( int myarray [] [3] [4])

Observe que os colchetes primeiro ] [ são deixadas em branco, enquanto os seguintes não são. Isto é assim porque o compilador deve ser capaz de determinar dentro da função que é a profundidade de cada dimensão adicional.

Matrizes, simples ou multidimensionais, passadas como parâmetros da função são uma fonte muito comum de erros para programadores iniciantes. Eu recomendo a leitura do capítulo sobre ponteiros para uma melhor compreensão sobre como os arrays operar.

Seqüências de caracteresComo você já sabe, o padrão C + + Biblioteca implementa uma poderosa cadeia de classe, que é muito útil para segurar e manipular cadeias de caracteres.No entanto, porque as cordas estão em seqüências de caracteres de fato, podemos representá-los também como matrizes simples de char elementos.

Por exemplo, a matriz seguinte:

 char jenny [20];

é uma matriz que pode armazenar até 20 elementos do tipo char . Ele pode ser representado como:

 Portanto, nessa matriz, em teoria, podemos armazenar as seqüências de caracteres até 20 caracteres. Mas também podemos armazenar seqüências mais curtas. Por exemplo, jenny poderia armazenar em algum ponto em um programa a seqüência de "Olá" ou a seqüência de "Feliz Natal" , já que ambos são menores do que 20 caracteres.

Portanto, uma vez que a matriz de caracteres pode armazenar seqüências mais curtas do que o seu comprimento total, um caractere especial é usado para sinalizar o fim da seqüência válida: o caractere nulo , cuja constante literal pode ser escrito como '\ 0' (barra invertida, zero) .

Page 54: Tutorial c++

Nossa matriz de 20 elementos do tipo char , chamada Jenny , pode ser representado armazenar as seqüências de caracteres "Olá" e "Feliz Natal" como:

 Note como depois do conteúdo válido um caractere nulo ( '\ 0' ) foi incluída a fim de indicar o fim da seqüência. Os painéis em cor cinza representam charelementos com valores indeterminados.

Inicialização de seqüências de caracteres terminada por nuloComo arrays de caracteres são vetores comuns que seguem todas as regras mesmo. Por exemplo, se quisermos inicializar um array de caracteres com alguma seqüência pré-determinada de caracteres que podem fazê-lo, tal como qualquer outra matriz:

 char myword [] = { 'H' , 'e' , 'l' , 'l' , 'o' , '\ 0' };

Neste caso, teria declarado um array de 6 elementos do tipo char inicializado com os personagens que formam a palavra "Olá" mais um personagem nulo '\ 0' no final.Mas arrays de char elementos têm um método adicional para inicializar seus valores: usando literais string.

Nas expressões que usamos em alguns exemplos nos capítulos anteriores, constantes que representam seqüências inteiras de personagens já apareceram várias vezes. Estes são especificados envolvendo o texto para tornar-se uma seqüência literal entre aspas (") por exemplo.:

 "O resultado é:"

é uma constante string literal que temos, provavelmente, já utilizado.

citado cordas duplas ( " ) são constantes literais cujo tipo é na verdade um nulo encerrado matriz de caracteres. Então strings literais entre aspas duplas sempre tem um caractere nulo ( '\ 0' ) automaticamente anexado ao final.

Portanto, podemos inicializar a matriz de char elementos chamados myword terminou com um nulo seqüência de caracteres de um destes dois métodos:

12char myword [] = { 'H' , 'e' , 'l' , 'l' , 'o' , '\ 0' };char myword [] = "Olá" ;

Em ambos os casos, a matriz de caracteres myword é declarada com um tamanho de 6 elementos do tipo char : os 5 caracteres que compõem a palavra "Olá"mais um caractere nulo final ( '\ 0' ), que especifica o fim da seqüência e que , no segundo caso, quando se utilizam aspas ( " ) é anexado automaticamente.

Por favor note que estamos falando de inicializar um array de caracteres no momento em que está sendo declarado, e não sobre a atribuição de valores a eles uma vez que já tinha sido declarado. De facto, porque este tipo de nulo matrizes de caracteres terminadas são matrizes regulares que têm as mesmas restrições que nós temos com qualquer outra matriz, de modo que não são capazes de copiar blocos de dados com uma operação de atribuição.

Assumindo mystext é um [] char variáveis, expressões dentro de um código como:

12mystext = "Olá" ; mystext [] = "Olá" ;

Page 55: Tutorial c++

que não seja válido, nem como seria:

 mystext = { 'H' , 'e' , 'l' , 'l' , 'o' , '\ 0' };

A razão para isso pode se tornar mais compreensível uma vez que você sabe um pouco mais sobre ponteiros, desde então ele vai ficar claro que uma matriz é na verdade um ponteiro constante que aponta para um bloco de memória.

Usando null terminada seqüências de caracteres

NULL-Terminated seqüência de caracteres são a maneira natural de tratar strings em C + +, para que possam ser usadas enquanto tal em muitos procedimentos. Na verdade, literais string regulares deste tipo ( char [] ) e também pode ser usado na maioria dos casos.

Por exemplo, cin e corte apoio nulo seqüências denunciado como recipientes válida para seqüências de caracteres, para que eles possam ser usados diretamente para extrair seqüências de caracteres de cin ou para inseri-los em tribunal . Por exemplo:

1234567891011121314

/ / NULL-Terminated seqüência de caracteres # include <iostream> usando namespace std;

int main () { char pergunta [] = "Por favor, digite seu primeiro nome:" ; char saudação [] = "Olá" ; char [seunome 80]; cout <<questão; cin>> seunome; cout <<saudação <seunome <<< "!" ; retornar 0;}

Por favor, digite seu primeiro nome: John Olá, João!

Como você pode ver, temos declarou três arrays de char elementos. Os dois primeiros foram inicializados com constantes seqüência literal, enquanto a terceira foi deixado não inicializado. Em qualquer caso, temos que speficify o tamanho da matriz: nos dois primeiros ( pergunta e saudação ), o tamanho foi definido implicitamente pelo comprimento da constante literal foram inicializados com. Enquanto que para seunome temos explicitamente especificado que tem um tamanho de 80 caracteres.

Finalmente, as seqüências de caracteres armazenados em char arrays podem ser facilmente convertidos em cadeia de objetos usando apenas o operador de atribuição:

123

mystring string;char myntcs [] = "algum texto" ; mystring = myntcs;

PonteirosJá vimos como as variáveis são vistos como células de memória que pode ser acessada usando seus identificadores. Desta forma, não precisa se preocupar com a localização física dos nossos dados dentro da memória, nós simplesmente usamos a sua identificação sempre que queria referir-se à nossa variável.

A memória do seu computador pode ser imaginado como uma sucessão de células de memória, cada um do tamanho mínimo que computadores administrem (um byte). Estas células de memória de um byte são numeradas de forma consecutiva, de modo a, dentro de qualquer bloco de memória, cada célula tem o mesmo número que a anterior mais um.

Page 56: Tutorial c++

Desta forma, cada célula pode ser facilmente localizado na memória, porque ele tem um endereço único e todas as células de memória segue um padrão sucessivas. Por exemplo, se nós estamos olhando para celular 1776, sabemos que vai ser mesmo entre células de 1775 e 1777, exatamente mil células após 776 e exatamente mil células antes de pilha 2776.

Referência do operador (&)Assim que nós declaramos uma variável, a quantidade de memória necessária é atribuído por ele em uma posição específica na memória (o endereço de memória). Nós geralmente não ativamente decidir o local exato da variável dentro do painel de células que nós imaginamos que a memória seja - Felizmente, essa é uma tarefa executada automaticamente pelo sistema operacional durante a execução. No entanto, em alguns casos, podemos estar interessados em conhecer o endereço onde a variável está sendo armazenado durante a execução, a fim de operar com posições relativas a ele.

O endereço que localiza uma variável na memória é o que chamamos de referência para essa variável. Esta referência a uma variável podem ser obtidas pelo anterior o identificador de uma variável com um sinal de E comercial ( & ), conhecido como operador de referência, e que pode ser traduzido literalmente como "endereço". Por exemplo:

 ted = &andy;

Isso seria atribuir a ted o endereço da variável andy , desde quando precede o nome da variável andy com o operador de referência ( & ) não estamos mais falando sobre o conteúdo da variável em si, mas sobre a sua referência (ou seja, o seu endereço na memória).

De agora em diante vamos supor que Andy é colocado durante a execução no endereço de memória 1776 . Esse número ( 1.776 ) é apenas uma suposição arbitrária estamos inventando agora, a fim de ajudar a esclarecer alguns conceitos neste tutorial, mas na realidade, não podemos saber antes de tempo de execução real do valor do endereço de uma variável terá na memória.

Considere o seguinte fragmento de código:

123

andy = 25; fred = andy; ted = &andy;

Os valores contidos em cada variável após a execução do presente, são mostrados no diagrama abaixo: 

 Primeiro, temos atribuído o valor de 25 a andy (uma variável cujo endereço na memória que temos assumido como sendo 1776).

A segunda declaração copiado para fred o conteúdo da variável andy (que é 25). Esta é uma operação de atribuição padrão, como temos feito muitas vezes por isso antes.

Finalmente, as cópias de declaração de terceiros para Ted não o valor contido na andy , mas uma referência a ele (isto é, seu endereço, que assumiram a 1776 ). O motivo é que nesta terceira operação de atribuição temos precedidas do identificador andy com o operador de referência ( & ), de modo que já não estavam se referindo ao valor de Andy, mas a sua referência (o seu endereço na memória).

A variável que armazena a referência a outra variável (como Ted , no exemplo anterior) é o que chamamos de ponteiro . Os ponteiros são um poderoso recurso muito da linguagem C + + que tem muitos usos na programação avançada. Mais à frente, vamos ver como esse tipo de variável é utilizada e declarada.

Page 57: Tutorial c++

Operador de referência (*)

Acabamos de ver que uma variável que armazena uma referência a outra variável é chamado de ponteiro. Ponteiros são disse para "apontar para" a variável cuja referência que eles armazenam.

Usando um ponteiro, podemos acessar diretamente o valor armazenado na variável que ele aponta. Para fazer isso, nós simplesmente temos que precedem o ponteiro identificador com um asterisco (*), que atua como operador dereference e que pode ser traduzido literalmente como "valor apontado por".

Assim, seguindo com os valores do exemplo anterior, se escrevermos: 

 * Beth = ted;

(Que poderíamos ler como: " Beth igual ao valor apontado por ted ") beth levaria o valor 25 , já que Ted é 1776 , eo valor apontado pelo 1776 é 25.

 Você deve diferenciar claramente que a expressão de Ted se refere ao valor 1,776 , enquanto * Ted (com um asterisco * antes do identificador) refere-se ao valor armazenado no endereço 1776 , que neste caso é 25 . Observe a diferença de seleção incluindo ou não o operador dereference (Eu incluí um comentário explicativo de como cada uma dessas duas expressões pode ser lido):

12beth = ted; / beth igual a) ted (1776beth = * ted; / beth igual ao valor apontado por ted (25)

Observe a diferença entre os operadores de referência e dereference:

E é o operador de referência e pode ser lido como "endereço de" * É o operador de dereferência e pode ser lido como "valor apontado por"

Assim, eles têm complementar (ou oposta) significados. Uma variável referenciada com e pode ser dereferenced com * .

Anteriores foram realizados os seguintes operações de atribuição:

12andy = 25; ted = &andy;

Logo após essas duas afirmações, todas das seguintes expressões daria certo como resultado:

1234

andy == 25 & andy == 1776 1776 == * ted ted == 25

Page 58: Tutorial c++

A primeira expressão é bastante clara, considerando que a operação de cessão realizados em andy foi andy = 25 . O segundo usa o operador de referência ( & ), que retorna o endereço da variável andy , que assumiu que para ter um valor de 1776 . O terceiro é um tanto óbvio, já que a segunda expressão era verdade e da operação de cessão realizados em Ted foi ted = & Andy . A quarta expressão usa o operador de dereferência ( * ) que, como acabamos de ver, pode ser lido como "valor apontado por" e, o valor apontado por ted é realmente 25 .

Então, depois de tudo isso, você também pode-se inferir que, enquanto o endereço apontado por ted permanece inalterada a seguinte expressão também será verdadeiro:

 * Ted == andy

Declarando variáveis de tipos de ponteiroDevido à capacidade de um ponteiro para se referem diretamente para o valor que ele aponta, torna-se necessário especificar em sua declaração de que tipo de dados de um ponteiro que vai apontar. Não é a mesma coisa para apontar para um char como para apontar para um int ou float .

A declaração de ponteiros segue este formato:

nome do tipo *; 

onde tipo é o tipo de dados do valor que o ponteiro se destina a apontar. Este tipo não é o tipo do ponteiro em si! mas o tipo de dados que o ponteiro aponta. Por exemplo:

123

int * número;char caracter *;float greatnumber *;

Estes são três declarações de ponteiros. Cada um deles destina-se a apontar para um tipo de dados diferentes, mas na verdade todos eles são ponteiros e todos eles vão ocupar o mesmo espaço na memória (o tamanho da memória de um ponteiro depende da plataforma onde o código está acontecendo para executar). No entanto, os dados a que ponto a não ocupar o mesmo espaço nem são do mesmo tipo: o primeiro aponta para um int , o segundo para um char eo último a uma bóia . Portanto, embora estas três variáveis exemplo são todos os ponteiros que ocupam o mesmo tamanho em memória, é dito que eles têm diferentes tipos: int * , char * e * float , respectivamente, dependendo do tipo eles apontam.

Quero enfatizar que o sinal asterisco ( * ) que usamos quando declarar um ponteiro significa apenas que ele é um ponteiro (que faz parte do seu especificador composto tipo), e não deve ser confundido com o operador dereference que temos visto uma pouco mais cedo, mas que também é escrito com um asterisco ( * ). Eles simplesmente são duas coisas diferentes representados com o mesmo sinal.

Agora dê uma olhada neste código:

12345678910111213141516

/ / Meu primeiro ponteiro # include <iostream> usando namespace std;

int main () { int firstvalue, secondValue; int mypointer *; mypointer = mypointer &firstvalue; * = 10; mypointer = & secondValue; mypointer * = 20; cout << "secondValue é" <<secondValue <<endl; retornar 0;}

Page 59: Tutorial c++

17

Observe que, embora nunca diretamente definir um valor para qualquer firstvalue ou secondValue , tanto acabar com um valor definido indiretamente através do uso de mypointer . Este é o procedimento:

Primeiro, temos atribuído como valor da mypointer uma referência para firstvalue usando o operador de referência ( & ).

A fim de demonstrar que um ponteiro pode tomar vários valores diferentes no mesmo programa que eu tenho repetido o processo com secondValue e que mesmo ponteiro, mypointer .

Aqui está um exemplo um pouco mais elaborada:

1234567891011121314151617181920

/ Mais ponteiros / # include <iostream> usando namespace std;

int main () { int firstvalue = 5, secondValue = 15; int * p1, * p2, p1 = &firstvalue; / / p1 = endereço de firstvalue p2 = & secondValue; / / p2 = endereço do secondValue * p1 = 10; / / valor apontado por p1 = 10 * * p2 = p1; / / valor apontado por p2 = valor apontado por p1 p1 = p2; / / p1 = p2 (valor do ponteiro é copiado) * p1 = 20; / / valor apontado por p1 = 20 cout << "firstvalue é" <<firstvalue <<endl; cout << "secondValue é" <<secondValue <<endl; retornar 0;}

firstvalue secondValue é de 10 a 20

Tenho incluído como um comentário em cada linha como o código pode ser lido: "comercial ( & ) como "endereço de" e um asterisco ( * ) como "valor apontado por".

Observe que há expressões com ponteiros p1 e p2 , ambos com e sem operador dereference ( * ). O significado de uma expressão usando o operador de dereferência ( * ) é muito diferente de um que não: Quando este operador precede o nome do ponteiro, a expressão se refere ao valor que está sendo apontado, ao mesmo tempo quando um nome de ponteiro aparece sem este operador, refere-se para o valor do ponteiro em si (ou seja, o endereço do que o ponteiro está apontando).

Outra coisa que pode chamar sua atenção é a linha: 

 int * p1, * p2;

Este declara dois ponteiros usado no exemplo anterior. Mas repare que há um asterisco (*) para cada ponteiro, de modo que ambos têm o tipo int * (ponteiro para int ).

Caso contrário, o tipo para a segunda variável declarada em que a linha teria sido int (e não int * ), devido às relações de precedência. Se tivéssemos escrito:

 int * p1, p2;

p1 teria certamente int * tipo, mas p2 teria tipo int (espaços não importa a todos para essa finalidade). Isto é

Page 60: Tutorial c++

devido às regras de precedência do operador. Mas de qualquer maneira, basta lembrar que você tem que colocar um asterisco por ponteiro é suficiente para a maioria dos usuários do ponteiro.

Ponteiros e matrizesO conceito de matriz é muito ligado ao do ponteiro. Na verdade, o identificador de uma matriz é equivalente para o endereço do seu primeiro elemento, como um ponteiro é equivalente para o endereço do primeiro elemento que chama a atenção para, então na verdade eles são o mesmo conceito. Por exemplo, supondo que estas duas declarações:

12int números [20],int * p;

A operação de atribuição a seguir seria válido: 

 p = números;

Depois disso, p e números seriam equivalentes e teria as mesmas propriedades. A única diferença é que nós podemos mudar o valor do ponteiro p por outro, ao passo que os números vão sempre apontar para o primeiro dos 20 elementos do tipo int com o qual ele foi definido. Portanto, ao contrário p , que é um ponteiro comum, números é uma matriz e uma matriz pode ser considerado um ponteiro constante . Portanto, a atribuição a seguir não seriam válidas:

 números = p;

Porque os números é um array, por isso funciona como um ponteiro constante, e não podemos atribuir valores para constantes.

Devido às características de variáveis, todas as expressões que incluem ponteiros no exemplo a seguir são perfeitamente válidas:

1234567891011121314151617

/ / Ponteiros mais # include <iostream> usando namespace std;

int main () { int números [5], int * p, p = números; * p = 10; = 20; p + p +; * p = & números [2] ; * p = 30, p = número + 3; * p = 40, p = números; * (p 4) = 50; para ( int n = 0, n <5; n + +) cout <<números [n] << "," ; retornar 0;}

No capítulo sobre matrizes usamos colchetes ( [] ) várias vezes a fim de especificar o índice de um elemento da matriz a que queria referir. Bem, estes assinam operadores colchete [] também são uma dereference operador conhecido como operador de offset .Eles dereference a variável seguem apenas como * faz, mas também adicionar o número entre parênteses para o endereço a ser dereferenced. Por exemplo:

Page 61: Tutorial c++

12um [5] = 0; / / a [deslocamento de 5] = 0* (a 5) = 0; / / apontada por (a 5) = 0

Estas duas expressões são equivalentes e válidas tanto se um é um ponteiro, ou se um é uma matriz.

inicialização do ponteiroAo declarar ponteiros, a gente pode querer especificar explicitamente qual a variável que queremos que eles apontam para:

12int número;int tommy * = &number;

O comportamento deste código é equivalente a:

123

int número;int * tommy, tommy = &number;

Quando a inicialização do ponteiro tem lugar sempre estamos atribuindo o valor de referência para onde o ponteiro aponta ( Tommy ), nunca o valor que está sendo apontado ( * tommy ). Você deve considerar que, no momento de declarar um ponteiro, o asterisco (* ) indica apenas que é um ponteiro, ele não é o operador de dereferência (embora ambos usam o mesmo sinal: *). Lembre-se, são duas funções diferentes de um sinal. Assim, devemos tomar cuidado para não confundir com o código anterior:

123

int número;int * tommy, tommy * = &number;

que está incorreto, e mesmo assim não teria muito sentido neste caso, se você pensar sobre isso.

Como no caso de matrizes, o compilador permite que o caso especial que queremos para inicializar o conteúdo ao qual o ponteiro aponta com constantes no mesmo instante, o ponteiro é declarado:

 char * terry = "Olá" ;

Neste caso, o espaço de memória é reservada para conter "Olá" e, em seguida, um ponteiro para o primeiro caractere do bloco de memória é atribuído a Terry . Se imaginarmos que "Olá" é armazenado na memória locais que começam em endereços 1702, podemos representar a declaração anterior como:

 É importante indicar que terry contém o valor de 1702, e não "h" , nem "Olá" , embora na verdade 1702 é o endereço de ambos.

O ponteiro terry aponta para uma seqüência de caracteres e pode ser lido como se fosse uma matriz (lembre-se que uma matriz é igual a um ponteiro constante). Por exemplo, podemos acessar o quinto elemento do array com qualquer uma dessas duas expressões:

Page 62: Tutorial c++

12* Terry (terry 4) [4]

Ambas as expressões têm um valor de 'o' (o quinto elemento da matriz).

aritmética de ponteiro

Para realizar operações aritméticas em ponteiros é um pouco diferente do que para conduzi-los em tipos de dados inteiros regular. Para começar, a única adição e subtração são operações de poder ser realizado com eles, os outros não fazem sentido no mundo dos ponteiros. Mas a adição e subtração têm um comportamento diferente com os ponteiros de acordo com o tamanho do tipo de dados para que eles apontam.

Quando vimos os diferentes tipos de dados fundamentais, vimos que alguns ocupam menos espaço, ou mais do que outros na memória. Por exemplo, vamos supor que em um dado compilador para uma máquina específica, char tem 1 byte, short ocupa 2 bytes etempo leva 4.

Suponha que nós definimos três ponteiros neste compilador: 

123

char MyChar *;curto myshort *;longo mylong *;

e que nós sabemos que eles apontam para posições de memória 1000 , 2000 e 3000 , respectivamente.

Então, se escrever: 

123

MyChar + +; myshort + +; mylong + +;

MyChar , como você pode esperar, deve conter o valor de 1001 . Mas não tão obviamente, myshort conteria o valor 2002 , e mylong conteria 3004 , apesar de terem sido aumentado cada uma só vez. A razão é que, ao adicionar um a um ponteiro que estamos fazendo com que ela aponte para o elemento seguinte do mesmo tipo com o qual ele foi definido e, portanto, o tamanho em bytes do tipo apontado é adicionado ao ponteiro.

 Isto é aplicável tanto ao somar e subtrair qualquer número para um ponteiro. Ele iria acontecer exactamente o mesmo se escrever:

123

MyChar MyChar = + 1; myshort myshort = + 1; mylong mylong = + 1;

Page 63: Tutorial c++

Ambos aumentam o ( + + ) e diminuir ( - ) Os operadores têm precedência de operador maior do que o operador de dereferência ( * ), mas ambos têm um comportamento especial quando usada como sufixo (a expressão é avaliada com o valor que tinha antes de ser aumentada) . Portanto, a seguinte expressão pode levar à confusão:

 * P + +

Porque + + tem maior precedência que * , esta expressão é equivalente a * (p + +) . Portanto, o que faz é aumentar o valor de p (de modo que agora aponta para o elemento seguinte), mas porque + + é usado como postfix toda a expressão é avaliada como o valor apontado pela referência original (o endereço do ponteiro apontado para antes de ser aumentada).

Observe a diferença com:

(* P) + + Aqui, a expressão teria sido avaliado como o valor apontado por p aumentado em um. O valor de p (o próprio ponteiro) não seria alterada (o que está sendo modificada é o que está sendo apontada por este ponteiro).

Se escrevermos:

 * P = q + + + + *;

Porque + + tem uma precedência maior do que * , ambos p e q são aumentados, mas porque os operadores aumento ( + + ) são utilizados como postfix e não o prefixo, o valor atribuído à p * é * q antes de ambos p e q são aumentadas . E então ambos serão aumentados. Seria equivalente a:

123

* P = q *; + + p, q + +;

Como sempre, eu recomendo que você use parênteses () , a fim de evitar resultados inesperados e para dar maior legibilidade ao código.

Ponteiros para ponteirosC + + permite o uso de ponteiros que apontam para ponteiros, que estes, por sua vez, apontam para dados (ou até mesmo para outros ponteiros). A fim de fazer isso, só precisamos adicionar um asterisco ( * ) para cada nível de referência nas suas declarações:

123456

char : umchar * b;char ** c, a = 'z' , b &a; c = &b;

Isto, supondo que a memória de locais escolhidos aleatoriamente para cada variável de 7230 , 8092 e 10502 , pode ser representado como:

 

Page 64: Tutorial c++

O valor de cada variável é escrito dentro de cada célula, as células estão sob seus respectivos endereços na memória.

A novidade neste exemplo é variável c , que pode ser utilizado em três diferentes níveis de indireção, cada uma delas corresponderia a um valor diferente:

c tem o tipo char ** e um valor de 8092 C * tem um tipo char * e um valor de 7230 ** C tem o tipo char e um valor de 'z'

ponteiros voidO vazio tipo de ponteiro é um tipo especial de ponteiro. Em C + +, anular representa a ausência do tipo, nula ponteiros para os ponteiros que apontam para um valor que não tem nenhum tipo (e, portanto, também uma duração indeterminada e propriedades dereference indeterminado).

Isso permite que os ponteiros void para apontar para qualquer tipo de dados, a partir de um valor inteiro ou um float para uma seqüência de caracteres. Mas em troca, eles têm uma grande limitação: os dados apontados por eles não podem ser directamente dereferenced (que é lógico, pois não temos nenhum tipo de dereference a), e por isso sempre teremos de lançar o endereço do ponteiro nulo para algum tipo outro ponteiro que aponta para um tipo de dados concretos antes de dereferencing-lo.

Um de seus usos podem ser para passar parâmetros genéricos para uma função: 

123456789101112131415161718192021

/ / Increaser # include <iostream> usando namespace std;

void aumento ( void * dados, int psize) { se (psize == sizeof ( char )) { char * pchar; pchar = ( char *) Dados; + + (* PChar);} mais se (psize == sizeof ( int )) { int pint *; pint = ( int *) Dados; + + (* litro);}}

int main () { char a = 'x' ; int b = 1602; aumento (& a, sizeof (a)); aumento (& b, sizeof (b)); cout <<a << "," <<b <<endl; retornar 0;}

sizeof é um operador integrado na linguagem C + + que retorna o tamanho em bytes do seu parâmetro. Para tipos de dados dinâmicos não esse valor é uma constante. Assim, por exemplo, sizeof (char) é um , porque char é um tipo byte.

ponteiro nuloUm ponteiro nulo é um ponteiro regular de qualquer tipo de ponteiro que tem um valor especial que indica que ele não está apontando para qualquer referência válida ou endereço de memória. Este valor é o resultado de tipo-casting o valor inteiro zero para qualquer tipo de ponteiro.

12int * p, p = 0; / p tem um valor de ponteiro nulo

Não confundir com ponteiros nulos ponteiros void. Um ponteiro nulo é um valor que pode assumir qualquer

Page 65: Tutorial c++

ponteiro para representar que ele está apontando para "lugar nenhum", enquanto um ponteiro void é um tipo especial de ponteiro que pode apontar para algum lugar sem um tipo específico. Uma se refere ao valor armazenado no ponteiro propriamente dito e outro para o tipo de dados que ele aponta.

Ponteiros para funçõesC + + permite operações com ponteiros para funções. O uso típico deste é para passar uma função como um argumento para outra função, desde que estes não podem ser passados dereferenced. Para declarar um ponteiro para uma função que temos que declará-la como o protótipo da função, exceto que o nome da função é incluída entre parênteses () e um asterisco ( * ) é inserido antes do nome:

123456789101112131415161718192021222324252627

/ / Ponteiro para funções # include <iostream> usando namespace std;

int soma ( int a, int b) { retorno (a + b);}

int subtração ( int a, int b) { retorno (ab);}

int operação ( int x, int y, int (* functocall) ( int , int )) { int g, g = (* functocall) (x, y); retorno (g);}

int {main () int m, n; int (* menos) ( int , int ) = subtração; m operação = (7, 5, a adição); n = operação (20, m, de menos); cout <<n; retornar 0;}

No exemplo, a menos é um ponteiro para uma função que tem dois parâmetros do tipo int . Ele é imediatamente atribuído a apontar para a função de subtração , tudo em uma única linha:

 int (* menos) ( int , int ) = subtração;

Memória dinâmicaAté agora, em todos os nossos programas, só temos tido tanta memória disponível como nós declaradas para nossas variáveis, tendo o tamanho de todos eles a ser determinado no código-fonte, antes da execução do programa. Mas, e se precisamos de uma quantidade variável de memória que só pode ser determinada durante a execução? Por exemplo, no caso em que precisamos de alguma entrada do usuário para determinar a quantidade necessária de espaço de memória.

A resposta é a memória dinâmica , para a qual C + + integra os operadores novos e excluir .

Os operadores [e novos]Para solicitar a memória dinâmica, usamos o operador de novo . novo é seguido por um tipo de dados e, se uma seqüência de mais de um elemento é necessário, o número destes entre colchetes [] . Ela retorna um ponteiro para o início do novo bloco de memória alocada. Sua forma é:

ponteiro = novo tipoponteiro = [number_of_elements] novo tipo

Page 66: Tutorial c++

A primeira expressão é usada para alocar memória para conter um único elemento do tipo do tipo . O segundo é usado para atribuir um bloco (uma matriz) de elementos de tipo tipo , onde number_of_elements é um valor inteiro que representa a quantidade destes. Por exemplo:

12int * bobby, Bobby = novo int [5];

Neste caso, o sistema atribui dinamicamente espaço para cinco elementos do tipo int e retorna um ponteiro para o primeiro elemento da sequência, que é atribuído a Bobby . Portanto, agora, bobby aponta para um bloco válido de memória com espaço para cinco elementos do tipo int .

 

O primeiro elemento apontado por Bobby pode ser acessado tanto com a expressão Bobby [0] ou a expressão * Bobby . Ambos são equivalentes, como foi explicado na seção sobre ponteiros. O segundo elemento pode ser acessado tanto com Bobby [1] ou * (bobby +1) e assim por diante ...

Você pode estar se perguntando a diferença entre declarar uma matriz normal e atribuição dinâmica de memória para um ponteiro, como acabamos de fazer. A diferença mais importante é que o tamanho de uma matriz tem que ser um valor constante, o que limita seu tamanho para o que nós decidimos no momento da concepção do programa, antes de sua execução, enquanto que a alocação de memória dinâmica permite atribuir memória durante o execução do programa (runtime) usando qualquer variável ou um valor constante de seu tamanho.

A dinâmica de memória solicitada pelo nosso programa é atribuído pelo sistema a partir da pilha de memória. No entanto, a memória do computador é um recurso limitado, e pode ser esgotado. Portanto, é importante ter algum mecanismo para verificar se o nosso pedido para alocar a memória foi bem sucedida ou não.

C + + fornece dois métodos padrão para verificar se a alocação foi bem sucedida:

Uma delas é o tratamento de exceções. Usando este método uma exceção do tipo bad_alloc é acionada quando a alocação falhar. As exceções são a C + + poderoso recurso explicado mais tarde nesses tutoriais. Mas, por agora você deve saber que, se essa exceção é lançada e não é tratado por um manipulador específico, a execução do programa é encerrado.

Este método de exceção é o método padrão usado pelo novo, e é o utilizado em uma declaração como:

 bobby = novo int [5]; / / se não uma exceção é lançada

O outro método é conhecido como nothrow , eo que acontece quando ele é usado é que, quando uma alocação de memória falha, ao invés de lançar um bad_alloc exceção ou encerrar o programa, o ponteiro retornado por novos é um ponteiro nulo, eo programa continua sua execução .

Este método pode ser especificado usando um objeto especial chamado nothrow , declarado no cabeçalho <new> , como argumento para nova :

 bobby = nova (nothrow) int [5];

Neste caso, se a atribuição deste bloco de memória falha, a falha pode ser detectada verificando se bobby teve um valor de ponteiro nulo:

1int * bobby, Bobby = nova (nothrow) int [5];

Page 67: Tutorial c++

2345

se (bobby == 0) { / atribuição de erro de memória /. Tomar medidas. };

Este nothrow método requer mais trabalho do que o método de exceção, uma vez que o valor retornado deve ser verificada após cada alocação de memória, mas vou usá-lo nos nossos exemplos, devido à sua simplicidade. De qualquer forma esse método pode se tornar tedioso para projetos maiores, onde o método de exceção é geralmente preferido. O método de exceção será explicado em detalhe mais tarde neste tutorial.

Operadores de apagar e delete []Desde a necessidade de memória dinâmica é geralmente limitada a momentos específicos dentro de um programa, uma vez que não é mais necessária que deve ser liberada para que a memória se torna disponível novamente para outras solicitações de memória dinâmica. Este é o objetivo do operador delete , cujo formato é:

12excluir ponteiro;excluir ponteiro] [;

A primeira expressão deve ser usada para apagar a memória alocada para um único elemento, eo segundo para a memória alocada para arrays de elementos. 

O valor passado como argumento para excluir deve ser um ponteiro para um bloco de memória previamente alocado com nova , ou um ponteiro nulo (no caso de um ponteiro nulo, excluir não produz nenhum efeito).

12345678910111213141516171819202122232425262728

/ / Rememb-o-matic <iostream> # include # include <new> usando namespace std;

int main () { int n i, int * p; cout << "Quantos números que você gostaria de escrever?" ; cin>> i, p = nova (nothrow) int [i]; se (p == 0) cout << "Erro: A memória não pode ser atribuída" ; outra { para (n = 0, n <i, n + +) {cout << "Digite o número:" ; cin>> p [n];} cout << "Você digitou:" , para (n = 0, n <i, n + +) cout <<p [n] << "" ; excluir [] p;} retornar 0;}

Quantos números que você gostaria de escrever? 5 Digite o número: 75 Digite o número: 436 Digite o número: 1067 Digite o número: 8 Digite o número: 32 Você já entrou: 75, 436, 1067, 8, 32,

Observe como o valor entre parêntesis na nova declaração é um valor variável digitada pelo usuário ( i ), e não um valor constante:

 p = nova (nothrow) int [i];

Page 68: Tutorial c++

Mas o usuário poderia ter entrado um valor para i tão grande que nosso sistema não pode lidar com isso. Por exemplo, quando eu tentei dar um valor de 1000 milhões para o "Quantos números" pergunta, meu sistema não pôde alocar a memória tanto para o programa e recebi a mensagem de texto que preparou para este caso ( erro: memória não pôde ser atribuídos ). Lembre-se que no caso em que tentou alocar a memória, sem especificar o parâmetro nothrow na nova expressão, uma exceção seria lançada, que se não for tratado termina o programa.

É uma boa prática de sempre verificar se um bloco de memória dinâmica com êxito foi atribuído. Portanto, se você usar o nothrow método, você deve sempre verificar o valor do ponteiro retornado. Caso contrário, use o método de exceção, mesmo se você não tratar a exceção. Desta forma, o programa será encerrado neste ponto, sem causar resultados inesperados de continuar a execução de um código que pressupõe um bloco de memória de ter sido atribuída, quando na verdade ele não tem.

Dinâmica de memória em ANSI-C

Operadores de novo e apagar são exclusivos do C + +. Eles não estão disponíveis na linguagem C. Mas usando a linguagem C pura e sua biblioteca, a memória dinâmica também pode ser usado com as funções malloc , calloc , realloc e livre , que também estão disponíveis em C + +, incluindo o <cstdlib> arquivo de cabeçalho (vejacstdlib para mais informações).

Os blocos de memória alocada por essas funções não são necessariamente compatíveis com os retornados pelo novo, por isso cada um deve ser manipulada com seu próprio conjunto de funções ou operadores.

Estruturas de DadosNós já aprendemos como os grupos de dados seqüenciais podem ser usados em C + +. Mas isso é um pouco restritivo, pois em muitas ocasiões que queremos loja não são seqüências simples de todos os elementos do mesmo tipo de dados, mas conjuntos de elementos diferentes, com diferentes tipos de dados.

As estruturas de dadosUma estrutura de dados é um grupo de elementos de dados agrupados sob um nome. Estes elementos, conhecidos como membros , podem ter diferentes tipos e tamanhos diferentes. As estruturas de dados são declarados em C + + usando a seguinte sintaxe:

struct {structure_namemember_type1 member_name1;member_type2 member_name2;member_type3 member_name3;..Object_names};

onde structure_name é um nome para o tipo de estrutura, object_name pode ser um conjunto de identificadores válidos para objetos que tenham o tipo desta estrutura. Dentro de chaves {} existe uma lista com os membros de dados, cada um é especificado com um tipo e um identificador válido como o seu nome.

A primeira coisa que temos que saber é que uma estrutura de dados cria um novo tipo: Quando uma estrutura de dados é declarado, um novo tipo de identificador especificado como structure_name é criado e pode ser usado no resto do programa como se fosse qualquer outro tipo. Por exemplo:

1234567

struct produto { int peso; float preço;}; maçã produto; produto banana, melão;

Temos primeiro declarado um tipo de estrutura chamado produto com dois membros: o peso eo preço , cada um de um tipo diferente fundamental. Temos, em seguida, usou este nome do tipo de estrutura ( produto ) para declarar três objetos desse tipo: maçã , banana e melão como teria feito com qualquer tipo de dados

Page 69: Tutorial c++

fundamental.

Uma vez declarada, produto tornou-se um tipo novo nome válido como os fundamentais int , char ou short e desse ponto em que somos capazes de declarar objetos (variáveis) desse novo tipo de composto, tal como temos feito com maçã , banana e melão .

Logo no final da struct declaração, e antes do ponto e vírgula final, podemos usar o campo opcional object_name diretamente declarar objetos do tipo de estrutura. Por exemplo, podemos também declarar a estrutura de objetos de maçã , banana e melão no momento em que definir o tipo de estrutura de dados desta forma:

1234

struct produto { int peso; float preço;} maçã, banana, melão;

É importante diferenciar claramente entre o que é o nome do tipo de estrutura, eo que é um objeto (variável) que tem esse tipo de estrutura. Podemos instanciar muitos objetos (variáveis, ou seja, como maçã , banana e melão ) de um tipo de estrutura simples ( produto ).

Uma vez que tenhamos declarado nossos três objetos de um tipo de estrutura determinada ( maçã , banana e melão ), podemos atuar diretamente com os seus membros. Para isso, usamos um ponto ( . ) inserido entre o nome do objeto eo nome do membro. Por exemplo, poderíamos operar com qualquer um desses elementos como se fossem variáveis padrão de seus respectivos tipos:

123456

apple.weight apple.price melon.price melon.weight banana.weight banana.price

Cada um deles tem o tipo de dados correspondente ao membro que se referirem: apple.weight , banana.weight e melon.weight são do tipo int , enquanto apple.price , banana.price e melon.price são do tipo float .

Vamos ver um exemplo real, onde você pode ver como um tipo de estrutura pode ser usado da mesma forma como tipos fundamentais:

12345678910111213141516171819202122

/ / Exemplo sobre as estruturas <iostream> # include # include <string> # include <sstream> usando namespace std;

struct ; movies_t seqüência {title int ano;} meu, seu;

void (movies_t) filme; mostrafilme

int main () { mystr string; mine.title = "2001 Uma Odisséia no Espaço" ; mine.year = 1968; cout << "Digite o título:"getline (cin, yours.title); cout << "Digite o ano:" ; getline (cin , mystr); mystr (stringstream)> yours.year>; cout << "Meu filme favorito é: \ n" ;; cout <<) (mostrafilme mina "E o seu é: \ n" ; mostrafilme (seu); retorno 0;}

void mostrafilme (movies_t filme) {tribunal <<movie.title; cout << "(" <<movie.year << ") \ n" ;}

Page 70: Tutorial c++

23242526272829303132333435363738

O exemplo mostra como podemos usar os membros de um objeto como variáveis regular. Por exemplo, o membro yours.year é uma variável válida do tipo int , e mine.title é uma variável válida do tipo string .

Os objetos de minas e seu também podem ser tratados como variáveis do tipo válido movies_t , por exemplo, nós passamos a eles a função mostrafilme como teria feito com variáveis regular. Portanto, uma das vantagens mais importantes das estruturas de dados é que podemos fazer referência aos seus membros individualmente ou para toda a estrutura como um bloco com apenas um identificador.

Estruturas de dados são um recurso que pode ser usado para representar bancos de dados, especialmente se considerarmos a possibilidade de construção de matrizes de um deles:

123456789101112131415161718192021222324252627282930313233343536

/ / Matriz de estruturas <iostream> # include # include <string> # include <sstream> usando namespace std;

# define N_MOVIES 3

struct ; movies_t seqüência {title int ano;} filmes [N_MOVIES];

void mostrafilme (filme movies_t);

int main () mystr {string, int n;

para (n = 0, n <N_MOVIES; n + +) {cout << "Digite o título:" ; getline [n]. título); cout <<(cin, os filmes "Enter ano: " ; (cin, mystr); stringstream> (mystr)> [filmes n]. getline ano;} cout << "\ nVocê entrou com estes filmes: \ n" , para (n = 0; N_MOVIES <n; n + +) mostrafilme (filmes [n]); retornar 0;}

void movies_t (filme mostrafilme) {cout <<movie.title; cout << "(" <<movie.year << ") \ n" ;}

Page 71: Tutorial c++

37383940

Ponteiros para estruturasComo qualquer outro tipo, as estruturas podem ser apontados pelo seu próprio tipo de ponteiros:

1234567

struct ; movies_t seqüência {title int ano;}; Amovie movies_t; pmovie * movies_t;

Aqui Amovie é um objeto do tipo de estrutura movies_t e pmovie é um ponteiro para apontar para objetos do tipo estrutura movies_t . Então, o seguinte código também seria válido:

 pmovie = &amovie;

O valor do ponteiro pmovie seria atribuído a uma referência ao objeto Amovie (seu endereço de memória).

Agora vamos ir com outro exemplo que inclui ponteiros, que servem para introduzir um novo operador: o operador seta ( -> ):

12345678910111213141516171819202122232425262728293031

/ / Ponteiros para estruturas <iostream> # include # include <string> # include <sstream> usando namespace std;

struct ; movies_t seqüência {title int ano;};

int main () {mystr string; Amovie movies_t; pmovie * movies_t; pmovie = &amovie; cout << "Digite o título:"(cin,-title> pmovie); cout << "Digite o ano:" ; getline (cin, mystr); (stringstream) mystr>> pmovie-> ano; cout << nNão digitou: \ n" ; cout <<"title> pmovie; cout << "(" <<pmovie-> ano << ") \ n" ;

retornar 0;}

Page 72: Tutorial c++

O código anterior inclui uma introdução importante: o operador seta ( -> ). Este é um operador dereference que é usado exclusivamente com ponteiros para objetos com os membros. Este operador serve para acessar um membro de um objeto para o qual temos uma referência. No exemplo que usamos:

 título pmovie->

Qual é para todos os efeitos equivalente a: 

 (* Pmovie). Título

Ambas as expressões -title> pmovie e (* pmovie). títulos são válidos e ambos significam que estamos avaliando o membro do título da estrutura de dados apontada por um ponteiro chamado pmovie . Deve ser claramente diferenciado:

 * Pmovie.title

que é equivalente a:

 * (Pmovie.title)

E isso poderia acessar o valor apontado pelo ponteiro membro de um hipotético chamado título do objeto estrutura pmovie (que neste caso não seria um ponteiro). O quadro a seguir resume as combinações possíveis de ponteiros e os membros da estrutura:

Expressão

O que é avaliadoEquivalent

e

ab B Membro do objeto a

a-> b B Membro do objeto apontado por um (* A). B

* Ab Valor apontado por b membro de um objeto * (Ab)

Nidificação estruturas

Estruturas também podem ser aninhados de modo que um elemento válido de uma estrutura também pode ser, por sua vez uma outra estrutura.

123456789101112

struct ; movies_t seqüência {title int ano;};

struct } friends_t string {string nome; e-mail movies_t; favorite_movie; charlie, maria; pfriends * friends_t = &charlie;

Page 73: Tutorial c++

Após a declaração anterior, podemos usar qualquer uma das seguintes expressões:

1234

charlie.name maria.favorite_movie.title favorite_movie.year pfriends-> charlie.favorite_movie.year

(Onde, aliás, as duas últimas expressões se referem a um mesmo membro). 

Outros tipos de dadosDefinidos os tipos de dados (typedef)C + + permite a definição de nosso próprios tipos baseados em outros tipos de dados existentes. Podemos fazer isso usando a palavra-chave typedef , cujo formato é:

typedef tipo_existente novo_nome_de_tipo;

onde existing_type é um C + + fundamentais ou compostos tipo e new_type_name é o nome para o novo tipo que estamos definindo. Por exemplo:

1234

typedef char C;typedef unsigned int WORD;typedef char * PChar;typedef char campo [50];

Neste caso, temos a definição de quatro tipos de dados: C , WORD , PChar e campo como char , unsigned int , char e char [50] , respectivamente, que poderíamos perfeitamente usar em declarações posteriores válida como qualquer outro tipo:

1234

C MyChar, anotherchar, * ptc1; myword WORD; nome do campo; Entrada PTC2 PChar;

typedef não cria tipos diferentes. Ela só cria sinónimos de tipos existentes. Isso significa que o tipo de myword pode ser considerado tanto WORD ou unsigned int , já que ambos são, de facto, do mesmo tipo.

typedef pode ser útil para definir um apelido para um tipo que é usado freqüentemente dentro de um programa. Também é útil para definir os tipos, quando é possível que teremos de alterar o tipo em versões posteriores do nosso programa, ou se um tipo que você quiser usar tem um nome que é muito longo ou confuso.

SindicatosUniões permitem uma mesma porção de memória a ser acessada como diferentes tipos de dados, uma vez que todos eles são de fato o mesmo local na memória. Sua declaração e utilização é semelhante ao de estruturas, mas sua funcionalidade é totalmente diferente:

union_name union {member_type1 member_name1; member_type2 member_name2; member_type3 member_name3;. . Object_names};

Todos os elementos da união declaração ocupar o mesmo espaço físico na memória. Seu tamanho é o de o maior elemento da declaração. Por exemplo:

1234

União mytypes_t { char c; int i; float f;} mytypes;

Page 74: Tutorial c++

5

define três elementos:

123

mytypes.c mytypes.i mytypes.f

cada um com um tipo de dados diferentes. Uma vez que todos eles estão se referindo ao mesmo local na memória, a modificação de um dos elementos afetará o valor de todos eles. Não podemos armazenar valores diferentes nas mesmas independentes umas das outras.

Um dos usos de uma união pode ter é a de unir um tipo elementar com um conjunto de elementos ou estruturas de menor dimensão. Por exemplo:

12345678

União mix_t { longa l; struct { short oi; curto lo;} s; char c [4];} mistura;

define três nomes que nos permitem acessar o mesmo grupo de 4 bytes: mix.l , mix.s e mix.c e que podemos usar de acordo com a forma como queremos acessar esses bytes, como se fossem um único longa -tipo dados, como se fossem duas curtas elementos ou como uma matriz de char elementos, respectivamente. Tenho tipos mistos, matrizes e estruturas na união de modo que você pode ver as diferentes formas que podemos acessar os dados. Para um little-endian sistema (PC maioria das plataformas), esta união pode ser representada como:

 O alinhamento exato ea ordem dos membros de um sindicato na memória depende da plataforma. Portanto, esteja ciente das questões de portabilidade possível com este tipo de uso.

Anónimo sindicatosEm C + +, temos a opção de declarar os sindicatos anônimo. Se declarar uma união sem qualquer nome, o sindicato será anônimo e seremos capazes de acessar seus membros diretamente por seus nomes de membros. Por exemplo, olhar para a diferença entre essas duas declarações estrutura:

estrutura com o sindicato regularmente estrutura com união anônima

struct {char título [50]; autor char [50]; união {float dólares, ienes int;} preço;} livro;

struct {char título [50]; autor char [50]; união {float dólares, ienes int;};} livro;

A única diferença entre os dois pedaços de código é que no primeiro temos dado um nome para o sindicato ( dos preços ) e na segunda não temos. A diferença é visto quando o acesso a membros de dólares e ienes de um objeto desse tipo. Para que um objeto do primeiro tipo, seria:

12book.price.dollars book.price.yen

Page 75: Tutorial c++

Considerando que para um objeto do segundo tipo, seria: 

12book.dollars book.yen

Mais uma vez devo lembrar que, porque é uma união e não uma estrutura, os membros de dólares e ienes ocupar o mesmo espaço físico na memória de modo que não pode ser usado para armazenar dois valores diferentes em simultâneo. Você pode definir um valor para o preço em dólares ou ienes, mas não em ambos.

Enumerações (enum)Enumerações de criar novos tipos de dados para conter algo diferente que não está limitada aos valores fundamentais tipos de dados podem tomar. Sua forma é o seguinte:

enumeration_name enum {valor1, valor2, valor3. . Object_names};

Por exemplo, poderíamos criar um novo tipo de variável chamada colors_t para armazenar as cores com a seguinte declaração:

 enum {colors_t preto, azul, verde, turquesa, vermelho, roxo, amarelo, branco};

Observe que não incluem qualquer tipo de dados fundamental na declaração. Para dizê-lo de alguma forma, criamos um todo novo tipo de dados a partir do zero, sem baseá-lo em qualquer outro tipo existente. Os possíveis valores que as variáveis deste novo tipo color_t pode tomar é a constante de novos valores incluídos dentro de chaves. Por exemplo, uma vez que o colors_tenumeração é declarado as seguintes expressões serão válidos:

1234

mycolor colors_t; mycolor = azul;se (== mycolor verde) mycolor = vermelho;

Enumerações são tipos compatíveis com as variáveis numéricas, para que seus constantes são sempre atribuído um valor numérico inteiro internamente. Se não for especificado, o equivalente ao valor inteiro possível, o primeiro valor é equivalente a 0 e as seguintes seguem uma progressão 1. Assim, em nosso tipo de dados colors_t que temos acima definidos, preto seria equivalente a 0, azul seria equivalente a um , verde a 2 , e assim por diante.

Podemos especificar explicitamente um valor inteiro para qualquer um dos valores constantes que o nosso tipo enumerado pode assumir. Se o valor constante que se segue não é dado um valor inteiro, ele é automaticamente assumido o mesmo valor do anterior mais um. Por exemplo:

123

enum months_t {janeiro = 1, fevereiro, março, abril, maio, junho, julho, agosto, setembro, outubro, novembro, dezembro, y2k};

Neste caso, a variável y2k do tipo enumerado months_t pode conter qualquer um dos 12 possíveis valores que vão de janeiro a dezembro , e que são equivalentes aos valores entre 1 e 12 (não entre 0 e 11 , já fizemos janeiro igual a 1 ) .

Classes (I)Uma classe é um conceito expandido de uma estrutura de dados: em vez de realizar apenas os dados, ele pode armazenar os dados e funções.

Page 76: Tutorial c++

Um objeto é uma instanciação de uma classe. Em termos de variáveis, uma classe seria o tipo, e um objeto seria a variável.

As aulas são geralmente declaradas usando a palavra-chave class , com o seguinte formato:

class_name classe {access_specifier_1: member1; access_specifier_2: member2; ... Object_names};

Onde class_name é um identificador válido para a classe, object_names é uma lista de nomes para os objetos desta classe. O corpo da declaração pode conter membros, que podem ser dados ou declarações de função e, opcionalmente, especificadores de acesso.

Tudo é muito parecido com a declaração sobre as estruturas de dados, exceto que agora podemos incluir também as funções e os membros, mas também essa coisa nova chamada especificador de acesso . Um especificador de acesso é uma das três seguintes palavras-chave: privado , público ou protegido . Estes especificadores de modificar os direitos de acesso que os seguintes membros para que adquiram:

privado membros de uma classe são acessíveis somente através de outros membros da mesma classe ou de seus amigos .

protegidos membros são acessíveis a partir de membros da sua mesma classe e de seus amigos, mas também de membros de suas classes derivadas.

Finalmente, públicas integrantes são acessíveis de qualquer lugar onde o objeto é visível.

Por padrão, todos os membros de uma classe declarada com a classe de palavras-chave têm acesso privado para todos os seus membros. Portanto, qualquer membro que seja declarada antes de um especificador de classe de outros automaticamente tem acesso privado. Por exemplo:

123456

classe CRectangle { int x, y; pública : void set_values ( int , int ); int área ( void );} rect;

Declara uma classe (ou seja, um tipo) chamado CRectangle e um objeto (ou seja, uma variável) desta classe chamado rect . Essa classe contém quatro membros: dois membros de dados do tipo int (membro x e membro y ), com acesso privado (pois privadas é o nível de acesso padrão) e duas funções de membro de acesso público: set_values () e area () , das quais para a agora só foram incluídos a sua declaração, e não sua definição.

Observe a diferença entre o nome da classe eo nome do objeto: No exemplo anterior, CRectangle era o nome da classe (ou seja, o tipo), enquanto rect era um objeto do tipo CRectangle . É a mesma relação int e um tem a seguinte declaração:

 int a;

onde int é o nome do tipo (a classe) e um é o nome da variável (o objeto).

Após as declarações anteriores do CRectangle e rect , podemos nos referir dentro do corpo do programa a qualquer um dos membros públicos do objeto rect como se fossem funções normais ou variáveis normal, apenas colocando objeto o nome seguido por um ponto ( . ) e depois o nome do membro. Tudo muito semelhante ao que fizemos com estruturas de dados comum antes. Por exemplo:

12rect.set_values (3,4); myarea rect.area = ();

Os únicos membros do rect que não podemos acessar a partir do corpo do nosso programa fora da classe

Page 77: Tutorial c++

são x e y , uma vez que têm acesso privado e só podem ser encaminhados a partir de outros membros da mesma classe.

Aqui está o exemplo completo da classe CRectangle:

12345678910111213141516171819202122

/ / Exemplo classes # include <iostream> usando namespace std;

classe CRectangle { int x, y; pública : void set_values ( int , int ); int area () { retorno (x * y);}};

void CRectangle: set_values ( int a, int b) {x = a, y = b;}

int main () {CRectangle rect; rect.set_values (3,4); cout << "Área:" <<rect.area (); retornar 0;}

A coisa mais nova e importante neste código é o operador de escopo ( :: , dois-pontos), incluído na definição de set_values () . Ele é usado para definir um membro de uma classe a partir de fora da definição da própria classe.

Você pode notar que a definição da função de membro area () foi incluída diretamente na definição do CRectangle classe dada a sua extrema simplicidade, ao passo que set_values () tem apenas o seu protótipo declarado dentro da classe, mas sua definição é fora dela. Nesta declaração fora, devemos usar o operador de escopo ( :: ) para especificar que estamos definindo uma função que é membro da classe CRectangle e não uma função global regular.

O operador de escopo ( :: ) especifica a classe a que o membro que está sendo declarado pertence, garantindo exatamente as mesmas propriedades de escopo como se essa definição de função foi directamente incluído na definição de classe. Por exemplo, na função de set_values () do código anterior, temos sido capazes de utilizar as variáveis x e y , que são membros privados da classe CRectangle , o que significa que eles só são acessíveis por outros membros da sua classe.

A única diferença entre a definição de um membro da classe funcionar completamente dentro de sua classe ou para incluir somente o protótipo e, posteriormente, a sua definição, é que no primeiro caso, a função será automaticamente considerado uma função inline, o compilador, enquanto no segundo ele irá ser um normal (não-inline) função de membro de classe, que na verdade supõe nenhuma diferença no comportamento.

Os membros x e y tem acesso privado (lembre-se que, se nada for dito, todos os membros de uma classe definida com a classe de palavras-chave têm acesso privado). Ao declarar-los privados negamos acesso a eles de qualquer lugar fora da classe. Isso faz sentido, uma vez que já definimos uma função membro para definir valores para os membros dentro do objeto: a função de membro set_values () . Portanto, o resto do programa não precisa de ter acesso direto a eles. Talvez em um exemplo simples assim, como isto, é difícil ver qualquer utilidade na proteção dessas duas variáveis, mas em projetos maiores, pode ser muito importante que os valores não podem ser modificadas de forma inesperada (inesperada do ponto de vista do objeto ).

Uma das maiores vantagens de uma classe é que, como qualquer outro tipo, podemos declarar vários objetos do mesmo. Por exemplo, seguindo com o exemplo anterior da classe CRectangle , poderíamos ter declarado o objeto rectb além do objeto rect :

1/ / Exemplo: uma classe, dois objetos # include <iostream>

Page 78: Tutorial c++

23456789101112131415161718192021222324

usando namespace std;

classe CRectangle { int x, y; pública : void set_values ( int , int ); int area () { retorno (x * y);}} ;

void CRectangle: set_values ( int a, int b) {x = a, y = b;}

int main () {rect CRectangle, rectb; rect.set_values (3,4); rectb.set_values (5,6) ; cout << "área rect:" <<rect.area () <<endl; cout << "área rectb:" <<rectb.area () <<endl; retornar 0;}

Neste caso concreto, a classe (tipo de objetos) para o qual estamos falando é CRectangle , do qual existem duas instâncias ou objetos: rect e rectb . Cada um deles tem suas próprias variáveis de membro e funções membro.

Observe que a chamada para rect.area () não dá o mesmo resultado que a chamada para rectb.area () . Isso ocorre porque cada objeto da classe CRectangle tem suas próprias variáveis x e y , uma vez que, de alguma forma, também têm suas próprias funções membros set_value () e área () que cada um usa seu próprio variáveis de seu objeto de operar.

Esse é o conceito básico de programação orientada a objeto : Os dados e as funções são ambos membros do objeto. Nós já não usam conjuntos de variáveis globais que passamos de uma função para outra, como parâmetros, mas sim como lidamos com objetos que possuem os seus próprios dados e funções incorporados como membros. Repare que nós não tivemos a dar os parâmetros em qualquer uma das chamadas para rect.area ou rectb.area . Essas funções de membro usado diretamente os membros de dados de seus respectivos objetos rect e rectb .

Construtores e destrutoresObjetos geralmente precisam para inicializar variáveis ou atribuir memória dinâmica durante o seu processo de criação para se tornar operativa e para evitar o retorno de valores inesperados durante a sua execução. Por exemplo, o que aconteceria se no exemplo anterior, chamamos a função de membro area () antes de ter função chamada set_values () ? Provavelmente, teríamos obtido um resultado indeterminado desde que os membros x e y nunca teria sido atribuído um valor.

A fim de evitar que, uma classe pode incluir uma função especial chamado construtor , que é chamado automaticamente sempre que um novo objeto desta classe é criado. Esta função construtor deve ter o mesmo nome da classe, e não pode ter qualquer tipo de retorno, nem mesmo void .

Vamos implementar CRectangle incluindo um construtor:

12345678910

/ / Exemplo: construtor da classe # include <iostream> usando namespace std;

classe CRectangle { int largura, altura; público : CRectangle ( int , int ); int area () { retorno (altura * largura);}}; CRectangle: CRectangle ( int a, int b) {width = a; height = b;}

int main () {rect CRectangle (3,4); CRectangle rectb (5,6); cout << "área rect:" <<rect . area ()

Page 79: Tutorial c++

11121314151617181920212223

<<endl; cout << "área rectb:" <<rectb.area () <<endl; retornar 0;}

Como você pode ver, o resultado deste exemplo é idêntico ao anterior. Mas agora temos removido a função de membro set_values () , e incluíram em vez de um construtor que executa uma ação semelhante: ele inicializa os valores de largura e altura com os parâmetros que são passados para ele.

Observe como esses argumentos são passados para o construtor no momento em que os objetos dessa classe são criadas:

12CRectangle rect (3,4); rectb CRectangle (5,6);

Construtores não podem ser chamados explicitamente como se fossem funções de membro regular. Eles só são executados quando um novo objeto da classe é criada.

Você também pode ver como nem a declaração do protótipo do construtor (dentro da classe), nem a definição do construtor últimos incluem um valor de retorno, nem mesmo void .

O destruidor cumpre a função oposta. É chamado automaticamente quando um objeto é destruído, ou porque o seu âmbito de existência, tem acabado (por exemplo, se ele foi definido como um objeto local dentro de uma função ea função termina), ou porque é um objeto dinamicamente atribuído a ele e é liberado usando o operador delete.

O destruidor deve ter o mesmo nome da classe, mas precedido de um sinal de til ( ~ ) e ele também deve retornar nenhum valor.

O uso de destrutores é especialmente adequada quando um objeto atribui memória dinâmica durante sua vida útil e no momento de serem destruídos queremos liberar a memória que o objeto foi alocado.

1234567891011121314151617181920

/ / Exemplo de construtores e destrutores # include <iostream> usando namespace std;

classe CRectangle { int * largura, altura *; público : CRectangle ( int , int ); ~ CRectangle (); int area () { retorno (* largura * * altura);}}; CRectangle: CRectangle ( int a, int b) {width = novo int ; height = novo int ; largura * = a; * height = b;} CRectangle:: ~ CRectangle () { delete largura ; excluir altura;}

int main () {rect CRectangle (3,4), rectb (5,6); cout << "área rect:" <<rect.area () <<endl; cout << área rectb " : " <<rectb.area () <<endl; retornar 0;}

Page 80: Tutorial c++

21222324252627282930

Sobrecarga de ConstrutoresComo qualquer outra função, um construtor também pode ser sobrecarregado com mais de uma função que tem o mesmo nome mas diferentes tipos ou número de parâmetros. Lembre-se que a sobrecarga de funções que o compilador irá chamar a cujos parâmetros coincidir com os argumentos usados na chamada de função. No caso dos construtores, que são automaticamente chamados quando um objeto é criado, o executado é o único que combina os argumentos passados sobre a declaração do objeto:

1234567891011121314151617181920212223242526272829

/ / Construtores de classe sobrecarga # include <iostream> usando namespace std;

classe CRectangle { int largura, altura; público : CRectangle (); CRectangle ( int , int ); int área ( void ) { retorno (altura * largura);}} ; CRectangle: CRectangle () {width = 5; height = 5;} CRectangle: CRectangle ( int a, int b) {width = a; height = b;}

int main () {rect CRectangle (3,4) ; CRectangle rectb; cout << "área rect:" <<rect.area () <<endl; cout << "área rectb:" <<rectb.area () <<endl; retornar 0;}

Neste caso, rectb foi declarada sem argumentos, para que ele tenha sido inicializado com o construtor que não tem parâmetros, que inicializa tanto largura e altura , com um valor de 5.

Importante: Observe como se declarar um novo objeto e nós queremos usar o construtor padrão (um sem parâmetros), não incluir parênteses () :

12rectb CRectangle; / direitarectb CRectangle (); / / errado!

Page 81: Tutorial c++

construtor DefaultSe você não declarar nenhum construtor em uma definição de classe, o compilador assume que a classe tem um construtor padrão sem argumentos. Portanto, depois de declarar uma classe como esta:

12345

classe CExample { público : int , a b, c; void multiplica ( int n, int m) {a = n; m = b, c = a * b;}};

O compilador assume que CExample tem um construtor padrão, então você pode declarar os objetos desta classe, basta declará-los sem argumentos:

 CExample ex;

Mas tão logo você declarar seu próprio construtor para uma classe, o compilador não fornece um construtor padrão implícito. Então você tem que declarar todos os objetos dessa classe de acordo com o construtor de protótipos que você definiu para a classe:

123456

classe CExample { público : int , a b, c; CExample ( int n, int m) {a = n; m = b;}; void multiplica () {c = a * b;};};

Aqui temos declarado um construtor que recebe dois parâmetros do tipo int. Portanto, a declaração do objeto a seguir seria correta:

 CExample ex (2,3);

Mas,

 CExample ex;

Poderia não ser correcta, uma vez que temos declarado que a classe tem um construtor explícito, substituindo assim o construtor padrão.

Mas o compilador não só cria um construtor padrão para você se você não especificar o seu próprio. Ele fornece três funções membro especiais no total, que são declarados implicitamente, se você não declarar o seu próprio. Estes são o construtor de cópia , o operador de atribuição de cópia , eo destrutor padrão.

O construtor de cópia ea cópia cópia operador de atribuição de todos os dados contidos em outro objeto para os membros de dados do objeto atual. Para CExample , o construtor de cópia declarada implicitamente pelo compilador seria algo semelhante a:

123

CExample: CExample ( const CExample & RV) {a = rv.a; rv.b b = c = rv.c;}

Portanto, as duas declarações de objeto a seguir seria correta:

1CExample) ex (2,3; CExample ex2 (ex); / construtor de cópia (dados copiados da ex)

Page 82: Tutorial c++

2

Ponteiros para classesIsso é perfeitamente válido para criar indicadores que apontam para as aulas. Nós simplesmente temos que considerar que, uma vez declarada, a classe se torna um tipo válido, por isso podemos usar o nome da classe como o tipo de ponteiro. Por exemplo:

 CRectangle prect *;

é um ponteiro para um objeto da classe CRectangle .

Como aconteceu com estruturas de dados, a fim de se referir diretamente a um membro de um objeto apontado por um ponteiro, podemos usar o operador seta ( -> ) de engano. Aqui está um exemplo com algumas combinações possíveis:

12345678910111213141516171819202122232425262728293031323334

/ / Ponteiro para classes exemplo # include <iostream> usando namespace std;

classe CRectangle { int largura, altura; pública : void set_values ( int , int ); int área ( void ) { retorno (altura * largura);}};

void CRectangle: set_values ( int a, int b) {width = a; height = b;}

int * () {CRectangle a, b *, c principal; CRectangle * d = novo CRectangle [2], b = novo CRectangle; = &a; a.set_values (3,4);-> set_values d (5,6); d [1];. set_values (7,8) cout << "um área: " <<a.area () <<endl; cout << "área de c *" <<c> área () <<endl; cout << "d [0] área:" <<d [0] <. área () <<endl <tribunal, "d [1] área:" delete ] [d; excluir b; retornar 0;}

Em seguida você tem um resumo sobre como você pode ler alguns ponteiro e operadores de classe ( * , & , . , -> , [] ) que aparecem no exemplo anterior:

expressão

pode ser lido como

* X apontado por x

& X Endereço de x

Page 83: Tutorial c++

xy membro y do objeto x

x, y> y membro do objeto apontado por x

(* X). Y y membro do objeto apontado por x (equivalente ao anterior)

x [0] primeiro objeto apontado por x

x [1] segundo objeto apontado por x

[N] x (N +1) th objeto apontado por x

Tenha certeza que você entender a lógica em todas essas expressões antes de prosseguir com as próximas seções. Se você tiver dúvidas, leia novamente esta seção e / ou consultar as seções anteriores sobre ponteiros e estruturas de dados.

Classes definidas com estrutura e uniãoAs classes podem ser definidas não só com palavra-chave classe , mas também com palavras-chave struct e união .

Os conceitos de classe e de estrutura de dados são tão semelhantes que tanto palavras-chave ( struct e classe ) pode ser usado em C + + para declarar classes (ou seja, struct s também pode ter membros de função em C + +, não apenas os membros de dados). A única diferença entre ambos é que os membros das classes declaradas com a palavra-chave struct têm acesso público, por padrão, enquanto os membros das classes declaradas com a palavra-chave classe tem acesso privado. Para todos os fins palavras-chave são equivalentes.

O conceito de união é diferente da de classes declaradas com struct e classe , já que os sindicatos só armazenam um membro de dados em um momento, mas, no entanto, eles também são classes e assim também pode se prender membros de função. O acesso padrão em classes de união é pública.

Classes (II)Sobrecarga de operadoresC + + incorpora a opção de utilizar os operadores padrão para realizar operações com classes para além dos tipos fundamentais. Por exemplo:

12int a, b, c, a = b + c;

Isto é obviamente um código válido em C + +, uma vez que as diferentes variáveis da adição são todos os tipos fundamentais. No entanto, não é tão óbvio que poderíamos realizar uma operação semelhante à seguinte:

12345

struct produto {string; float preço;} a, b, c, a = b + c;

Na verdade, isso irá causar um erro de compilação, já que não temos definido o comportamento de nossa classe deve ter com as operações de adição. No entanto, graças ao C + + recurso para sobrecarregar operadores, nós podemos projetar classes capaz de executar operações usando os operadores padrão. Aqui está uma lista de todos os operadores que podem ser sobrecarregados:

operadores pode ser sobrecarregado

+ - * / = <> + = -= *= / = <<>> <<=>> = == = <=> = + + -!% & ^! | ~ & = ^ = | = & & | | =% [] (), -> * -> Novo delete] [novo delete []

Page 84: Tutorial c++

Para sobrecarregar um operador, a fim de usá-lo com aulas declaramos funções de operador , que são funções regulares cujos nomes são os operadores -chave, seguida do sinal do operador que queremos sobrecarga. O formato é:

Inscreva-se operador de tipo (parâmetros) {} /*...*/

Aqui você tem um exemplo que sobrecarrega o operador de adição ( + ). Vamos criar uma classe para armazenar vetores bidimensionais e, em seguida, vamos acrescentar duas delas: uma (3,1) e b (1,2) . A adição de dois vetores bidimensionais é uma operação simples como adicionar os dois x coordenadas para obter o resultado x coordenar e adicionando os dois y coordenadas para obter o resultado y . Neste caso, o resultado será (3 +1,1 +2) = (4,3) .

1234567891011121314151617181920212223242526272829303132

/ / Vetores: a sobrecarga de operadores exemplo # include <iostream> usando namespace std;

classe CVector { público : int , y x; CVector () {}; CVector ( int , int ); CVector operador + (CVector);}; CVector: : CVector ( + (CVector param) {CVector temp.y + y = param .; y retorno (temp);}

int main () {CVector um (3,1); CVector b (1,2); CVector c, c = a + b; cout <<cx << "," << cy; retornar 0;}

Pode ser um pouco confuso de ver muitas vezes de modo que o CVector identificador. Mas, considere que alguns deles se referem ao nome da classe (tipo) CVector e alguns outros são funções com esse nome (construtores devem ter o mesmo nome da classe). Não confundi-los:

12CVector ( int , int ); / / nome da função CVector (construtor)CVector operador + (CVector); / função retorna / a CVector

A função de operador + de classe CVector é aquele que está encarregado de sobrecarregar o operador de adição ( + ). Esta função pode ser chamado de forma implícita usando o operador, ou explicitamente usando o nome da função:

12c = a + b, c = a. operador + (b);

Page 85: Tutorial c++

As duas expressões são equivalentes.

Observe também que nós incluímos o construtor vazio (sem parâmetros) e temos definido com um bloco vazio:

 CVector () {};

Isso é necessário, pois temos outro construtor declarado explicitamente:

 CVector ( int , int );

E quando explicitamente declarar todos os construtores, com qualquer número de parâmetros, o construtor padrão sem parâmetros que o compilador pode declarar automaticamente não seja declarado, por isso precisamos declará-la nós mesmos, a fim de ser capaz de construir objetos desse tipo sem parâmetros . Caso contrário, a declaração:

 CVector c;

incluído no main () não teria sido válido.

Enfim, tenho de avisá-lo que um bloco vazio é uma má aplicação de um construtor, uma vez que não cumpriu a funcionalidade mínima que geralmente é esperado de um construtor, que é a inicialização de todas as variáveis de membro de sua classe. No nosso caso o construtor deixa as variáveis x e y indefinido. Portanto, uma definição mais aconselhável seria algo semelhante a isto:

 CVector () {x = 0, y = 0;};

que, para simplificar e mostrar apenas o ponto do código eu não ter incluído no exemplo.

Bem como uma classe inclui um construtor padrão e um construtor de cópia, mesmo que não são declarados, mas também inclui uma definição padrão para o operador de atribuição ( = ) com a própria classe como parâmetro. O comportamento que é definido por padrão é copiar todo o conteúdo dos membros de dados do objeto passado como argumento (o do lado direito do sinal) para o outro no lado esquerdo:

123

CVector d (2,3); CVector e E = d; / / operador de atribuição de cópia

A cópia função operador de atribuição é a função de membro único operador implementada por padrão. Claro, você pode redefini-lo para qualquer outra funcionalidade que você deseja, como por exemplo, copiar apenas os membros certa classe ou executar procedimentos de inicialização adicionais.

A sobrecarga de operadores não força o seu funcionamento para suportar uma relação com o habitual ou significado matemático do operador, embora seja recomendado. Por exemplo, o código pode não ser muito intuitivo, se você usar o operador + para subtrair duas classes ouoperador == para preencher com zeros uma classe, embora seja perfeitamente possível fazê-lo.

Embora o protótipo de uma função de operador + pode parecer óbvio, uma vez que leva o que está no lado direito do operador como o parâmetro para a função de membro do operador do objeto em seu lado esquerdo, a outros operadores pode não ser tão óbvio. Aqui você tem uma tabela com um resumo sobre como funciona o operador diferentes têm de ser declaradas (substitua @ pelo operador, em cada caso):

Expressã Operador função de membro função global

Page 86: Tutorial c++

o

@ A + - * &! ~ + + - A:: operador @ () operador @ (A)

um @ + + - A:: operador @ (int) operador @ (A, int)

um @ b + - ^ / *% & | <==> = <=> = <<>> & & | |, A:: operador @ (B) operador @ (A, B)

um @ b = + = -= *= / =% = & = ^ = | = <<=> = [] A:: operador @ (B) -

a (b c. ..) () Um operador:: () (B, C. ..) -

a-> x -> Um operador::> () -

Sempre que um é um objeto da classe A , b é um objeto da classe B e C é um objeto da classe C .

Você pode ver neste painel que existem duas maneiras de sobrecarregar alguns operadores de classe: como uma função de membro e como uma função global. Seu uso é indistinta, no entanto, devo lembrar que as funções que não são membros de uma classe não pode acessar os membros private ou protected da classe a menos que a função global é o seu amigo (a amizade é explicado mais adiante).

A palavra-chave destaA palavra-chave desta representa um ponteiro para o objeto cujo membro função está sendo executada. É um ponteiro para o objeto em si.

Um de seus usos podem ser para verificar se um parâmetro passado para uma função de membro é o próprio objeto. Por exemplo,

12345678910111213141516171819202122

/ / Este # include <iostream> usando namespace std;

classe CDummy { público : int isitme (CDummy e param);};

int CDummy: isitme (CDummy e param) { se (e param == presente ) retornar true ; mais retornar false ; }

int main () {CDummy um; CDummy b * &a; = se (-> isitme b (a)) cout << "sim, e é uma b" ; retornar 0;}

sim, e é uma b

Também é freqüentemente usada em operador = funções membro que retornam objetos por referência (evitando o uso de objetos temporários). Seguindo com os exemplos do vetor visto antes, poderíamos ter escrito um operador = função semelhante a esta:

123456

CVector & CVector: operador = ( const CVector e param) {x = param.x; y = param.y; voltar * este ;}

Page 87: Tutorial c++

Na verdade, esta função é muito semelhante ao código que o compilador gera implicitamente para esta classe, se não incluir um operador = função de membro para copiar objetos dessa classe.

Os membros estáticosUma classe pode conter estático membros, dados ou funções.

Membros de dados estáticos de uma classe também são conhecidos como "variáveis de classe", porque só existe um valor único para todos os objetos da mesma classe. Seu conteúdo não é diferente de um objeto desta classe para outra.

Por exemplo, pode ser usado para uma variável dentro de uma classe que pode conter um contador com o número de objetos dessa classe que estão actualmente afectados, como no exemplo a seguir:

12345678910111213141516171819202122

/ / Membros estáticos em classes # include <iostream> usando namespace std;

classe CDummy { público : static int n; CDummy () {n + +;}; ~ CDummy () {n -;};};

int CDummy: n = 0;

int main () {CDummy um; CDummy b [5]; CDummy c * = nova CDummy; cout <<um <<endl; apagar c; cout <<CDummy: n <<endl; retornar 0;}

De fato, membros estáticos têm as mesmas propriedades como variáveis globais, mas eles gostam de escopo de classe. Por esse motivo, e para evitar que sejam declaradas várias vezes, só podemos incluir o protótipo (sua declaração) na declaração da classe, mas não a sua definição (sua inicialização). Para inicializar um membro de dados estáticos, que deve incluir uma definição formal fora da classe, no âmbito global, como no exemplo anterior:

 int CDummy: n = 0;

Porque é um valor variável única para todos os objetos da mesma classe, pode ser referido como um membro de qualquer objeto dessa classe ou mesmo diretamente pelo nome da classe (é claro que isso só é válido para membros estáticos):

12Cout <<uma; cout <<CDummy:: n;

Essas duas chamadas incluídas no exemplo anterior, se referindo à mesma variável: a variável estática n dentro da classe CDummy compartilhada por todos os objetos dessa classe.

Mais uma vez, devo lembrar que na verdade é uma variável global. A única diferença é o seu nome e eventuais restrições de acesso externo à classe.

Assim como podemos incluir dados estáticos em uma classe, podemos incluir também funções estáticas. Eles

Page 88: Tutorial c++

representam a mesma coisa: são funções globais que são chamados como se fossem membros do objeto de uma determinada classe. Eles só podem fazer referência a dados estáticos, em nenhum caso a membros não-estáticos da classe, bem como não permitir a utilização da palavra-chave deste , já que faz referência a um ponteiro de objeto e, de facto, essas funções não são membros de qualquer objeto, mas membros diretos da classe.

Amizade e herançafunções AmigoEm princípio, os membros privados e protegidos de uma classe não pode ser acessado de fora da mesma classe na qual elas são declaradas. No entanto, esta regra não afeta amigos .

Amigos são funções ou classes declaradas com o amigo -chave.

Se queremos declarar uma função externa, como amigo de uma classe, permitindo assim que esta função para ter acesso aos membros privados e protegidos da classe, nós fazemo-lo, declarando um protótipo desta função externa dentro da classe, e precedendo-o com a palavra-chave amigo :

1234567891011121314151617181920212223242526272829303132

Amigo / funções / # include <iostream> usando namespace std;

classe CRectangle { int largura, altura; pública : void set_values ( int , int ); int area () { retorno (altura * largura);} amigo CRectangle duplicado (CRectangle) ;};

void CRectangle: set_values ( int a, int b) {width = a; height = b;} CRectangle duplicado (rectparam CRectangle) CRectangle rectres {; rectres.width * rectparam.width = 2; rectres.height rectparam = . altura * 2; retorno (rectres);}

int main () {rect CRectangle, rectb; rect.set_values (2,3); rectb = duplicado (rect); cout rectb.area <(); retornar 0;}

A duplicação da função é um amigo de CRectangle . De dentro dessa função, temos sido capazes de acessar os membros de largura e altura de diferentes objetos do tipo CRectangle , que são membros privados. Observe que nem na declaração de duplicate () nem em seu uso posterior no main () temos observado duplicar um membro da classe CRectangle . Mas não é! Ele simplesmente tem acesso aos seus membros privados e protegidos, sem ser membro.

As funções de amigo pode servir, por exemplo, a realização de operações entre duas classes diferentes. Geralmente, o uso de funções é amigo de uma metodologia de programação orientada a objeto, por isso sempre que possível, é melhor usar membros da mesma classe para realizar operações com eles. Tal como no exemplo anterior, teria sido mais curto para integrar duplicado () dentro da classe CRectangle .

Page 89: Tutorial c++

classes amigoAssim como temos a possibilidade de definir uma função de amigo, também podemos definir uma classe como amigo do outro, garantindo que o acesso de primeira classe para os membros protected e private da segunda.

123456789101112131415161718192021222324252627282930313233343536

Classe / amigo / # include <iostream> usando namespace std;

classe CSquare;

classe CRectangle { int largura, altura; público : int area () { retorno (altura * largura);} void convert (CSquare a);};

classe CSquare { privado : int lado; pública : void set_side ( int a) {lado = a;} amigo de classe CRectangle;};

void CRectangle: converter (CSquare a) {width = a.side, altura a.side =;} int main () { sqr CSquare; CRectangle rect; sqr.set_side (4); rect.area (); <(rect.convert sqr); cout <

retorn 0;}

Neste exemplo, declaramos CRectangle como um amigo de CSquare modo que CRectangle funções membro podem ter acesso aos membros protegidos e privados de CSquare , mais concretamente para CSquare:: lado , que descreve a largura do lado do quadrado.

Você também pode ver algo novo no início do programa: uma declaração vazia de classe CSquare . Isso é necessário porque dentro da declaração de CRectangle nos referimos CSquare (como um parâmetro em convert () ). A definição deCSquare incluído mais tarde, então se não incluir uma declaração vazia anterior para CSquare esta classe não seria visível a partir da definição de CRectangle .

Considere que as amizades não são correspondidos, se não especificar explicitamente assim. No nosso exemplo, CRectangle é considerada como uma classe amigo por CSquare , mas CRectangle não considera CSquare ser um amigo, entãoCRectangle pode acessar o e privadas membros protegidos de CSquare mas não da maneira inversa. É claro que poderíamos ter declarado também CSquare como amigo CRectangle se quiséssemos.

Outra propriedade das amizades é que eles são não transitivo : O amigo de um amigo não é considerado para ser um amigo a menos que explicitamente especificado.

Herança entre classesUma característica fundamental de classes C + + é a herança. A herança permite criar classes que são derivadas de outras classes, de modo que automaticamente inclui alguns dos seus "pais" os membros, além de seus próprios. Por exemplo, vamos supor que queremos declarar uma série de classes de polígonos que

Page 90: Tutorial c++

descrevem como a nossa CRectangle , ou como CTriangle . Eles têm algumas propriedades comuns, como ambos podem ser descritas por meio de apenas dois lados: altura e base.

Isso poderia ser representado no mundo das classes com uma classe CPolygon das quais se derivam as duas outras: CRectangle e CTriangle .

 A classe CPolygon deve conter elementos que são comuns para ambos os tipos de polígono. No nosso caso: largura e altura . E CRectangle e CTriangle seriam suas classes derivadas, com características específicas que são diferentes de um tipo de polígono para o outro.

As classes que são derivadas de outras herdar todos os membros acessível da classe base. Isso significa que se uma classe base inclui um membro de A e obtemos que a outra classe com outro membro chamado B , a classe derivada contém ambos os membros A e B .

A fim de derivar uma classe da outra, usamos dois pontos ( : ) na declaração da classe derivada usando o seguinte formato:

derived_class_name classe: base_class_name público/*...*/ {};

Onde derived_class_name é o nome da classe derivada e base_class_name é o nome da classe em que se baseia. O público especificador de acesso pode ser substituído por qualquer um dos especificadores de acesso a outras protegidas eprivadas . Este especificador limita o acesso a níveis mais acessíveis para os membros herdados da classe base: Os membros com um nível mais acessíveis são herdadas com este nível em vez disso, enquanto os membros com mais restritivas de acesso de nível igual ou manter o seu nível restritivas na classe derivada .

1234567891011121314151617181920212223242526272829

/ / Classes derivadas # include <stdio.h>

class CPolygon { protegida : int largura, altura; públic : void set_values ( int a, int b) {width = a; height = b;}};

class CRectangle: public CPolygon { public : int area () { retorn (altura * largura);}};

class CTriangle: public CPolygon { public : int area () { retorn (altura * largura / 2);}}; int main () { CRectangle rect; CTriangle trgl; rect.set_values (4,5); trgl.set_values (4,5); printf (rect.area () ); printf (trgl.area ());

retorn 0; }

Page 91: Tutorial c++

30313233

Os objetos das classes CRectangle e CTriangle cada conter membros herdados CPolygon . Estes são: largura , altura e set_values () .

O protegido especificador de acesso é semelhante ao privado . Sua única diferença ocorre de fato com a herança. Quando uma classe herda de outra, os membros da classe derivada pode acessar os membros protected herdado da classe base, mas não os seus membros particulares.

Desde que queríamos largura e altura para ser acessível aos membros das classes derivadas CRectangle e CTriangle e não apenas por membros da CPolygon , temos utilizado protegida de acesso em vez de privada .

Podemos resumir os tipos de acesso diferentes de acordo com quem pode acessá-los da seguinte forma: 

Acessopúblic

oprotegida

sprivad

a

membros da mesma classe sim sim sim

membros de classes derivadas

sim sim não

não membros sim não não

Onde "não membros" representa qualquer acesso de fora da classe, como de main () , a partir de outra classe ou de uma função.

No nosso exemplo, os membros herdados por CRectangle e CTriangle ter as permissões de acesso mesmo que eles tinham na sua base de classe CPolygon :

12345

CPolygon: largura / / acesso protegidoCRectangle: largura / / acesso protegido

CPolygon: set_values () / / acesso públicoCRectangle: set_values () / acess / public

Isto é porque nós usamos o público palavra-chave para definir a relação de herança em cada uma das classes derivadas:

 classe CRectangle: público CPolygon {... }

Este público palavra-chave após os dois pontos ( : ) indica o nível mais acessíveis os membros herdados da classe que se lhe segue (neste caso CPolygon ) terá. Desde público é o nível mais acessível, especificando essa palavra-chave da classe derivada vai herdar todos os membros com os mesmos níveis que tinham na classe base.

Se especificar um nível de acesso mais restritivo como protegido , todos os membros públicos da classe base são herdadas, que é protegida na classe derivada. Considerando que se especifique mais a restrição de acesso a todos os níveis:privado , todos os membros da classe base são herdadas como privado.

Por exemplo, se a filha era uma classe derivada da mãe, que definimos como:

 classe filha: protegida mãe;

Page 92: Tutorial c++

Tal conjunto protegido como o nível máximo de acesso para os membros da filha que herdou da mãe . Ou seja, todos os membros que estavam no público a mãe ficaria protegida em filha . Claro, isso não restringiria a filha para declarar seus próprios membros do público. Esse nível de acesso máxima é definida apenas para os membros herdados da mãe .

Se não especificar explicitamente qualquer nível de acesso para a herança, o compilador assume particular para as classes declaradas com a classe de palavra-chave e público para as declaradas com struct .

O que é herdado da classe base?Em princípio, uma classe derivada herda todos os membros de uma classe base, exceto:

seu construtor e seu destruidor seu operador = () membros seus amigos

Embora os construtores e destruidores da classe base não são herdados si, o seu construtor padrão (ou seja, o construtor sem parâmetros) e seu destruidor é sempre chamado quando um novo objeto de uma classe derivada é criado ou destruído.

Se a classe base não tem nenhum construtor padrão ou você quer que um construtor sobrecarregado é chamado quando um novo objeto derivado é criado, você pode especificá-lo em cada definição de construtor da classe derivada:

derived_constructor_name (parâmetros): base_constructor_name (parâmetros) {...}

Por exemplo: 

123456789101112131415161718192021222324252627282930

/ / Construtores e classes derivadas # include <stdio>

class mãe { public : a mãe () {printf("mãe: sem parâmetros \ n");} mãe ( int a) {printf ("mãe: int \ n parâmetro ");}};

class filha: public mãe { public : a filha ( int a) {printf ("filha: parâmetro int \ n \ n");}};

class filho: public mãe { public : o filho ( int a): mãe () {printf ("o filho: parâmetro int \ \ n \ n");}};

int main () {filha cynthia (0); daniel filho (0); retorn 0;}

Page 93: Tutorial c++

Observe a diferença entre as quais a mãe é construtor é chamado quando um novo filha objeto é criado e que quando é um filho do objeto. A diferença é porque a declaração construtor da filha e filho :

12filha ( int a) / / nada especificado: padrão chamadafilho ( int a): mãe (uma) / / construtor especificado: chamar esse

Herança múltiplaEm C + + é perfeitamente possível que uma classe herda os membros de mais de uma classe. Isso é feito simplesmente separar as classes base diferentes com vírgulas na declaração da classe derivada. Por exemplo, se tivéssemos uma classe específica para impressão em tela ( COutput ) e quisemos que nosso classes CRectangle e CTriangle também herdam seus membros, para além das de CPolygon podemos escrever:

12class CRectangle: public CPolygon, public COutput;class CTriangle: public CPolygon, public COutput;

aqui está o exemplo completo: 

123456789101112131415161718192021222324252627

/ / Herança múltipla # include <stdio.h>

classe CPolygon { protegida : int largura, altura; public : void set_values ( int a, int b) { width = a; height = b; }};

classe COutput { públicos : void saída ( int i);

};

void COutput: saída ( int i) {printf (i);}

classe CRectangle: public CPolygon, public COutput { público : int area () { retorn largura (altura * );}};

classe CTriangle: público CPolygon, public COutput { public : int area () { retorn (altura * largura / 2);}}; int main () { CRectangle rect; trgl CTriangle; rect.set_values (4, 5); trgl.set_values (4,5); rect.output (rect.area ()); trgl.output (trgl.area ());

retorn 0;}

Page 94: Tutorial c++

282930313233343536373839404142

PolimorfismoAntes de chegar a este ponto, é recomendável que você tenha uma compreensão adequada dos ponteiros e herança de classe. Se qualquer uma das seguintes afirmações parecer estranho para você, você deve rever as seções indicadas:

Declaração: Explicado em:

int a: b (int c) {} Classes

a-> b Estruturas de Dados

Classe A: b pública {}; Amizade e herança

Ponteiros para classes baseUma das principais características de classes derivadas é que um ponteiro para uma classe derivada é de tipo compatível com um ponteiro para sua classe base. Polimorfismo é a arte de tirar proveito desse recurso simples, mas poderosa e versátil, que traz Metodologias Orientada a Objetos para o seu pleno potencial.

Vamos começar por reescrever o nosso programa sobre o retângulo eo triângulo da seção anterior, tendo em conta esta propriedade compatibilidade ponteiro:

12345678910

/ / Ponteiros para classe base # include <iostream> usando namespace std;

classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;}};

classe CRectangle : público CPolygon {

Page 95: Tutorial c++

11121314151617181920212223242526272829303132333435

público : int area () { retorno (altura * largura);}};

classe CTriangle: público CPolygon { público : int area () { retorno (altura * largura / 2);}};

int main () {CRectangle rect; trgl CTriangle; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl;-> set_values ppoly1 (4,5);-> set_values ppoly2 (4,5); cout <<rect.area () <<endl ; cout trgl.area <() <<endl; retornar 0;}

Em função principal , criamos dois ponteiros que apontam para objetos de classe CPolygon ( ppoly1 e ppoly2 ). Então vamos atribuir referências ao rect e trgl a esses ponteiros, e porque ambos são objetos de classes derivadas de CPolygon , ambos são operações de atribuição é válida.

A única limitação no uso ppoly1 * e * ppoly2 vez de rect e trgl é que ambos ppoly1 * e * ppoly2 são do tipo * CPolygon e, portanto, só podemos utilizar esses ponteiros para se referir aos membros que CRectangle e CTriangle herdam CPolygon . Por essa razão, quando nós chamamos a área () membros, no final do programa, tivemos de usar diretamente os objetos rect e trgl em vez de os ponteiros ppoly1 * e * ppoly2 .

A fim de utilizar área () com os ponteiros para a classe CPolygon , esse membro deve também ter sido declarado na classe CPolygon , e não apenas em suas classes derivadas, mas o problema é que CRectangle e CTriangle implementar diferentes versões de área , por isso não pode aplicá-lo na classe base. Isto é, quando se tornam membros virtuais em mãos:

membros VirtualUm membro de uma classe que pode ser redefinida em suas classes derivadas é conhecida como um membro virtual. Para declarar um membro de uma classe como virtual, que deve preceder sua declaração com a palavra-chave virtual :

1234567891011121314151617

/ / Membros virtuais # include <iostream> usando namespace std;

classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;} virtual int area () { retorno (0);}};

classe CRectangle: público CPolygon { público : int area () { retorno (altura * largura);}};

classe CTriangle: público CPolygon { público : int area () { retorno (altura * largura / 2);}};

Page 96: Tutorial c++

181920212223242526272829303132333435363738394041

int main () {CRectangle rect; CTriangle trgl; CPolygon poli; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl; CPolygon * ppoly3 = &poly;-> set_values (4,5);-> set_values ppoly3 (4,5); cout <<,> Área ppoly1 () <<endl; cout <<,> Área ppoly2 () <<endl; cout <<ppoly3-> area () <<endl; retornar 0;}

Agora as três classes ( CPolygon , CRectangle e CTriangle ) têm todos os membros da mesma: largura , altura , set_values () e Area () .

A função de membro area () foi declarada como virtual na classe base, porque é tarde redefinido em cada classe derivada. Você pode verificar se quiser que se você remover esse virtual palavra-chave da declaração de área () dentro CPolygon , e depois de executar o programa, o resultado será 0 para os três polígonos ao invés de 20 , 10 e 0 . Isso é porque em vez de chamar o correspondente área () função para cada objeto ( CRectangle: area () , CTriangle:: Área () e CPolygon: area () , respectivamente), CPolygon:: Área () será chamado em todos os casos, uma vez que as chamadas são através de um ponteiro cujo tipo é * CPolygon .

Portanto, o que o virtual não é palavra-chave para permitir que um membro de uma classe derivada com o mesmo nome de um na classe base deve ser chamado apropriadamente de um ponteiro e, mais precisamente quando o tipo do ponteiro é um ponteiro para a classe base mas está apontando para um objeto da classe derivada, como no exemplo acima.

Uma classe que declara ou herda uma função virtual é chamada de classe polimórfica .

Note que, apesar da sua virtualidade, também temos sido capazes de declarar um objeto do tipo CPolygon e chamar o seu próprio domínio () função, que sempre retorna 0.

base de classes abstratasclasses base abstratas são algo muito semelhante ao nosso CPolygon classe do nosso exemplo anterior. A única diferença é que no nosso exemplo anterior, temos definido um válido área () função com um mínimo de funcionalidade para os objetos que eram de classe CPolygon (como o objeto poli ), enquanto em uma classe abstrata de base poderíamos deixar essaárea () membro funcionar sem a aplicação a todos. Isso é feito acrescentando = 0 (igual a zero) para a declaração da função.

Um resumo da classe base CPolygon poderia ficar assim:

1234567

/ / Classe abstrata CPolygon classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;} virtual int área () = 0;};

Page 97: Tutorial c++

89

Observe como nós anexado = 0 para área int virtual () em vez de especificar uma aplicação para a função. Este tipo de função é chamada de função virtual pura , e todas as classes que contêm pelo menos uma função virtual pura são classes base abstratas .

A principal diferença entre uma classe base abstrata e uma classe regular polimórficos porque é que nas classes de base abstrata, pelo menos, um dos seus membros não tem aplicação não podemos criar instâncias (objetos) do mesmo.

Mas uma classe que não pode instanciar objetos não é totalmente inútil. Nós podemos criar ponteiros para isso e tirar proveito de todas as suas capacidades polimórficas. Portanto uma declaração como:

 CPolygon poli;

não seria válida para a classe base abstrata que acabamos de declarar, porque tenta instanciar um objeto. No entanto, as seguintes indicações:

12CPolygon * ppoly1; CPolygon * ppoly2;

seria perfeitamente válido.

Isto é assim por quanto tempo CPolygon inclui uma função virtual pura e, portanto, é uma classe abstrata de base. No entanto, os ponteiros para essa classe abstrata de base pode ser usado para apontar para objetos de classes derivadas.

Aqui você tem o exemplo completo: 

1234567891011121314151617181920212223242526272829

/ / Classe abstrata de base # include <iostream> usando namespace std;

classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;} virtual int área ( void ) = 0;};

classe CRectangle: público CPolygon { público : int área ( void ) { retorno (altura * largura);}};

classe CTriangle: público CPolygon { público : int área ( void ) { retorno (altura * largura / 2);}};

int main () {CRectangle rect; trgl CTriangle; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl;-> set_values set_values (4,5>); tribunal ppoly2 <ppoly1-> Área <() <<endl; cout <<,> Área ppoly2 () <<endl; retornar 0;}

Page 98: Tutorial c++

30313233343536

Se você revisar o programa que você irá notar que nos referimos a objetos de classes diferentes, mas relacionados com um tipo único de ponteiro ( CPolygon * ). Isto pode ser tremendamente útil. Por exemplo, agora podemos criar uma função de membro da classe base abstracta CPolygon que é capaz de imprimir na tela o resultado da área () função, emboraCPolygon em si não tem aplicação para essa função:

123456789101112131415161718192021222324252627282930313233343536373839

/ / Pure membros virtuais podem ser chamados / / na classe base abstrata # include <iostream> usando namespace std;

classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;} virtual int área ( void ) = 0; void printArea ( void ) {cout << este -> Área () <<endl;}};

classe CRectangle: público CPolygon { público : int área ( void ) { retorno (altura * largura);}};

classe CTriangle: público CPolygon { público : int área ( void ) { retorno (altura * largura / 2);}};

int main () {CRectangle rect; trgl CTriangle; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl;-> set_values (4,5);-> set_values ppoly2 (4,5);-> printArea ppoly1 ();-> printArea ppoly2 (); retornar 0;}

membros Virtual e classes abstratas conceder C + + as características polimórficas que fazem programação orientada a objetos como um instrumento útil em grandes projetos. Claro, temos visto muito simples usa esses recursos, mas esses recursos podem ser aplicados a arrays de objetos ou objetos alocados dinamicamente.

Vamos terminar com o mesmo exemplo de novo, mas desta vez com os objetos que são alocados dinamicamente:

12/ Alocação dinâmica de / e polimorfismo # include <iostream>

Page 99: Tutorial c++

34567891011121314151617181920212223242526272829303132333435363738

usando namespace std;

classe CPolygon { protegida : int largura, altura; pública : void set_values ( int a, int b) {width = a; height = b;} virtual int área ( void ) = 0; void printArea ( void ) {cout << este -> Área () <<endl;}};

classe CRectangle: público CPolygon { público : int área ( void ) { retorno (altura * largura);}} ;

classe CTriangle: público CPolygon { público : int área ( void ) { retorno (altura * largura / 2);}};

int main () {CPolygon ppoly1 * = nova CRectangle; CPolygon ppoly2 * = nova CTriangle;-> set_values ppoly1 (4,5);-> set_values ppoly2 (4,5);-> printArea ppoly1 ();-> printArea ppoly2 (); excluir ppoly1; excluir ppoly2; retornar 0;}

Observe que o ppoly ponteiros:

12CPolygon ppoly1 * = nova CRectangle; CPolygon ppoly2 * = nova CTriangle;

são declaradas sendo do tipo ponteiro para CPolygon mas os objetos dinamicamente alocados tenham sido declarados com o tipo de classe derivada diretamente.

ModelosModelos de funçãoFunção modelos são funções especiais que podem operar com tipos genéricos . Isso nos permite criar um modelo de função cuja funcionalidade pode ser adaptado para mais de um tipo ou classe, sem repetir o código inteiro para cada tipo.

Em C + +, este pode ser conseguido usando parâmetros do modelo . Um parâmetro do modelo é um tipo especial de parâmetro que pode ser usado para passar um tipo como argumento: como regular os parâmetros de função pode ser usada para passar os valores para uma função, parâmetros do modelo permitem a passagem também tipos para uma função. Estes modelos de função pode usar esses parâmetros como se fossem qualquer outro tipo regular.

O formato para declarar a função de modelos com parâmetros de tipo é:

<class modelo function_declaration identificador>;template <typename identificador>;

Page 100: Tutorial c++

A única diferença entre os dois protótipos é o uso de qualquer palavra-chave classe ou a palavra-chave typename . Seu uso é indiferente, pois ambas as expressões têm exatamente o mesmo significado e se comportam exatamente da mesma maneira.

Por exemplo, para criar um modelo de função que retorna o maior um dos dois objetos que podemos usar: 

1234

template < classe myType> myType GetMax (myType um myType, b) { retorno (a, b> b? a:);}

Aqui nós criamos uma função de modelo com myType como parâmetro do modelo. Este parâmetro do modelo representa um tipo que ainda não foi especificado, mas que pode ser usado na função de modelo como se fosse um tipo normal. Como você pode ver, o modelo de função GetMax retorna o maior dos dois parâmetros deste tipo ainda indefinido.

Para utilizar este modelo de função usamos o seguinte formato para chamar a função:

<tipo> function_name (parâmetros);

Por exemplo, para chamar GetMax para comparar dois valores inteiros do tipo int , podemos escrever:

12int x, y; GetMax < int > (x, y);

Quando o compilador encontra esta chamada para uma função de modelo, ele usa o modelo para gerar automaticamente a função de substituir cada aparição de myType pelo tipo passado como o parâmetro do modelo atual ( int neste caso) e depois chama-lo. Este processo é realizado automaticamente pelo compilador e é invisível para o programador.

Aqui está o exemplo completo:

1234567891011121314151617181920

/ Template / função # include <iostream> usando namespace std;

modelo < classe T> T GetMax (T a, T b) {resultado T; resultado = (a> b)? a: b; retorno (resultado);}

int main () { int i = 5, k = 6, j; longa l = 10, m = 5, n, k = GetMax < int > (i, j); n = GetMax < longo > (l, m); cout <<k <<endl; cout <<n <<endl; retornar 0;}

Neste caso, usamos T como o nome do parâmetro do modelo, em vez de myType porque é mais curto e de fato é um modelo de parâmetro de nome muito comum. Mas você pode usar qualquer identificador que quiser.

No exemplo acima foi utilizado o modelo de função GetMax () duas vezes. A primeira vez com argumentos do tipo int eo segundo com argumentos do tipo longa . O compilador tem instanciado e chamado então cada vez

Page 101: Tutorial c++

que a versão apropriada da função.

Como você pode ver, o tipo T é usado dentro do GetMax () função de modelo mesmo para declarar novos objetos desse tipo:

 T resultado;

Portanto, resultado será um objeto do mesmo tipo como os parâmetros de um e b quando o modelo é instanciado com um tipo específico.

Neste caso específico em que o tipo genérico T é usado como parâmetro para GetMax o compilador pode descobrir automaticamente que tipo de dados tem para criar uma instância sem explicitamente especificá-lo nos parênteses em ângulo (como nós fizemos antes de especificar <int> e <longo > ). Assim, poderíamos ter escrito em vez disso:

12int i, j; GetMax (i, j);

Uma vez que tanto i e j são do tipo int , o compilador pode automaticamente descobrir que o parâmetro do modelo só pode ser int . Este método implícito produz exatamente o mesmo resultado:

123456789101112131415161718

/ / Função do modelo II # include <iostream> usando namespace std;

modelo < classe T> T GetMax (T a, T b) { retorno (a, b>? a, b);}

int main () { int i = 5 , k = 6, j; longa l = 10, m = 5, n, k = GetMax (i, j), n = GetMax (m l); cout <<k <<endl; cout <<n << endl; retornar 0;}

Note como neste caso, chamamos o nosso modelo de função GetMax () sem especificar explicitamente o tipo de ângulo entre parênteses <> . O compilador determina automaticamente o tipo é necessária em cada chamada.

Porque a nossa função de modelo inclui apenas um parâmetro do modelo ( classe T ) e do modelo de função em si aceita dois parâmetros, ambos da T tipo, não podemos chamar de nosso modelo de função com dois objetos de diferentes tipos de argumentos:

123

int i;longa l, k = GetMax (, l i);

Isso não seria correto, desde a nossa GetMax modelo de função espera dois argumentos do mesmo tipo, e neste convite a ele que use objetos de dois tipos diferentes.

Page 102: Tutorial c++

Nós também podemos definir a função de modelos que aceitam mais de um parâmetro do tipo, simplesmente especificando mais parâmetros de modelo entre os colchetes. Por exemplo:

1234

template < classe T, classe U> T getMin (T a, U b) { retorno (a, b <b? a:);}

Neste caso, nosso modelo de função getMin () aceita dois parâmetros de tipos diferentes e retorna um objeto do mesmo tipo do primeiro parâmetro ( T ) que é passado. Por exemplo, depois daquela declaração que poderíamos chamar getMin () com:

123

int i, j;longa l, i = getMin < int , longo > (l, j);

ou simplesmente:

 i = getMin (l, j);

embora j e l têm diferentes tipos, uma vez que o compilador pode determinar a instanciação de qualquer maneira adequada.

Modelos de classeTemos também a possibilidade de escrever modelos de classe, de modo que uma classe pode ter membros que utilizam parâmetros do modelo como tipos. Por exemplo:

123456789

template < classe T>classe mypair {valores de T [2]; público : mypair (primeiro T, T segundo) {valores [0] = primeiro; valores [1] = segundo;}};

A classe que acabamos de definir serve para armazenar dois elementos de qualquer tipo válido. Por exemplo, se quiséssemos declarar um objeto desta classe para guardar dois valores inteiros do tipo int com os valores de 115 e 36 poderíamos escrever:

 mypair < int myobject> (115, 36);

esta mesma classe também seria usado para criar um objeto para armazenar qualquer outro tipo:

 mypair < duplo > myfloats (3,0, 2,18);

A função de membro somente no modelo de classe anterior foi definido em linha com a declaração de classe em si. No caso de se definir uma função membro fora da declaração do modelo de classe, devemos sempre preceder a definição com o modelo <...> prefixo:

1/ / Modelos de classe

Page 103: Tutorial c++

234567891011121314151617181920212223242526

# include <iostream> usando namespace std;

modelo < classe T>classe mypair {T a, b; público : mypair (primeiro T, T segundo) {a = primeiro, b = segundo;} T GetMax () ;};

modelo < classe T> T <T> mypair: GetMax () {retval T; retval = a> b? a: b; retorno retval;}

int main () {mypair < int > myobject (100, 75); cout <<myobject.getmax (); retornar 0;}

Observe que a sintaxe da definição de GetMax função de membro:

12template < classe T> T <T> mypair: GetMax ()

Confuso com tantos T s '? Há três T 's nesta declaração: O primeiro é o parâmetro do modelo. O segundo T se refere ao tipo retornado pela função. E o terceiro T (o ângulo entre parênteses) é também uma exigência: Especifica que a função do modelo de parâmetro este também é o parâmetro do modelo de classe.

Modelo de especializaçãoSe queremos definir uma implementação diferente para um modelo quando um tipo específico é passado como parâmetro do modelo, podemos declarar uma especialização desse modelo.

Por exemplo, vamos supor que temos uma classe muito simples chamado MyContainer que pode armazenar um elemento de qualquer tipo e que tem apenas uma função de membro chamada aumentar , o que aumenta seu valor. Mas nós achamos que quando se armazena um elemento do tipo char que seria mais conveniente ter uma implementação completamente diferente com um membro da função de letras maiúsculas , por isso decidimos declarar uma especialização de modelo para esse tipo de classe:

12345678910111213141516

/ / Especialização modelo # include <iostream> usando namespace std;

/ template / aula: modelo < classe T>classe MyContainer {elemento T; público : MyContainer (arg T) = elemento {arg;} T aumento () { retorno + + elemento;}};

/ especialização de modelo de classe: template <>class MyContainer < char > { char elemento; público : MyContainer ( char arg) {arg = elemento;} char maiúsculas () { se (> elemento (= 'a' ) & & (<= elemento 'z' )) elemento + = 'A' - 'a' ; retorno elemento;}};

Page 104: Tutorial c++

171819202122232425262728293031323334

int main () {MyContainer < int > myint (7); <MyContainer char MyChar> ( 'j' ); cout <<myint.increase () <<endl; cout mychar.uppercase <() <<endl; retornar 0;}

Esta é a sintaxe usada no modelo de especialização da classe:

 template <> class MyContainer < char > {... };

Antes de mais nada, aviso que precede o nome do modelo de classe com um vazio template <> lista de parâmetros. Este é declarar explicitamente como um modelo de especialização.

Mas mais importante que esse prefixo, é o <char> parâmetro de especialização após o nome do modelo de classe. Este parâmetro de especialização em si identifica o tipo para o qual vamos declarar uma classe de modelo de especialização ( char ).Observe as diferenças entre o modelo de classe genérica e especialização:

12template < classe T> classe MyContainer {... };template <> class MyContainer < char > {... };

A primeira linha é o modelo genérico, ea segunda é a especialização.

Quando declaramos especializações de uma classe de modelo, devemos também definir todos os seus membros, mesmo aqueles exatamente igual à classe de modelo genérico, porque não há "herança" dos membros a partir do modelo genérico para a especialização.

parâmetros não-tipo para modelosAlém dos argumentos de modelo que são precedidas pela classe ou typename palavras-chave, que representam tipos, modelos também podem ter regular os parâmetros digitados, semelhantes aos encontrados em funções. Como exemplo, ter um olhar para este modelo de classe que é usado para conter sequências de elementos:

1234567891011121314

/ Template seqüência / # include <iostream> usando namespace std;

modelo < classe T, int N>classe mysequence {T MemBlock [N]; pública : void setmember ( int x, o valor T); T GetMember ( int x);} ;

modelo < classe T, int N>void <T,N> mysequence: setmember ( int x, o valor T) {MemBlock [x] = valor;}

modelo < classe T, int N> <T,N> mysequence T :: GetMember ( int x) { retorno MemBlock [x];}

int main () {mysequence < int , 5> myInts; mysequence < duplos , 5> myfloats; myints.setmember (0100); myfloats.setmember (3,3.1416 ); cout <<myints.getmember (0) <<

Page 105: Tutorial c++

1516171819202122232425262728293031

<<myfloats.getmember (3) << '\ n' ; retornar 0;}

Também é possível definir valores padrão ou tipos de parâmetros do modelo de classe. Por exemplo, se a definição do modelo anterior de classe foram:

 template < classe T = char , int N = 10> classe {..}; mysequence

Podemos criar objetos usando os parâmetros de modelo padrão, declarando:

 mysequence> <myseq;

Qual seria equivalente a:

 mysequence < char , 10> myseq;

Modelos e projetos de vários arquivosDo ponto de vista do compilador, os modelos não são funções normais ou classes. Eles são compilados em demanda, significando que o código de uma função de modelo não é compilado até uma instanciação com argumentos de modelo específico é necessário. Naquele momento, quando uma instância for necessário, o compilador gera uma função específica para esses argumentos do modelo.

Quando os projetos crescem é usual dividir o código de um programa em diferentes arquivos de código fonte. Nestes casos, a interface ea implementação são geralmente separados. Tomando uma biblioteca de funções como exemplo, a interface geralmente consiste de declarações dos protótipos de todas as funções que podem ser chamados. Estes são geralmente declarados em um arquivo "header" com uma extensão h. Ea implementação (a definição dessas funções) está em um arquivo independente, com código C + +.

Como os modelos são compilados quando requerido, isso força uma restrição para projetos multi-arquivo: a execução (definição) de uma classe de modelo ou a função deve estar no mesmo arquivo como sua declaração. Isso significa que não podemos separar a interface em um arquivo de cabeçalho separado, e que deve incluir tanto a interface e implementação em qualquer arquivo que usa os modelos.

Já que nenhum código é gerado até que um modelo é instanciado quando necessário, compiladores estão dispostos a permitir a inclusão de mais de uma vez o arquivo de mesmo modelo com as duas declarações e definições em um projeto sem gerar erros de ligação.

NamespacesNamespaces permitem entidades do grupo, como classes, objetos e funções sob um nome. Desta forma, o escopo global podem ser divididos em "sub-espaços, cada uma com seu próprio nome.

O formato de namespaces é:

Page 106: Tutorial c++

namespace identificador{entidades}

Onde identificador é qualquer identificador válido e entidades é o conjunto de classes, objetos e funções que estão incluídos dentro do namespace. Por exemplo:

1234

namespace myNamespace { int a, b;}

Neste caso, as variáveis de um e b são variáveis normais declarada dentro de um namespace chamado myNamespace . Para acessar essas variáveis de fora domyNamespace namespace temos que usar o operador de escopo :: . Por exemplo, para acessar as variáveis anteriores de fora myNamespace podemos escrever:

12myNamespace: um myNamespace: b

A funcionalidade dos namespaces é especialmente útil no caso em que há uma possibilidade de que um objeto global ou função usa o mesmo identificador como um outro, causando erros de redefinição. Por exemplo:

12345678910111213141516171819

/ / Namespaces # include <iostream> usando namespace std;

namespace {primeiro int var = 5;}

namespace segundo { dupla var = 3,1416;}

int main () {tribunal <<primeiro: </ var <endl; cout <<segunda :: var <<endl; retornar 0;}

5 3.1416

Neste caso, há duas variáveis globais com o mesmo nome: var . Uma delas é definida dentro do namespace primeiro e outro no segundo . Sem erros redefinição acontecer graças aos namespaces.

usandoA palavra-chave usando é usada para introduzir um nome de um espaço para a declarativa região atual. Por exemplo:

12345678

/ / Usando # include <iostream> usando namespace std;

namespace primeiro { int x = 5; int y = 10;}

namespace segundo {

Page 107: Tutorial c++

910111213141516171819202122232425

dupla x = 3,1416; duplo y = 2,7183;}

int main () { usando primeiro: x ; usando segunda: y; cout <<x <<endl; cout <<y <<endl; cout <<primeira: y <<endl; cout <<segunda: x <<endl; retornar 0;}

Note como neste código, x (sem qualquer qualificativo nome) refere-se a primeira: x Considerando que y se refere à segunda: y , exatamente como o nossocom as declarações especificadas. Nós ainda temos acesso à primeira: y e segunda: x usando seus nomes totalmente qualificados.

A palavra-chave usando também pode ser usado como uma directiva relativa à introdução de um namespace inteiro:

123456789101112131415161718192021222324

/ / Usando # include <iostream> usando namespace std;

namespace {primeiro int x = 5; int y = 10;}

namespace segundo { dupla x = 3,1416; duplo y = 2,7183;}

int main () { usando namespace tribunal; primeiro <<x <<endl; cout <<y <<endl; cout <<segunda: x <<endl; cout <<segunda: y <<endl; retornar 0;}

Neste caso, desde que tenham declarado que estávamos usando o primeiro espaço , todos os usos diretos de x e y sem qualificadores nome estava se referindo às suas declarações no primeiro espaço .

usando e usando namespace ter validade apenas no mesmo bloco em que estão estabelecidas ou em todo o código se eles são usados diretamente no escopo global. Por exemplo, se tivéssemos a intenção de usar primeiro os objetos de um namespace e então os de outro, poderíamos fazer algo como:

1234

/ / Namespace exemplo, usando # include <iostream> usando namespace std;

namespace primeiro {

5 3.1416

Page 108: Tutorial c++

5678910111213141516171819202122232425

int x = 5;}

namespace segundo { dupla x = 3,1416;}

int main () {{ usando namespace em primeiro lugar; cout <<x <<endl; {} usando namespace segundo; cout <<x <<endl;} retornar 0;}

alias Namespace

Podemos declarar nomes alternativos para namespaces existentes de acordo com o seguinte formato:

namespace new_name current_name =;

Namespace stdTodos os arquivos na biblioteca C + + padrão de declarar todas as suas entidades no std namespace. É por isso que geralmente incluíram o using namespace std; declaração em todos os programas que usou qualquer entidade definida no iostream .

ExceçõesExceções fornecem uma maneira de reagir a circunstâncias excepcionais (como erros de runtime) no nosso programa de transferência de controle para funções especiais chamados manipuladores .

Para capturar exceções, devemos colocar uma porção de código sob inspeção exceção. Isto é feito colocando a parte do código em um bloco try . Quando uma circunstância excepcional surge dentro desse bloco, será apresentada uma exceção que transfere o controle para o manipulador de exceção. Se nenhuma exceção é lançada, o código continua normalmente e todos os manipuladores são ignorados.

Uma exceção é lançada, usando a palavra-chave throw de dentro do bloco try. manipuladores de exceção são declarados com a palavra-chave catch , que deve ser colocado imediatamente após o bloco try:

123456789101112131415

/ / Exceção # include <iostream> usando namespace std;

int main () { tentar { throw 20;} catch ( int e) {cout << "Ocorreu uma exceção Nr. exceção". <<e <<endl;} retorno 0;}

Uma exceção ocorreu. Exceção n º. 20

Page 109: Tutorial c++

O código sob a manipulação de exceção é fechado em uma tentativa de bloco. Neste exemplo, esse código simplesmente gera uma exceção:

 jogar 20;

Uma expressão de jogar aceita um parâmetro (neste caso o valor inteiro 20 ), que é passada como um argumento para o manipulador de exceção.

O manipulador de exceção é declarada com a captura de palavras-chave. Como você pode ver, segue-se imediatamente a chave de fechamento das tentar bloquear. O formato de captura é semelhante a uma função normal, que sempre tem pelo menos um parâmetro. O tipo desse parâmetro é muito importante, pois o tipo do argumento passado pela expressão throw é marcada contra ele, e somente no caso de eles corresponderem, a exceção é capturada.

Podemos encadear vários manipuladores (catch expressões), cada uma com um tipo de parâmetro diferentes. Apenas o manipulador que corresponde ao seu tipo com o argumento especificado na instrução throw é executado.

Se usamos reticências ( ... ) como o parâmetro de captura , que o manipulador vai pegar qualquer exceção, não importa qual o tipo do lance exceção é. Isso pode ser usado como um manipulador padrão que captura todas as exceções não capturadas pelos manipuladores, se isso é especificado no passado:

123456

tente { / / código aqui}catch ( int param) {cout << "Exceção int" ;}catch ( char param) {cout << "Exceção char" ;}catch (...) {cout << exceção padrão " " ;}

Neste caso, o último manipulador iria pegar qualquer exceção lançada com qualquer parâmetro que não é nem um int nem umchar .

Após uma exceção tem sido tratado o programa de execução é retomada após o try-catch bloco, e não após o lance indicação!.

Também é possível aninhar try-catch blocos dentro de mais externa tente blocos. Nestes casos, temos a possibilidade de que um interno de captura para a frente do bloco a excepção ao seu nível externo. Isso é feito com a expressão throw; sem argumentos.Por exemplo:

1234567891011

tente { tentar { / / Código aqui } catch ( int n) { throw ;}}catch (...) {cout << "Exceção" ;}

especificações de exceção

Quando declarar uma função que pode limitar o tipo de exceção que possam, direta ou indiretamente lançar anexando um lancesufixo para a declaração de função:

 float myfunction ( char param) throw ( int );

Page 110: Tutorial c++

Isto declara uma função chamada myfunction que leva um agument do tipo char e retorna um elemento do tipo float . A única exceção que esta função pode lançar uma exceção do tipo int . Se ele lança uma exceção com um tipo diferente, directa ou indirectamente, não pode ser capturada por um regular int tipo de manipulador.

Se esse lance especificador for deixado vazio sem nenhum tipo, isso significa que a função não é permitido lançar exceções.Funções sem jogar especificador (funções regulares) estão autorizados a lançar exceções de qualquer tipo:

12int myfunction ( int param) throw (); / sem exceções permitidas int myfunction ( int param); / / todas as exceções permitidas

exceções StandardA biblioteca C + + padrão fornece uma classe base especificamente concebido para declarar objetos a serem lançados como exceções. Ela é chamada de exceção e é definido no <exception> arquivo de cabeçalho sob o namespace std . Essa classe tem o padrão normal e copiar os construtores, operadores e destruidores, além de uma função membro virtual adicional chamado o que retorna uma seqüência de caracteres NULL-finalizada ( char * ) e que pode ser substituído em classes derivadas para conter algum tipo de descrição do exceção.

123456789101112131415161718192021222324

/ / Exceções padrão # include <iostream> # include <exception> usando namespace std;

classe MyException: público exceção { virtual const char * que () const jogar () { retorno "Minha exceção aconteceu" ;}} myex;

int main ( ) { tentar { throw myex;} catch (exceção & e) {tribunal <<e.what () <<endl;} retornar 0;}

Meu exceção aconteceu.

Nós colocamos um manipulador de exceção que pega objetos por referência (note o comercial e depois o tipo), pois esta captura também classes derivadas de exceção , como o nosso myex objeto da classe MyException .

Todas as exceções geradas por componentes da biblioteca C + + padrão lançar exceções derivadas deste std: exceção: classe.São eles:

exceção descrição

bad_alloc Descartado pelo novo em caso de falha de alocação

bad_cast Descartado por dynamic_cast quando não com um tipo de referência

bad_exception Descartado quando um tipo de exceção não corresponde a nenhuma das

Page 111: Tutorial c++

capturas

bad_typeid Descartado por typeid

ios_base: falha Descartado por funções na biblioteca iostream

Por exemplo, se usamos o operador de novo ea memória não pode ser atribuído, uma exceção do tipo bad_alloc é lançada:

12345678

tente{ int * myarray = novos int [1000];}catch (bad_alloc &) {cout << "Erro ao alocar memória." <<endl;}

Recomenda-se incluir todas as alocações de memória dinâmica dentro de um bloco try que trava esse tipo de exceção para realizar uma ação de limpeza em vez de uma finalização anormal do programa, que é o que acontece quando esse tipo de exceção é lançada e não pegou. Se quiser forçar uma bad_alloc exceção para vê-lo em ação, você pode tentar alocar uma enorme variedade; No meu sistema, tentando alocar 1.000 milhões int s jogou uma bad_alloc exceção.

Porque bad_alloc é derivado do padrão da classe base exceção , podemos lidar com essa mesma exceção pegando referências àexcepção da classe:

12345678910111213141516

/ / Padrão de exceção bad_alloc <iostream> # include # include <exception> usando namespace std;

int main () { tentar { int * myarray = novos int [1000];} catch (exceção & e) {cout << "exceção padrão: " <e.what <() <<endl;} retornar 0;}

Tipo castingConvertendo a expressão de um determinado tipo em outro tipo é conhecido como tipo-casting . Nós já vimos algumas maneiras de conversão de tipo:

Conversão implícitaAs conversões implícitas não requerem qualquer operador. Eles são executados automaticamente quando o valor é copiado para um tipo compatível. Por exemplo:

123

curto = 2000, umint b, b = a;

Aqui, o valor de um foi promovido a partir curto para int e nós não tivemos que especificar qualquer tipo de vazamento do operador. Isso é conhecido como uma conversão padrão. conversões Standard afetam os tipos de dados fundamentais, bem como permitir conversões, tais como a conversão entre tipos numéricos

Page 112: Tutorial c++

( curto para int , int para float , dobro para int ...), ou de bool , e algumas conversões de ponteiro. Algumas destas conversões pode implicar uma perda de precisão, que o compilador pode sinalizar com uma advertência. Isto pode ser evitado com uma conversão explícita.

As conversões implícitas incluem também construtor ou operador de conversões, que afetam as classes que incluem construtores ou funções específicas do operador para realizar conversões. Por exemplo:

12345

class A {};classe B { público : B (A a) {}}; = a b a A, B;

Aqui, uma conversão implícita aconteceu entre os objetos da classe A e classe B , pois B tem um construtor que leva um objeto da classe A como parâmetro. Portanto conversões implícitas de um para B são permitidos.

conversão explícitaC + + é uma linguagem fortemente tipada. Muitas conversões, especialmente aqueles que implicam uma interpretação diferente do valor, necessitam de uma conversão explícita. Já vimos duas notações para a conversão explícita: funcional e c-como a seleção de elenco:

1234

short a = 2000;int b, b = ( int ) a; / c-cast, como a notaçãob = int (a); / funcional notação /

A funcionalidade destes operadores de conversão explícita é suficiente para a maioria das necessidades de tipos de dados fundamentais. No entanto, estes operadores podem ser aplicados indiscriminadamente em classes e ponteiros para classes, que pode levar a um código que ao ser sintaticamente correta pode causar erros de execução. Por exemplo, o código a seguir está sintaticamente correta:

12345678910111213141516171819202122

/ / Classe do tipo-casting # include <iostream> usando namespace std;

classe CDummy { float i, j;};

classe CAddition {int x, y;

público : CAddition ( int a, int b) {x = a, y = b;}int resultado () { retorno x + y;}};

int main () {CDummy d; CAddition * padd; padd = (CAddition *) &D; cout padd-> resultado <(); retornar 0;}

O programa declara um ponteiro para CAddition , mas, em seguida, ele atribui a ele uma referência a um objeto de outro tipo incompatível com explícita tipo-casting:

 padd = (CAddition *) &D;

Page 113: Tutorial c++

Tradicional explícita tipo de fundição permite converter qualquer ponteiro ponteiro em qualquer outro tipo, independentemente dos tipos eles apontam. A chamada subseqüente ao membro resultado vai produzir tanto um erro em tempo de execução ou de um resultado inesperado.

Para controlar esses tipos de conversões entre as classes, temos quatro operadores de casting específico: dynamic_cast , reinterpret_cast , static_cast e const_cast . O seu formato é seguir o novo tipo de ângulo fechado entre colchetes ( <> ) e logo após, a expressão a ser convertido entre parênteses.

<new_type> dynamic_cast (expressão)<new_type> reinterpret_cast (expressão)<new_type> static_cast (expressão)<new_type> const_cast (expressão)

Os equivalentes a tradicional carcaça tipo para essas expressões seriam:

(New_type) expressãonew_type (expressão)

mas cada um com suas próprias características especiais:

dynamic_cast

dynamic_cast pode ser usado somente com ponteiros e referências a objetos. Sua finalidade é garantir que o resultado da conversão de tipo é um objeto válido completo da classe solicitada.

Portanto, dynamic_cast sempre é bem sucedida quando lançamos uma classe para uma das suas classes base:

12345678

classe cBase {};classe CDerived: público cBase {}; cBase b; cBase pb *; CDerived d; CDerived * pd; pb = dynamic_cast <CBase*> (& d); / / ok: derivado-a-basepd = dynamic_cast <CDerived*> (& b); / / errado: base para derivados

A segunda conversão, neste pedaço de código produziria um erro de compilação desde a derivados de conversões de base não são permitidas com dynamic_cast a menos que a classe base é polimórfico.

Quando uma classe é polimórfico, dynamic_cast realiza uma verificação especial durante o tempo de execução para garantir que a expressão produz uma completa válido objeto da classe solicitada:

12345678910111213141516171819

/ / Dynamic_cast <iostream> # include # include <exception> usando namespace std;

classe cBase { virtual void dummy () {}};classe CDerived: público cBase { int a;};

int main () { tentar {cBase * pba = novo CDerived; cBase * PBB = novo cBase; CDerived * pd; pd = dynamic_cast <CDerived*> (PBA); se (pd == 0) cout << "ponteiro nulo no primeiro tipo fundido" <<endl; pd = dynamic_cast <CDerived*> (PBB), se (pd == 0) cout << "ponteiro nulo no segundo tipo fundido" <<endl;} catch (exceção & e) {cout << "Exceção:" <<e . what ();} retornar 0;}

Page 114: Tutorial c++

20212223

Nota de Compatibilidade: dynamic_cast requer o Run-Time Type Information (RTTI) para manter o controle dos tipos de dinâmicas. Alguns compiladores suportam esse recurso como uma opção que está desabilitada por padrão. Este deve estar habilitado para a verificação de tipo em tempo de execução usando dynamic_cast para funcionar corretamente.

O código tenta executar duas dinâmicas lança a partir de objetos do tipo ponteiro * cBase ( PBA e PBB ) para um objeto do tipo ponteiro CDerived * , mas só o primeiro é bem sucedida. Observe as suas respectivas inicializações:

12CBase pba * = nova CDerived; PBB cBase * = nova cBase;

Mesmo que ambos são indicadores do tipo * cBase , pba aponta para um objeto do tipo CDerived , enquanto PBB aponta para um objeto do tipo cBase . Assim, quando as respectivas tipo de peças fundidas são realizadas usando dynamic_cast , pba está apontando para um objeto cheio de classe CDerived , enquanto PBB está apontando para um objeto da classe cBase , que é um objeto da classe incompleta CDerived .

Quando dynamic_cast não pode converter um ponteiro, porque não é um objeto completo do requerido de classe como a conversão em segundo lugar no exemplo anterior, ele retorna um ponteiro nulo para indicar a falha. Se dynamic_cast é usado para converter para um tipo de referência e na conversão não for possível, uma exceção do tipo bad_cast é acionada em seu lugar.

dynamic_cast também pode lançar ponteiros nulos, mesmo entre os ponteiros para classes independentes, e também pode lançar qualquer tipo de ponteiros para ponteiros ( void * ).

static_caststatic_cast pode realizar conversões entre os ponteiros para classes relacionadas, não só da classe derivada de sua base, mas também de uma classe base de seus derivados. Isso garante que pelo menos as classes são compatíveis se o próprio objeto é convertido, mas não há verificação de segurança durante a execução é realizada para verificar se o objeto a ser convertido é de fato um objeto completo do tipo de destino. Portanto, é responsabilidade do programador para garantir que a conversão é segura. Por outro lado, a sobrecarga dos controlos de segurança do tipo de dynamic_cast é evitado.

1234

classe cBase {};classe CDerived: público cBase {}; cBase * a = nova cBase; CDerived * b = static_cast <CDerived*> (a);

Isso seria válido, apesar de b seria apontar para um objeto da classe incompleta e poderia levar a erros de execução, se dereferenced.

static_cast também pode ser usado para executar qualquer não-ponteiro conversão de outros que também poderiam ser realizados implicitamente, como por exemplo a conversão de padrão entre os tipos fundamentais:

12dupla d = 3.14159265;int i = static_cast < int > (d);

Ou qualquer conversão entre classes com construtores explícitos ou funções de operador conforme descrito em "conversões implícitas" acima.

Page 115: Tutorial c++

reinterpret_castreinterpret_cast converte qualquer tipo de ponteiro para ponteiro qualquer outro tipo, mesmo de classes independentes. O resultado da operação é um binário simples cópia do valor de um ponteiro para o outro. Todas as conversões de ponteiro são permitidos: nem o conteúdo apontado nem o tipo de ponteiro em si é marcada.

Ele também pode converter ponteiros ou de tipos inteiros. O formato em que este valor representa um ponteiro inteiro é específico de plataforma. A única garantia é que um pointer cast para um tipo inteiro grande o suficiente para contê-la plenamente, é concedido a poder ser lançado de volta para um ponteiro válido.

As conversões que podem ser executadas por reinterpret_cast mas não por static_cast não têm usos específicos, em C + + são operações de baixo nível, cuja interpretação resulta em um código, que geralmente é específico do sistema e, portanto, não-portáteis. Por exemplo:

1234

class A {};classe B {}; A * a = nova A, B = b * reinterpret_cast <B*> (a);

Isto é válido C + + código, apesar de não fazer muito sentido, já que agora temos um ponteiro que aponta para um objeto de uma classe de incompatibilidade e, portanto, dereferencing é inseguro.

const_castEste tipo de vazamento manipula o constness de um objeto, seja para fixar ou a ser removido. Por exemplo, para passar a const argumento para uma função que espera um parâmetro de não-constante:

1234567891011121314

/ / Const_cast # include <iostream> usando namespace std;

void print ( char * str) {cout <<str <<endl;}

int main () { const char * c = "texto de exemplo" ; impressão ( const_cast < char * > (c)); retornar 0;}

texto de exemplo

typeidtypeid permite verificar o tipo de uma expressão:

typeid (expressão)

Este operador retorna uma referência a um objeto de constante do tipo type_info que é definido no cabeçalho do arquivo padrão <typeinfo> . Este valor retornado pode ser comparada com outra usando os operadores == e ! = ou pode servir para obter uma seqüência terminada por caractere nulo que representa o tipo de dados ou nome da classe usando o seu nome () membro.

123456789

/ / Typeid <iostream> # include # include <typeinfo> usando namespace std;

int main () { int * a, b, a = 0, b = 0; se ( typeid (a) =! typeid (b)) { cout << "a e b são de diferentes tipos: \ n" ; cout << "é:" << typeid (a) <. name () < '\ n' ; cout << "b é:" < < typeid b). (nome () << '\ n' ;} retornar 0;}

a e b são de tipos diferentes: uma é a seguinte: int * b é: int

Page 116: Tutorial c++

10111213141516

Quando typeid é aplicada às classes typeid usa o RTTI para controlar o tipo de objetos dinâmicos. Quando typeid é aplicado a uma expressão cujo tipo é uma classe polimórfica, o resultado é o tipo de derivado objeto mais completa:

1234567891011121314151617181920

/ / Polimórficos, classe typeid <iostream> # include # include <typeinfo> # include <exception> usando namespace std;

classe cBase { virtual void f () {}};classe CDerived: público cBase {};

int main () { tente cBase * {a = nova cBase; cBase b * = nova CDerived; cout << "é:" << typeid (a) <. name () < (nome () << '\ n' ; cout << "* é:" << typeid (* a) <. name () < '\ n' ; cout << "b * é : " << typeid(exceção & e) {cout << "Exceção:" <e.what <() <<endl;} retornar 0;}

Observe como o tipo que typeid considera para ponteiros é o tipo de ponteiro em si (tanto um e b são do tipo classe cBase * ). No entanto, quando typeid é aplicado a objetos (como a * e * b ) typeid produz seu tipo dinâmico (ou seja, o tipo de seus derivados objeto mais completa).

Se o tipo de typeid avalia é um ponteiro precedido pelo operador dereference ( * ), e este ponteiro tem um valor nulo, typeid lança um bad_typeid exceção.

directivas Preprocessordirectivas Preprocessor são linhas incluídas no código dos nossos programas que não são declarações do programa, mas diretrizes para o pré-processamento. Estas linhas são sempre precedidas por um cardinal ( # ). O pré-processador é executado antes da compilação real de código começa, portanto, o pré-processador digere todas estas diretrizes antes de qualquer código é gerado pelas declarações.

Estas directivas de pré-processamento abrange apenas através de uma única linha de código. Assim como um caractere de nova linha é encontrada, a directiva de pré-processamento é considerada como final. Nenhum ponto e vírgula (;) é esperado no final de uma directiva de pré-processamento. A única forma de uma directiva de pré-processamento pode se estender por mais de uma linha é precedendo o caractere newline no final da linha por uma barra invertida ( \ ).

definições de macro (# define, # undef)Para definir macros pré-processamento, podemos usar # define . Seu formato é:

# Define identificador de substituição

Quando o pré-processador encontra presente directiva, que substitui qualquer ocorrência de identificador no resto do código por substituição . Esta substituição pode ser uma expressão, uma declaração, um bloco ou simplesmente nada. O pré-processador não entende C + +, ele simplesmente substitui qualquer ocorrência de identificador de substituição .

Page 117: Tutorial c++

123

# Define TABLE_SIZE 100 int table1 [TABLE_SIZE];int tabela2 [TABLE_SIZE];

Após o pré-processador substitui TABLE_SIZE , o código torna-se equivalente a:

12int table1 [100],int tabela2 [100];

Este uso de # define como definidor constante já é conhecido por nós de tutoriais anteriores, mas # define pode trabalhar também com os parâmetros para definir macros de função:

 # Define GetMax (a, b) a, b> a:? B

Isso substituir qualquer ocorrência de GetMax seguido por dois argumentos a expressão de substituição, mas também substituindo cada argumento por seu identificador, exatamente como seria de esperar se fosse uma função:

1234567891011121314

/ / Macro função # include <iostream> usando namespace std;

# define GetMax (a, b) ((a)> (b):? (a) (b))

int main () { int x = 5, y; y = GetMax (x, 2); cout <<y <<endl; cout <<GetMax (7, x) <<endl; retornar 0;}

macros definido não são afetados pela estrutura do bloco. Uma macro dura até que é indefinido com a directiva de pré-processamento # undef:

12345

# Define TABLE_SIZE 100 int table1 [TABLE_SIZE];# undef TABLE_SIZE # define TABLE_SIZE 200 int tabela2 [TABLE_SIZE];

Isto irá resultar no mesmo código:

12int table1 [100],int tabela2 [200];

As definições de função macro aceitar dois operadores especiais ( # e # # ) na seqüência de substituição:Se o operador # é usado antes de um parâmetro é usado na seqüência de substituição, esse parâmetro é substituído por uma seqüência literal (como se fosse entre aspas duplas)

1# Define str (x) # xcout <<str (teste);

Page 118: Tutorial c++

2

Isso seria traduzido em:

 Cout << "teste" ;

O operador # # concatena dois argumentos não deixando espaços em branco entre eles:

12# Define cola (a, b) um # # bcola (c, a) << "teste" ;

Isto também ser traduzido em:

 Cout << "teste" ;

Porque as substituições de pré-processamento acontecer antes de qualquer C + + verificar a sintaxe, as definições de macro pode ser uma característica difícil, mas tenha cuidado: um código que depende fortemente de macros complicadas podem parecer obscuras para outros programadores, uma vez que a sintaxe que eles esperam é, em muitas ocasiões diferentes das expressões regulares programadores esperam em C + +.

inclusões condicional (# ifdef, # ifndef, # if, # endif, # else e # elif)

Essas diretrizes permitem incluir ou descartar parte do código de um programa, se uma determinada condição seja atendida.

# ifdef permite uma seção de um programa a ser compilado somente se a macro que é especificado como o parâmetro foi definido, não importa qual o seu valor. Por exemplo:

123

# Ifdef TABLE_SIZE int tabela [TABLE_SIZE];# endif

Neste caso, a linha de código int tabela [TABLE_SIZE]; só é compilado se TABLE_SIZE foi previamente definido com # define , independentemente do seu valor. Se não foi definida, essa linha não será incluída na compilação do programa.

# Ifndef serve para o exato oposto: o código entre # ifndef e # endif directivas só é compilado se o identificador especificado não foi previamente definido. Por exemplo:

1234

# TABLE_SIZE ifndef # define TABLE_SIZE 100 # endif int tabela [TABLE_SIZE];

Neste caso, se, quando chegar a este pedaço de código, o TABLE_SIZE macro não foi definido ainda, que seria definida como um valor de 100. Se ele já existisse, seria manter o valor anterior já que o # define directiva não seria executado.

O # if , # else e # elif (ou seja, "else if") as directivas servem para especificar algumas condições a serem cumpridas para que a parte do código que cercam a ser compilado. A condição que segue # if ou # elif só pode avaliar expressões constantes, incluindo expressões macro. Por exemplo:

Page 119: Tutorial c++

123456789

1011121314

# If TABLE_SIZE> 200 # undef TABLE_SIZE # define TABLE_SIZE 200 # elif TABLE_SIZE <50 # undef TABLE_SIZE # define TABLE_SIZE 50 # else # undef TABLE_SIZE # define TABLE_SIZE 100 # endif int tabela [TABLE_SIZE];

Observe como toda a estrutura de # if , # elif e # else directivas acorrentado termina com # endif .

O comportamento do # ifdef e # ifndef também pode ser conseguido usando os operadores especiais definidos e definidos! , respectivamente, em qualquer # if ou # elif directiva:

12345

if! # definido TABLE_SIZE # define TABLE_SIZE 100 # elif definido ARRAY_SIZE # define TABLE_SIZE ARRAY_SIZE int tabela [TABLE_SIZE];

Linha de comando (# line)Ao compilar um programa e um erro acontece durante o processo de compilação, o compilador mostra uma mensagem de erro com referências ao nome do arquivo onde o erro ocorreu e um número de linha, por isso é mais fácil encontrar o código gerando o erro.

A linha # directiva permite-nos controlar ambas as coisas, os números de linha dentro do código de arquivos, bem como o nome do arquivo que queremos que aparece quando um erro ocorre. Seu formato é:

# Número da linha "filename"

Onde número é o número da linha nova que será atribuído para a linha de código seguinte. Os números de linha de sucessivas linhas será aumentado um por um a partir deste ponto.

"Nome" é um parâmetro opcional que permite redefinir o nome do arquivo que será mostrado. Por exemplo:

12# Linha 20 "variável atribuindo" int a?;

Este código irá gerar um erro que será mostrado como erro no arquivo "variável de atribuição" , linha 20.

directiva de erro (erro #)Esta directiva aborta o processo de compilação quando ele for encontrado, gerando uma compilação de erros que pode ser especificado como parâmetro:

123

# __cplusplus Ifndef # Um erro de compilador C + + é necessário! # endif

Este exemplo aborta o processo de compilação se o nome da macro __cplusplus não está definido (este nome de macro é definido por padrão em todos os compiladores C + +).

Page 120: Tutorial c++

Fonte arquivo de inclusão (# include)Esta directiva também tem sido usado assiduamente em outras seções deste tutorial. Quando o pré-processador encontra um # include directiva substitui-lo por todo o conteúdo do arquivo especificado. Há duas maneiras para especificar um arquivo para ser incluído:

12# Include "arquivo" # include <file>

A única diferença entre as duas expressões são os locais (diretórios) onde o compilador vai olhar para o arquivo. No primeiro caso, onde o nome do arquivo é especificado entre aspas, o arquivo é procurado no mesmo diretório que inclui o arquivo que contém a directiva. No caso de que ele não está lá, o compilador procura o arquivo no diretório padrão onde ele está configurado para procurar pelos arquivos de cabeçalho padrão.Se o nome do arquivo é colocado entre parênteses de ângulo > < o arquivo é procurado diretamente onde o compilador está configurado para procurar pelos arquivos de cabeçalho padrão. Portanto, os arquivos de cabeçalho padrão são normalmente incluídos no ângulo entre parênteses, enquanto outros arquivos de cabeçalho específicos estão incluídos o uso de aspas.

Pragma directiva (# pragma)Esta diretiva é usada para especificar diversas opções para o compilador. Essas opções são específicas para a plataforma e compilador que você usa. Consulte o manual ou a referência do seu compilador para obter mais informações sobre os possíveis parâmetros que podem ser definidos com # pragma .

Se o compilador não oferece suporte a um argumento específico para o # pragma , é ignorado - nenhum erro é gerado.

Predefinidos nomes de macroOs nomes de macro a seguir são definidos a qualquer momento:

macro valor

__LINE__ Valor inteiro que representa a linha atual do arquivo de código fonte a ser compilado.

__FILE__ Uma string literal contendo o nome da presumida do arquivo fonte a ser compilado.

__DATE__Uma seqüência literal, sob a forma "Mmm dd / aaaa", contendo a data em que começou o processo de compilação.

__TIME__Uma seqüência literal, sob a forma "hh: mm: ss" com o momento em que começou o processo de compilação.

__cplusplusUm valor inteiro. Todos os compiladores C + + tem este constante definida para algum valor. Se o compilador é totalmente compatível com o padrão C + + o seu valor for igual ou superior a 199711L dependendo da versão da norma que respeitar.

Por exemplo:

12345678910111213

/ / Padrão de nomes de macro # include <iostream> usando namespace std;

int main () {cout << "Este é o número da linha" <<__LINE__; cout << "do arquivo" <<__FILE__ << . "\ n"cout << "Sua elaboração começou" <__DATE__ <; cout << "a" <<__TIME__ << "n \". ; cout << "O compilador dá um valor __cplusplus de" <__cplusplus <; retornar 0;}

Page 121: Tutorial c++

Entrada / Saída com arquivosC + + fornece as seguintes classes para realizar a saída ea entrada de caracteres para / de arquivos: 

ofstream: classe Stream para escrever em arquivos ifstream: classe Stream para ler arquivos fstream: classe Stream para ler e escrever de / para arquivos.

Essas classes são derivadas, direta ou indiretamente das classes istream e ostream . Nós já usamos objetos cujos tipos eram essas classes: cin é um objeto da classe istream e tribunal é um objeto da classe ostream . Therfore, já estamos utilizando as classes que estão relacionadas com o nosso arquivo de córregos. E, de fato, podemos usar o nosso arquivo de córregos da mesma forma que já são usados para usar cin e judiciais , com a única diferença que temos de associar esses riachos com arquivos físicos. Vamos ver um exemplo:

123456789101112

/ / Operações básicas com arquivos # include <iostream> # include <fstream> usando namespace std;

int main () {ofstream myfile; myfile.open ( "example.txt" ); <<myfile escrevendo isso para um arquivo. "\ n " ; myfile.close (); retornar 0;}

Esse código cria um arquivo chamado example.txt e insere uma frase para ele da mesma maneira que estamos acostumados a ver com a tribunal , mas usando o fluxo de arquivo myfile vez.

Mas vamos ir passo a passo:

Abrir um arquivo

A primeira operação executada em um objeto de uma dessas classes é associá-lo a um arquivo real. Este procedimento é conhecido como a abrir um arquivo . Um arquivo aberto é representado por um programa, um objeto de fluxo (uma instância de uma dessas classes, no exemplo anterior, este foi myfile ) e qualquer operação de entrada ou de saída realizada sobre este objeto de fluxo será aplicado ao arquivo físico que lhe estão associados .

Para abrir um arquivo com um objeto de fluxo que usamos a função de membro do open () :

open (filename, mode);

Onde nome é uma seqüência terminada por caractere nulo do tipo char * const (do mesmo tipo que tem strings literais), que representa o nome do arquivo a ser aberto, eo modo é um parâmetro opcional com uma combinação das seguintes bandeiras:

ios:: in Aberto para as operações de entrada.

ios:: out Aberto para as operações de saída.

ios:: binary

Abra no modo binário.

ios:: comeu

Defina a posição inicial no final do arquivo.Se este sinalizador não estiver definido para qualquer valor, a posição inicial é o começo do arquivo.

ios:: app Todas as operações de saída são realizadas no final do arquivo, anexando o conteúdo com o conteúdo

Page 122: Tutorial c++

atual do arquivo. Esta bandeira só pode ser usada em correntes abertas para as operações de saída apenas.

ios:: trunc

Se o arquivo aberto para operações de saída já existia antes, seu conteúdo anterior é apagado e substituído por um novo.

Todos estes sinalizadores podem ser combinados usando o operador OR bit a bit ( | ). Por exemplo, se queremos abrir o arquivo example.bin em modo binário para adicionar dados que poderia fazê-lo pelo seguinte chamada para função de membro do open () :

12ofstream myfile; myfile.open ( "example.bin" , ios:: out | ios:: app | ios:: binary);

Cada um dos (aberto) funções de membro das classes ofstream , ifstream e fstream tem um modo padrão que é usada se o arquivo é aberto, sem um segundo argumento:

classeparâmetro modo

padrão

ofstream

ios:: out

ifstream ios:: in

fstream ios:: in | ios:: out

Para ifstream e ofstream classes, ios:: in e ios:: out , respectivamente, estão automaticamente e assumiu, mesmo que de um modo que não incluí-los é passado como segundo argumento para o open () função de membro.

O valor padrão é aplicado somente se a função é chamada sem especificar qualquer valor para o parâmetro mode. Se a função é chamada com qualquer valor nesse parâmetro o modo padrão é substituído, não combinado.

Fluxos de arquivo aberto em modo binário e executar operações de entrada e de saída independentemente de quaisquer considerações formato. Binário arquivos não são conhecidos como arquivos de texto , e algumas traduções podem ocorrer devido a formatação de alguns caracteres especiais (como a nova linha e caracteres de retorno de carro).

Desde a primeira tarefa que é executada em um objeto de fluxo de arquivos é geralmente para abrir um arquivo, essas três classes incluem um construtor que chama automaticamente o open () função de membro e tem os mesmos parâmetros exatamente como esse membro. Portanto, também poderíamos ter declarado o anterior myfile objeto e conduziu a operação de abertura mesmo em nosso exemplo anterior, por escrito:

 myfile ofstream ( "example.bin" , ios:: out | ios:: app | ios:: binary);

Combinando a construção do objeto e da abertura de fluxo em uma única instrução. Ambas as formas de abrir um arquivo são válidos e equivalentes.

Para verificar se um fluxo de arquivo foi bem-sucedida abertura de um arquivo, você pode fazê-lo, chamando ao membro nome esta_aberta () sem argumentos. Esta função de membro retorna um valor bool da verdade no caso que de fato o objeto stream está associado a um arquivo aberto, ou false caso contrário:

 se (myfile.is_open ()) { / * ok, continue com saída * / }

Page 123: Tutorial c++

Fechando um arquivoQuando terminarmos com a nossa entrada e saída de operações em um arquivo devemos fechá-lo para que seus recursos estejam disponíveis novamente. A fim de fazer isso temos que chamar o fluxo da função de membro close () . Esta função de membro leva sem parâmetros, e que ele faz é para liberar os buffers associados e feche o arquivo:

 myfile.close ();

Uma vez que esta função membro é chamado, o objeto de fluxo pode ser usado para abrir um outro arquivo, eo arquivo está disponível novamente para ser aberto por outros processos.

No caso em que um objeto é destruído enquanto ainda associado a um arquivo aberto, o destruidor chama automaticamente a função de membro close () .

Arquivos de textoarquivo de texto correntes são aqueles em que não incluem o ios:: binary bandeira no seu modo de abertura. Esses arquivos são projetados para armazenar texto e, portanto, todos os valores que a entrada ou saída de / para eles pode sofrer algumas transformações de formatação, que não necessariamente correspondem às suas binário valor literal.

operações de saída de dados em arquivos de texto são executadas da mesma maneira que nós operamos com tribunal :

12345678910111213141516

/ / Escrita em um arquivo de texto <iostream> # include # include <fstream> usando namespace std;

int main () {myfile ofstream ( "example.txt" ); se (myfile.is_open ()) {myfile << "Este é uma linha n \ ". ; <<myfile "é outra linha. Este \ n" ; myfile.close ();} mais cout << "Não foi possível abrir o arquivo" ; retornar 0;}

A entrada de dados de um arquivo também pode ser realizada da mesma forma que fizemos com cin :

12345678910111213141516171819

/ / Ler um arquivo de texto <iostream> # include # include <fstream> # include <string> usando namespace std;

int main () {string linha; myfile ifstream ( "example.txt" ); se (myfile.is_open () ) { tempo (myfile.good ()) {getline (linha, myfile); cout <linha <<<endl;} myfile.close ();}

mais cout << "Não foi possível abrir o arquivo" ;

retornar 0;}

Page 124: Tutorial c++

20212223

Este último exemplo lê um arquivo texto e imprime seu conteúdo na tela. Observe como usamos uma função de membro novo, chamado bom () que retorna verdadeiro no caso em que o fluxo está pronto para / saída de operações de entrada. Criamos um laço while que termina quando na verdade myfile.good () não é mais verdade, o que vai acontecer ou se o fim do arquivo foi alcançado ou se algum outro erro ocorreu.

Verificando as bandeiras do estadoAlém de bom () , que verifica se o fluxo está pronto para / saída de operações de entrada, existem outras funções de membro para verificar a estados específicos de um riacho (todos eles retornam um valor booleano):

ruim ()Retorna true se uma operação de leitura ou gravação falha. Por exemplo, no caso em que tentamos escrever para um arquivo que não está aberto para a escrita ou se o dispositivo em que tento escrever não tem espaço sobrando.

fail ()

Retorna true nos mesmos casos tão ruim (), mas também no caso de um erro de formato acontece, como quando um caractere alfabético é extraído quando estamos a tentar ler um número inteiro.

eof ()

Retorna true se um arquivo aberto para leitura chegou ao fim.

bom ()

É a bandeira do estado mais genérico: ele retorna false nos mesmos casos em que chamar qualquer uma das funções anteriores retornaria verdadeiro.

A fim de restaurar as bandeiras de estado marcado por qualquer dessas funções membro já vimos que podemos usar a função de membro clear () , que leva sem parâmetros.

chegar e colocar os ponteiros fluxoTudo o que eu / o córregos objetos têm, pelo menos, um ponteiro de fluxo interno:

ifstream , como istream , tem um ponteiro conhecido como o ponteiro começar que aponta para o elemento a ser lido na próxima operação de entrada.

ofstream , como ostream , tem um ponteiro conhecido como o ponteiro colocado que aponta para o local onde o elemento seguinte tem de ser escrito.

Finalmente, fstream , herda tanto, a chegar e colocar os ponteiros, de iostream (que é derivada de ambos os istream e ostream ).

Esses ponteiros internos córrego que apontam para a leitura ou escrita locais dentro de um fluxo pode ser manipulado usando as funções de membro a seguir: 

tellg () e tellp ()Estas duas funções de membro não tem parâmetros e retorna um valor do tipo membro pos_type , que é um tipo de dados inteiro que representa a posição atual do ponteiro do stream get (no caso de tellg ) ou o ponteiro de fluxo de venda (no caso de tellp ).

seekg () e seekp ()Estas funções permitem alterar a posição dos ponteiros stream get e put. Ambas as funções são sobrecarregadas com dois tipos de protótipos. O primeiro protótipo é:

seekg (posição);seekp (posição);

Page 125: Tutorial c++

Usando este protótipo do ponteiro de fluxo é alterado para a posição absoluta posição (contando a partir do início do arquivo). O tipo para este parâmetro é o mesmo que aquele retornado por funções tellg e tellp : o tipo de membro pos_type , que é um valor inteiro.

O protótipo outros para estas funções é:

seekg (offset, direção);seekp (offset, direção);

Usando este protótipo, a posição do ponteiro ou colocar recebe é definido para um valor de deslocamento em relação a algum ponto específico determinado pelo parâmetro sentido . offset é do tipo membro off_type , que também é um tipo inteiro. E direção é do tipo seekdir , que é um tipo enumerado ( enum ) que determina o ponto de onde é contada a partir de deslocamento, e que pode ter qualquer dos seguintes valores:

ios:: beg compensado, contados a partir do fluxo de

ios:: act offset contados a partir da posição atual do ponteiro de fluxo

ios:: end offset, contados do término do fluxo

O exemplo a seguir usa as funções de membro que acabamos de ver para obter o tamanho de um arquivo: 

123456789101112131415

/ / Tamanho do ficheiro obtenção <iostream> # include # include <fstream> usando namespace std;

int main () { tempo com início e final; myfile ifstream ( "example.txt" ); begin = myfile.tellg (); myfile.seekg (0, ios:: end); fim = myfile.tellg (); myfile.close (); cout << "O tamanho é:" <<(fim-de começar) << "n bytes. \" ; retornar 0; }

Os arquivos bináriosEm arquivos binários, para entrada e saída de dados com os operadores de inserção e extração ( << e > ) e funciona como getline não é eficiente, já que não precisam de qualquer formato de dados, e os dados não podem utilizar os códigos usados por separação arquivos de texto para separar os elementos (como, newline, espaço, etc ..).

Fluxos de arquivo incluem duas funções de membro projetado especificamente para entrada e saída de dados binários em seqüência: escrever e ler . O primeiro ( escrever ) é uma função de membro do ostream herdada por ofstream . E ler é uma função membro de istream que é herdado porifstream . Objetos da classe fstream têm ambos os membros. Seus protótipos são:

write (tamanho memory_block);leia (tamanho memory_block);

Onde memory_block é do tipo "ponteiro para char ( char * ), e representa o endereço de um array de bytes, onde os elementos de dados são armazenados ou lidos a partir do qual os elementos de dados a serem gravados são tomadas. O tamanho do parâmetro é um valor inteiro que especifica o número de caracteres a ser lida ou escrita de / para o bloco de memória.

123

/ / Ler um arquivo binário completo <iostream> # include # include <fstream> usando namespace std; ifstream:: size pos_type;

Page 126: Tutorial c++

45678910111213141516171819202122232425

char MemBlock *;

int main () {ifstream arquivo ( "example.bin" , ios:: in | ios:: binary | ios:: comeu); se (file.is_open ()) {tamanho file.tellg = (); MemBlock = novo char [tamanho]; file.seekg (0, ios:: beg); arquivo. ler (MemBlock, tamanho); File.close (); cout << "o conteúdo do arquivo completo está na memória" ;

excluir MemBlock] [;} mais cout << "Não foi possível abrir o arquivo" ; retornar 0;}

Neste exemplo, o arquivo inteiro é lido e armazenado em um bloco de memória. Vamos examinar como isso é feito:

Primeiro, o arquivo é aberto com o ios:: comeu bandeira, o que significa que o ponteiro começa será posicionado no final do arquivo. Dessa forma, quando chamamos ao membro tellg () , vamos obter diretamente o tamanho do arquivo. Observe o tipo que temos usado para declarar variáveis de tamanho :

 tamanho pos_type;: ifstream:

ifstream:: pos_type é um tipo específico usado para o posicionamento e arquivo de buffer e é o tipo retornado por file.tellg () . Este tipo é definido como um tipo inteiro, por isso podemos realizar nela as mesmas operações que realizamos sobre qualquer outro valor inteiro, e pode seguramente ser convertidos para outro tipo inteiro grande o suficiente para conter o tamanho do arquivo. Para um arquivo com um tamanho de 2GB em que poderíamos usar int :

12int size, size = ( int file.tellg) ();

Uma vez que tenhamos obtido o tamanho do arquivo, pedimos a atribuição de um bloco de memória grande o suficiente para manter o arquivo inteiro:

 MemBlock = novo char [tamanho];

Logo depois, passamos a definir o ponteiro começar no início do arquivo (lembre-se que abrimos o arquivo com este ponteiro no final), em seguida, ler o arquivo inteiro e, finalmente, fechá-lo:

123

file.seekg (0, ios:: beg); File.close (); file.read (tamanho MemBlock);

Neste ponto, poderia operar com os dados obtidos a partir do arquivo. Nosso programa simplesmente anuncia que o conteúdo do arquivo está na memória e depois termina.

Page 127: Tutorial c++

Amortecedores e sincronização

Quando operar com fluxos de arquivo, estes estão associados a um buffer interno do tipo streambuf . Este buffer é um bloco de memória, que age como intermediário entre o fluxo eo arquivo físico. Por exemplo, com um ofstream , cada vez que a função de membro colocar (que escreve um único personagem) é chamado, o personagem não foi escrita diretamente para o arquivo físico com o qual o fluxo é associado. Em vez disso, o personagem está inserido no buffer de fluxo de intermediários que.

Quando o buffer é liberado, todos os dados contidos nele são gravados no meio físico (se é um fluxo de saída), ou simplesmente libertado (se é um fluxo de entrada). Este processo é chamado de sincronização e tem lugar em qualquer das seguintes circunstâncias:

Quando o arquivo é fechado: antes de fechar um arquivo de todos os buffers que ainda não foram liberados estão sincronizados e todos os dados pendentes são escritos ou lidos ao meio físico.

Quando o buffer está cheio: amortecedores têm um determinado tamanho. Quando o buffer estiver cheio, é automaticamente sincronizado.

Explicitamente, com manipuladores: Quando certos manipuladores são utilizados em rios, uma sincronização explícita ocorre. Estes manipuladores são: flush e endl .

Explicitamente, a função de sincronização com o membro (): chamada de fluxo de função de membro sync () , que leva sem parâmetros, faz uma sincronização imediata. Esta função retorna um int valor igual a -1 se o fluxo não tem buffer associado ou em caso de falha. Caso contrário (se o buffer corrente foi sincronizada com êxito) ele retorna 0 .

Operações booleanasUm bit é a quantidade mínima de informações que podemos imaginar, uma vez que apenas armazena um valor 1 ou 0, o que representa sim ou não, ligado ou desligado, verdadeiro ou falso, etc .. ou seja: dois estados possíveis de cada um frente ao outro, sem qualquer possibilidade de sombras. Vamos considerar que os dois possíveis valores de um bit são 0 e 1.

Várias operações podem ser realizadas com bits, quer em conjunto com outros pedaços ou sozinhos. Essas operações recebem o nome de operações booleanas , uma palavra que vem do nome de um dos matemáticos que contribuíram mais para esse campo: George Boole (1815-1864).

Todas estas operações têm um comportamento estabelecidos e todos eles podem ser aplicados a qualquer bit, não importa qual o valor que eles contêm (0 ou 1). Em seguida, você tem uma lista das operações booleanas básicas e uma tabela com o comportamento de que a operação com cada combinação possível de bits.

E ou ANDEsta operação é realizada entre dois bits, que chamaremos de um e b . O resultado da aplicação da presente operação e é 1 se os dois uma e b são iguais a 1 e 0 nos demais casos (isto é, se uma ou ambas as variáveis é 0).

E (&) 

um ba & b

0 0 0

0 1 0

1 0 0

1 1 1

OU ou OREsta operação é realizada entre dois bits ( um e b ). O resultado é 1 se qualquer um dos dois bits for 1, ou se ambos são 1. Se nenhum for igual a 1, o resultado é 0.

OR (|)

um b a | b

Page 128: Tutorial c++

0 0 0

0 1 1

1 0 1

1 1 1

XOR (ou exclusivo)Esta operação é realizada entre dois bits ( um e b ). O resultado é 1 se qualquer um dos dois bits é 1, mas não no caso em que ambos são. Há pois, se nem deles ou ambos são iguais a 1, o resultado é 0.

XOR (^)

um ba ^ b

0 0 0

0 1 1

1 0 1

1 1 0

NÃO ou notEsta operação é realizada em um único bit. Seu resultado é a inversão do valor real do bit: se ele foi definido para 1 se torna 0, e se ele for 0, torna-se 1:

NÃO (~)

um ~ A

0 1

1 0

Estas são as quatro operações básicas booleano (AND, OR, XOR e NOT). Combinando estas operações, podemos obter qualquer resultado possível a partir de dois bits.

Em C + +, estes operadores podem ser usados com variáveis de qualquer tipo de dados inteiro, a operação booleana é realizado para todos os bits de cada variável envolvida. Por exemplo, supondo-se duas variáveis: uma e b , ambos do tipo unsigned char , onde um contém 195 (11000011 em binário) e b contém 87 (ou 01010111 em binário). Se escrever o seguinte código:

1234

unsigned char a = 195;unsigned char b = 87;unsigned char c; c = &b;

Isso quer dizer que fizemos um bit a bit E operação entre um e b . A operação é realizada entre os bits das duas variáveis que estão localizados na mesma posição: O bit mais à direita de c vai conter o resultado de realizar a operação AND entre os bits mais à direita de um e b :

Page 129: Tutorial c++

 A mesma operação também é realizada entre a segunda bits de ambas as variáveis, eo terceiro, e assim por diante, até que a operação é realizada entre todos os pedaços de ambas as variáveis (cada um apenas com o mesmo pedaço de outra variável).

O binário valor final de c é 01000011, ou seja, 67 em números decimais. Assim, 195 e 87 é igual a 67 .

Bases NuméricasDesde que éramos crianças, todos nós temos usado para expressar quantidades decimais. Esta nomenclatura que parece tão lógico para nós pode não parecer assim para um habitante de Roma Clássica. Para eles, cada símbolo que eles escreveram para expressar um número sempre representou o mesmo valor:

I 1 II 2 III 3 IV 4 V 5

Tudo o que eu sempre sinais representa o valor de 1 (um) onde eles são colocados, eo V signo sempre representa um valor de 5 (cinco). No entanto, que não tem lugar em nosso sistema decimal. Quando nós escrevemos o símbolo decimal 1 não estamos sempre a falar de um valor de um (I em algarismos romanos). Por exemplo:

1 I 10 X 100 C

Nestes casos, o nosso símbolo 1 não tem sempre um valor de um (ou eu em algarismos romanos). Por exemplo, no segundo caso, o símbolo de uma representa um valor de dez (ou X em romano) e no terceiro, 1 representa um valor de cem (ou C).

Por exemplo:

275 não é equivalente a 2 7 5, poderia ser decomposto em vez de 200 70 5:

200 + 70 5 --- 275

portanto, o primeiro "2" sinal é equivalente a 200 (2 x 100), o segundo "7" sinal é equivalente a 70 (7 x 10) Considerando que o último sinal que corresponde ao valor de cinco (5 x 1).

Isso é porque o nosso sistema é um sistema numeral posicional. Como tal o valor de um algarismo depende de sua posição dentro do número inteiro a ser representada. Todo o acima pode ser matematicamente representado em uma forma muito simples. Por exemplo, para representar o valor 182.736 podemos supor que cada algarismo é o produto de si mesmo, multiplicada por 10 powered para o seu lugar como expoente, a partir da direita, com 10 0 , na sequência com 10 1 , 10 2 , e assim por diante:

 

números octais (base 8)Tal como o nosso "normal" são números de base 10 (ou radix 10), porque temos 10 algarismos diferentes (do 0 ao 9):

0123456789

Page 130: Tutorial c++

octals os números incluem apenas as representações para os valores de 0-7: 

01234567

e, por conseguinte, a sua base matemática é 8. Em C + + números octais são denotados por início sempre com um 0 dígito. Vamos ver como é que iria escrever os primeiros números em octal:

octal decimal ----- ------- 0 0 (zero) 01 1 (um) 02 2 (dois) 03 3 (três) 04 4 (quatro) 05 5 (cinco) 06 6 (seis) 07 7 (sete) 010 8 (oito) 011 9 (nove) 012 10 (dez) 013 11 (onze) 014 12 (doze) 015 13 (treze) 016 14 (quatorze) 017 15 (quinze) 020 16 (dezesseis) 021 17 (dezessete)

Assim, por exemplo, o número 17 (dezessete, ou XVII, em romano) é expresso 021 como um número octal em C + +. Podemos aplicar o mesmo mecanismo que vimos anteriormente para números decimais para os números octal simplesmente por considerar que sua base é 8. Por exemplo, se o número octal 071263 :

 portanto, o número octal 071263 é expressa em 29.363 em números decimais.

Os números hexadecimais (base 16)Como os números decimais possuem 10 dígitos diferentes para serem representadas (0123456789) e números octal tem 8 (01234567), números hexadecimais ter 16 dígitos diferentes, que são representados pelos números 0-9 e as letras A, B, C, D, E e F, que, juntos, servem-nos para representar os 16 símbolos diferentes que precisamos expressar base 16 números:

decimal hexadecimal ----------- ------- 0 0 (zero) 0x1 1 (um) 0x2 2 (dois) 0x3 3 (três) 0x4 4 (quatro) 0x5 5 (cinco) 0x6 6 (seis) 0x7 7 (sete) 0x8 8 (oito) 0x9 9 (nove) 0xA 10 (dez) 0xB 11 (onze) 0xC 12 (doze) 0xD 13 (treze) 0xE 14 (quatorze) 0xF 15 (quinze) 0x10 16 (dezesseis) 0x11 17 (dezessete)

Em C + +, números hexadecimais são precedidos por 0x (zero, x).

Mais uma vez podemos utilizar o mesmo método para traduzir um número de uma base para outra: 

 

representações bináriasOctal e hexadecimal números têm uma vantagem considerável sobre os nossos números decimais no mundo dos bits, e é que as suas bases (8 e 16) são múltiplos perfeito de 2 (dois 3 e 2 4 , respectivamente), o que nos permite realizar conversões mais fácil a partir dessas bases para binário do que de números decimais (cuja base é 2x5). Por exemplo, suponha que queremos traduzir a seguinte seqüência binária para números de outras bases:

110011111010010100

Page 131: Tutorial c++

A fim de traduzi-lo em decimal seria preciso realizar uma operação matemática semelhante ao que usamos anteriormente para converter de hexadecimal ou octal, o que nos daria o número decimal 212628.

No entanto a passagem desta seqüência em octal só vai nos levar de alguns segundos e até mesmo os menos qualificados em matemática, pode fazê-lo apenas por vê-la: Desde 8 é 2 3 , vamos separar o valor binário em grupos de 3 números:

110 011 111 010 010 100

e agora só temos de traduzir para radix numberal octal cada grupo separadamente: 

110 011 111 010 010 100 6 3 7 2 2 4

dando o número 637.224 como resultado. Esse mesmo processo pode ser inversamente realizados para passar de octal para binário.A fim de realizar a operação com os números de hexadecimal só temos para executar o mesmo processo, mas separar o valor binário em grupos de 4 números, porque 16 = 2 4 :

11 0011 1110 1001 0100 E 3 3 9 4

Portanto, a expressão 110011111010010100 binários podem ser representados em C + + ou como 212628 (decimal), com 0.637.224 (octal) ou como 0x33e94 (hexadecimal).

O código hexadecimal é especialmente interessante em ciência da computação desde hoje, os computadores são baseados em bytes composto de 8 bits binários e, portanto, corresponde a cada byte com o intervalo que 2 números hexadecimal pode representar. Por isso, é tão freqüentemente utilizada para representar valores traduzidos para ou de base binária.

ReferênciaReferência da Linguagem C + + Biblioteca, com descrições detalhadas dos seus elementos e exemplos sobre como utilizar as suas funçõesO padrão C + + biblioteca é uma coleção de funções, constantes, classes, objetos e modelos que estende a linguagem C + + fornecendo funcionalidade básica para executar várias tarefas, como aulas de interagir com o sistema operacional, os recipientes de dados, manipuladores de operar com eles e algoritmos comumente necessário.

As declarações dos diferentes elementos fornecidos pela biblioteca são divididas em vários cabeçalhos que devem ser incluídas no código, a fim de ter acesso aos seus componentes:

algoritmo complexo exceção lista pilha

bitset csetjmp fstream localidade stdexcept

cassert csignal funcionais mapa strstream

cctype cstdarg iomanip memória streambuf

cerrno cstddef ios novo string

cfloat cstdio iosfwd numéricos typeinfo

ciso646 cstdlib iostream ostream utilitário

climits cstring istream fila valarray

clocale ctime iterador conjunto vector

CMATH deque limites sstream

Ela pode ser dividida em:

Biblioteca COs elementos da biblioteca da linguagem C também estão incluídos como um subconjunto da biblioteca C + + padrão. Estes abrangem muitos aspectos, a partir de funções de utilidade geral e macros para funções de entrada / saída e funções de gerenciamento dinâmico de memória:

Page 132: Tutorial c++

cassert (assert.h) Biblioteca C Diagnostics (cabeçalho)

cctype (ctype.h) Personagem funções de manipulação (cabeçalho)

cerrno (errno.h) C erros (cabeçalho)

cfloat (float.h) Características dos tipos de ponto flutuante (cabeçalho)

ciso646 (iso646.h) ISO 646 grafias alternativas operador (cabeçalho)

climits (limits.h) Tamanhos dos tipos integrais (cabeçalho)

clocale (locale.h) C biblioteca de localização (cabeçalho)

CMATH (math.h) Biblioteca C numéricos (cabeçalho)

csetjmp (setjmp.h) local não pula (cabeçalho)

csignal (signal.h) biblioteca C para lidar com sinais (cabeçalho)

cstdarg (stdarg.h) tratamento de argumentos variável (cabeçalho)

cstddef (stddef.h) Standard definições C (cabeçalho)

cstdio (stdio.h) biblioteca C para executar / Output operações de entrada (cabeçalho)

cstdlib (stdlib.h) Standard C Library General Utilitários (cabeçalho)

cstring (string.h) C Strings (cabeçalho)

ctime C Time Library (cabeçalho)

C + + biblioteca padrão: as bibliotecas DiversosIdioma biblioteca de apoio :

limites limites numéricos (cabeçalho)

novo Dinâmica de memória (cabeçalho)

typeinfo Digite informações (cabeçalho)

exceção Standard classe de exceção (classe)

biblioteca de Diagnóstico :

stdexcept classes de exceção (cabeçalho)

Biblioteca Geral utilitários :

utilitário componentes Utility (cabeçalho)

funcionais Função objetos (cabeçalho)

memória Os elementos de memória (cabeçalho)

biblioteca de Strings :

string Biblioteca C + + Strings (biblioteca)

Page 133: Tutorial c++

biblioteca Localização :

localidade Localização biblioteca (cabeçalho)

Biblioteca C + + Padrão: Standard Template Library (STL)biblioteca Contentores :

bitset Bitset (modelo de classe)

deque Dupla fila acabou (modelo de classe)

lista Lista (modelo de classe)

mapa Mapa (modelo de classe)

Multimap Chave-mapa múltipla (modelo de classe)

multiset -Set múltiplo (modelo de classe)

priority_queue Prioridade da fila (modelo de classe)

fila Fila FIFO (modelo de classe)

conjunto Set (modelo de classe)

pilha Pilha LIFO (modelo de classe)

vector Vector (modelo de classe)

biblioteca iteradores :

iterador definições Iterator (cabeçalho)

Biblioteca de Algoritmos :

STL Algoritmos Standard Template Library: Algoritmos (biblioteca)

biblioteca numérica :

complexo biblioteca de números complexos (cabeçalho)

valarray Biblioteca para matrizes de valores numéricos (cabeçalho)

numéricos numéricos operações generalizadas (cabeçalho)

Biblioteca C + + padrão: Input / Biblioteca fluxo de saídaFornece funcionalidade de usar uma abstração chamada córregos especialmente projetado para realizar operações de entrada e saída em seqüências de caracteres, como arquivos ou strings.Essa funcionalidade é fornecida através de várias classes, como mostra no mapa seguinte relação com os nomes de cabeçalho do arquivo correspondente no topo:

Page 134: Tutorial c++