UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL
INSTITUTO DE INFORMÁTICA
CENTRO DE EMPREENDIMENTOS DE INFORMÁTICA
Curso de
Introdução à Programação em C++
por
MARCELO DE OLIVEIRA JOHANN
monitoria
GLAUCO BORGES VALIM DOS SANTOS
Porto Alegre, agosto de 2004.
C++ e' como sexo na Adolescencia
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1) Esta' na cabeca de todo mundo
2) Todo mundo fala sobre isto o tempo todo
3) Na realidade, quase ninguem esta' fazendo
4) Os poucos que estao fazendo:a) Fazem da maneira errada
b) Desculpam falando que a proxima vez talvez sera' melhor
c) Nao praticam com seguranca
Resumo................................................................................................. 4
1 Introdução................................................................................. 5
2 Recursos para Programação
Estruturada............................... 6
2.1 Compilando o primeiro
programa: Hello World.................................................. 6
2.2 Declaração de variáveis
e tipos de dados........................................................... 7
2.3 Operadores, expressões e
precedência.............................................................. 8
2.4 Usando alguns vetores......................................................................................... 8
2.5 Construções de controle
de fluxo........................................................................ 8
2.5.1 A construção if........................................................................................................ 8
2.5.2 Blocos e escopos.................................................................................................... 9
2.5.3 A construção for..................................................................................................... 9
2.5.4 As construções while
e do....................................................................................... 9
2.5.5 As declarações break
e continue.......................................................................... 10
2.5.6 A construção switch.............................................................................................. 10
2.5.7 O operador ?........................................................................................................ 11
2.5.8 C++ tem label e goto, mas não
contem para ninguém............................................ 11
2.6 Recursos próprios de C++................................................................................. 11
2.6.1 Namespaces.......................................................................................................... 11
2.6.2 Strings em C++ e em C......................................................................................... 11
2.6.3 Entrada e saída em C++ e em C............................................................................ 11
2.6.4 Tratamento de exceções........................................................................................ 12
3 Estruturas de Dados, Funções
e Ponteiros............................ 13
3.1 Vetores................................................................................................................ 13
3.2 Funções............................................................................................................... 13
3.3 Estruturas........................................................................................................... 14
3.3.1 struct..................................................................................................................... 14
3.3.2 union..................................................................................................................... 14
3.3.3 enum..................................................................................................................... 14
3.4 typedef................................................................................................................ 14
3.5 Ponteiros............................................................................................................. 15
3.6 Constantes.......................................................................................................... 16
3.7 Os quatro tipos de cast....................................................................................... 16
3.8 Referências......................................................................................................... 17
3.9 Ponteiros para
estruturas................................................................................... 17
3.10 Alocação de Memória........................................................................................ 17
4 Recursos para Programação
Orientada a Objetos............... 18
4.1 Orientação a Objetos......................................................................................... 18
4.1.1 Teoria................................................................................................................... 18
4.2 Controle de Acesso............................................................................................ 18
4.3 Construtores e
destrutores................................................................................ 19
4.4 Sobrecarga de operadores................................................................................. 20
4.5 Membros estáticos e
constantes....................................................................... 20
4.6 Herança de classes............................................................................................. 20
4.6.1 Classes base e derivada......................................................................................... 21
4.6.2 Membros protected.............................................................................................. 21
4.6.3 Construtores.......................................................................................................... 21
4.6.4 Funções virtuais..................................................................................................... 21
4.6.5 Funções virtuais puras e classes
abstratas............................................................... 21
4.6.6 Herança múltipla.................................................................................................... 22
5 Modelagem e Estilos de
Codificação...................................... 23
5.1 Regras e recomendações................................................................................... 23
5.1.1 Algumas regras de programação............................................................................ 23
5.1.2 Algumas regras básicas de formatação................................................................... 23
5.2 Programação em módulos.................................................................................. 24
5.3 Listas encadeadas por
herança e com ponteiros.............................................. 24
5.4 Relações do tipo “é um”
e “tem um”................................................................ 25
6 Templates e STL..................................................................... 26
Anexo 1 Operadores e
palavras reservadas..................................... 27
Bibliografia......................................................................................... 29
Esta apostila é material de apoio a um curso de extensão ministrado pelo professor Marcelo de Olieira Johann na UFRGS, em agosto de 2004. O curso é eminentemente prático, com exemplos que demonstram os recursos e funcionalidades sendo oferecidos pelo professor, alterados em sala de aula, e com a proposição de exercícios para os alunos, a serem realizados durante a aula e como trabalhos extra-classe de fixação de conteúdo.
Devido à extensão e complexidade dos recursos oferecidos pela linguagem C++, o curso tem uma abordagem seletiva de conteúdos, e prioriza o desenvolvimento da habiliade de programação produtiva com um subconjunto adequadamente selecionado destes recursos. Outros recursos oferecidos pela linguagem, como sobrecarga de operadores, herança múltipla, programação de algoritmos em templates, são apresentados pela sua definição, objetivo, forma geral, mas não trabalhados em detalhe, permitindo que os alunos se concentrem em um modelo orientado a objetos e não se dispersem com a complexidade e detalhes de todas as opções da linguagem..
Palavras-chave: Programação, Linguagens, C++
A linguagem C++ foi desenvolvida inicialmente por Bjarne Stroustrup na AT&T, de 1979 a 1983, à partir da linguagem C, tendo como idéia principal a de agregar o conceito de classes, de orientação à objetos, àquela linguagem. Razão porque iniciamente chamva-se de “C com classes”. Bjarne procurou tanto quanto possível manter retrocompatibilidade com C, de modo que programas em C pudessem ser compilados por um compilador C++ com um mínimo de alterações. Entretanto, encarar C++ como um superconjunto de C é um erro, e C++ deve ser vista como uma “outra linguagem”, por diversas razões. Em primeiro lugar, nem todo o programa escrito em C é compilado em C++ sem erros, e pode nem mesmo gerar o mesmo resultado, já que a sintaxe e a semântica de algumas construções diferem. Ao ligar-se partes de um programa em C++ com partes em C, estas devem ser bem especificadas, pois as convenções de funcionamento do código compilado também diferem. Além disso, C++ oferece um conjunto de mecanismos básicos que não estavam presentes em C, e estes devem ser usados para produzir software mais modular e confiável explorando-se as verificações disponíveis no compilador. Finalmente, os mecanismos de C++ devem inspirar a programação segundo o paradigma de orientação a objetos e, portanto, não se deve programar em C++ como se faz em C.
A partir da primeira versão de 1983, a linguagem foi sendo revisada e evoluindo, tornou-se disponível fora da AT&T em 1985, e após um longo processo foi padronizada pela ISO no final de 1997, pelo padrão ISO/IEC 14882. Você pode obter mais informações sobre o desenvolvimento da linguagem na página do próprio autor em [STR 2004].
STL é uma parte do padrão C++, e consiste em uma biblioteca de funções e estruturas de dados que todo compilador C++ deve oferecer, provê as implementações mais comuns em um programa, e pode-se utilizá-la com diferentes tipos de dados. Um bom ponto de partida para leitura específica sobre STL é na página da Silicon Graphics, Inc. em [STL 2004].
De forma geral, é possível obter muito material de
referência da linguagem através de páginas na Internet. Tente por exemplo
pesquisar com a expressão “C++ reference”
no Google. Também é muito produtivo criar o hábito de pesquisar por problemas
bem específicos. Tente, por exemplo, pesquisar “iostream.h iostream difference”, ou “STL hash_map code example”. Pode-se obter cursos [BRA 1998],
livros e exemplos de código [ECK 2000]
Os compiladores do projeto GNU também podem ser obtidos livremente através da Internet. O ambiente BloodShed Dev-C++ roda sobre Windows e utiliza os compiladores gcc e g++. É possível baixá-lo de: http://www.bloodshed.net/devcpp.html. Há também inúmeros fóruns e listas de discussão sobre aspectos técnicos da linguagem. Então, use e abuse da Internet para aprendê-la e resolver seus problemas.
Este capítulo apresenta recursos básicos da linguagem C++ que servem para o modelo de programação conhecido como bloco-estruturado. São vistos os tipos de dados primitivos e as principais construções que servem para descrever operações e algoritmos, como controle de fluxo, juntamente com recursos diversos da linguagem. Em função da compatibilidade, muitos desses recursos são idênticos aos da liguagem C, e por isso essa parte é semelhante a uma breve “revisão” da linguagem C, seguida da apresentação dos mecanismos que são próprios de C++.
O melhor meio de aprender uma linguagem é através de exemplos. Eis o primeiro programa, que dá sinal de vida apresentano uma mensagem. Identificamos nele os elementos: #include, função main, geração de saída de dados na tela, e como se colocam comentários.
A diretiva #include serve para fazer com que o compilador inclua como parte desse código outro arquivo de código fonte. Em geral, esse recurso é usado para incluir definições de dados e código que serão utilizados por nosso programa, mas já foram compilados e estão disponíveis em uma biblioteca. É função da etapa de ligação montar o executável final incluindo o código compilado para o nosso programa com o código compilado dessas bibliotecas.
Todo programa em C++, assim como em C, pode ser visto como um conjunto de funções. As funções serão apresentadas no capítulo 2, mas é necessário saber primeiro que todos os comandos da linguagem devem estar dentro de funções, e que, portanto, um programa deve ter no mínimo uma função. Essa função principal tem o nome de main, sendo esse nome o que identifica por onde a execução inicia.
O namespace é um recurso utilizado para evitar que, quando se constróem grandes programas, nomes de variáveis, classes e funções conflitem. Esse conceito será apresentado mais adiante, mas aqui precisamos dele, pois a saída padrão, cout, está definida dentro do espaço chamado std, de standard. Dados podem ser impressos enviando-os para a saída adrão, cout. Da mesma forma, dados podem ser lidos através da entrada padrão, cin. Também nesse caso o tipo de dado lido depende da variável para a qual se está fazendo a leitura.
Todas as declarações e comandos da linguagem devem ser terminados por ‘;’. Esse sinal não serve apenas como separador nas declarações, mas serve para identificar composição de seqüência entre os comandos, isto é, primeiro é executado um e depois o outro. Utilize vários comandos de impressão para gerar uma saída mais longa.
Declarações de variáveis, ao contrário dos comandos, podem ser feitas tanto dentro quanto fora de funções. Se estão fora de uma função, elas são globais, e acessíveis a qualquer função do programa. As variáveis declaradas dentro de uma função são variáveis locais, e são acessíveis somente a essa função. As variáveis podem ser inicializadas, opcionalmente, na sua declaração, ou terem algum valor atribuído dentro de uma função, através da atribuição. Ao contrário de C, C++ não exige que todas as declarações de variáveis ocorram antes de todos os comandos em uma função. As declarações podem ser feitas em qualquer lugar.
chapter2/2.2-01-declaration.cpp
A tabela 1 apresenta os tipos de dados básicos da linguagem, exemplos de valores literais, e intervalos de valores aceitos. Cada tipo de dado possui representação de valores literais adequados, tanto para inicialização quanto para atribuições. Em geral usa-se bool, char, int, long e double mais freqüentemente, onde long é uma abreviação para long int.
bool |
Valores booleanos |
true false |
true false |
char |
Caracteres simples |
‘a’ ‘A’ ‘+’ ‘\t’ ‘\n’ |
0 a 255, isto é, 8 bits |
int |
Números inteiros |
100 0x3F |
16 bits ou mais, 32 normal |
float |
Números reais |
1.2f .23f 1.f 1.5e-15f |
|
double |
Reais dupla precisão |
1.2 .23 1. 1.5e-15 |
|
Há quatro modificadores que alteram a representação desses tipos básicos. Os modificadores signed e unsigned alteram o significado dos dados para representação de números negativos. Os modificadores short e long alteram a quantidade de bits com que o dado é representado. A linguagem não padroniza completamente o tamanho da representação binária de números, e esse é um aspecto que pode comprometer a portabilidade.
Assim como C, C++ é uma linguagem que permite programação em baixo nível. Assim, a linguagem não possui propriamente caracteres, mas o tipo char é apenas um núemro de no mínimo 8 bits, e os literais (‘a’,’X’) podem ser atribuídos a qualquer variável numérica.
chapter2/2.2-02-declaration.cpp
Um tipo de dado pode ser convertido em outro tipo com um cast. O cast deve preceder um valor e consiste no tipo para o qual se deseja converter, entre parênteses, como, por exemplo: (int) ‘a’. Esse cast simples introduzido aqui é o mesmo da linguagem C, mas deve-se evitar utilizá-lo, pois em seu lugar C++ possui outros quatro tipos diferentes de cast que são mais seguros, a serem apresentados adiante.
Operadores são elementos fundamentais para efetuar computação. Eles atuam sobre um ou mais operandos, e produzem um resultado, que é o valor da expressão formada por eles e seus operandos. Por exemplo 2 + 2 é uma expressão que utiliza o operador +, ordena que o computador efetue esse cálculo, e oferece como resultado o valor 4. Assim, se você escreve a = 3 * (2 + 2), a variável a deve receber o valor 12. Elementar, por enquanto! Mas ocorre que C++ possui 42 operadores, com as mais variadas finalidades. Além disso, como em C, o conceito de expressão é extremamente abrangente, e utilizado em várias construções. Isso será mostrado passo por passo, mas iniciemos guardando bem esses conceitos: variável, literal, atribuição, operador, operando e expressão.
chapter2/2.3-01-operadores.cpp
Assim como declaramos uma variável de um tipo
determinado, podemos declarar um vetor de qualquer tipo com a seguinte
sintraxe: tipo nome[tamanho]. Por
exemplo, o programa abaixo declara um vetor de inteiros, assinala valores a
algumas posições, e as imprime. Vetores serão melhor estudados adiante, mas são
aqui introduzidos para enriquecer os exemplos iniciais.
A seguir são apresentadas as principais construções de
controle de fluxo de execução. Elas servem para produzir seqüências de execução
mais complexas que a seüência do texto do programa, por desvio ou repetição. Em
uma linguagem bloco-estruturada, não se deve pensar em desvios para posições
arbitrárias, mas em uma formação do programa como conjunto de repetições e
execuções condicionais propriamente aninhadas.
A construção if tem a seguinte sintaxe, sendo opcional a parte do else em diante:
if (condição) comando_para_resultado_verdadeiro; else comando_para_falso;
Exercícios:
1-
Escreva um programa que lê um valor da linha de comando
e diz se ele é par ou ímpar.
2- Escreva um programa que recebe a nota final de um aluno e imprime seu conceito, conforme a tabela abaixo:
de 9 a 10 |
A |
de 7,5 a 9 |
B |
de 6 a 7,5 |
C |
de 0 a 6 |
D |
3-
Escreva um programa que lê 3 valores na linha de
comando e imprime a média, dizendo "Parabéns!!!" se a media for
superior a um limiar pré-defido, ou "Que pena..." se for inferior.
Se for necessário colocar mais de um comando aninhado
dentro de uma construção condicional (ou de iteração) usa-se um bloco. Um bloco
é uma construção que permite tratar um conjunto de comandos como se eles fossem
um só. O bloco consiste nesse conjunto de comando entre chaves ( ´{´, ´}´).
Um bloco define um escopo de variáveis. Isto significa que se uma nova variável
é declarada dentro de um bloco com o mesmo nome de uma variável fora desse
bloco, somente a variável interna é acessada desse ponto em diante até o final
do bloco.
A construção for serve para fazer repetições, e é bastante flexível ao contrário da maioria das linguagens. ela é composta por 4 elementos, identificados assim:
for ( expr1; expr2; expr3 ) comando;
A execução do for corresponde à seguinte seqüência:
expr1; expr2; {comando;
expr3; expr2; } {comando; expr3; expr2;} ...
Em expr1 e expr3 pode-se escrever qualquer código
de inicialização e de preparação para a próxima repetição, atuando sobre
quaisquer variáveis. Essas expressões podem conter vários assinalamentos
separados por vírgulas. A expressão expr2
controla o laço, e também pode conter código arbitrário. Ela é executada antes
da primeira repetição, de modo que o comando não é executado nenhuma vez se a
expressão expr2 é falsa inicialmente.
Exercícios:
1- Escreva um programa que lê um conjunto de 10 valoes inteiros e verifica se algum deles é negativo.
2-
Escreva um programa que lê um conjunto de 10 números na
linha de comando e imprime o número de pares e ímpares
A construção while (expr) comando; é idêntica a um for (;expr;) comando;. Já a construção do comando; while (expr) difere das anteriores no sentido de que executa pelo menos uma vez o comando, mesmo quando a expressão expr é inicialmente falsa.
Exercícios:
1- Escreva um programa que fique indefinidamente lendo valores de entrada do usuário, dizendo se o valor é par ou ímpar, até que este digite um número especial de saída, como 99, por exemplo.
2-
Escreva o programa que lê 3 valores na linha de comando
e imprime a média de forma que, após realizar o primeiro cálculo de média, ele
pergunte ao usuário se ele deseja fazer outro.
A declaração break interrompe o comando de repetição mais interno dentro da qual ela se encontra. Serve também para separar casos da construção switch, vista adiante. Já a declaração continue interrompe apenas uma das repetições do laço, passando diretamente à execução da próxima repetição.
Exercícios:
1- Escreva um programa que lê dez valores e os imprime em ordem inversa após a leitura. Porém a leitura deve ser interrompida caso um valor negativo seja lido, imprimindo "VALOR INCORRETO".
2-
Escreva um programa para dar o conceito de alunos em
função da nota, conforme a mesma tabela de exercício anterior, mas que consulte
três vetores de números inteiros, cada um com 10 posições, correspondendo aos
10 alunos. O primeiro vetor tem 0 se o aluno desistiu e cancelou o curso, e
você não deve simplesmente ignorá-lo, ou 1 em caso contrário. O segundo vetor
tem o número de aulas assistidas, e se for menor do que 3 (de 5 aulas) o aluno
receberá conceito ‘FF’ (falta de freqüência). Finalmente o terceito vetor
contém a nota do aluno, para classificação segundo os outros conceitos.
A construção switch serve para testar uma variável ou expressão contra diversos valores constantes. Casa valor deve aparecer separado em uma cláusula case. E cada cláusula case precisa ser interrompida por uma declaração break, caso contrário os comandos deste ponto em diante sontinuam sendo executados.
Exercícios:
1- Escreva um programa que leia valores entre 0 e 100 indefinidamente e calcule sempre a média atual. Deve ser impresso "Média ok", "Média insatisfatória" e "Média insuficiente" respectivamente, para valores nos intervalos [80-100], [60-79] e [0-59]. Caso o valor lido não seja um número entre 0 e 100, abortar o programa com uma mensagem de erro
O par de operadores ? : funciona como a construção condicional if, mas sob a forma de expressão. Isto é, esse operador retorna um valor, o valor da expressão que foi avaliada. Um exemplo de uma expressão com esse operador, já usada em um assinalamento, é a seguinte:
int a = (b>10) ? 10 : b;
// a recebe 10 se b maior que 10 ou b caso contrário
Assim como C, C++ também possui o comando goto, que desvia a execução do programa para um label que identifica uma outra linha de código. Deve-se evitar ao máximo o uso desse recurso, pois ele quebra a estrutura do programa em termos de iterações, condições e blocos. Entretanto, ele pode ser bem utilizado para geração automática de código, como, por exemplo, na implementação de autômatos.
A seguir apresentamos alguns recursos e construções que
são próprias de C++, não aparecendo em C, ou cuja implementação é completamente
diferente.
Namespaces
servem para separar espaços de nomes em módulos e bibliotecas inteiras,
evitando que nomes de variáveis, estruturas, funções e classes conflitem. Os namespaces são abertos, isto é, podem
ser declarados várias vezes, cada qual acrescentando novos elementos dentro do
mesmo namespace.
Em C uma string
é apenas um vetor de caracteres terminado por ´\0´ (valor binário zero). Sendo um vetor, ele é caracterizado pelo
seu endereço inicial. Esse endereço pode ser constante, quando foi declarado
como vetor ou literal, ou variável, quando declarado como ponteiro. Nesse
último caso, ele deve ser inicializado para apontar para uma área de memória
com a string, seja ela constante, de
outro vetor, ou alocada dinamicamente. Ocasionalmente será necessário usar
esses vetores de caracateres em C++, mas a linguagem C++ tem objetos do tipo string com muitas operações convenientes
e fáceis de usar. Atribuição, cópia e concatenação, que somente podem ser
feitas por funções em C, são feitas apenas com = e + em C++, por exemplo.
Os exemplos a seguir mostram como se faz entrada e saída
de dados em C e C++. O primeiro apresenta exemplos de entrada e saída de dados
formatados por console, enquanto o segundo programa apresenta leitura de
arquivos em C e C++.
Para que serve tratamento de exceções? Em programas pequenos, seu uso pode não ser justificado, pois tudo que eles oferecem pode ser obtido com um simples teste (if) após a realização de uma operação. Mas em programas grandes, o local onde uma situação de erro, ou exceção ao funcionamento desejado, é identificada pode não ter uma relação direta com o local onde essa situação deve ser tratada. Por exemplo, para uma função que procura por um valor em um vetor, não encontrá-lo é uma exceção. Mas quem a chamou pode estar simplesmente consultando o vetor justamente para saber se o elemento está lá, e nesse caso a resposta negativa é uma situação normal. Ao contrário, se a chamada é parte de uma função que colocou esse valor e precisa dele para continuar, o fato de não estar lá pode ser um erro gravíssimo, e necessitar um relato ao usuário e interrupção do programa. Essas diferenças de tratamento podem ocorrer entre funções que foram chamadas em vários níveis, inclusive com diferentes funções tendo diferentes interpretações, ações e mensagens para a mesma situação. É por isso que um mecanismo simples de testes e codificação de retorno se torna inapropriado para a tarefa.
O mecanismo de tratamento de exceções da linguagem serve
justamente para isso. Ele especifica o registro de uma situação de exceção, e o
desvio para um local que declara tratá-lo diretamente, cruzando as fronteiras
de funções e controle de fluxo (mas fazendo as operações necessárias de
desativação dessas funções, como um retorno prematuro). No disparo da exceção,
é possível passar quaisquer informações ao tratamento. Aqui é utilizado um
número inteiro apenas, mas será tipicamente um objeto de uma classe “exceção”
em uma aplicação real.
Neste capítulo exploramos estruturas de dados básicas que fazem parte da sintaxe da linguagem, como vetores, estruturas, uniões, a organização básica em funções e passagem de parâmetros, e finalmente o conceito de endereços, ponteiros, referênciação e de-referenciação, vendo como esses conceitos relacionam-se entre si. O uso das estruturas de dados básicas, indispensável em outras linguagens, dará lugar ao uso de classes e templates da STL, introduzidas posteriormente, mas completam até aqui um conjunto de recursos completo para programação estruturada.
Já foi vista a declaração e uso de vetores simples. Se um
vetor tem inicialização, pode-se omitir seu tamanho entre os colchetes, e ele
será calculado pleo número de elementos da inicialização. Se o tamanho do vetor
é declarado, pode-se inicializá-lo com menos valores do que seu tamanho, e os
restantes receberão 0, mas não se pode inicializá-lo com mais valores do que
seu tamanho. Não é possível assinalar ou copiar vetores inteiros. Outras
características de vetores são melhor compreendidas adiante com funções e
ponteiros.
Uma função agrupa um conjunto de comandos da linguagem, podendo esse conjunto ser chamado de várias partes de um programa. Funções servem para economizar código-fonte e organizar melhor um programa. Uma função possui um nome, um tipo de valor de retorno, e um conjunto, possivelmente vazio, de parâmetros de entrada, cada um com seu tipo específico. Esses elementos caracterizam uma assinatura da função, que, para ser completa, deve ser seguida de sua implementação, ou corpo. Um protótipo de uma função é uma definição com o corpo omitido, e se utiliza esse recurso para declarar previamente uma função e fazer seu nome conhecido antes de dar sua implementação.
Ao contrário de C, em C++ pode-se ter várias funções com exatamente o mesmo nome, desde que os tipos ou número de parâmetros de entrada variem. Chama-se isso de sobrecarga de funções. Embora se possa utilizar esse recurso arbitrariamente, ele faz sentido para dar versões diferentes de uma mesma função que estão disponíveis para diferentes parâmetros de entrada. As funções em C++ podem ter parâmetros com valores default. No caso de argumentos para esses parâmetros serem omitidos na chamada os valores default são utilizados. Somente pode ser omitido o valor de um parâmetro se todos os outros à sua esquerda também tiverem sido omitidos.
O qualificador inline, utilizado antes da declaração de uma função, faz com que o código dessa seja substituído no lugar da chamada, se possível, para que o programa rode mais rapidamente. Em C isso deve ser implementado com a substituição textual das macros (#define).
Como exercício, experimente escrever um programa que
imprime os números de 1 a 10 através de um laço, mas sem usar as
construções for ou while, e sim com chamada de função recursiva.
Este exercício demonstra que pode-se utilizar o paradigma de programação
funcional em uma linguagem como C++, onde todo programa é expresso como um
conjunto de funções aplicadas umas às outras. Programação funcional é mais do
que isso, mas a semelhança existe.
O programador deve modelar o problema que deseja resolver
ou processar utilizando-se de algoritmos e estruturas de dados. Do ponto de
vista de recursos de linguagem, alguns recursos são oferecidos para facilitar a
descrição dessas estruturas. De fato, um deles possui exatamente esse nome, de
estrutura, mas é nada mais do que um agregado de valores.
Em C++, uma estrutura é um tipo de dado conhecido pela linguagem. Assim, ela entende a todas as antigas formas de declaração de C, e adicionalmente o nome da estrutura pode ser diretamente utilizado sem o prefixo struct.
Uma união é apenas uma estrutura onde todos os campos
utilizam a mesma posição. Ela pode ser utilizada para dar diferentes
interpretações a uma área de memória, ou armazenar de forma compacta dados
mutuamente excludentes.
Uma enumeração é um novo tipo de dado que possui valores
discretos específicos. Pode-se declarar uma enumeração para o tipo dia, e uma
variável do tipo dessa enumeração poderá ter os valores: segunda, terca, e
assim por diante. Uma enumeração é nada mais do que um número inteiro para o
qual são dados nomes específicos a alguns valores. De fato, pode-se definir
exatamente quais são esses valores. As enumerações servem para organização
interna do código, mas quando seu valor é impresso, é interpretado apenas como
número.
A declaração typedef define um novo tipo de dado tendo como base um tipo conhecido. Por exemplo, pode-se fazer:
typedef int int32;
typedef short int16;
E a partir de então passar a usar somente int16 e int32 nas declarações de um programa. Se esse programa for portado
para outra máquina e compilador onde int
tem só 16 bits, pode-se redefinir int32
como long apenas em uma posição.
Todas as variáveis estão armazenadas em memória. Cada uma tem seu endereço e seu conteúdo. Quando usamos o nome de uma variável num programa, o compilador compila no código o endereço, para que, quando executado, o processador acesse o conteúdo. Isso significa que o compilador só vê o endereço e o programador só vê o conteúdo. Para ver o endereço, usa-se o operador & na frente da variável, que significa “endereço de”, e é chamado operador de referenciação. Ao contrário, para manipular um valor armazenado em um endereço, usa-se o operador * na frente do endereço, que significa “valor apontado por”, e é chamado operador de de-referenciação. Um ponteiro é apenas uma variável cujo conteúdo é um endereço de memória, provavelmente o endereço de outra variável. Esse endereço pode ser obtido por referenciação, e posteriormente o dado daquela variável apontada pode ser manipulado por de-referenciação. Para declarar um ponteiro, acrescenta-se * entre o tipo e o nome da variável (nesse caso variável apontador). Convém já notar que uma variável do tipo ponteiro, ou apontador, como todas as outras, também tem seu conteúdo (que vai ser o endereço de outra) e seu próprio endereço em memória. É por isso que aparecem os ponteiros para ponteiros.
Uma boa sugestão para aprendizagem é declarar outras
variáveis e vetores e imprimir seus valores e conteúdos. Utilize os programas
abaixo para fazer essas experiências, incluindo fazer atribuições às variáveis
que são ponteiros e às variáveis apontadas por elas. Sempre considere que as
variáveis locais aparecem em ordem inversa na memória, pois são criadas na
pilha do sistema, e que existem regras de alinhamento, e, portanto, variáveis
declaradas consecutivamente podem aparecer separadas por espaços para
satisfazer tais regras.
A palavra const é um qualificador, assim como signed ou short, e serve em primeiro lugar para dar nome a valores constantes posteriormente usados no programa. Já que a variável não pode ser posteriormente atribuída, toda declaração de const deve ter inicialização.
Mas o significado de const é um pouco mais sutil. Ele define que determinado valor não vai ser alterado, e não precisa necessariamente ser aplicado a um valor de fato constante. Pode-se passar uma variável para uma função que recebe uma constante, significando que essa função não vai alterar esse valor. Ao passar argumentos por valor, isso nem faz muita diferença, mas no caso de ponteiros sim. Se uma função recebe const char*, pode-se passar qualquer ponteiro para caracteres, mesmo não constante. Nesse caso, const serve para especificar (e enforçar) que a função não vai e nem pode alterar o caracter apontado pelo ponteiro que ela recebeu.
Em uma declaração, o qualificador const pode ser usado em diversas posições, com diferentes significados. Pode-se ler da esquerda para direita (em inglês) para compreender as interpretações. A construção *const é uma construção especial que significa ponteiro constante.
char *const cp; |
// ponteiro constante para um caracter |
char const * p; |
// ponteiro para um caracter constante |
const char * p2; |
// outro ponteiro para um caracter constante |
const char *const cp2; |
// ponteiro constante para um caracter constante |
Deve-se evitar o uso do cast em estilo C: (tipo). C++ introduz quatro novos tipos de cast com sintaxe e funcionalidade própria. Assim, é mais fácil identificar cada um desses tipos de conversão no código e corrigi-las se necessário. Os quatro tipos de cast são:
static_cast<T> e Conversão de um valor entre tipos de dados conhecidos pela linguagem, produzindo a representação correta do valor de e no novo tipo T. Também é utilizada para conversão de tipos de ponteiros;
reinterpret_cast<T> e Informa ao compilador para passar o valor de e para um destino do tipo T simplesmente pela cópia da representação binária. É utilizado para conversão entre tipos não relacionados, para passar informações em baixo nível de um formato para outro, em implementações dependentes de máquina e menos portáveis;
const_cast<T>
e Elimina o qualificador const de uma expressão e, produzindo um valor do tipo T em lugar de const T;
dynamic_cast<T>
e É uma conversão de ponteiros
inteligente, onde os tipos de dado de origem e destino são identificados em
tempo de execução para verificar se um ponteiro do tipo T pode ser utilizado
para apontar para e;
Uma referência é um nome alternativo para uma variável. Ela pode ser usada em passagem de parêmetros e retorno de resultados, para fazer com que se opere na variável original, implementando passagem por referência sem usar ponteiros. Nesse sentido ela faz algo bem semelhante a um ponteiro. Entretanto, uma referência não é um ponteiro, não ocupa espaço em memória, e não se podem efetuar operações sobre ela (somente sobre a variável que ela referencia).
chapter3/3.8-01-reference1.cpp
chapter3/3.8-02-reference2.cpp
chapter3/3.8-03-reference3.cpp
Um uso muito comum de ponteiros é para que apontem para estruturas. Uma estrutura pode ter um campo que aponta para outra estrutura ou para outra instância da mesma estrutura. Para acessar o valor de um campo de uma estrutura tendo um ponteiro para ela, usa-se: “(*pt).campo”. A sintaxe pt->campo substitui essa construção, simplificando a navegação através de ponteiros, principalmente porque esse campo pode ser outro ponteiro. Lembre-se, entretanto, que é muito comum haver ponteiros nulos e estes não devem ser acessados, de forma que qualquer navegação deve ser precedida de teste para saber se o novo ponteiro não é nulo.
Para alocar memória adicional, ou de tamanho variável, em C, usa-se a função calloc, e para retornar a memória ao sistema usa-se a função free. Em C++, essa funções não serão usadas, e em seu lugar há dois operadores, new e delete, que fazem as funções respectivas, mas compreendem os tipos de dados e operações de criação envolvidas. Os operadores new e delete funcionam apenas para os tipos escalares. Para alocar e retornar vetores usa-se os operadores new[] e delete[].
As estruturas simples da linguagem C são substituídas pelas classes de C++. Uma classe é uma estrutura que tem, além de dados, funções membro, chamadas métodos. As classes agregam outros conceitos de orientação a objetos, como controles de acesso e polimorfismo. Nesse capítulo são vistos os principais recursos que C++ oferece para a programação O-O. Procura-se caracterizar a variedade de recursos da linguagem, incluindo herança múltipla e sobrecarga de operadores, mas é dada maior ênfase nos mecanismos “puros” mais fundamentais, deixando recursos mais intrincados para estudos futuros.
Orientação a Objetos é uma forma de programação. A idéia
é agrupar os dados ao seu processamento. Ou seja, em vez de haver dados
declarados e funções que os alteram arbitrariamente, cada pequeno conjunto de
dados estará acompanhado por um conjunto de funções que operam sobre eles
fazendo todas as operações desejáveis. Nenhum outro trecho de código do
programa acessará esses dados diretamente, mas sempre chamando as funções
corretas. Dessa forma, é mais fácil manter consistência dos dados, encontrar e
corrigir erros, fazer manutenção, implementar novas funcionalidades ou trocar representações
de dados. Então, quando se programa módulos em uma linguagem como C pela
definição de estruturas e conjunto completo de funções que sobre elas operam,
pode-se já estar usando uma boa dose de programações orientada a objetos.
Na teoria do paradigma de orientação a objetos (O-O), o
programa é formado por um conjunto de classes
que são modelos para criação de objetos.
As classes e objetos possuem membros
que são dados privados, e métodos de
acesso, que são as funções. Quando um trecho de código quer fazer uma operação
sobre um objeto ele emite uma mensagem
para esse objeto requisitando a operação. Na prática, pode-se implementar isso
como uma simples chamada de função (chamada de método), que é o que ocorre em
C++. A principal característica de O-O é o encapsulamento,
que é justamente o fato de os dados não estarem acessíveis diretamente, mas
apenas através dos métodos permitidos. Para completar o conceito de O-O, o
mecanismo de herança faz a definição
de classes mais especializadas a partir de classes básicas. Nesse caso, se pode
reimplementar métodos de uma classe básica na classe mais especializada e
posteriormente tratar diversos objetos diferentemente especializados através
dos mesmos métodos, o que chama-se de polimorfismo.
A especificação de classes cuja única função é definir conjuntos de métodos dá
origem ao conceito de classes abstratas
e as diferencia dos tipos concretos
que são aqueles que realmente implementam os métodos.
O controle de acesso é um recurso que proteje os dados,
apenas em tempo de compilação, para enforçar a idéia de encapsulamento. Uma boa
regra é sempre declarar todas as variáveis em uma classe como privadas, e
oferecer métodos para inspecionar e alteras aquelas que devem ser permitidas. É
natural que muitas classes então tenham dados privados, um conjunto de funções get para retornar cada dado, e um
conjunto de funções set para setar
cada dado. Sempre faça assim, mesmo que paraça inútil à primeira vista. Desta
forma é garantido que: 1- somente os dados para os quais você oferece métodos
serão acessados diretamente; 2- todo acesso é feito através desses métodos, e
portanto é possível deixá-los consistentes um a um e entre eles; 3- qualquer
alteração de formato, representação, origem nos dados poderá ser feita em
apenas um lugar.
chapter4/4.1-01-firstclass.cpp
chapter4/4.1-02-secondclass.cpp
Cada classe pode implementar funções construtoras e
destrutoras de objetos, que têm, respectivamente o nome da classe e o nome da
classe precedido por ‘~’. As funções construtoras servem para fazer
inicialização do objeto, cuja memória já está alocada, e para criação de
sub-objetos, vetores ou quaisquer outras estruturas que este necessite para
estar completo. Se na inicialização de um objeto foram criados outros objetos
por alocação (com os operadores new e
new[]), então na sua destruição estes
devem ser apropriadamente retornados para o sistema (com os operadores delete e delete[]). Esta é a finalidade das funções destrutoras.
chapter4/4.3-01-construtor.cpp
chapter4/4.3-04-destrutor2.cpp
A existência das funções destrutoras traz um problema à
tona. Objetos podem ser copiados e assinalados livremente. Mas quando um objeto
contém um ponteiro para algo que ele criou (outro objeto, vetor, etc...) esse
ponteiro será copiado pelo seu valor binário, e não o conteúdo de um sub-objeto
para o outro. No exemplo acima, se dois cursos são criados e você assinala um
ao outro, não somente os dois ficam com o ponteirointerno apontando para a
mesma lista de alunos, como uma das listas de alunos fica solta sem ninguém a
referenciando na memória. Quando os objetos sairem do escopo (neste caso, ou
forem deletados com delete), as
funções destrutoras irão ser chamadas e irão deletar duas vezes a mesma lista
de alunos, causando mal funcionamento do programa. Para resolver isso,
implementa-se uma função especial para fazer o assinalamento. Isso é possível
através da sobrecarga de operadores. Define-se uma nova função para o operador
de assinalamento ‘=’, que deverá atuar entre um objeto dessa classe à sua
esquerda e um outro objeto da mesma classe à sua direita. A função
impplementada faz a cópia correta dos valores de objetos apontados pelo nosso
objeto principal sendo assinalado, e é a primeira aplicação prática e muito
útil de sobrecarga de operadores.
chapter4/4.3-05-assignment.cpp
Sobrecarregar o operador de assinalamento como no exemplo
anterior foi uma forma muito elegante de resolver um problema comum. Em C++,
dos 42 operadores existentes, 38 podem ser sobrecarregados. Mas deve-se evitar
fazer coisas muito complexas. Aqui são apresentados mais dois exemplos de
sobrecarga simples e que produz construções mais elegantes.
chapter4/4.4-01-elegantoverload.cpp
chapter4/4.4-02-safevector.cpp
chapter4/4.4-03-safevector2.cpp
Membros estáticos são aqueles que pertencem não a uma instância em particular mas ao escopo global. Em uma classe, se pode ter variáveis e métodos estáticos. Variáveis estáticas são conhecidas como variáveis de classe, pois só haverá uma instância delas, e não uma por objeto, como nas variáveis de objeto. Na linguagem C++, além de serem declaradas dentro da classe, elas devem ser declaradas e inicializadas fora de todas as classes, representando que são como variáveis globais.
Já para métodos a diferença não está na existência de múltiplas instâncias, mas apenas na dependência dos objetos. Métodos estáticos são como funções globais quaisquer, e não necessitam de um objeto da classe para serem chamados.
As variáveis e métodos também podem ser cconstantes ou
não. Variáveis constantes são aquelas que não serão alteradas. Elas precisam
ser inicializadas na sua declaração,já que não podem sofrer atribuições. Já os
métodos constantes, que se caracterizam pela palavra const logo após a lista de parâmetros entre parênteses, são aqueles
que apenas fazem inspeção do objeto, mas não alteram seu valor. Esses são os
únicos métodos que podem acessar objetos declarados como const no código, pois garantem que não alterarão seu valor. O
correto uso do qualificador const
confere maior clareza ao código.
chapter4/4.5-04-constfunction.cpp
A herança de classes permite especializar classes
genéricas. A classe mais genérica é chamda de classe base e a classe que herda
suas características é camada de classe derivada. A herança pode ser pública ou
privada. Quando é privada, default,
as características públicas da classe base se tornam privadas na classe
derivada.
Um ponteiro de uma classe base pode apontar para umobjeto
de classe dela derivada, mas não vice-versa. Métodos podem ser redefinidos em
classes derivadas, mas o método chamado depende do ponteiro atravé do qual é
chamado.
Pode-se verificar que quando uma classe deriva de uma
outra classe base, mesmo sendo ela um tipo especializado e sendo também do tipo
da classe base, ela não tem acesso aos membros provados da classe base. Para
permitir esse acesso, a classe base deve declarar membros protected. Membros protected
são privados desta classe na visão de todo o programa, mas acessíveis a classes
derivadas.
O construtor de uma classe derivada pode (e deve) chamar
construtores de classes base das quais deriva, passando os parâmetros corretos,
para que todas os objetos sejam construidos adequadamente.
Embora um ponteiro para uma classe base possa apontar
para um objeto de uma classe derivada, ele não pode chamar os métodos da classe
derivada, mais especializada, pois é um ponteiro genérico. Para isso existem as
funções virtuais. Se uma função é qualificada como virtual, quando as classes
derivadas reimplementam essa mesma função, é a versão mais especializada delas
que será chamada, mesmo que o ponteiro seja somente para a classe base.
Uma função virtual pura é aquela para a qual nenhuma
implementação é dada na classe base. Ela deve ser obrigatoriamente implementada
pelas classes derivadas. Isso significa que a classe base com funções virtuais
não pode ser utilzada para criar objetos, pois é uma especificação incompleta,
a que chamamos de tipo abstrato, diferindo dos tipos concretos, que são so
completos.
A linguagem C++ permite herança múltipla. Herança múltipla permite flexibilidade mais traz problemas adicionais, como múltiplas ocorrências de algum membro comum às classes base. Assim, não é uma opção muito interessante em arquitetura de software. É preferível especificar interfaces abstratas, que são classes com funções virtuais que apenas especificam que tipos de operações podem ser executadas sobre um determinado tipo de objeto, do que realmente implementar herança de múltiplos tipos concretos.
Com os recursos vistos até aqui, que já foram selecionados, é possível escrever programas com diversos tipos de organização e estilo de codificação, mesmo porque é em geral possível tanto modelar os dados quanto implementar algoritmos de diferentes formas em qualquer liguagem. Este capítulo dá algumas referências e exemplos sobre modelagem O-O e estilos de codificação, mostrando vantagens e desvantagens de cada um.
Cada organização procura disciplinar o desenvolvimento de
código fonte de alguma forma, para que seu desenolvimento seja seguro e a
manutenção mais fácil. É usual existirem regras para a codificação e formatação
de código. Aqui apenas apresentamos algumas pequenas regras gerais que você
deve seguir, na opinião do autor, mas muitas outras são necessárias para um estilo
de programação mais perfeito.
· sempre inicialize as variáveis na sua declaração;
· sempre teste a validade dos parâmetros recebidos em uma função: se forem números, seus limites, e se for ponteiro, para que não seja nulo;
· declare todas as variáveis de objeto como privadas, sem exceções;
· jamais utilize números no código: declare constantes que digam seu significado e sejam inicializadas com seu valor;
· sempre desconfie quando você escrever três vezes o mesmo código: deve haver um meio genérico de fazer o mesmo com repetições, vetores, indireção ou templates;
· não use variáveis globais, variáveis estáticas, goto, defines;
· prefira construções simples e claras à aglomerados de expressões que fazem o mesmo processamento;
· use o qualificador const sempre que possível;
· evite o uso de ponteiros: use referências e as estruturas de STL, guardando objetos em vetores, listas e principalmente maps, onde eles podem ser localizados, recuperados e alterados através do uso de identificadores e iteradores;
· sempre use edentação correta, preferencialmente com as chaves alinhadas;
· comente no inicio do arquivo o que ele faz, seu autor, data de criação, e ao menos data da última atualização e qual foi: se possível, mantenha a lista das atualizações, datas e quem as executou;
· use comentários antes das implementações das funções dizendo o que elas fazem, a estratégia, o algorimo;
· use comentários separando partes do código e anunciando o que será feito;
· use comentários no fim da linha para explicar detalhes daquela linha;
· declare apenas uma variável por linha;
· use nomes de variáveis que expliquem seu uso;
· faça
tabulações na declaração de variáveis (em classes e métodos) de forma que
fiquem alinhados verticalmente: tipos de retorno, nomes, parâmetros,
comentários;
Programas grandes devem ser divididos em módulos, e
estarem implementados em arquivos diferentes, para melhor organização e
compilação por partes.
O primeiro exempo é um programa completo em C, onde
podem-se identificar diversos tipos de declarações que formam o programa: includes, defines, estruturas, protótipos de função, variáveis globais função
principal e implementação das funções utilitárias. Todas as definições quenão
geram código devem estar em um arquivo de cabeçalho, para que outros módulos
possam conhecer suas interfaces. O programa principal também deve estar
separado de todos os módulo, para que esses possam ser aproveitados. O programa
foi então dividido nos três arquivos que seguem.
De forma análoga, um programa em C++ também é composto por um conjunto de definições e depois as suas implementações. As definições devem ir para um arquivo de cabeçalho (.h) e as implementações para arquivo do módulo (.cpp).
O exemplo abaixo mostra como evitar a reimplementação de
listas construindo classes base genéricas para lista e depois especializando-as
para os tipos de dados que se deseja armazenar. Mas também é possível fazer uma
lista genérica sem herança, com nodos que contenham ponteiros void. Esses ponteiros podem apontar para
qualquer tipo de objeto, e então é possível armazenar quaisquer objetos dentro
dela, desde que as conversões de ponteiros sejam indicadas com cast. Sugere-se implementar uma lista
desse tipo como exercício.
chapter5/5.3-01-listoptions.cpp
chapter5/5.3-02-listoptions2.cpp
As duas alternativas vistas para implementação da lista
encadeada evocam a questão de quando se deve usar herança ou quando se devem
usar apontadores (ou mesmo cópias) para outros objetos. Em alguns casos tanto
uma como outra alternativapoderão funcionar, apresentando vantagens e
desvantagens. Mas existe uma regra básica que é a seguinte. Quando há dois
elementos (classes) relacionados sendo especificados, pergunta-se se a relação
dentre os dois é do tipo “é um” ou “tem um”. Por exemplo, um funcionário tem um emprego, e não é um emprego. Ao contrário, um aluno é uma pessoa, e não tem
uma pessoa. Na maioria dos casos, essa resposta é bem clara. Em alguns outros,
será possível identificar as alternativas. Por exemplo, um botão é um objeto
gráfico, mas ele também pode ter uma funcionalidade própria e um objeto gráfico
associado.
Um template é uma evolução das macros de C, mas que inclui todos os recursos sofisticados e verificações de alto nível da linguagem C++. É uma maneira de especificar estruturas e algoritmos configuráveis. STL é uma biblioteca padronizada contendo um conjunto de templates que implementa as estruturas de dados e os algoritmos mais utilizados na construção de um programa. Templates devem ser usados para prover maior produtividade, evitando que se tenha que re-inventar a roda a cada novo programa, ou que se dependa de bibliotecas produzidas por terceiros e os problemas associados de portabilidade. Por essa razão, aqui será tratado apenas o conceito e o praticado o uso de templates da STL, evitando-se toda complexidade associada à definição de novos templates de classes e métodos. Mesmo com a exclusão desse conteúdo, apresenta-se brevemente no final o conceito de programação genérica, cujo potencial é bastante atraente.
chapter6/6.1-01-hello.cpp Anexo 1 Operadores e
palavras reservadas É essencial para a programação conhecer os operadores da
linguagem C++. Listas de operadores, descrições e exemplos podem ser
encontrados em muitas fontes, como em “http://www.cplusplus.com/doc/tutorial/tut4-2.html”
[CPP 2004], por exemplo. A
seguir está uma lista de todos os operadores da linguagem , com sua
associatividade e finalidade, retirada de “http://www.cs.stmarys.ca/~porter/csc/ref/cpp_operators.html”
[SCO 2004]. A tabela inicia com os operadores de mais alta precedência, e cada
nível de menos precedência está separado pelas linhas pontilhadas. A
precedência define qual operação será efetuada primeiro quando aparece ao lado
de outras com operadores de mesma precedência. Já a associatividade diz qual
operação será edetuada primeiro quando os operadores têm mesmo nível de
precedência. A associatividade pode ser à esquerda (LR) ou à direita (RL). Operators
Assoc Description ---------------------------------------------------------------------- (expression)
parentheses used for grouping ::
RL (unary) global scope
resolution operator ::
LR class scope resolution
operator ---------------------------------------------------------------------- ()
LR parentheses used for a
function call ()
LR value construction, as in
type(expression) .
LR member selection via struct or
class object ->
LR member selection via pointer []
LR array element access const_cast
LR specialized type cast dynamic_cast LR specialized type cast reinterpret_cast
LR specialized type cast static_cast
LR specialized type cast typeid
LR type identification ++ -- LR
postfix versions of increment/decrement ---------------------------------------------------------------------- All the operators in this section are unary (one argument)
operators. ++ -- RL
prefix versions of
increment/decrement + -
RL unary versions !
RL logical NOT ~ RL bitwise complement ("ones
complement") &
RL address of *
RL dereference new
RL allocates memory to dynamic
object delete
RL de-allocates memory allocated
to dynamic object new []
RL allocates memory to dynamic
array delete []
RL de-allocates memory allocated
to dynamic array sizeof
RL for computing storage size of
data (type)
RL cast (C-style type conversion) ---------------------------------------------------------------------- .*
LR struct/union/object pointer
(member dereference) ->*
LR pointer to struct/union/object
pointer
(indirect member dereference) ---------------------------------------------------------------------- * / %
LR multiplication and division ---------------------------------------------------------------------- + - LR addition and subtraction ---------------------------------------------------------------------- >>
<< LR input and output stream operators ---------------------------------------------------------------------- < <= >
>= LR inequality relational ---------------------------------------------------------------------- == != LR
equality relational ---------------------------------------------------------------------- &
LR bitwise AND ---------------------------------------------------------------------- ^
LR bitwise XOR ---------------------------------------------------------------------- |
LR bitwise OR ---------------------------------------------------------------------- &&
LR logical AND ---------------------------------------------------------------------- || LR logical OR ---------------------------------------------------------------------- ?: RL conditional ---------------------------------------------------------------------- =
RL assignment *=
RL multiplication and assignment /=
RL division and assignment %=
RL modulus (remainder) and
assignment +=
RL addition and assignment -=
RL subtraction and assignment ---------------------------------------------------------------------- throw
LR throw exception ---------------------------------------------------------------------- , LP the operator, not the separator
(combines two expressions into one) ---------------------------------------------------------------------- As palavras reservadas da linguagem C++ são as da tabela
abaixo. Elas não podem ser utilizadas como nomes de identificadores, e isso
pode conflitar com programas escritos em C e que sejam portados para C++.
asm |
do |
if |
public |
this |
auto |
double |
inline |
register |
throw |
break |
else |
int |
return |
try |
case |
enum |
long |
short |
typedef |
catch |
explicit |
mutable |
signed |
union |
char |
extern |
namespace |
sizeof |
unsigned |
class |
float |
new |
static |
using |
const |
for |
operator |
struct |
virtual |
continue |
friend |
private |
switch |
void |
default |
goto |
protected |
template |
volatile |
delete |
|
|
|
while |
Bibliografia
[STR 97] Stroustrup,
Bjarne. The C++ Programming Language –
Third edition. Addison-Wesley, 1997.
[STR 2004] Stroustrup, Bjarne. The C++ Programming Language. 2004. Disponível em: “http://www.research.att.com/~bs/C++.html”
[HAM 98] Scott Hamilton. The father of C++ explains why Standard C++ isn't just an object-oriented language. New York, IEEE, 1998. Disponível em: http://www.research.att.com/~bs/ieee_interview.html.
[STE 91] Al Stevens. Aprenda você mesmo C++. Rio de Janeiro, LTC, 1991.
[SGI 2004] SGI. Standard Template Library Programmer's Guide. 2004. Disponível em: http://www.sgi.com/tech/stl/
[BRA 1998] Eric Brasseur . C++ tutorial for C users. 1998. Disponível em: http://www.4p8.com/eric.brasseur/cppcen.html#l24
[CPP 2004] Cplusplus Recources. Overloading operators. Disponível em : “http://www.cplusplus.com/doc/tutorial/tut4-2.html”.
[SCO 2004] Porter Scobey. C++ Operators,
with Precedence and Associativity 2004 Disponível em : “http://www.cs.stmarys.ca/~porter/csc/ref/cpp_operators.html”.
[ECK 2000] Bruce Eckel. Thinking in C++
2nd Edition. 2004 Disponível em : “http://mindview.net/Books/TICPP/ThinkingInCPP2e.html”. (Free
Electronic Book)