Transição Do C para C++
Transição Do C para C++
Transição Do C para C++
TRANSIO PARA
C++
Para programadores de C
Dezembro 1998 Jos A. M. Cordeiro Escola Superior de Tecnologia / Instituto Politcnico de Setbal Rua do Vale de Chaves, Estefanilha 2910 Setbal Telefone (065) 790000 Fax (065) 721869 [email protected]
ndice
NDICE INTRODUO ALTERAES LINGUAGEM C ESTRUTURA DO PROGRAMA Ficheiros Fonte Comentrios Constantes Variveis ENTRADA E SADA DE DADOS Entrada e Sada de Dados Simples Sada de Dados Formatada FUNES Funes inline Redefinio de Funes Argumentos Pr-definidos Transmisso de Parmetros por Referncia Retorno de Valores por Referncia ESTRUTURAS DE DADOS DINMICAS Operador new Operador delete TPICOS VARIADOS Notao Funcional do Operador de Coero (cast) Inicializao Dinmica de Variveis Variveis por Referncia FICHEIROS MODELOS (TEMPLATES) REDEFINIO DE OPERADORES 2 3 3 3 4 4 4 4 5 5 9 10 10 11 12 12 13 14 14 14 16 16 16 16 16 20 22
Introduo
! 1
Declarao de funes obrigatria.
A linguagem C++ surge como uma extenso linguagem C trazendo consigo dois importantes conjuntos de novidades:
1. 2.
Informalmente diz-se que o primeiro conjunto leva quilo a que se chama um C melhorado (a better C) e representado pelo primeiro smbolo + do nome da linguagem. O segundo smbolo + vem das novas capacidades de programao orientada por objectos. As melhorias da linguagem C e algumas diferenas que apesar de tudo existem em relao ao C++ sero descritas neste manual. A programao orientada por objectos no ser abordada.
Alteraes Linguagem C
Apesar da linguagem C++ ser uma extenso da linguagem C existem, contudo, algumas alteraes. As principais diferenas entre o C e o C++ envolvem a declarao de funes. Em C++ obrigatrio fornecer a declarao ou definio de uma funo antes da sua utilizao. Como boa prtica deve-se sempre declarar todas as funes mesmo quando se faz a sua definio. Outra alterao relacionada com a declarao de uma funo diz respeito omisso dos parmetros na declarao. Em C isto significava que nada se dizia em relao aos argumentos da funo, em C++ isto significa que a funo no leva argumentos, sendo void.
Estrutura do Programa
Embora os programas em C++ apresentem uma estrutura semelhante dos programas em C, a utilizao de algumas das novas caractersticas leva-os a apresentarem um aspecto diferente. Assim, as entradas e sadas de dados que utilizavam anteriormente as funes printf e scanf passam a utilizar as novas operaes >> e << com um formato bastante diferente e que ser descrito na prxima seco. Tambm os comentrios e a definio de constantes sofrem algumas alteraes. O local de definio de variveis outra das novidades.
Ficheiros Fonte
Uma actualizao a ter em considerao o nome dos ficheiros fonte. Uma vez que a linguagem C++ uma nova linguagem os seus ficheiros fonte iro ter uma extenso diferente: habitualmente .cpp (C plus plus ou seja C++) ou .C ( letra maiscula) em alguns compiladores para Unix, em contraste com a extenso .c dos programas em C.
Comentrios
Em C++ alm de se poderem usar os comentrios no estilo do C (ou seja '/*' para iniciar o comentrio e '*/' para terminar o comentrio), acrescentado um novo tipo de comentrio: o comentrio de uma linha.
Comentrios
comeados pelos smbolos // e vlidos apenas na linha corrente
O comentrio de uma linha iniciado pela sequncia de smbolos '//', e leva o compilador a ignorar qualquer caracter que se encontre a seguir a esses smbolos dentro da mesma linha. Esta forma de comentar prefervel por ser mais fcil de gerir e originar menos erros.
Constantes
modificador const
As constantes em C++ devem ser definidas a partir do modificador const, ou seja, serem definidas como variveis acrescentando-se antes do tipo a palavra const. Convm recordar que sempre que se defina uma constante desta forma deve-se obrigatoriamente inicializar a mesma. A forma anterior de definir constantes que utilizava a directiva #define deve ser abandonada, uma vez que menos eficiente e pode originar erros de programao de difcil deteco.
Variveis
Em C as variveis apenas podiam ser declaradas a seguir ao inicio de um bloco (aps o caracter '{' ).
Declarao de variveis em qualquer
linha de cdigo
Em C++ agora possvel declarar uma varivel em qualquer linha de cdigo. Recomenda-se, no entanto, que as variveis sejam declaradas o mais prximo possvel do local onde so utilizadas. Esta recomendao no se aplica se forem usadas em vrios conjuntos de instrues distintos dentro de um mesmo bloco, pelo que neste caso ser prefervel a sua declarao no incio do bloco. Outra novidade relaciona-se com as variveis do tipo struct. Em C quando se declarava uma varivel deste tipo era necessrio escrever a palavra struct antes do nome da estrutura. Em C++ pode-se omitir a palavra struct e em consequncia no ser necessria a criao de um novo tipo definido pelo utilizador para esse efeito. Na listagem 1 apresentam-se alguns exemplos da declarao de constantes e variveis
C
#include <stdio.h> #define JANEIRO 1 #define FEVEREIRO 2 . . . struct DATA{ int dia; int mes; int ano; }; /* Comentrio estilo C */ void main(void) { struct DATA hoje; int dias[31]; /* declarao de dias */ int i; /* Declarao de i */ hoje.dia = 1; hoje.mes = JANEIRO; hoje.ano = 1998; for( i=0; i<31; i++ ) { dias[i] = 0; . . . } } } }
C++
#include <iostream.h> const int JANEIRO = 1; const int FEVEREIRO = 2; . . . struct DATA{ int dia; int mes; int ano; }; // Comentrio estilo C++ void main(void) { DATA hoje; // Sem a palavra struct hoje.dia = 1; hoje.mes = JANEIRO; hoje.ano = 1998; int dias[31]; // Declarao de dias for( int i=0; i<31; i++ ) // declarao de i { dias[i] = 0; . . .
As operaes bsicas de escrita no ecr e de leitura do teclado iro ser protagonizadas pelas novas operaes << e >> em vez dos antigos e confusos printf e scanf. Para a escrita no ecr utiliza-se cout e o operador binrio << denominado neste contexto como operador de insero. Na operao referida o primeiro operando a varivel cout que representa o ecr, e o segundo operando o valor que se pretende escrever. Assim, para se visualizar o valor inteiro 15 pode-se escrever a seguinte linha de cdigo:
cout << 15;
Da mesma forma o valor real 15.5 pode ser escrito atravs de:
cout << 15.5;
De uma maneira geral o operando do lado direito pode ser um inteiro, um real (double ou float) ou qualquer outro dos tipos bsicos. Tambm os tipos char* e void* so reconhecidos, sendo interpretados como, respectivamente, uma cadeia de caracteres e um endereo de memria e escritos no ecr de acordo. Exemplo:
char *str="Teste do sistema\n"; cout << str; cout << (void *)str;
Resultado:
ainda possvel escrever mais que um valor a partir da mesma instruo, bastando para isso encadear os diversos valores atravs do operador de insero como no seguinte exemplo:
cout << "Peso: " << 12.2 << " Kgs.\n";
Resultado:
Entrada de dados
feita atravs do operador de extraco >> usado em conjunto com a varivel cin
De uma forma anloga, a leitura de valores a partir do teclado, efectuada usando cin e o operador >> (operador de extraco). Os valores lidos so colocados na varivel fornecida como segundo operando da operao de extraco, cin o primeiro operando. Como exemplo da leitura de dois valores, um do tipo double e um inteiro (int) temos:
double dval; int ival; cout << "Valor real: "; cin >> dval; cout << "Valor inteiro: "; cin >> ival;
A leitura para uma varivel do tipo char* ou char[] equivale a ler uma palavra para o endereo de memria respectivo. Apenas uma palavra lida, terminando a operao de leitura quando se encontrar um espao branco. de referir que tambm a leitura de valores, incluindo do tipo char, no recebe os espaos em branco. Tambm possvel ler vrios valores para um grupo de variveis a partir de uma nica instruo de leitura. Isto conseguido fornecendo as variveis separadas pelo operador de extraco como no seguinte exemplo:
cin >> dval >> ival;
NOTA
Os smbolos '>>' e '<<' usados, respectivamente, nas operaes de leitura e escrita de valores representam a direco do fluxo de informao. Assim, para cin utiliza-se o smbolo '>>' a indicar que a informao fli de cin para as variveis e com cout '<<', fluindo a informao, aqui, no sentido dos valores para cout.
C
#include <stdio.h> void main(void) { int i; char str[80]; [ ] printf("Ol mundo\n"); /* Leitura de um valor */ printf("Introduza um nmero: "); scanf("%d",&i); /* Mostrar o valor lido */ printf("O nmero introduzido foi: %d \n", i ); /* Leitura de uma palavra */ printf("Introduza uma palavra: " ); scanf("%s", str); /* Mostrar a palavra lida */ printf("A palavra intorduzida foi: "); printf("%s", str); } Resultado: Ol mundo Introduza um nmero: 12 O nmero introduzido foi: 12 Introduza uma palavra: carro A palavra introduzida foi: carro } Resultado: void main(void) { int i; char str[80]; [ ]
C++
#include <iostream.h>
cout << "Ol mundo" << endl; // Leitura de um valor cout << "Introduza um nmero: "; cin >> i; // Mostrar o valor lido cout << "O nmero introduzido foi: " << i << "\n"; // Leitura de uma palavra cout << "Introduza uma palavra: "; cin >> str; // Mostrar a palavra lida cout << "A palavra introduzida foi: "; cout << str;
Ol mundo Introduza um nmero: 12 O nmero introduzido foi: 12 Introduza uma palavra: carro A palavra introduzida foi: carro
Existem ainda outras formas de escrita e leitura de valores, entre elas ser importante referir as funes de leitura get e getline e de escrita put. A funo get possui vrios formatos adaptados a diferentes objectivos:
1.
Leitura de caracteres - sem argumentos, retornado o caracter lido do dispositivo de entrada. Leitura de caracteres - com um argumento do tipo char em que o caracter lido por referncia para dentro da varivel char passada como argumento.
2.
3.
Leitura de texto com trs argumentos em que o primeiro a cadeia de caracteres que vai receber o texto, o segundo o nmero de caracteres possveis de armazenar na referida cadeia de caracteres e o terceiro, que se pode omitir, o caracter que indica o fim da leitura que por omisso o caracter \n.
A funo getline idntica na sua forma e funcionamento funo get usada com trs argumentos com a diferena que o caracter que termina o texto lido e descartado. A funo get no descarta o referido caracter. A funo put leva como argumento um char e escreve-o no dispositivo de sada. A utilizao destas funes aparece associada varivel cin duma forma que pode parecer um pouco estranha: a seguir ao nome da varivel segue-se um ponto e a chamada funo. A razo para este facto tem a ver com a programao orientada por objectos. A listagem 3 apresenta alguns exemplos de utilizao destas funes. O grupo de funes descrito tem como equivalente na linguagem C as funes getchar, putchar e gets (ou fgets).
C
#include <stdio.h> void main() { int i; char str[129]; char z1; printf("Ler uma linha de texto: "); /* Forma 1: int getchar() */ i=0; while ( (z1=getchar()) != '\n' str[i++]=z1; str[i] = '\0'; printf( "\nTexto: %s\n", str ); #include <iostream.h> void main() { int i; char str[129]; char z1; cout << "Ler uma linha de texto: "; // Forma 1: int cin.get() i=0; while ( (z1=cin.get()) != '\n' str[i++]=z1; str[i] = '\0';
C++
cout << endl << "Texto: "<< str << endl; cout << "Ler uma linha de texto: "; // Forma 2: cin.get(char &) i=0; do { cin.get(z1); str[i++]=z1; } while ( z1 != '\n' ); str[--i] = '\0'; cout << endl << "Texto: "<< str << endl;
printf("Ler uma linha de texto: "); /* Forma 3: fgets(char *, int, FILE *) */ fgets(str, 129, stdin); printf( "\nTexto: %s\n", str ); }
cout << "Ler uma linha de texto: "; // Forma 3: // cin.get(char *, int, char='\n') cin.get(str, 129); cout << endl << "Texto: "<< str << endl; }
Em C era possvel efectuar uma sada de dados formatada utilizando a instruo printf. Entre as funcionalidades oferecidas tinham-se o nmero mnimo de caracteres na escrita de um valor, o nmero de casas decimais de um valor real, a apresentao do sinal ou no para valores numricos positivos, etc. Em C++ utilizando cout existem igualmente vrias possibilidades de formatao da sada de dados.
A formatao da sada de dados
A formatao da sada de dados em C++ efectuada atravs de um comando especial, o manipulador, que includo na lista de informao a enviar para o ecr atravs da operao de insero. Os manipuladores podem levar argumentos. Os principais manipuladores e a sua funcionalidade encontram-se descritos na tabela 1. Manipuladores
Nome
endl setw hex
Descrio
Semelhante a \n em C. Limpa tambm o buffer de escrita Total de caracteres com que se escreve o prximo valor Passa a mostrar os valores inteiros na notao hexadecimal Passa a mostrar os valores inteiros na notao octal Passa a mostrar os valores inteiros na notao decimal Nmero de casas decimais para valores reais. No mostrar o expoente. Notao cientifica (com expoente). Mostrar as casas decimais, mesmo que sejam zeros . Mostrar sempre o sinal para nmeros positivos Colocar espaos no centro. Encostar esquerda (espaos direita) Encostar direita (espaos esquerda)
------Nmero de casas decimais (ios::fixed) (ios::scientific) (ios::showpoint) (ios::showpos) (ios::internal) (ios::left) (ios::right)
oct dec setprecision setiosflags setiosflags setiosflags setiosflags setiosflags setiosflags setiosflags
A exemplificao do uso dos manipuladores feita na listagem 4. Na operao de leitura de dados tambm existe um manipulador especial: ws cuja funcionalidade descartar todos os espaos em branco que apaream na leitura de dados.
NOTA
Quando se utilizam manipuladores em C++ deve-se incluir o ficheiro iomanip.h no incio do programa, ou seja acrescentar a linha: #include <iomanip.h>.
C
#include <stdio.h> int main(void) { printf("\n"); printf("%8.2lf\n",3298.2341); printf("Teste"); printf("\n%15s", "Hello there "); printf("----" ); } #include <iostream.h> #include <iomanip.h>
C++
int main(void) { cout << endl << setw(8) // Total de caracteres << setprecision(2) // Casas decimais << setiosflags(ios::fixed) // s/ expoente << 3298.2341 << endl << "Teste"; cout << endl << setw(15) << "Hello there "; cout << "----"; return 0; }
Funes
No captulo das funes em C++ foram introduzidas novidades muito importantes, entre elas esto a possibilidade de definir vrias funes com o mesmo nome, a possibilidade de os argumentos poderem ser passados por referncia e de estes poderem tambm possuir valores pr-definidos.
Funes inline
Funes inline
permitem a colocao directa do cdigo da funo nos locais em que esta chamada
A novidade mais simples no que diz respeito s funes o novo modificador inline. Este modificador colocado na declarao da funo antes do tipo de retorno leva a que o compilador em vez de colocar um salto para o cdigo da funo, nos locais em que esta for chamada, coloque directamente o cdigo da funo. A vantagem da utilizao de funes inline tem a ver com questes de eficincia, uma vez que o cdigo est no local de chamada da funo esta ir ser executada mais rapidamente, como desvantagem se a mesma for chamada diversas vezes o cdigo dela ir estar repetido as mesmas vezes. Neste caso o executvel do programa ir normalmente ocupar mais memria. Esta novidade foi introduzida principalmente devido a questes relacionadas com a eficincia na programao orientada por objectos. O modificador inline dever ser utilizado principalmente em funes pequenas que no possuam ciclos ou instrues de seleco do tipo switch. Tambm necessrio que o compilador veja a definio da funo inline antes do local em que esta chamada para que seja possvel obter e colocar o seu cdigo no local de chamada. A utilizao do modificador inline no obriga o compilador a colocar o cdigo da funo no local de chamada, apenas um pedido para que isso acontea.
10
Redefinio de Funes
Uma das novidades mais importantes do C++ diz respeito redefinio de funes. Em C++ possvel definir vrias funes com o mesmo nome. Uma vez que as funes redefinidas iro ter o mesmo nome a distino entre elas feita pelos argumentos com que so chamadas. Os argumentos podem ser diferenciados pelo nmero e/ou pelo seu tipo. O tipo de retorno no permite diferenciar duas funes com o mesmo nome e o mesmo tipo e nmero de argumentos.
#include <iostream.h>
C++
void charline( void ); void charline( char car ); void charline( char car, int ncar ); void main(void) { charline(); cout << " Aluno charline('-',25); cout << " Joao Martins << " Pedro Oliveira << " Jose Silva charline('='); } void charline( void ) { for (int j=0; j<30; j++) cout << '*'; cout << endl; } void charline( char car ) { for (int j=0; j<30; j++) cout << car; cout << endl; } void charline( char car, int ncar ) { for (int j=0; j<ncar; j++) cout << car; cout << endl; }
void main(void) { astline(); printf(" Aluno minusline(); printf(" Joao Martins printf(" Pedro Oliveira printf(" Jose Silva equalline(); } void astline() { int j; for (j=0; j<30; j++) printf("*"); printf("\n"); } void equalline( void ) { int j; for (j=0; j<30; j++) printf("="); printf("\n"); }
Nota" << endl; 14 " << endl 9 " << endl 11 " << endl;
void minusline( void ) { int j; for (j=0; j<25; j++) printf("-"); printf("\n"); } RESULTADO: ****************************** Aluno Nota ------------------------Joao Martins 14 Pedro Oliveira 9 Jose Silva 11
Quando existirem dvidas por parte do compilador sobre qual a funo redefinida a ser chamada gerado um erro de compilao onde se menciona uma chamada de funo ambgua
11
No exemplo da listagem 5 mostra-se como trs funes com objectivos semelhantes (escrever uma linha de caracteres no ecr), que em C necessitavam de ter identificadores diferentes, podem ser definidas em C++ com um nome apenas.
Omisso de argumentos na
chamada de uma funo atravs da atribuio de valores pr-definidos aos parmetros na declarao ou na definio da funo
possvel omitir alguns argumentos na chamada de uma funo desde que na declarao ou na definio da funo (e em apenas um dos stios) aparea o valor que o argumento ir ter se for omitido. O valor por omisso fornecido juntando um sinal de igual seguido desse valor a seguir ao nome do argumento.
NOTA
Argumentos Pr-definidos
#include <iostream.h> void charline( char car='*', int ncar=30 ); void main(void) { charline(); cout << " Aluno charline('-',25); cout << " Joao Martins << " Pedro Oliveira << " Jose Silva charline('='); }
Nota" << endl; 14 " << endl 9 " << endl 11 " << endl;
void charline( char car, int ncar ) { for (int j=0; j<ncar; j++) cout << car; cout << endl; }
Em C++ possvel passar parmetros por valor e por referncia. Os argumentos passados para uma funo podem agora ser passados por referncia semelhana do que acontece na linguagem PASCAL quando se utiliza a palavra VAR antes do argumento formal. Apenas as variveis podem ser passadas como parmetros por referncia.
12
parmetros possvel usando o operador & antes do nome do argumento no cabealho da funo
Para indicar um argumento por referncia utiliza-se o smbolo & imediatamente antes do nome do parmetro. As variveis passadas por referncia iro reflectir as alteraes ao seu valor feitas no interior da funo.
C
#include <stdio.h> #include <math.h> struct complex { double x,y; }; int iabs( int val ) { if( val < 0 ) return( -val ); return( val ); } #include <iostream.h> #include <math.h> struct complex { double x,y; };
C++
inline double abs( double val ) { if( val < 0.0 ) return( -val ); return( val ); } inline long abs( long val ) { if( val < 0 ) return( -val ); return( val ); }
/* passagem por valor de val */ double cabs( struct complex val ) { return sqrt(val.x*val.x + val.y*val.y); } void main( void ) { int i=-10; double d=-15.0; struct complex c={-2.0,3.0}; long l=-10L; printf("Int printf("Double printf("Complex printf("Long } i d c l -> -> -> -> %d\n" , %lf\n", %lf\n", %ld\n", iabs(i) fabs(d) cabs(c) labs(l) ); ); ); );
// passagem por referncia de val inline double abs( complex &val ) { return sqrt(val.x*val.x + val.y*val.y); } void main( void ) { int i=-10; double d=abs(-15.0); complex c={-2.0,3.0}; long l=-10L; cout cout cout cout } << << << << "Int "Double "Complex "Long i d c l ->" ->" ->" ->" << << << << abs(i) abs(d) abs(c) abs(l) << << << << endl; endl; endl; endl;
Tal como possvel passar valores por referncia, tambm possvel retornar por referncia. O retorno por referncia funciona como se retornasse uma varivel. A principal vantagem deste mtodo evitar que se produza uma cpia da varivel no retorno da funo o que resultaria numa perda de eficincia. preciso tomar cuidado com esta forma de retornar, uma vez que a varivel retornada tem que ter existncia fora da funo que a retornou, o que impossibilita o retorno de variveis locais.
13
O operador new utilizado em C++ em substituio de malloc. Uma vantagem na utilizao deste operador o facto de no ser necessrio utilizar a operao de cast para converter o ponteiro retornado num ponteiro compatvel com a varivel de afectao. A forma de utilizao deste operador :
<ponteiro> = new <tipo>;
ou
<ponteiro> = new <tipo> [<total de elementos a alocar>];
O operador delete utilizado em C++ com o objectivo de libertar a memria previamente alocada por new. Uma nota importante na utilizao deste operador que se torna possvel libertar memria a partir dum ponteiro que possua o valor NULL, esta aco que seria desastrosa em C na funo free, no ir surtir qualquer efeito em C++. Forma de utilizao:
delete <ponteiro>; ou delete [ ] <ponteiro>;
Exemplo: delete [] str; Na listagem 8 apresenta-se um exemplo da utilizao de estruturas de dados dinmicas atravs dos operadores new e delete.
14
C
#include <stdio.h> #include <string.h> typedef struct telefone{ char *nome; long *telefone; }TELEFONE; #define max_numeros 100 void main( void ) { TELEFONE *agenda; char str[128+1]; long tf; char resp; int i,j; str[0] = '\0'; agenda = (TELEFONE *)malloc( sizeof(TELEFONE)*max_numeros)); /* Alocar array de telefones */ if( agenda == NULL) return; printf("%30s", "AGENDA TELEFONICA"); for( i=0; i<max_numeros; i++ ) { printf("\n\n"); printf("Deseja inserir mais nmeros ? "); fflush(stdin); scanf("%c", &resp); if( resp == 'n' || resp == 'N' ) break; printf("Nome: "); fflush(stdin); fgets(str,128,stdin); printf("Telefone: "); scanf(" %ld", &tf); agenda[i].nome = malloc( strlen(str)+1 ); if( agenda[i].nome ) strcpy(agenda[i].nome, str); agenda[i].telefone = (long *) malloc( sizeof(long) ); if( agenda[i].telefone ) *(agenda[i].telefone) = tf; } if( i ) printf("%30s","AGENDA TELEFONICA"); for( j=0; j<i; j++ ) { printf("\nNome: %s",agenda[j].nome); printf("\nTelefone: %ld\n", *agenda[j].telefone); } /* Libertar toda a memria alocada */ for( j=0; j<i; j++) { if( agenda[j].nome ) free(agenda[j].nome); if( agenda[j].telefone ) free(agenda[j].telefone); } free( agenda ); }
C++
#include <iostream.h> #include <iomanip.h> #include <string.h> struct TELEFONE{ char *nome; long *telefone; }; const max_numeros = 100; void main( void ) { TELEFONE *agenda; char str[128+1]; long tf; char resp; str[0] = '\0'; agenda = new TELEFONE [max_numeros]; //Alocar array de telefones if( agenda == NULL ) return; cout << setw(30) << "AGENDA TELEFONICA"; for( int i=0; i<max_numeros; i++ ) { cout << endl << endl << "Deseja inserir mais nmeros ? "; cin >> ws >> resp; if( resp == 'n' || resp == 'N' ) break; cout << "Nome: "; cin >> ws; cin.getline(str,128); cout << "Telefone: "; cin >> tf; agenda[i].nome=new char [strlen(str)+1]; if( agenda[i].nome ) strcpy(agenda[i].nome, str); agenda[i].telefone = new long; if( agenda[i].telefone ) *(agenda[i].telefone) = tf; } if( i ) cout << endl << endl << setw(30) << "AGENDA TELEFONICA"; for( int j=0; j<i; j++ ) { cout << endl << "Nome: " << agenda[j].nome << endl; cout << "Telefone: " << *(agenda[j].telefone) << endl; } // Libertar toda a memria alocada for( j=0; j<i; j++) { delete [] agenda[j].nome; delete agenda[j].telefone; } delete [] agenda; }
15
Tpicos variados
Notao Funcional do Operador de Coero (cast)
Operao de cast
utiliza os parnteses para os valores e no para o tipo
A operao de coero (cast) que permite a converso entre tipos diferentes, possui uma nova notao em C++. Enquanto em C esta operao obtinha-se colocando o tipo, para o qual se pretendia converter, entre parnteses antes do valor a converter (Ex. Converso de double para int (int)32.3), agora em C++ deve-se fazer colocando entre parnteses o valor e no o tipo (Ex. double para int int(32.3) ). Esta notao tem uma sintaxe semelhante das funes cujo nome dado pelo tipo da converso e o argumento pelo valor a converter.
Inicializao Dinmica de Variveis
Outra novidade importante que se podem inicializar dinamicamente as variveis locais e globais, ou seja, o seu valor inicial pode ser obtido atravs de uma expresso ou da chamada de uma funo.
Variveis por Referncia
Um novo tipo de variveis que aparece em C++ so as variveis de referncia. possvel criar variveis que no so mais que referncias a variveis que j existem. Isto funciona como se fosse um outro nome para uma mesma varivel. Estas variveis devem ser obrigatoriamente inicializadas quando so criadas.
Ficheiros
O tratamento de ficheiros em C++ feito principalmente com base em variveis dos novos tipos ifstream (para ficheiros de leitura), ofstream (para ficheiros de escrita) e fstream (para escrita e leitura). Os ficheiros de texto possuem tambm uma abordagem diferente da dos ficheiros binrios. As operaes bsicas com ficheiros mantm-se inalteradas em C++. Assim possvel abrir e fechar ficheiros, efectuar operaes de leitura e escrita e gerir o ponteiro de escrita e leitura dentro dos ficheiros. Em primeiro lugar a abertura de ficheiros feita declarando uma varivel do tipo ifstream para operaes de leitura ou do tipo ofstream para operaes de escrita. O nome do ficheiro pode ser fornecido entre parnteses directamente na declarao da varivel ou atravs de uma funo open que chamada associada varivel referida. Por exemplo, para abrir o ficheiro teste.txt para escrita poderia-se escrever:
ofstream fich( "teste.txt" );
ou alternativamente:
ofstream fich; fich.open("teste.txt");
O ficheiro automaticamente fechado quando a varivel que lhe deu origem deixa de existir, ou seja no fim do bloco onde foi declarada.
close associada varivel que lhe deu origem: fich.close();
16
operadores >> e << associados a variveis dos tipos ifstream e ofstream para leitura e escrita de dados
Quando se trabalha com ficheiros de texto utiliza-se a mesma forma de escrita e leitura que quando se trabalha com o ecr e o teclado. Por exemplo para escrever a linha Hoje Sbado dia 10 poderia-se fazer:
fich << Hoje Sbado dia << 10 << endl;
Neste caso apenas a varivel cout substituda pela varivel que representa o ficheiro. As funes get, getline e put referidas na seco de escrita e leitura de dados tambm podem ser utilizadas. Em relao utilizao de ficheiros binrios, estes requerem uma abertura efectuada de forma diferente. A funo de abertura de ficheiro descrita anteriormente, utiliza na realidade a nova funcionalidade da linguagem C++ que possibilita os argumentos por omisso. De facto, a funo referida aplicada a variveis do tipo ofstream possui os seguintes argumentos:
open(char*,int=ios::out,int=filebuf::openprot);
O primeiro argumento leva o nome do ficheiro a abrir e o segundo o modo de abertura, que no caso referido por omisso o modo para escrita no ficheiro. Assim sendo, para abrir ficheiros no modo binrio, era necessrio referir este modo na abertura do ficheiro. Os diferentes modos de abertura dos ficheiros so essencialmente os mesmos que em C embora com uma sintaxe diferente que acrescenta ios:: antes da abreviatura do modo. Podem-se igualmente juntar vrios modos utilizando o operador ou ao nvel do bit. importante referir ainda que estes mesmos parmetros esto disponveis quando se abre o ficheiro a partir da definio da varivel associada. Assim, por exemplo, para abrir um ficheiro para leitura em modo binrio poderia-se utilizar:
ifstream fich( "teste.dat", ios::in | ios::binary );
ou ento,
ifstream fich; fich.open("teste.dat", ios::in | ios::binary );
17
Funo Para leitura Para escrita Posiciona-se no fim do ficheiro aps a abertura (at end) Para adicionar dados (append) Inicia o ficheiro a zero (limpa-o se existir) Falha a abertura se o ficheiro existir Falha a abertura se o ficheiro no existir Abre em modo binrio
As operaes de escrita ou leitura em modo binrio utilizam habitualmente as novas funes read e write associadas tambm s variveis dos ficheiros. Estas funes possuem dois argumentos, o primeiro o endereo da posio de memria onde se encontra a informao a ler ou a escrever, e o segundo o nmero de bytes a ler ou a escrever. As funes get e put descritas anteriormente para os ficheiros de texto tambm esto disponveis para utilizao com ficheiros binrios. O fecho de ficheiros binrios funciona da mesma forma da dos ficheiros de texto. Uma outra funcionalidade na utilizao de ficheiros em C era o controlo do posicionamento de um ponteiro de escrita ou leitura dentro dos ficheiros. Esta funcionalidade disponibilizada em C++ atravs de ponteiros independentes para escrita e leitura, controlados pelas funes seekg, seekp, tellg e tellp.
A funo seekg leva como argumento a posio de leitura (seekg significa seek get) contada a partir do incio do ficheiro. Esta funo possui uma segunda forma com dois argumentos em que o primeiro a posio relativa e o segundo o local de inicio na determinao da posio efectiva. O local de inicio pode ser dado por ios::beg princpio do ficheiro, ios::cur posio corrente ou ios::end - fim do ficheiro). A funo seekp possui igualmente as mesmas possibilidades em relao ao ponteiro de escrita (seekp significa seek put). Por outro lado as funes tellg e tellp permitem obter respectivamente as posies actuais dos ponteiros de leitura e de escrita nos ficheiros. Existem ainda um conjunto de funes de teste associadas utilizao de ficheiros, que so usadas em conjunto com as variveis que representam os ficheiros e que se encontram descritas na tabela 3.
18
argumento ---------
descrio Retorna True se o ponteiro estiver no fim do ficheiro Retorna True se existiu erro na ltima operao com o ficheiro Retorna True se falhou a ltima operao com o ficheiro Retorna True se todas as funes anteriores retornarem False
C
#include <stdio.h> #include <conio.h> void { FILE char fout main() *fout, *fin; str[129]; = fopen("\\AUTOEXEC.BAT","wt"); #include <fstream.h> #include <conio.h>
C++
void main() { ofstream fout("texto.dat"); // ou fout.open("texto.dat"); if( !fout ) { cout << "Erro a abrir o ficheiro"; return; } fout << "Teste de escrita " << "em ficheiro" << endl; fout << 10.4 << endl; fout.close(); ifstream fin("texto.dat"); char str[129]; if( !fin ) { cout << "Erro a abrir o ficheiro"; return; } fin >> str; while( !fin.eof() ) { cout << str << " "; fin >> str; } fin.close(); // No necessrio getch(); }
if( fout == NULL ) { printf("Erro a abrir o ficheiro"); return; } fprintf( fout, "Teste de escrita " ); fprintf( fout, "em ficheiro\n" ); fprintf( fout, "%lf\n", 10.4 ); fclose( fout ); fin = fopen("texto.dat","rt"); if( fin == NULL ) { printf("Erro a abrir o ficheiro"); return; }
19
Modelos (templates)
Funes padro
permitem definir modelos para a criao de funes que possuem o mesmo cdigo aplicado a argumentos de tipos diferentes
Em C++ possvel criar funes padro (templates), que servem de modelo para a criao de funes. Nestas funes um ou mais dos tipos de dados dos argumentos e do valor de retorno no so fornecidos directamente no cabealho da funo. Esta caracterstica permite que se criem funes com o cdigo idntico, que possuem implementaes diferentes motivadas pelos tipos de dados dos argumentos. A ttulo de exemplo admita-se uma funo max que devolve o maior dos valores que eram passados como argumentos. Se esses valores se referissem a inteiros, a funo poderia ser:
int max(int x, int y) { if( x > y ) return x; return y; };
Se, por outro lado estivssemos a lidar com valores reais, ento teramos:
double max(double x, double y) { if( x > y ) return x; return y; };
Embora o tipo dos argumentos tenha sido alterado, o cdigo da funo permaneceu idntico. As funes padro permitem que os tipos envolvidos no prottipo da funo funcionem como variveis. Segundo este princpio a funo max iria ter ento a seguinte definio:
template <class T> T max(T x, T y) { if( x > y ) return x; return y; };
T neste caso um nome escolhido pelo programador dado a um tipo arbitrrio. Uma chamada funo max na seguinte forma:
double z = max( 2.0, 3.4);
levaria o compilador a criar um funo max com o tipo T concretizado como um double. Se houvesse uma nova chamada funo max, em que agora o tipo envolvido fosse, por exemplo, o tipo char, ento seria criado o cdigo de uma outra nova funo max onde T seria substitudo por char.
20
Como regra sempre que o compilador encontra uma chamada a uma funo segue os seguintes passos para resolver essa chamada:
1.
Encontrar a funo respectiva que dever possuir o mesmo nmero de argumentos e do mesmo tipo dos utilizados na chamada funo. Utilizar a funo encontrada. Caso no encontre a funo descrita em 1. Procura uma funo padro em que os tipos dos argumentos envolvidos se ajustem ao modelo. Caso isso acontea cria essa funo para os tipos envolvidos. Em ltimo caso se no resolveu a chamada funo em 1. ou 2. tenta a converso dos valores dos argumentos de forma a se ajustarem s funes existentes. Esta situao ocorre apenas para funes realmente definidas e no para funes padro.
2.
3.
Na definio de funes padro utiliza-se uma linha inicial com a lista de tipos diferentes que podero constar da lista de argumentos. Esta lista aparece a seguir palavra template e entre os smbolos < e >. Os nomes dos tipos devero ser precedidos da palavra reservada class. Sintaxe usada:
template <class nome1 [, class nome2, ...]>
Os tipos declarados na lista de tipos devem aparecer obrigatoriamente pelo menos uma vez na lista de argumentos da funo. Os tipos declarados podem ser utilizados mais que uma vez na lista de argumentos e podem ser igualmente utilizados como tipo de retorno. Podem coexistir tipos bsicos ou tipos definidos anteriormente conjuntamente com os tipos declarados na lista de argumentos da funo.
#include <iostream.h> #include <string.h> template <class T> T max(T x, T y) { return (x > y) ? x : y; }; char *max(char *str1, char *str2) { if(strcmp(str1,str2)>0) return str1; return str2; }
void main() { double x=1.0, y=2.0; cout << "valor double maximo: "<< max(x,y) << endl; // criada a funo double max(double, double) char z1 = 'a', z2 = 'b'; cout << "valor char maximo: "<< max(z1,z2) << endl; // criada a funo char max(char, char) char *str1="Texto um", *str2 = "Texto dois"; cout << "valor texto maximo: " << max(str1,str2) << endl; // utilizada a funo char *max(char*, char*) }
21
Redefinio de operadores
aos operadores efectuada fornecendo a funo: operator <smbolo do operador> (. . .)
Regra geral todos os operadores do C++ esto preparados para trabalhar com os tipos de dados pr-definidos na linguagem. Por exemplo, o operador soma (+) funciona com os tipos inteiro, caracter ou mesmo ponteiro. Mas se o programador definir um tipo novo, por exemplo a partir de uma estrutura, o compilador no consegue realizar a operao e gera uma mensagem de erro. Em C++ possvel fornecer uma funo para a realizao de uma determinada operao que envolva operadores. Esta funo ir ento ser chamada automaticamente e realizar a operao. Isto apenas ser possvel se pelo menos um dos operandos for de um tipo definido pelo utilizador. Por exemplo se tivermos o seguinte tipo
struct COMPLEXO{ double real; double imag; };
bastando para isso redefinir a operao soma para operandos do tipo COMPLEXO.
A redefinio da operao passa assim pela criao de uma funo especial que ir ser chamada quando a operao e o tipo dos operandos corresponderem funo criada. Esta funo especial caracterizada por possuir como nome a palavra reservada operator seguida do smbolo do operador e como argumentos variveis do mesmo tipo dos operandos. Retomando o exemplo anterior teramos o seguinte cdigo para a redefinio da operao de soma:
COMPLEXO operator + ( COMPLEXO c1, COMPLEXO c2 ) { COMPLEXO aux; aux.real = c1.real + c2.real; aux.imag = c1.imag + c2.imag; return aux; }
Como regra geral todos os operadores podem ser redefinidos excepto os da tabela 4. Deve-se ter em ateno que os argumentos da funo correspondem aos operandos, o que significa que os operadores unrios iro possuir funes com um argumento apenas.
22
Operadores no redefiniveis
?: . :: .*
istream e ostream permitem a leitura e escrita de tipos de dados definidos pelo utilizador nas operaes de entrada e sada de dados
Uma redefinio de operadores muito til a redefinio do operador << para a operao de escrita atravs de cout e do operador >> para a operao de leitura atravs de cin. Na realidade cout funciona como uma varivel de um tipo pr-definido na linguagem: o tipo ostream. A operao de escrita no ecr resulta ento da redefinio que feita do operador << para que ele funcione com este tipo da forma conhecida. possvel, no entanto, redefinir esta operao para o segundo operando de um novo tipo definido pelo utilizador. Por exemplo para a escrita de um COMPLEXO utilizando cout poderia-se redefinir a operao anterior da seguinte forma:
ostream & operator<<(ostream &os, COMPLEXO c1 ) { os << '(' << c1.real << ',' << c1.imag << ')'; return os; }
Para a leitura de valores de tipos definidos pelo utilizador pode-se, de uma forma anloga, redefinir o operador >>. Note-se que neste caso cin funciona como uma varivel do tipo istream. Teramos ento:
istream & operator>>(istream &is, COMPLEXO &c1 ) { cout << Real: ; is >> c1.real; cout << Imag: ; is >> c1.imag; return is; }
23
#include <iostream.h> struct COMPLEXO{ double real; double imag; }; COMPLEXO operator + (COMPLEXO c1, COMPLEXO c2) { c1.real = c1.real + c2.real; c1.imag = c1.imag + c2.imag; return c1; } COMPLEXO operator + (COMPLEXO c1, double val) { c1.real = c1.real + val; return c1; } ostream &operator<<(ostream &os, COMPLEXO c1) { os << '(' <<c1.real<< ',' <<c1.imag<< ')'; return os; } istream &operator>>(istream &is, COMPLEXO &c1) { cout << endl << "Real: "; is >> c1.real; cout << "Imag: "; is >> c1.imag; return is; } void main() { COMPLEXO c1 = {2.0,3.0}, c2={3.2,-1.3}, c3; c3 = c1 + c2; cout << endl<< "c3:" << c3 <<endl; cout << c3 + 2.0; cin >> c1; cout << c3 + c1; } Resultado: c3(5.2,1.7) (7.2,1.7) Real: -1 Imag: 4 (4.2,5.7)
24