Preview only show first 10 pages with watermark. For full document please download

Microsoft Word - Mi01-0316 - Manual C

Microsoft Word - MI01-0316 - Manual C

   EMBED

  • Rating

  • Date

    December 2018
  • Size

    331.4KB
  • Views

    7,439
  • Categories


Share

Transcript

Manual de C++ Curso: Nome do Manual: Versão: Data: Elaborado Por: Verificado Por: Empresa: Morada: Telefone: Fax: Internet: Email: Presidente: Dir Qualidade: Dir Pedagógica Dir Tec. Informação MI01-0316 C ++ MI01-0316 - Manual de C++ Versão 01 28-07-03 Ana Paula Regageles Sérgio Gouveia NHK - Formação e Novas Tecnologias, Unipessoal, Lda Av. Duque D’ Ávila , 72 B 1050 - 083 Lisboa 213 301 888 213 301 886 www.nhk.pt [email protected] Dr. Karen Bolbol Dra Fátima Gil Dra Eva Rosa Sérgio Gouveia ATENÇÃO: Este manual destina-se a apoiar os formados da NHK no curso. Não pretendendo ser um manual exaustivo do curso em questão, apresentase como uma ferramenta de consulta quer durante a duração do curso, quer após a conclusão do curso. O manual foi criado para satisfazer os requisitos das formações da NHK. Contudo poderá não corresponder na integra ao conteúdo programático do curso, sendo que será possível que alguns temas aqui abordados não façam parte do conteúdo programático do curso, assim como poderá acontecer que durante o curso sejam dados alguns temas que não se encontram neste manual. A NHK reserva o direito de alterar o conteúdo do curso ou do manual de forma a satisfazer a sua oferta de cursos. Copyright - NHK - Formação e Novas Tecnologias, Unipessoal, Lda Este manual não pode ser reproduzido total ou parcialmente sem a autorização da NHK Formação e Novas Tecnologias, Unipessoal, Lda V01 -5- NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 Índice INTRODUÇÃO ................................................................ 8 1. 1.1. H ISTÓRIA DO C++ ..........................................................8 2. O PRIMEIRO PROGRAMA EM C++ ................................. 8 2.1. ESCREVER NO STANDARD OUTPUT. .......................................9 2.2. V ARIÁVEIS E S EUS TIPOS. ...............................................9 2.3. L ER DO S TANDARD O UTPUT .............................................. 11 2.3.2. Váriaveis Constantes ................................................... 11 2.4.OPERAÇÕES A RITMÉTICAS COM V ARIÁVEIS NUMÉRICAS .............. 12 2.4.1. Operadores de Incremento e Decremento ...................... 13 2.4.2. Quantificadores .......................................................... 14 3. INSTRUÇÕES DE CONTROLO DE EXECUÇÃO ................. 14 3.1. OPERADORES L ÓGICOS .................................................... 15 3.2. OPERAÇÕES DE RELAÇÃO ................................................. 15 3.4. INSTRUÇÃO IF-ELSE ........................................................ 16 3.5. INSTRUÇÃO SWITCH ....................................................... 19 3.6. INSTRUÇÃO WHILE ......................................................... 20 3.6.1. Instrução do-while ...................................................... 22 3.7. INSTRUÇÃO 4. FOR ............................................................ 22 ARRAYS....................................................................... 24 4.1. A RRAYS 5. DE CARACTERES .................................................. 26 FUNÇÕES..................................................................... 28 5.1. FUNÇÕES RECURSIVAS .................................................... 30 6. ENUMERAÇÕES............................................................ 30 7. REFERÊNCIAS.............................................................. 31 7.1. PONTEIROS ................................................................. 32 7.2. MEMÓRIA D INAMICA ....................................................... 33 8. O OBJECTO STRING ..................................................... 34 8.1. INTRODUÇÃO AOS ITERADORES .......................................... 35 9. ACESSO A FICHEIROS (MODO TEXTO) ......................... 36 10. CLASSES...................................................................... 37 10.1. CLASSES, ASPECTOS B ÁSICOS .......................................... 39 10.2. CONSTRUTORES .......................................................... 41 10.3. D ESTRUIDORES ........................................................... 41 11. TEMPLATES .................................................................... 42 11.1.TEMPLATES DE CLASSE ................................................... 42 12. ITERADORES .................................................................. 44 12.1. L ISTA S IMPLESMENTE V01 LIGADA .......................................... 45 -6- Manual de C++ MI01-0316 13. HERANÇA E POLIMORFISMO........................................... 46 14. OPERAÇÕES RELACIONADAS COM BITS ....................... 48 15. BIBLIOGRAFIA ............................................................ 51 V01 -7- NHK – Formação e Novas Tecnologias Unipessoal Lda 1. MI01-0316 INTRODUÇÃO O C++ é uma linguagem de programação de alto-nível, basicamente consiste numa extensão do C, ou que pode ser "chamado" um "C melhor". O C++ inclui novos conceitos não suportados pelo C, como o paradigma de programação orientado por objectos e todos os conceitos associados a ele. 1.1. HISTÓRIA DO C++ O C++ foi desenvolvido por Bjarne Stroustrup na empresa Bell Labs, o berço da linguagem C. Stroustrup desenvolveu o C++ principalmente como um complemento em termos de orientação por objectos da linguagem C. Como consequência disso, o C++ tem em comum com a linguagem C a maior parte das formas de sintaxe, palavras-chave e bibliotecas. A linguagem C++ continua a desenvolver-se. Actualmente a evolução do C++ passou pelas mãos dos laboratórios Bell para a comissão ANSI C++. Esta comissão está a trabalhar no sentido de normalizar a linguagem C++ e as suas principais bibliotecas. Embora o C++ proporcione novos utensílios de orientação por objectos aos programadores de C, existem algumas pequenas diferenças entre as duas linguagens. Estas pequenas diferenças podem ser encontradas na criação de variáveis, e na declaração de tipos definidos pelo utilizador. 2. O PRIMEIRO PROGRAMA EM C++ Um programa em C++ é um conjunto de funções. O conceito de função será explicado mais à frente, por agora só é preciso saber que todo o programa em C++ tem obrigatoriamente de conter uma função main. Aqui está o programa mais básico do C++, um programa que não faz nada: // programa que não faz nada! void main() { /* o código que faria o programa funcionar deveria estar aqui */ } O texto a bold, é um comentário, os comentários são ignorados pelo compilador e devem ser usados abundantemente, para fazer com que o código seja mais fácil de compreender. Existem duas maneiras de realizar comentários, usando "//", o que fará com que toda a linha, apartir daí, seja comentada, e "/* ... */", em que o texto que fica entre "/*" e "*/" é um comentário. Antes de main() está a palavra void, que é uma palavra reservada do C++ (keyword do C++). Por agora não é necessário compreender estes conceitos, mas sim compreender que o exemplo acima é a base de qualquer programa em C++. O C++ é Case Sensitive, ou seja é diferente escrever mAin(), Main(), MAIN(). Se não houver uma função com o nome exactamente igual a main(), o programa não funcinará! V01 -8- Manual de C++ MI01-0316 Os ficheiros fonte da linguagem C++ têm a extensão ".cpp". Sempre que criar um ficheiro com código em C++, ponha-lhe um nome do género: "nome_ficheiro.cpp". 2.1. ESCREVER NO STANDARD OUTPUT. Antes de mais, o que é o Standard output? Sempre que me referir ao standard output falo do sistema de output por defeito no C++. Neste caso falo de um output para DOS. De seguida encontra-se um programa para quem começa a aprender uma linguagem de programação: // este programa escreve no standard output a mensagem "Olá Mundo!" #include // inclui o necessário para poder usar cout<<"..."; using namespace std; void main() { cout<<"Olá Mundo!"< faz é incluir no programa o conteúdo do ficheiro iostream. Já dentro da função main() encontra-se a instrução cout<<"Olá Mundo!";, que fará aparecer no ecrã "Olá Mundo!". A palavra endl diz ao programa para mudar de linha e todas as instruções dentro do main() têm de ser concluídas com ponto e vírgula ';'. Não há nada melhor para apreender estes conceitos que experimentar! Faça um programa parecido, mude as palavras... Existem três momentos na realização de um programa: tempo de criação do código (design time), quando o compilador está a criar o executável (compile time) e durante a execução do programa (run time). 2.2. VARIÁVEIS E SEUS TIPOS. Até aqui já sabemos escrever no standard output, mas é fundamental que escrevamos aquilo que queremos e quando queremos. Ao escrever cout<<"Olá!; sempre que corrermos o programa aparecerá "Olá!", e se quisermos escrever outras coisas? O conceito de variável nasce daqui, variável é tipo uma gaveta onde temos um valor que pode mudar quando quisermos e há vários tipos de variáveis, sendo os mais simples os seguintes: V01 -9- NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 // declaração de variáveis void main() { int a; // cria uma variável numérica de tipo inteiro com o nome a double b; // cria uma variável real com o nome b char c; // cria uma variável que contém um único caractér bool d; // cria uma variável booleana (true ou false) } A linguagem C++ aceita os seguintes tipos de dados: int, char, float, double, bool e void. Este último trata-se de um tipo muito especial: não tem valor. Para alterar o conteúdo das variáveis existe o operador '='. O exemplo seguinte mostra como declarar, inicializar e mostrar no standard output variáveis. // declaração, inicialização e escrita de variáveis no standard output #include using namespace std; void main() { int a; // declarar a variável a como inteiro a = 2; // afectar a variável a com o número 2 cout<<"a = "< // a variável de leitura também se encontra neste ficheiro using namespace std; void main() { int a; // declaração da variável a cout<<"introduza um número inteiro:"<>a; // lê do standard input um valor inteiro e afecta a variável a com ele cout< using namespace std; void main() { // declarações de variáveis int a, b, c; // o mesmo que int a; int b; int c; double media; /* variável para armazenar o valor da média, double porque o resultado pode não ser inteiro */ cout<<"Introduza 3 números inteiros: "; cin>>a>>b>>c; // o mesmo que cin>>a; cin>>b; cin>>c; // calcular a média media = (a+b+c) / 3; // apresentar o resultado cout< using namespace std; void main() { int i = 2, j = 3; // declaração e inicialização das variáveis j e i int a = 0; // para testes a = ++i; a = j++; // a=3, i=3 // b=3, j=4 --i; // i = 2 j--; // j=3 a += --(++j + i++); } V01 - 13 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 Existem duas maneiras de aplicar os operadores ++ e --. Se colocarmos estes operadores á esquerda dos seus operandos eles alterarão o valor do operando antes de este atribuir o seu valor a uma expressão. Da mesma forma, se colocarmos estes operadores à direita dos seus operandos eles alterarão o valor do operando depois de este atribuir o seu valor a uma expressão. Se os operadores ++ e – forem os únicos operadores presentes numa instrução, não fará qualquer diferença serem utilizados antes ou depois. 2.4.2. QUANTIFICADORES Aos tipos base podem-se acrescentar certos qualificadores. A seguinte tabela mostra essas variantes: qualificador aplica-se a representa short int menor dimensão long int ; double maior dimensão signed char ; int com sinal unsigned char ; int sem sinal Equivalências: 3. unsigned int unsigned short int short long int long INSTRUÇÕES DE CONTROLO DE EXECUÇÃO Já aprendemos a usar variáveis, a fazer cálculos e apresentar resultados. Agora chega a a parte de aprender a fazer com que o nosso programa tome decisões, decida entre fazer isto e aquilo, fazer a mesma coisa um número pré-definido de vezes, ou simplesmente enquanto nós quisermos! Nos próximos capítulos falaremos do que são as instruções de Controlo e Execução, porém, para as podermos utilizar convenientemente e com sabedoria, é necessário primeiro conhecer os operadores lógicos. V01 - 14 - Manual de C++ MI01-0316 3.1. OPERADORES LÓGICOS As seguintes tabelas definem todos os operadores lógicos do C++. Sejam a e b duas variáveis booleanas (do tipo bool): Operação AND (&&) Operação OR ( || ) Operaçâo NOT (!) a b a && b a b a || b a !a 0 0 0 0 0 0 0 1 0 1 0 0 1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 Operação IGUAL ( ==) a b a == b 0 0 1 0 1 0 1 0 0 1 1 1 Recorde-se que um bool apenas permite os dois valores lógicos true e false, tente fazer o seguinte exercício: void main() { bool b = true, c = false; bool a = 0; /* embora se possa inicializar ou afectar um bool com 0 ou 1 recomenda-se sempre que se use true ou false */ // exemplo b = !a; // b passou a false c = a || b; // c continua a false // tente encontrar o novo valor de a a = (!b) || (a && c); } 3.2. OPERAÇÕES DE RELAÇÃO operador descrição a>b retorna true se a maior que b a= b retorna true se a maior ou igual a b a == b retorna true se a igual a b a != b retorna true se a diferente de b V01 - 15 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 No próximo capítulo veremos uma aplicação mais útil do tipo bool e dos operadores lógicos e de relacção. Em qualquer linguagem de programação os construtores de tomada de decisões permitem que as aplicações analisem as condições e seleccionem as alternativas de implementação. As diversas linguagens de programação apresentam construtores de tomadas de decisão com características diferentes. Neste capítulo vamos estudar os construtores de tomada de decisões do C++ e abordar os seguintes temas: • • • • • A A A A A instrução instrução instrução instrução instrução if-else switch while do-while for 3.4. INSTRUÇÃO IF-ELSE A instrução if-else tem a seguinte sintaxe: if( ) else ou simplesmente if( ) No primeiro caso, se for true, então realizar-se-á a , se for false passa para a . No segundo caso apenas se for true se poderá efectuar a . Vejamos o seguinte exemplo práctico: #include using namespace std; void main() { int a, b; // pedir dois valores inteiros cout<<"Introduza 2 valores inteiros: "<>a>>b; if( a == b ) cout<<"Os valores são iguais!"; else cout<<"Os valores sao diferentes!"; cin.get(); } V01 - 16 - Manual de C++ MI01-0316 No parâmetro de teste do if pode-se ter qualquer espressão, mas só será efectuada a instrução correspondente se o resultado dessa expressão for verdadeira (true). Quem está a acompanhar este Manual, talvez se pergunte: Então e se eu quiser por mais que uma instrução? Aqui entra o conceito de scoop. Um scoop é sempre limitado por chavetas, por exemplo, existe o scoop da função main. Penso que com o seguinte exemplo se tornará mais claro: #include using namespace std; void main() { int a, b; // pedir dois valores inteiros cout<<"Introduza 2 valores inteiros: "<>a>>b; if( a == b ) { cout<<"Os valores são iguais!"; cout<<"Vêm, foi usada outra instrução dentro deste if!"; } else{ cout<<"Os valores são diferentes!"; } cin.get(); } Se a condição do if for verdadeira (true), ele realiza a instrução seguinte, e um scoop é um conjunto de instruções (também pode ter só uma como se vê no else). Atenção que uma variável declarada dentro de um scoop deixa de existir fora dele! Exemplo: #include using namespace std; void main() { bool b = true; if(b){ // entrará sempre pois b = true int a = 4; cout<<"a= "<<4; } a = 3; // ERRO!!! /* a variavel só existe dentro do scoop em que foi declarada! */ } V01 - 17 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 O conceito do if está explicado. O operador ternário é um caso especial de if e tem a seguinte sintaxe: ( ) ? : ; Veremos este programa exemplo em que o operador ternário afecta a variável a com o módulo da variável b. Chama-se módulo de um número à sua versão positiva. Exemplo: módulo de -1 é 1, módulo de 2 é 2, módulo de 0 é 0; #include using namespace std; void main() { int a, b; cout<<"Introduza um número inteiro diferente de zero:"; cin>>b; a = ( b>0) ? b : -b; /* lê-se: o b é maior que 0? se sim retorna b : se não retorna -b */ cout<<"O modulo de "< using namespace std; void main() { char c; cout<<"Introduza uma vogal minúscula:"<>c; switch(c) { case 'a': cout<<"Introduziu um a!"< using namespace std; // programa que diz se um número introduzido é maior, igual ou menor que 3 void main() { int a; cout<<"Introduza um número entre 0 e 5:"<>a; switch(a) { case 0: case 1: case 2: cout<<"O número "< ) Para este programa de exemplo, devo fazer uma introdução. O cin.get() que usávamos para esperar por um ENTER, na verdade lê qualquer caracter, e podemos inclusivé fazer char a=cin.get(); Há vários caractéres especiais (dos quais falaremos mais à frente). O que vamos usar no próximo programa é o '\n', que tem a função de mudar de linha. V01 - 20 - Manual de C++ MI01-0316 Fazer cout<<"Olá"< using namespace std; // programa que pede que o utilizador escreva uma linha de texto e conta os caractéres void main() { unsigned charCount = 0; // inteiro sem sinal, >= 0 cout<<"Introduza uma linha de texto:"< O programa irá ler todos os caractéres até encontrar e por cada caractér que ler incrementará o charCount. O operador ternário está lá para completar a frase com "." ou com "ers.", consoante o número de caractéres. No cout, em vez de se estar a fazer cout<<"Atirei o pau ao gato"< using namespace std; // programa que soma números até o utilizador escolher -1 void main() { int soma = 0, num=0; cout<<"Introduza vários números. -1 para acabar"<>num; } while( num != -1 ); cout<<"Soma: "< ; ; ) V01 - 22 - Manual de C++ MI01-0316 Programa de exemplo: #include using namespace std; // programa que escreve o alfabeto void main() { for( char c = 'A'; c <= 'Z' ; ++c ) { cout<<' '<< c; if( (c - 'A' + 1) % 4 == 0 ) cout< using namespace std; // programa que pede um caracter n vezes void main() { int num; char c; caracter e um numero(n) e escreve esse cout<<"introduza um numero:"; cin>>num; cout<<"introduza um caracter:"; cin>>c; cout< using namespace std; // programa que lê 3 números e os escreve de ordem inversa void main() { // declarar as variáveis int a, b, c; // pedir as variáveis cout<<"Introduza 3 inteiros:"<> a >> b >> c; V01 - 24 - Manual de C++ MI01-0316 // escrever cout<<"Os numeros de ordem inversa:"< using namespace std; // programa que lê n números e os escreve de ordem inversa void main() { // declarar as variáveis const int n_elementos = 3; int i; int a[n_elementos]; /* a variável a fica com 3 "casas". a[0], a[1] e a[2] e podemos trabalhar com cada uma delas como sendo variáveis distintas */ // pedir as variáveis for( i=0; i>a[ i ]; cout<=0; cout< using namespace std; // programa que encontra o maior de 10 números lidos void main() { const int n_elementos = 10; int i, e[n_elementos]; cout<<"Introduza 10 números: "<> e[ i ]; int max = 0; // variável que irá conter o índice do valor máximo for( i = 0; i < n_elementos; ++i ) if( e[ i ] > e[ max ] ) max = i; cout<<"O maior número foi o "<< e[ max ] <<" e foi o "<< max <<"º Número a ser introduzido."; cin.get(); } 4.1. ARRAYS DE CARACTERES Até agora praticamente só tratámos números, com arrays de caractéres temos a possibilidade de tratar também strings. Um array de caractéres é um array normal, só que tem letras em cada índice. #include using namespace std; void main() { // maneiras de inicializar arrays de caracteres char s1[ 5 ] = "Olá\0"; char s2[ 6 ] = { 'A', 'd', 'e', 'u', 's', '\0' }; char nome[10]; cout<<"Introduza o seu nome: (max 10 letras)"<> nome; cout<< s1 << nome << endl; cout<< s2; cin.get(); } V01 - 26 - Manual de C++ Este exemplo mostra como se pode pedir uma string ao utilizador e depois escrevê-la no standard output novamente. De notar que todas as strings que foram inicializadas têm '\0' como último caractér. MI01-0316 Sugestões Ou Dicas Todas as cadeias de caractéres em C++ têm de terminar com '\0', o caractér terminador. Não se podem afectar arrays, não se poderia fazer nome = "Pedro"; O que se teria de fazer era afectar cada casa do array com a letra correspondente. Em princípio eu devia neste Manual mostrar uma série de algoritmos para tratamento de strings, como concatenar, substituir letras... etc. Não o faço porque o C++ fornece ao utilizador um objecto string que já faz essas coisas todas e onde se pode fazer string p,r; p="Pedro"; p += r; Este objecto será tratado mais à frente. Tabela de Caracteres Especiais V01 Caractér Descrição \a bell, emite um som \b retrocesso ( backspace ) \0 caractér terminador \f início de página \n mudança de linha \r início de linha \t tab horizontal \v tab vertical \\ backslash \' plica \" aspas \ooo caractér cujo código em octal é ooo \xhh caractér cujo código em hexadecimal é hh - 27 - NHK – Formação e Novas Tecnologias Unipessoal Lda 5. MI01-0316 FUNÇÕES As funções são as pedras fundamentais que aumentam as capacidades da linguagem C++ de modo a que ela sirva perfeitamente os objectivos do utilizador. A linguagem C, que esteve na origem do C++, é mais orientada para as funções do que o C++. Esta diferença deve-se ao facto de o C++ suportar a existência de classes, de heranças e de outras características de programação orientada por objectos. Contudo, as funções desempenham um papel vital em C++. Já tinha dito que a função main é a primeira a ser chamada, é como se fosse o corpo do programa. Mas o C++ não vive só da função main e podemos criar quantas funções quisermos, como quisermos, para fazer aquilo que quisermos! #include using namespace std; // programa que introduz as funções void { ola_mundo() // declaração + definição cout<<"OlA Mundo!"< using namespace std; // programa que introduz as funções int max( int a, int b ); // declaração void main() { int v1, v2; cout<<"introduza dois inteiros: "<> v1>> v2; cout<=b ? a : b; } A função max recebe dois valores como parâmetro, o que se vai fazer é que o compilador irá criar duas variáveis a e b e irá afectá-las com os valores de v1 e v2 respectivamente. É como se fossem criadas cópias dos valores originais. Todas as funções que não retornem void têm obrigatoriamente de ter a palavra return a retornar qualquer coisa. No caso da função max o operador ternário irá ver qual o maior e retorna esse valor. Sempre que o return é chamado a função acaba, e pode ser chamado a qualquer altura. Aproveito para lançar também o conceito de variável global. Se houvesse uma variável declarada fora da função main ela estaria acessível em todas as funções do ficheiro. Exemplo: #include using namespace std; //variável global int v1; int max( int a, int b ); // declaração void main() { int a; v1 = 2; // correcto, v1 está acessível pois é global } void { f1() // definição v1 = 5; // correcto, v1 está acessível pois é global a = 2; // ERRO! variavel declarada na função main e portanto só está acessivel em // main! teria de haver também uma declaração int a; nesta função para que // não desse erro } V01 - 29 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 É standard C++ que o tipo de retorno da função main seja inteiro. Há mesmo compiladores que não aceitam void main(). Por norma usa-se int main() { /*.. */ return 0; } A partir de agora não usarei mais void main(). 5.1. FUNÇÕES RECURSIVAS O C++ suporta funções recursivas, as quais são funções que se chamam a si próprias. Não há necessidade de se utilizar uma sintaxe especial para indicar que se trata de uma função recursiva. A seguir podemos ver um exemplo simples de cálculo de um factorial utilizando a recursividade. A função factorial () indica o valor factorial do parâmetro x através de uma chamada recursiva a si própria. Double factorial (int x) //recursive factorial function { return ( x > 1 ) ? (double) X * factorial(x – 1) : 1.0;} 6. ENUMERAÇÕES As enumerações são uma alternativa a definir constantes e, visto que em C++ são mesmo um tipo, são mesmo muito úteis. Exemplo: enum { GREEN, YELLOW, RED }; era o mesmo que criar três constantes com os valores 0, 1 e 3. Por defeito o primeiro valor de uma enum é zero. Exemplos: enum Mes { JAN = 1, FEV, MAR, ABR, MAI, JUN, JUL, AGO, SET, NOV, DEZ }; // o valor de FEV = 2, MAR = 3... por ai adiante Mes mes; // ... switch( mes ) { case JAN: //... break; case FEV: //... break; } As enums ajudam muito em termos de raciocínio pois podemos usar palavras para descrever o que quisermos. Por norma as constantes são escritas com letras maiúsculas. #include using namespace std; enum Especialidade { VENDAS, PUBLICIDADE }; struct Empregado { V01 - 30 - Manual de C++ MI01-0316 int num]; int idade; Especialidade esp; }; void main() { // diferentes maneiras de inicalizar estruturas Empregado emp1 = { 1, 35, VENDAS }; Empregado emp2; // utiliza-se o ponto '.' para se aceder a cada atributo da variavel emp2 emp2.num = 2; emp2.idade = 19; emp2.esp = PUBLICIDADE; cout<<"Empregados:" < using namespace std; // demonstração do uso de referências int main() { int i; int &i_ref = i; // i_ref irá conter o endereço na memória da variável i // a partir de agora pode-se afectar o conteúdo de i com i_ref cout<< i <<" = "<< i_ref <> tam; V01 - 33 - números o NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 // reservar espaço int *a = new int[tam]; cout<<"Introduza os numeros..."<>a[ i ]; // escrever ao contrario for(i=tam-1; i>=0; --i) cout< // necessário para se ter acesso ao objecto string #include using namespace std; // demonstração do uso de strings int main() { string nome, sobrenome; cout<<"Introduza o seu nome:"<> nome; cout<<"Então " + nome + ", que tal este Tutorial? :) "<> sobrenome; sobrenome = " " + sobrenome; nome += sobrenome; Sobrenome" // para ficar " Sobrenome" // para ficar "Nome cout<<"O seu nome completo é: "<< nome; cin.get(); return 0; } V01 - 34 - Manual de C++ MI01-0316 Penso que este exemplo demonstra algumas das facilidades dadas pelo objecto string. De facto, tratar strings como um objecto em vez de como um array de caractéres é mesmo muito prático. 8.1. INTRODUÇÃO AOS ITERADORES Os iteradores serão dados mais à frente, mas fica aqui já o seu conceito e toda a sua utilidade. Um iterador é uma generalização de um ponteiro, de facto, é construído apartir de um ponteiro. Ainda é difícil demonstrar a utilidade de um iterador, por isso deixo somente uma demonstração da sua utilização. #include string #include using namespace std; // necessário para se ter acesso ao objecto // demonstração do uso de iteradores de strings // este programa conta o número de vogais de uma frase introduzida int main() { string::iterator it; // declarar um iterador de strings string p; int a,e,i,o,u, consoantes; a = e = i = o = u = consoantes = 0; cout<<"Introduza uma palavra (letras minusculas): "; cin>>frase; // percorrer a string for( it = p.begin() ; it < p.end() ; ++it ) switch( *it ) { case 'a': ++a; break; case 'e': ++e; break; case 'i': ++i; break; case 'o': ++o; break; case 'u': ++u; break; default: if( *it != " " ) ++consoantes; break; } cout<<"A palavra: \""<< p <<"\" tem"< // necessário para se ter acesso a ficheiros #include using namespace std; // demonstração de escrita e leitura em ficheiros int main() { char aux[10]; // criar o ficheiro ofstream out("output_file.txt"); // agora irá escrever no ficheiro out << "Ficheiro criado recorrendo C++"<> aux ) // enquanto houver alguma coisa para ler cout< using namespace std; // demonstração do uso da class Cadeira // incluir o ficheiro onde se encontra a classe cadeira #include "Cadeira.h" int main() { // criado um objecto cadeira Cadeira cadeira ( 10, 20, BRANCO ); // a seguinte linha daria erro // cout<< cadeira.peso // isto porque não se pode aceder aos métodos privados cout<< cadeira.retornarPeso() < porque só os ficheiros standard do C++ é que devem/podem estar entre <>. Todos os ficheiros criados pelo programador estão entre aspas. 10.1. CLASSES, ASPECTOS BÁSICOS O C++ permite-nos declarar uma classe onde sejam englobados os elementos de dados e as funções dos elementos. Estas funções alteram os valores dos elementos de dados. Iremos falar dos aspectos mais básicos de uma classe, aqueles que todos devem saber para fazer um melhor uso das classes. Não há nada melhor do que um exemplo. Irei desenvolver uma classe Ponto que, por acaso, até costuma ser muito usada pelos programadores gráficos. #ifndef _TUT_PONTO_H_ #define _TUT_PONTO_H_ // para leitura e escrita #include using namespace std; class Ponto { V01 - 39 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 /* por defeito os atributos/métodos de uma classe são sempre privados por isso não é necessário por aqui private: */ public: // mEtodos publicos // atributos do ponto, tambEm se poderia adicionar a cor // mas como nAo irei mexer em grafismo, não vale a pena int x, y; // construtor Ponto( const int &_x = 0, const int &_y = 0 ): x(_x), y(_y) { } // construtor por copia Ponto& Ponto( const Ponto & p ); // destrutor ~Ponto() { /* não há nada para ser feito */ } // afetar um Ponto com outro Ponto& operator= ( const Ponto &p ); // somar dois pontos Ponto operator+ ( const Ponto &p ); Ponto operator+= ( const Ponto &p ); // subtrair... Ponto operator- ( const Ponto &p ); Ponto operator-= ( const Ponto &p ); // operadores de incremento Ponto& operator++ (); Ponto& operator++ (int); Ponto& operator-- (); Ponto& operator-- (int); e decremento // ex: Ponto a; ++a; // ex: Ponto a; a++; // ex: Ponto a; --a; // ex: Ponto a; a--; // operadores relacionais lógicos bool operator== ( const Ponto &p ); bool operator!= ( const Ponto &p ); bool operator>= ( const Ponto &p ); bool operator<= ( const Ponto &p ); bool operator> ( const Ponto &p ); bool operator< ( const Ponto &p ); }; // nunca esquecer deste ; // funções globais de escrita e leitura ostream& operator << ( ostream& os, const Ponto &p ); // para fazer Ponto _k; cout<<_k; istream& operator >> ( istream& is, const Ponto &p ); // para fazer Ponto a; cin>>a; #endif // _TUT_PONTO_H_ Muitos dos métodos não têm código nenhum associado... Todos os que têm estão definidos inline, ou seja, por cada objecto criado esse código será "anexado" a essa instância. Este é um bom método para pouco código, mas se fosse uma função muito grande, não dava nada jeito estar a inclui-la inline, e o executável ficaria com um tamanho enorme. Outra maneira é definir os métodos num cpp. Desta maneira essas funções estarão num sitio da memória e cada objecto criado utilizará as mesmas funções. V01 - 40 - Manual de C++ MI01-0316 Outra novidade é a maneira como inicializei os atributos no construtor, de facto o que fiz tem o mesmo efeito prático que fazer Ponto( ..... ) { x=_x; y=_y; } 10.2. CONSTRUTORES Os Construtores e os destruidores do C++ funcionam automáticamente para garantirem a melhor forma de criação e eliminação de uma instância de classe. Regras dos Construtores O C++ possui as seguintes características e normas relativamente aos construtores:     O nome do construtor deve ser idêntico ao nome da classe a que pertence. Não é permitido incluir nenhum tipo de retorno, nem mesmo o tipo Void. Uma classe pode ter um número indeterminado de construtores, incluindo nenhum construtor. Neste caso o compilador cria automáticamente um construtor para uma clase. O construtor por defeito é aquele que não possui nenhum parâmetro ou então possui uma lista de parâmetros em que todos os parâmetros utilizam argumentos assumidos por defeito. 10.3. DESTRUIDORES As classes de C++ podem conter destruidores que eliminam automáticamente as instâncias da classe. Regras dos Destruidores O C++ possui as seguintes características e normas relativamente aos destruidores:      V01 O nome do destruidor deve começar com um til (~). O resto do nome do destruidor deve ser idêntico ao nome da sua classe. Não é permitido incluir nenhum tipo de retorno, nem mesmo o tipo void. Uma classe não pode ter mais de um destruidor. Se não houver nenhum destruidor o compilador criará automaticamente um. O destruidor não deve ter nenhum parâmetro. O sistema de execução do programa invoca automaticamente um destruidor da classe quando a instância dessa classe estiver fora do raio de alcance. - 41 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 11. TEMPLATES Os templates aplicam-se a funções e a classes e dão uma grande liberdade ao utilizador. Vejamos o seguinte caso: // funções para somar dois valores int soma( int a, int b ) { return a+b; } double soma( double a, double b ) { return a+b; } float soma( float a, float b ) { return a+b; } O que fiz foi sobrecarga da função soma, para o compilador, apesar de terem todas o mesmo nome, são funções distintas, pois recebem parâmetros de diferentes tipos. Bem, e que tal fazermos agora o mesmo para a multiplicação, divisão, e fazer o mesmo para parâmetros de tipos diferentes... Em C haviam as macros, directivas do pré-processador com que se podia dar a volta a este problema. Em C++ existem os templates. Usando templates bastaria uma função para simbolizar todas as variantes de uma mesma função soma: // função template para somar dois valores template< typename T> T soma( T a, T b ) { return a+b; } O que estamos a dizer é que a função de template soma recebe dois parâmetros de um mesmo tipo T, que pode ser qualquer. Experimente... 11.1.TEMPLATES DE CLASSE Tal como se pode criar funções de template, também se pode criar templates de classes com uma sintaxe parecida. Para demonstrar um template de classes simularei um stack. Um stack é como uma pilha de papeis, vamos acrescentando, só dá para remover do topo e acrescentar ao topo. template< typename T, unsigned DIM = 10 > class Stack { T data[DIM]; unsigned size; // tamanho do stack public: Stack() { size = 0; } V01 - 42 - Manual de C++ MI01-0316 // retorna true se o stack estiver vazio bool empty() const { return size==0; } // retorna uma referência para o elemento no topo T& top() { return data[size-1]; } // acrescenta ao topo void push( const T &p ) { data[size++] = p; } // remover o ultimo void pop() { --size; } }; Esta classe é bastante limitada, como se pode ver, mas o objectivo é somente exemplificar um template de classes. Para declarar objectos desta classe faria-se Stack Int_Stack; para um stack de inteiros com DIM= 10 por defeito ou Stack Unsigned_Stack; para um stack de unsigned com dimensão de 20 elementos. Os métodos de um template de classes têm de ser sempre definido inline, de preferência no próprio ficheiro (.h), no ficheiro de exemplo mostro como isso se faz. V01 - 43 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 12. ITERADORES Já tinha dado uma breve introdução aos iteradores quando falei do objecto string, agora vamos aprofundar mais esse conceito. O iterador que aqui vou deixar é muito simples e serve para iterar todos os números pares. Este exemplo prático servirá para demonstrar o quão versátil pode ser um iterador. class ParIter { // valor corrente do iterador int value; public: // usando este construtor pode-se fazer // ParIter a(10); em que value ficarA com 10 // ou ParIter a; em que value ficarA com 0 ParIter( int v = 0 ) : value(v) { // caso algum nabo inicialize value com // um nUmero impar, muda-se value para o nUmero par anterior if( value%2 ) --value; } /* o operator ++ farA com que o ParIter avance para o prOximo valor vAlido, para o prOximo par */ ParIter operator++() { return value+=2; } ParIter operator++(int) { ParIter aux=*this; ++(*this); return aux; } ParIter operator--() { return value-=2; } ParIter operator--(int) { ParIter aux=*this; --(*this); return aux; } // operador desreferência int operator*() { return value;} }; Em programação enfrentamos muito o problema de armazenar os dados de uma forma eficiente e útil. Até agora só falei do array, que como tudo, tem vantagens e desvantagens. Outra forma usada para armazenar dados é a lista. Por último basta referir as árvores. O conceito de array já deve estar apreendido, o que vou fazer agora é dar uma breve introdução aos outros tipos de armazenamento de dados. Lista Tem a vantagem de se poder inserir e remover qualquer elemento com uma rapidez enorme, contudo, não pode ser indexada. V01 - 44 - Manual de C++ MI01-0316 A lista são um conjunto de nós ligados entre si: sent->a->b->c->d->sent, em que sent é o mesmo nó. Este mini exemplo seria uma lista simplesmente ligada. Para inserir ou remover bastaria efectuar uma troca de ponteiros. Árvore Uma árvore representa-se como aquelas árvores genelógicas que tão bem conhecemos. Devem ser usadas principalmente quando se quer realizar acções de procura, pois pela forma como estão organizadas, é muito mais rápido encontrar um elemento numa árvore do que num vector. E nas listas ainda é muito mais lento... 12.1. LISTA SIMPLESMENTE LIGADA Este é o exemplo mais simples de lista que conheço, uma lista simplesmente ligada. Mas o que é mesmo uma lista?... Num array todos os seus elementos estão dispostos na memória uns a seguir aos outros, numa lista, estão em posições completamente aleatórias. De seguida estão dois exemplos que tentam representar um array e uma lista. Uma lista é composta por diversos nós, nós esses que são estruturas que contém a informação pretendida e um ponteiro para o nó seguinte. Na implementação da lista que vou aqui fazer, o último nó aponta sempre para NULL. Quando a lista é inicializada, o head aponta sempre para NULL, tal como o tail. Quando a lista tem elementos, o head aponta sempre para o primeiro elemento da lista e o tail para o último. O seguinte esquema mostra o esqueleto da nossa TUT_IntSList: Esta é uma lista com apenas 2 elementos, de reparar que o último nó tem o atributo next a NULL. TUT_IntSList De seguida encontra-se a declaração da classe TUT_IntSList: class TUT_IntSList { // definição de Node struct Node { Node* next; int data; Node( Node* _next = NULL, int d = 0 ) : next(_next), data(d) {} }; /* atributos privados */ Node* head; Node* tail; int sz; /* métodos privados */ Node* newNode( int d = 0 ) const; public: TUT_IntSList(); V01 - 45 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 ~TUT_IntSList(); int size() const { return sz; } void insert( int d ); int remove(); void clear(); void printOn( std::ostream &os ) const; }; Na listagem está tudo comentado. Por isso vou só descrever o método que penso ser mais importante: insert(). Insert void TUT_IntSList::insert( int d ) // insere à cauda { Node* novo = newNode(d); // criar um novo nó if( tail->next != NULL ) // para o caso da lista estar vazia! tail->next->next = novo; // o último nó passa a apontar para o novo else head->next = novo; // head aponta para o primeiro tail->next = novo; ++sz; // actualizar tail // acrescentar 1 ao tamanho da lista } Não esquecer que quando a lista é inicializada head->next = tail->next = NULL. 13. HERANÇA E POLIMORFISMO Para introduzir estes conceitos vou fazer uma aplicação que gere uma loja. Esta aplicação apenas irá calcular o ordenado de cada empregado e só existem duas categorias de empregado, ou gerente, ou empregado de balcão. O que há de novo é a maneira como o objecto Empregado será representado. Termos uma classe geral de nome empregado e duas classes derivadas de empregado: gerente e empregado de balcão. Como se deriva uma classe? class BasicObject { private: int atributo; protected: int ID; public: int getID; }; // classe derivada class NamedObject : public BasicObject { string nome; public: string getName(); }; V01 - 46 - Manual de C++ MI01-0316 O que estamos a dizer na declaração de NamedObject é que NamedObject é também um Basic Object, ou seja, herda todos os atributos da classe mãe. Na classe BasicObject existe a palavra chave protected, que está, em termos de acesso, entre o private e o public, ou seja é público para as classe derivadas, mas privado para o utilizador directo. BasicObject b; NamedObject n; b.ID = 0; // erro! atributo ID protected n.getName(); // correcto n.getID(); // correcto, n herda todos os métodos e atributos public ou protected Resumindo, um NamedObject tem também um ID e um getID(), que herda da classe mãe. É claro que poderiamos alterar o conteúdo de getID, bastaria para isso redeclarar e redefinir esse método. Temos uma classe loja que tem um vector com ponteiros para todos os empregados. A relação de Loja com Empregado é que cada loja tem um (has a) Empregado. Depois, temos uma classe Empregado, da qual derivam a classe Gerente e a classe Empregado de Balcão. Realço que um Gestor é um Empregado, mas que um Empregado pode não ser um Gestor, e por ai adiante. O código é um pouco grande, por isso não o vou passar aqui, vou simplesmente comentar aqueles aspectos que são novidade. Ora bem, no método pagar da classe Loja queremos que seja mostrado todos os empregados presentes no vector empregados seguidos dos respectivos salários. Para isso faremos algo do género: int main() { Loja papelaria; Empregado *g = new Gerente("Antonio"); EmpBalcao *e = new EmpBalcao("Jose"); // ok - um Gerente também é um empregado papelaria.adicionarEmpregado(g); // ok - um Empregado de Balcao também é um empregado papelaria.adicionarEmpregado(e); // mostrar os ordenados papelaria.pagar(); delete g,e; cin.get(); return 0; } e na definição de Loja::pagar() void Loja::pagar() { // percorrer o vector empregados for( vector::iterator it = empregados.begin(); it < empregados.end(); ++it ) cout<< (*it)->getName() << " tem de receber: " << (*it)>calcOrdenado() <<"."<< endl; } V01 - 47 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 Como se pode ver, tratamos cada empregado de forma igual, não nos importando se são Gerentes ou Empregados de Balcão... Mas não é isso que queremos, dependendo das suas funções cada qual receberá um ordenado correspondente. Métodos virtuais É aqui que entra a palavra-chave virtual. O método calcOrdenado está declarado em Empregado como um método virtual, e quando é assim, todas as classes derivadas também herdam esse método como virtual. O que virtual faz é que, quando for chamado o método calcOrdenado sobre a classe Empregado, seja verificado a que categoria pertence mesmo esse empregado e seja chamado o método correspondente. 14. OPERAÇÕES RELACIONADAS COM BITS O C++ é uma linguagem que permite tratar as variáveis como elas são realmente, como um conjunto de bits. Os quadros seguintes demonstram as operações elementares: Operação AND (&) Operação OR ( | ) Operaçâo NOT (~) a b a&b a b a |b a ~a 0 0 0 0 0 0 0 1 0 1 0 0 1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 Nos operadores lógicos, já tinha apresentado uns operadores semelhantes a estes, naquele caso, eles tinham em conta o sentido booleano das expressões, nestes operadores é tido em conta todos os bits que formam a variável. Exemplo: a = 11110000 b = 00001111 a | b = 11111111 a & b = 00000000 ~a = 00001111 Para além destes também temos os operadores shift rifgt e shift left, que rodam para a direcção indicada, a quantidade de bits indicada. a = 11110000 b = 00001111 b << 1 = 00011110 a >> 2 = 00111100 Para realizar uma aplicação prática desta matéria vamos simular uma font em memória, e depois mostrá-la no ecrã: V01 - 48 - Manual de C++ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 1 0 0 MI01-0316 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 Bem, eu não sei se se nota bem, mas lá em cima está escrito CPP. Como podem ver cada letra é composta por um mapa de bits e, neste caso, os 1s significam que que essa célula está preenchida e os 0s o contrário. Primeiro temos que ter onde armazenar aquela informação, parece-se que um array de 8 posições vem mesmo a calhar, e Vocês perguntam, não devia ser de 8x8? Cada char tem 8 bits, e serão esses 8 bits que nos indicaram o "código" de cada linha. Mas, para quem não sabe, um char é, por defeito um signed char, ou seja, uma variável com sinal. Isso daria alguns problemas e então teremos de ignorar sinais fazendo unsigned char. Ao unsigned char, vamos chamar-lhe Byte. typedef unsigned char Byte; Byte letraC[ ] = { 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff }; Byte letraP[ ] = { 0xff, 0xff, 0xc3, 0xc3, 0xff, 0xff, 0xc0, 0xc0 }; Em casa posição do array está um codigo hexadecimal, e usa-se muito frequentemente para representar bits, imaginem a quantidade de uns e zeros que se teria de pôr... O prefixo 0x serve somente para indicar que se trata de um hexadecimal. V01 Decimal Hexadecimal Binário 0 0 0000 1 1 0001 2 2 0010 3 3 0011 4 4 0100 5 5 0101 6 6 0110 7 7 0111 8 8 1000 9 9 1001 10 A 1010 11 B 1011 12 C 1100 13 D 1101 14 E 1110 15 F 1111 - 49 - NHK – Formação e Novas Tecnologias Unipessoal Lda MI01-0316 Na tabela acima estão as correspondências entre hexadecimal, binário e hexadecimal para os primeiros 16 dígitos. Por exemplo, uma linha cheia de 1s ( 11111111 ) seria facilmente representada por 0xff. E é assim que o código para cada letra foi feito. Agora, o algoritmo usado para pintar letras: void paint( Byte v[] ) { for( int i=0; i<8; ++i ){ Byte mask = v[ i ]; for( int j=7; j>=0; --j ) if( mask>>j & 0x01 ) cout<<"X"; else cout<<" "; cout<>j & 0x01 ) é a que custa mais a perceber. O j é o índice( no Byte) que estamos a tratar de momento e colocamos o bit de índice j para a posição de menor peso (aquela mais à direita). Depois fazemos um and com 0x01 para apagar todos os bits excepto o de menor peso. Deste modo ficamos só com o bit a tratar. Depois é só ver se ficou 0 ou 1. V01 - 50 - Manual de C++ MI01-0316 15. BIBLIOGRAFIA Margaret Ellis & Bjarne Stroustrup. "The Annotated C++ Reference Manual ".Addison-Wesley, 1990. Scott Meyers. "Effective C++, 50 Specific Ways to Improve Your Programs and Designs". Addison-Wesley, 1992. The C++ Programming Language, B. Stroustrup C/C++ Users Journal - http://www.cuj.com/home/ V01 - 51 -