You are on page 1of 25

Escola

Superior de
Tecnologia

TRANSIÇÃO PARA C++


Para programadores de C

José Cordeiro © 1998


ESCOLA SUPERIOR DE TECNOLOGIA / INSTITUTO POLITÉCNICO DE SETÚBAL

Transição para C++

Para programadores de C

 Dezembro 1998 José A. M. Cordeiro


Escola Superior de Tecnologia / Instituto Politécnico de Setúbal
Rua do Vale de Chaves, Estefanilha • 2910 Setúbal
Telefone (065) 790000 • Fax (065) 721869
j.cordeiro@computer.org
Índice
ÍNDICE 2

INTRODUÇÃO 3

ALTERAÇÕES À LINGUAGEM C 3

ESTRUTURA DO PROGRAMA 3

Ficheiros Fonte 4

Comentários 4

Constantes 4

Variáveis 4

ENTRADA E SAÍDA DE DADOS 5

Entrada e Saída de Dados Simples 5

Saída de Dados Formatada 9

FUNÇÕES 10

Funções inline 10

Redefinição de Funções 11

Argumentos Pré-definidos 12

Transmissão de Parâmetros por Referência 12

Retorno de Valores por Referência 13

ESTRUTURAS DE DADOS DINÂMICAS 14

Operador new 14

Operador delete 14

TÓPICOS VARIADOS 16

Notação Funcional do Operador de Coerção (cast) 16

Inicialização Dinâmica de Variáveis 16

Variáveis por Referência 16

FICHEIROS 16

MODELOS (TEMPLATES) 20

REDEFINIÇÃO DE OPERADORES 22
Introdução
A linguagem C++ surge como uma extensão à linguagem C trazendo

!
consigo dois importantes conjuntos de novidades:

1. Melhorias e acrescentos à linguagem C

2. Possibilidade de programação orientada por objectos

1
Informalmente diz-se que o primeiro conjunto leva àquilo a que se chama
um C melhorado (“a better C”) e é representado pelo primeiro símbolo + do
nome da linguagem. O segundo símbolo + vem das novas capacidades de
programação orientada por objectos.

As melhorias da linguagem C e algumas diferenças que apesar de tudo


existem em relação ao C++ serão descritas neste manual. A programação
orientada por objectos não será abordada.

Alterações à Linguagem C
Apesar da linguagem C++ ser uma extensão da linguagem C existem,
contudo, algumas alterações.

Declaração de As principais diferenças entre o C e o C++ envolvem a declaração de


funções obrigatória. funções. Em C++ é obrigatório fornecer a declaração ou definição de uma
função antes da sua utilização. Como boa prática deve-se sempre declarar
todas as funções mesmo quando se faz a sua definição.

Outra alteração relacionada com a declaração de uma função diz respeito à


omissão dos parâmetros na declaração. Em C isto significava que nada se
dizia em relação aos argumentos da função, em C++ isto significa que a
função não leva argumentos, sendo void.

Estrutura do Programa

1
Embora os programas em C++ apresentem uma estrutura semelhante à dos
programas em C, a utilização de algumas das novas características leva-os a
apresentarem um aspecto diferente. Assim, as entradas e saídas de dados que
utilizavam anteriormente as funções printf e scanf passam a utilizar as
novas “operações” >> e << com um formato bastante diferente e que será
descrito na próxima secção.

Também os comentários e a definição de constantes sofrem algumas


alterações. O local de definição de variáveis é outra das novidades.

3
Ficheiros Fonte
Extensão cpp para Uma actualização a ter em consideração é o nome dos ficheiros fonte. Uma
os ficheiros fonte
vez que a linguagem C++ é uma nova linguagem os seus ficheiros fonte irão
ter uma extensão diferente: habitualmente .cpp (C plus plus ou seja C++) ou
.C ( letra maiúscula) em alguns compiladores para Unix, em contraste com a
extensão .c dos programas em C.

Comentários
Em C++ além de se poderem usar os comentários no estilo do C (ou seja
'/*' para iniciar o comentário e '*/' para terminar o comentário), é
acrescentado um novo tipo de comentário: o comentário de uma linha.

Comentários O comentário de uma linha é iniciado pela sequência de símbolos '//', e leva
começados pelos símbolos
// e válidos apenas na linha
o compilador a ignorar qualquer caracter que se encontre a seguir a esses
corrente símbolos dentro da mesma linha. Esta forma de comentar é preferível por
ser mais fácil de gerir e originar menos erros.

Constantes
Definição de As constantes em C++ devem ser definidas a partir do modificador const,
constantes a partir do ou seja, serem definidas como variáveis acrescentando-se antes do tipo a
modificador const
palavra const. Convém 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 programação de difícil detecção.

Variáveis
Em C as variáveis apenas podiam ser declaradas a seguir ao inicio de um
bloco (após o caracter '{' ).

Declaração de Em C++ é agora possível declarar uma variável em qualquer linha de código.
variáveis em qualquer Recomenda-se, no entanto, que as variáveis sejam declaradas o mais próximo
linha de código
possível do local onde são utilizadas. Esta recomendação não se aplica se
forem usadas em vários conjuntos de instruções distintos dentro de um
mesmo bloco, pelo que neste caso será preferível a sua declaração no início
do bloco.

Outra novidade relaciona-se com as variáveis do tipo struct. Em C quando se


declarava uma variável deste tipo era necessário escrever a palavra struct
antes do nome da estrutura. Em C++ pode-se omitir a palavra struct e
em consequência não ser necessária a criação de um novo tipo definido pelo
utilizador para esse efeito.

Na listagem 1 apresentam-se alguns exemplos da declaração de constantes e


variáveis

4
C C++
#include <stdio.h> #include <iostream.h>

#define JANEIRO 1 const int JANEIRO = 1;


#define FEVEREIRO 2 const int FEVEREIRO = 2;

. . . . . .

struct DATA{ struct DATA{


int dia; int dia;
int mes; int mes;
int ano; int ano;
}; };
/* Comentário estilo C */ // Comentário estilo C++

void main(void) void main(void)


{ {
struct DATA hoje; DATA hoje; // Sem a palavra struct
int dias[31]; /* declaração de dias */
int i; /* Declaração de i */ hoje.dia = 1;
hoje.mes = JANEIRO;
hoje.dia = 1; hoje.ano = 1998;
hoje.mes = JANEIRO;
hoje.ano = 1998; int dias[31]; // Declaração de dias
for( int i=0; i<31; i++ )
for( i=0; i<31; i++ ) // declaração de i
{ {
dias[i] = 0; dias[i] = 0;

. . . . . .

} }
} }

LISTAGEM 1 Declarações de constantes e variáveis

Entrada e Saída de Dados


Entrada e Saída de Dados Simples
Muito embora as funções de entrada e saída da linguagem C funcionem em
C++, estas não deverão ser utilizadas. Assim, existe um conjunto de novas
operações de entrada e saída standard para a linguagem C++. Este conjunto
de novas operações encontra-se declarado no ficheiro <iostream.h>,
que vem assim substituir o antigo <stdio.h> da linguagem C.

Saída de dados As operações básicas de escrita no ecrã e de leitura do teclado irão ser
feita através do operador de
inserção << usado em
protagonizadas pelas novas “operações” << e >> em vez dos antigos e
conjunto com a “variável” confusos printf e scanf.
cout

Para a escrita no ecrã utiliza-se cout e o operador binário << denominado


neste contexto como operador de inserção. Na operação referida o primeiro
operando é a ”variável” 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 código:

cout << 15;

Da mesma forma o valor real 15.5 pode ser escrito através de:

cout << 15.5;

5
:
De uma maneira geral o operando do lado direito pode ser um inteiro, um
real (double ou float) ou qualquer outro dos tipos básicos. Também os
tipos char* e void* são reconhecidos, sendo interpretados como,
respectivamente, uma cadeia de caracteres e um endereço de memória e
escritos no ecrã de acordo. Exemplo:

char *str="Teste do sistema\n";

cout << str;


cout << (void *)str;
Resultado:
Teste do sistema
0x25660098

É ainda possível escrever mais que um valor a partir da mesma instrução,


bastando para isso encadear os diversos valores através do operador de
inserção como no seguinte exemplo:

cout << "Peso: " << 12.2 << " Kgs.\n";


Resultado:
Peso: 12.2 Kgs.

Entrada de dados De uma forma análoga, a leitura de valores a partir do teclado, é efectuada
feita através do operador de
extracção >> usado em
usando cin e o operador >> (operador de extracção). Os valores lidos são
conjunto com a “variável” colocados na variável fornecida como segundo operando da operação de
cin
extracção, 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;

7
A leitura para uma variável do tipo char* ou char[] equivale a ler uma
palavra para o endereço de memória respectivo. Apenas uma palavra é lida,
terminando a operação de leitura quando se encontrar um espaço branco. É
de referir que também a leitura de valores, incluindo do tipo char, não
recebe os espaços em branco.

Também é possível ler vários valores para um grupo de variáveis a partir de


uma única instrução de leitura. Isto é conseguido fornecendo as variáveis
separadas pelo operador de extracção como no seguinte exemplo:

cin >> dval >> ival;

Na listagem 2 mostram-se alguns exemplos de operações de escrita e leitura


de dados simples.

6
NOTA

Os símbolos '>>' e '<<' usados, respectivamente, nas


operações de leitura e escrita de valores representam a direcção
do fluxo de informação. Assim, para cin utiliza-se o símbolo
'>>' a indicar que a informação flúi de cin para as variáveis e
com cout '<<', fluindo a informação, aqui, no sentido dos
valores para cout.

C C++
#include <stdio.h> #include <iostream.h>

void main(void) void main(void)


{ {
int i; int i;
char str[[80]]; char str[[80]];

printf("Olá mundo\n"); cout << "Olá mundo" << endl;

/* Leitura de um valor */ // Leitura de um valor


printf("Introduza um número: "); cout << "Introduza um número: ";
scanf("%d",&i); cin >> i;

/* Mostrar o valor lido */ // Mostrar o valor lido


printf("O número introduzido foi: %d \n", cout << "O número introduzido foi: "
i ); << i << "\n";

/* Leitura de uma palavra */ // Leitura de uma palavra


printf("Introduza uma palavra: " ); cout << "Introduza uma palavra: ";
scanf("%s", str); cin >> str;

/* Mostrar a palavra lida */ // Mostrar a palavra lida


printf("A palavra intorduzida foi: "); cout << "A palavra introduzida foi: ";
printf("%s", str); cout << str;
} }

Resultado: Resultado:

Olá mundo
Introduza um número: 12 Olá mundo
O número introduzido foi: 12 Introduza um número: 12
Introduza uma palavra: carro O número introduzido foi: 12
A palavra introduzida foi: carro
Introduza uma palavra: carro
A palavra introduzida foi: carro
LISTAGEM 2 Instruções de entrada e saída de dados

Existem ainda outras formas de escrita e leitura de valores, entre elas será
importante referir as funções de leitura get e getline e de escrita put.

A função get possui vários formatos adaptados a diferentes objectivos:

1. Leitura de caracteres - sem argumentos, é retornado o caracter


lido do dispositivo de entrada.

2. Leitura de caracteres - com um argumento do tipo char em que


o caracter é lido por referência para dentro da variável char
passada como argumento.

7
3. Leitura de texto – com três argumentos em que o primeiro é a
cadeia de caracteres que vai receber o texto, o segundo é o
número de caracteres possíveis de armazenar na referida cadeia
de caracteres e o terceiro, que se pode omitir, é o caracter que
indica o fim da leitura que é por omissão o caracter ’\n’.

A função getline é idêntica na sua forma e funcionamento à função get


usada com três argumentos com a diferença que o caracter que termina o
texto é lido e descartado. A função get não descarta o referido caracter.
A função put leva como argumento um char e escreve-o no dispositivo de
saída.
A utilização destas funções aparece associada à “variável” cin duma forma
que pode parecer um pouco estranha: a seguir ao nome da variável segue-se
um ponto e a chamada à função. A razão para este facto tem a ver com a
programação orientada por objectos. A listagem 3 apresenta alguns exemplos de
utilização destas funções.
O grupo de funções descrito tem como equivalente na linguagem C as
funções getchar, putchar e gets (ou fgets).

C C++
#include <stdio.h> #include <iostream.h>

void main() void main()


{ {
int i; int i;
char str[129]; char str[129];
char z1; char z1;

printf("Ler uma linha de texto: "); cout << "Ler uma linha de texto: ";

/* Forma 1: int getchar() */ // Forma 1: int cin.get()


i=0; i=0;
while ( (z1=getchar()) != '\n' ) while ( (z1=cin.get()) != '\n' )
str[i++]=z1; str[i++]=z1;
str[i] = '\0'; str[i] = '\0';

printf( "\nTexto: %s\n", str ); 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: "); cout << "Ler uma linha de texto: ";

/* Forma 3: // Forma 3:
fgets(char *, int, FILE *) */ // cin.get(char *, int, char='\n')
fgets(str, 129, stdin); cin.get(str, 129);

printf( "\nTexto: %s\n", str ); cout << endl << "Texto: "<< str << endl;
} }

LISTAGEM 3 Leitura de textos

8
Saída de Dados Formatada
Em C era possível efectuar uma saída de dados formatada utilizando a
instrução printf. Entre as funcionalidades oferecidas tinham-se o número
mínimo de caracteres na escrita de um valor, o número de casas decimais de
um valor real, a apresentação do sinal ou não para valores numéricos
positivos, etc. Em C++ utilizando cout existem igualmente várias
possibilidades de formatação da saída de dados.

A formatação da A formatação da saída de dados em C++ é efectuada através de um


saída de dados é “comando” especial, o manipulador, que é incluído na lista de informação a
obtida através dos novos
manipuladores da linguagem
enviar para o ecrã através da operação de inserção. Os manipuladores podem
C++. levar argumentos. Os principais manipuladores e a sua funcionalidade
encontram-se descritos na tabela 1.

Manipuladores
Nome Argumento Descrição

endl --- Semelhante a ’\n’ em C. Limpa também o buffer


de escrita
setw Número de Total de caracteres com que se escreve o próximo
caracteres valor
hex --- Passa a mostrar os valores inteiros na notação
hexadecimal

¿
oct --- Passa a mostrar os valores inteiros na notação octal

dec --- Passa a mostrar os valores inteiros na notação


decimal
setprecision Número de casas Número de casas decimais para valores reais.
decimais
setiosflags (ios::fixed) Não mostrar o expoente.

setiosflags (ios::scientific) Notação cientifica (com expoente).

setiosflags (ios::showpoint) Mostrar as casas decimais, mesmo que sejam zeros .

setiosflags (ios::showpos) Mostrar sempre o sinal para números positivos

setiosflags (ios::internal) Colocar espaços no centro.

setiosflags (ios::left) Encostar à esquerda (espaços à direita)

setiosflags (ios::right) Encostar à direita (espaços à esquerda)

TABELA 1 Principais manipuladores para formatação de dados em C++

A exemplificação do uso dos manipuladores é feita na listagem 4.

Na operação de leitura de dados também existe um manipulador especial: ws


cuja funcionalidade é descartar todos os espaços em branco que apareçam na
leitura de dados.

NOTA

Quando se utilizam manipuladores em C++ deve-se incluir o


ficheiro iomanip.h no início do programa, ou seja
acrescentar a linha: #include <iomanip.h>.

9
C C++
#include <stdio.h> #include <iostream.h>
#include <iomanip.h>

int main(void) int main(void)


{ {
printf("\n"); cout << endl
printf("%8.2lf\n",3298.2341); << setw(8) // Total de caracteres
printf("Teste"); << setprecision(2) // Casas decimais
printf("\n%15s", "Hello there "); << setiosflags(ios::fixed) // s/ expoente
printf("----" ); << 3298.2341 << endl << "Teste";
}
cout << endl << setw(15) << "Hello there ";

cout << "----";


return 0;
}

RESULTADO: RESULTADO:

3298.23 3298.23
Teste Teste
Hello there ----
Hello there ----

LISTAGEM 4 Exemplo de saída de dados formatada.

Funções
No capítulo das funções em C++ foram introduzidas novidades muito
importantes, entre elas estão a possibilidade de definir várias funções com o
mesmo nome, a possibilidade de os argumentos poderem ser passados por
referência e de estes poderem também possuir valores pré-definidos.

Funções inline
Funções inline A novidade mais simples no que diz respeito às funções é o novo
permitem a colocação
directa do código da função
modificador inline. Este modificador colocado na declaração da função antes
nos locais em que esta é do tipo de retorno leva a que o compilador em vez de colocar um salto para o
chamada código da função, nos locais em que esta for chamada, coloque directamente
o código da função.

A vantagem da utilização de funções inline tem a ver com questões de


eficiência, uma vez que o código está no local de chamada da função esta irá
ser executada mais rapidamente, como desvantagem se a mesma for
chamada diversas vezes o código dela irá estar repetido as mesmas vezes.
Neste caso o executável do programa irá normalmente ocupar mais
memória. Esta novidade foi introduzida principalmente devido a questões
relacionadas com a eficiência na programação orientada por objectos.

O modificador inline deverá ser utilizado principalmente em funções


pequenas que não possuam ciclos ou instruções de selecção do tipo switch.
Também é necessário que o compilador veja a definição da função inline
antes do local em que esta é chamada para que seja possível obter e colocar o
seu código no local de chamada.

A utilização do modificador inline não obriga o compilador a colocar o


código da função no local de chamada, é apenas um pedido para que isso
aconteça.

10
Redefinição de Funções
Funções com o Uma das novidades mais importantes do C++ diz respeito à redefinição de
mesmo nome são funções. Em C++ é possível definir várias funções com o mesmo nome.
possíveis e distinguem-se
pelo número e/ou tipo dos Uma vez que as funções redefinidas irão ter o mesmo nome a distinção entre
seus parâmetros
elas é feita pelos argumentos com que são chamadas. Os argumentos podem
ser diferenciados pelo número e/ou pelo seu tipo.
O tipo de retorno não permite diferenciar duas funções com o mesmo nome
e o mesmo tipo e número de argumentos.

C C++
#include <stdio.h> #include <iostream.h>

void equalline(); void charline( void );


void minusline(); void charline( char car );
void astline(); void charline( char car, int ncar );

void main(void) void main(void)


{ {
astline(); charline();
printf(" Aluno Nota\n"); cout << " Aluno Nota" << endl;
minusline(); charline('-',25);
printf(" Joao Martins 14 \n"); cout << " Joao Martins 14 " << endl
printf(" Pedro Oliveira 9 \n"); << " Pedro Oliveira 9 " << endl
printf(" Jose Silva 11 \n"); << " Jose Silva 11 " << endl;
equalline(); charline('=');
} }

void astline() void charline( void )


{ {
int j; for (int j=0; j<30; j++)
for (j=0; j<30; j++) cout << '*';
printf("*"); cout << endl;
printf("\n"); }
}

void equalline( void ) void charline( char car )


{ {
int j; for (int j=0; j<30; j++)
for (j=0; j<30; j++) cout << car;
printf("="); cout << endl;
printf("\n"); }
}

void minusline( void ) void charline( char car, int ncar )


{ {
int j; for (int j=0; j<ncar; j++)
for (j=0; j<25; j++) cout << car;
printf("-"); cout << endl;
printf("\n"); }
}
RESULTADO:
******************************
Aluno Nota
-------------------------
Joao Martins 14
Pedro Oliveira 9
Jose Silva 11

LISTAGEM 5 Redefinição de funções.

NOTA

Quando existirem dúvidas por parte do compilador sobre qual a


função redefinida a ser chamada é gerado um erro de compilação
onde se menciona uma chamada de função ambígua

11
No exemplo da listagem 5 mostra-se como três funções 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.

Argumentos Pré-definidos
Omissão de É possível omitir alguns argumentos na chamada de uma função desde que
argumentos na na declaração ou na definição da função (e em apenas um dos sítios) apareça
chamada de uma função
através da atribuição de
o valor que o argumento irá ter se for omitido.
valores pré-definidos aos
parâmetros na declaração ou O valor por omissão é fornecido juntando um sinal de igual seguido desse
na definição da função
valor a seguir ao nome do argumento.

NOTA

Os argumentos por omissão são sempre fornecidos a começar


no último e sequencialmente

#include <iostream.h>

void charline( char car='*', int ncar=30 );

void main(void)
{
charline();
cout << " Aluno Nota" << endl;
charline('-',25);
cout << " Joao Martins 14 " << endl
<< " Pedro Oliveira 9 " << endl
<< " Jose Silva 11 " << endl;
charline('=');
}

void charline( char car, int ncar )


{
for (int j=0; j<ncar; j++)
cout << car;
cout << endl;
}

LISTAGEM 6 Versão com argumentos pré-definidos do programa da listagem 5

Transmissão de Parâmetros por Referência


Em C++ é possível passar parâmetros por valor e por referência. Os
argumentos passados para uma função podem agora ser passados por
referência à semelhança do que acontece na linguagem PASCAL quando se
utiliza a palavra VAR antes do argumento formal.

Apenas as variáveis podem ser passadas como parâmetros por referência.

12
Passagem por Para indicar um argumento por referência utiliza-se o símbolo &
referência de imediatamente antes do nome do parâmetro.
parâmetros é possível
usando o operador & antes
do nome do argumento no As variáveis passadas por referência irão reflectir as alterações ao seu valor
cabeçalho da função feitas no interior da função.

C C++
#include <stdio.h> #include <iostream.h>
#include <math.h> #include <math.h>

struct complex { struct complex {


double x,y; double x,y;
}; };

int iabs( int val ) inline double abs( double val )


{ {
if( val < 0 ) if( val < 0.0 )
return( -val ); return( -val );
return( val ); return( val );
} }

inline long abs( long val )


{
if( val < 0 )
return( -val );
return( val );
}

/* passagem por valor de val */ // passagem por referência de val


double cabs( struct complex val ) inline double abs( complex &val )
{ {
return sqrt(val.x*val.x + val.y*val.y); return sqrt(val.x*val.x + val.y*val.y);
} }

void main( void ) void main( void )


{ {
int i=-10; int i=-10;
double d=-15.0; double d=abs(-15.0);
struct complex c={-2.0,3.0}; complex c={-2.0,3.0};
long l=-10L; long l=-10L;

printf("Int i -> %d\n" , iabs(i) ); cout << "Int i ->" << abs(i) << endl;
printf("Double d -> %lf\n", fabs(d) ); cout << "Double d ->" << abs(d) << endl;
printf("Complex c -> %lf\n", cabs(c) ); cout << "Complex c ->" << abs(c) << endl;
printf("Long l -> %ld\n", labs(l) ); cout << "Long l ->" << abs(l) << endl;
} }

LISTAGEM 7 Passagem de parâmetros por referência e redefinição de funções

Retorno de Valores por Referência


Retorno por Tal como é possível passar valores por referência, também é possível
referência é possível retornar por referência. O retorno por referência funciona como se
usando o operador & antes
do nome da função no
retornasse uma variável. A principal vantagem deste método é evitar que se
cabeçalho da função produza uma cópia da variável no retorno da função o que resultaria numa
perda de eficiência.

É preciso tomar cuidado com esta forma de retornar, uma vez que a variável
retornada tem que ter existência fora da função que a retornou, o que
impossibilita o retorno de variáveis locais.

13
Estruturas de Dados Dinâmicas
Em C++ a reserva e a libertação de memória deixam de utilizar
respectivamente as funções do C malloc (ou equivalente) e free,
passando a usar os novos operadores new e delete.

Operador new
Reserva de O operador new é utilizado em C++ em substituição de malloc. Uma
memória utiliza o novo vantagem na utilização deste operador é o facto de não ser necessário utilizar
operador new em vez da
função malloc
a operação de cast para converter o ponteiro retornado num ponteiro
compatível com a variável de afectação.

A forma de utilização deste operador é:

<ponteiro> = new <tipo>;

ou

<ponteiro> = new <tipo> [<total de elementos a alocar>];

Exemplo: char *str = new char [20+1];

Operador delete
Libertação de O operador delete é utilizado em C++ com o objectivo de libertar a
memória reservada memória previamente alocada por new.
utiliza o novo operador
delete em vez da função
free Uma nota importante na utilização deste operador é que se torna possível
libertar memória a partir dum ponteiro que possua o valor NULL, esta acção
que seria desastrosa em C na função free, não irá surtir qualquer efeito em
C++.

Forma de utilização:

delete <ponteiro>;

ou

delete [ ] <ponteiro>;

Exemplo: delete [] str;

Na listagem 8 apresenta-se um exemplo da utilização de estruturas de dados


dinâmicas através dos operadores new e delete.

14
C C++
#include <stdio.h> #include <iostream.h>
#include <string.h> #include <iomanip.h>
#include <string.h>
typedef struct telefone{
char *nome; struct TELEFONE{
long *telefone; char *nome;
}TELEFONE; long *telefone;
};
#define max_numeros 100 const max_numeros = 100;

void main( void ) void main( void )


{ {
TELEFONE *agenda; TELEFONE *agenda;
char str[128+1]; char str[128+1];
long tf; long tf;
char resp; char resp;
int i,j;
str[0] = '\0';
str[0] = '\0'; agenda = new TELEFONE [max_numeros];
agenda = (TELEFONE *)malloc( //Alocar array de telefones
sizeof(TELEFONE)*max_numeros));
/* Alocar array de telefones */ if( agenda == NULL )
if( agenda == NULL) return;
return;
cout << setw(30) << "AGENDA TELEFONICA";
printf("%30s", "AGENDA TELEFONICA");
for( int i=0; i<max_numeros; i++ )
for( i=0; i<max_numeros; i++ ) {
{ cout << endl << endl
printf("\n\n"); << "Deseja inserir mais números ? ";
printf("Deseja inserir mais números ? "); cin >> ws >> resp;
fflush(stdin); scanf("%c", &resp); if( resp == 'n' || resp == 'N' )
if( resp == 'n' || resp == 'N' ) break;
break; cout << "Nome: ";
printf("Nome: "); cin >> ws;
fflush(stdin); fgets(str,128,stdin); cin.getline(str,128);
printf("Telefone: "); cout << "Telefone: ";
scanf(" %ld", &tf); cin >> tf;

agenda[i].nome = malloc( strlen(str)+1 ); agenda[i].nome=new char [strlen(str)+1];


if( agenda[i].nome ) if( agenda[i].nome )
strcpy(agenda[i].nome, str); strcpy(agenda[i].nome, str);
agenda[i].telefone = agenda[i].telefone = new long;
(long *) malloc( sizeof(long) ); if( agenda[i].telefone )
if( agenda[i].telefone ) *(agenda[i].telefone) = tf;
*(agenda[i].telefone) = tf; }
}
if( i )
if( i ) cout << endl << endl << setw(30)
printf("%30s","AGENDA TELEFONICA"); << "AGENDA TELEFONICA";
for( int j=0; j<i; j++ )
for( j=0; j<i; j++ ) {
{ cout << endl << "Nome: "
printf("\nNome: %s",agenda[j].nome); << agenda[j].nome << endl;
printf("\nTelefone: %ld\n", cout << "Telefone: "
*agenda[j].telefone); << *(agenda[j].telefone) << endl;
} }

/* Libertar toda a memória alocada */ // Libertar toda a memória alocada


for( j=0; j<i; j++) for( j=0; j<i; j++)
{ {
if( agenda[j].nome )
free(agenda[j].nome); delete [] agenda[j].nome;
if( agenda[j].telefone )
free(agenda[j].telefone); delete agenda[j].telefone;
} }
free( agenda ); delete [] agenda;
} }
LISTAGEM 8 Alocação e libertação de memória

15
Tópicos variados
Notação Funcional do Operador de Coerção (cast)
Operação de cast A operação de coerção (cast) que permite a conversão entre tipos diferentes,
utiliza os parênteses para os
valores e não para o tipo
possui uma nova notação em C++. Enquanto em C esta operação
obtinha-se colocando o tipo, para o qual se pretendia converter, entre
parênteses antes do valor a converter (Ex. Conversão de double para int
à (int)32.3), agora em C++ deve-se fazer colocando entre parênteses
o valor e não o tipo (Ex. double para int à int(32.3) ). Esta
notação tem uma sintaxe semelhante à das funções cujo nome é dado pelo
tipo da conversão e o argumento pelo valor a converter.

Inicialização Dinâmica de Variáveis


Outra novidade importante é que se podem inicializar dinamicamente as
variáveis locais e globais, ou seja, o seu valor inicial pode ser obtido através
de uma expressão ou da chamada de uma função.

Variáveis por Referência


Um novo tipo de variáveis que aparece em C++ são as variáveis de
referência. É possível criar variáveis que não são mais que referências a
variáveis que já existem. Isto funciona como se fosse um outro nome para
uma mesma variável. Estas variáveis devem ser obrigatoriamente inicializadas
quando são criadas.

Ficheiros
O tratamento de ficheiros em C++ é feito principalmente com base em

1
variáveis dos novos tipos ifstream (para ficheiros de leitura), ofstream
(para ficheiros de escrita) e fstream (para escrita e leitura). Os ficheiros de
texto possuem também uma abordagem diferente da dos ficheiros binários.
As operações básicas com ficheiros mantêm-se inalteradas em C++. Assim é
possível abrir e fechar ficheiros, efectuar operações 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 variável do
tipo ifstream para operações de leitura ou do tipo ofstream para
operações de escrita. O nome do ficheiro pode ser fornecido entre
parênteses directamente na declaração da variável ou através de uma função
open que é chamada associada à variável 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 variável que lhe deu origem


deixa de existir, ou seja no fim do bloco onde foi declarada.
É possível, de qualquer forma, fechar um ficheiro através de uma função
close associada à variável que lhe deu origem:
fich.close();

16
Os ficheiros de Quando se trabalha com ficheiros de texto utiliza-se a mesma forma de
texto utilizam os escrita e leitura que quando se trabalha com o ecrã e o teclado. Por exemplo
operadores >> e <<
associados a variáveis dos
para escrever a linha “Hoje é Sábado dia 10” poderia-se fazer:
tipos ifstream e
ofstream para leitura e
fich << ”Hoje é Sábado dia ” << 10 << endl;
escrita de dados
Neste caso apenas a “variável” cout é substituída pela variável que
representa o ficheiro.
As funções get, getline e put referidas na secção de escrita e leitura
de dados também podem ser utilizadas.
Em relação à utilização de ficheiros binários, estes requerem uma abertura
efectuada de forma diferente. A função de abertura de ficheiro descrita
anteriormente, utiliza na realidade a nova funcionalidade da linguagem C++
que possibilita os argumentos por omissão. De facto, a função referida
aplicada a variáveis 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 omissão o modo para escrita no
ficheiro. Assim sendo, para abrir ficheiros no modo binário, era necessário
referir este modo na abertura do ficheiro.

Os diferentes modos de abertura dos ficheiros são essencialmente os


mesmos que em C embora com uma sintaxe diferente que acrescenta ios::
antes da abreviatura do modo. Podem-se igualmente juntar vários modos
utilizando o operador ‘ou’ ao nível do bit.

É importante referir ainda que estes mesmos parâmetros estão disponíveis


quando se abre o ficheiro a partir da definição da variável associada. Assim, por
exemplo, para abrir um ficheiro para leitura em modo binário poderia-se
utilizar:

ifstream fich( "teste.dat", ios::in | ios::binary );

ou então,

ifstream fich;
fich.open("teste.dat", ios::in | ios::binary );

17
Os modos de abertura encontram-se resumidos na tabela 2.

Modos de abertura de ficheiros


ficheiros
Modo Função

in Para leitura
out Para escrita
ate Posiciona-se no fim do ficheiro após a abertura (at end)
App Para adicionar dados (append)
Trunc Inicia o ficheiro a zero (limpa-o se existir)
nocreate Falha a abertura se o ficheiro existir
noreplace Falha a abertura se o ficheiro não existir
binary Abre em modo binário

TABELA 2 Lista dos modos de abertura dos ficheiros em C++

Os ficheiros As operações de escrita ou leitura em modo binário utilizam habitualmente


binários são lidos as novas funções read e write associadas também às variáveis dos ficheiros.
utilizando as funções read
ou get e escritos com as
Estas funções possuem dois argumentos, o primeiro o endereço da posição
funções write ou put de memória onde se encontra a informação a ler ou a escrever, e o segundo
o número de bytes a ler ou a escrever.
As funções get e put descritas anteriormente para os ficheiros de texto
também estão disponíveis para utilização com ficheiros binários.
O fecho de ficheiros binários funciona da mesma forma da dos ficheiros de
texto.
Uma outra funcionalidade na utilização de ficheiros em C era o controlo do
posicionamento de um ponteiro de escrita ou leitura dentro dos ficheiros.
Esta funcionalidade é disponibilizada em C++ através de ponteiros
independentes para escrita e leitura, controlados pelas funções seekg,
seekp, tellg e tellp.
A função seekg leva como argumento a posição de leitura (seekg significa

ž
seek get) contada a partir do início do ficheiro. Esta função possui uma
segunda forma com dois argumentos em que o primeiro é a posição relativa
e o segundo o local de inicio na determinação da posição efectiva. O local de
inicio pode ser dado por ios::beg – princípio do ficheiro, ios::cur –
posição corrente ou ios::end - fim do ficheiro). A função seekp possui
igualmente as mesmas possibilidades em relação ao ponteiro de escrita (seekp
significa seek put).
Por outro lado as funções tellg e tellp permitem obter respectivamente
as posições actuais dos ponteiros de leitura e de escrita nos ficheiros.
Existem ainda um conjunto de funções de teste associadas à utilização de
ficheiros, que são usadas em conjunto com as variáveis que representam os
ficheiros e que se encontram descritas na tabela 3.

18
Funções de teste em ficheiros
nome argumento descrição

eof --- Retorna True se o ponteiro estiver no fim


do ficheiro
bad --- Retorna True se existiu erro na última
operação com o ficheiro
fail --- Retorna True se falhou a última operação
com o ficheiro
good --- Retorna True se todas as funções
anteriores retornarem False
TABELA 3 Principais funções de teste em operações com ficheiros

C C++
#include <stdio.h> #include <fstream.h>
#include <conio.h> #include <conio.h>

void main() void main()


{ {
FILE *fout, *fin; ofstream fout("texto.dat");
char str[129]; // ou fout.open("texto.dat");
fout = fopen("\\AUTOEXEC.BAT","wt");

if( fout == NULL ) if( !fout )


{ {
printf("Erro a abrir o ficheiro"); cout << "Erro a abrir o ficheiro";
return; return;
} }
fprintf( fout, "Teste de escrita " ); fout << "Teste de escrita "
fprintf( fout, "em ficheiro\n" ); << "em ficheiro" << endl;

fprintf( fout, "%lf\n", 10.4 ); fout << 10.4 << endl;

fclose( fout ); fout.close();

fin = fopen("texto.dat","rt"); ifstream fin("texto.dat");


char str[129];
if( fin == NULL )
{ if( !fin )
printf("Erro a abrir o ficheiro"); {
return; cout << "Erro a abrir o ficheiro";
} return;
}

fscanf(fin,"%s",str); fin >> str;


while( !feof(fin) ) while( !fin.eof() )
{ {
printf("%s ", str ); cout << str << " ";
fscanf(fin,"%s",str); fin >> str;
} }

fclose(fin); fin.close(); // Não é necessário


getch(); getch();
}
}
LISTAGEM 9 Exemplo de escrita e leitura em ficheiros de texto

19
Modelos (templates)
Funções padrão Em C++ é possível criar funções padrão (templates), que servem de modelo
permitem definir modelos
para a criação de funções
para a criação de funções. Nestas funções um ou mais dos tipos de dados
que possuem o mesmo dos argumentos e do valor de retorno não são fornecidos directamente no
código aplicado a cabeçalho da função. Esta característica permite que se criem funções com o
argumentos de tipos
diferentes código idêntico, que possuem implementações diferentes motivadas pelos
tipos de dados dos argumentos.
A título de exemplo admita-se uma função max que devolve o maior dos
valores que eram passados como argumentos. Se esses valores se referissem a
inteiros, a função poderia ser:
int max(int x, int y)
{
if( x > y )
return x;
return y;
};
Se, por outro lado estivéssemos a lidar com valores reais, então teríamos:

double max(double x, double y)


{
if( x > y )
return x;
return y;
};

/
Embora o tipo dos argumentos tenha sido alterado, o código da função
permaneceu idêntico. As funções padrão permitem que os tipos envolvidos
no protótipo da função funcionem como variáveis. Segundo este princípio a
função max iria ter então a seguinte definição:

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


arbitrário. Uma chamada à função max na seguinte forma:
double z = max( 2.0, 3.4);
levaria o compilador a criar um função max com o tipo T concretizado
como um double. Se houvesse uma nova chamada à função max, em que
agora o tipo envolvido fosse, por exemplo, o tipo char, então seria criado o
código de uma outra nova função max onde T seria substituído por char.

20
Como regra sempre que o compilador encontra uma chamada a uma função
segue os seguintes passos para resolver essa chamada:

1. Encontrar a função respectiva que deverá possuir o mesmo número


de argumentos e do mesmo tipo dos utilizados na chamada à função.
Utilizar a função encontrada.
2. Caso não encontre a função descrita em 1. Procura uma função
padrão em que os tipos dos argumentos envolvidos se ajustem ao
modelo. Caso isso aconteça cria essa função para os tipos
envolvidos.
3. Em último caso se não resolveu a chamada à função em 1. ou 2.
tenta a conversão dos valores dos argumentos de forma a se
ajustarem às funções existentes. Esta situação ocorre apenas para
funções realmente definidas e não para funções padrão.

Ä
Na definição de funções padrão utiliza-se uma linha inicial com a lista de tipos
diferentes que poderão constar da lista de argumentos. Esta lista aparece a
seguir à palavra template e entre os símbolos < e >. Os nomes dos tipos
deverão ser precedidos da palavra reservada class. Sintaxe usada:

template <class nome1 [, class nome2, ...]>


Como regras na definição de funções padrão deve-se obedecer ao seguinte:

1. Os tipos declarados na lista de tipos devem aparecer obrigatoriamente


pelo menos uma vez na lista de argumentos da função.
2. Os tipos declarados podem ser utilizados mais que uma vez na lista de
argumentos e podem ser igualmente utilizados como tipo de retorno.
3. Podem coexistir tipos básicos ou tipos definidos anteriormente
conjuntamente com os tipos declarados na lista de argumentos da
função.

#include <iostream.h> void main()


#include <string.h> {
double x=1.0, y=2.0;
template <class T>
T max(T x, T y) cout << "valor double maximo: "<< max(x,y) << endl;
{ // é criada a função double max(double, double)
return (x > y) ? x : y;
}; char z1 = 'a', z2 = 'b';

char *max(char *str1, char *str2) cout << "valor char maximo: "<< max(z1,z2) << endl;
{ // é criada a função char max(char, char)
if(strcmp(str1,str2)>0)
return str1; char *str1="Texto um", *str2 = "Texto dois";
return str2;
} cout << "valor texto maximo: "
<< max(str1,str2) << endl;
// é utilizada a função char *max(char*, char*)
}

LISTAGEM 10 Utilização de funções padrão

21
Redefinição de operadores
A redefinição das Regra geral todos os operadores do C++ estão preparados para trabalhar
operações associadas com os tipos de dados pré-definidos na linguagem. Por exemplo, o operador
aos operadores é efectuada
fornecendo a função:
soma (+) funciona com os tipos inteiro, caracter ou mesmo ponteiro. Mas se
operator <símbolo do o programador definir um tipo novo, por exemplo a partir de uma estrutura,
operador> (. . .)
o compilador não consegue realizar a operação e gera uma mensagem de
erro.

Em C++ é possível fornecer uma função para a realização de uma


determinada operação que envolva operadores. Esta função irá então ser
chamada automaticamente e realizará a operação. Isto apenas será possível 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;
};

podemos fazer com que as seguintes linhas de código funcionem:

COMPLEXO c1={1.2,-2.3}, c2={1.1,1.0}, c3;

C3 = c2 + c1;

bastando para isso redefinir a operação soma para operandos do tipo


COMPLEXO.

ø
A redefinição da operação passa assim pela criação de uma função especial
que irá ser chamada quando a operação e o tipo dos operandos
corresponderem à função criada. Esta função especial é caracterizada por
possuir como nome a palavra reservada operator seguida do símbolo do
operador e como argumentos variáveis do mesmo tipo dos operandos.
Retomando o exemplo anterior teríamos o seguinte código para a redefinição
da operação 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 atenção que os argumentos da função
correspondem aos operandos, o que significa que os operadores unários irão
possuir funções com um argumento apenas.

22
Operadores não redefiniveis
?:
.
::
.*

TABELA 4 Operadores não redefiniveis

Os operadores >> Uma redefinição de operadores muito útil é a redefinição do operador <<
e << redefinidos para para a operação de escrita através de cout e do operador >> para a
istream e ostream
permitem a leitura e escrita
operação de leitura através de cin.
de tipos de dados definidos
pelo utilizador nas operações Na realidade cout funciona como uma variável de um tipo pré-definido na
de entrada e saída de dados
linguagem: o tipo ostream. A operação de escrita no ecrã resulta então da
redefinição que é feita do operador << para que ele funcione com este tipo
da forma conhecida. É possível, no entanto, redefinir esta operação 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
operação 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

q
forma análoga, redefinir o operador >>. Note-se que neste caso cin
funciona como uma variável do tipo istream. Teríamos então:

istream & operator>>(istream &is, COMPLEXO &c1 )


{
cout << “Real: “;
is >> c1.real;
cout << “Imag: “;
is >> c1.imag;

return is;
}

23
Na listagem 11 dá-se um exemplo completo de um programa envolvendo a
redefinição dos operadores.

#include <iostream.h>
void main()
struct COMPLEXO{ {
double real; COMPLEXO c1 = {2.0,3.0},
double imag; c2={3.2,-1.3},
}; c3;

COMPLEXO operator + (COMPLEXO c1, COMPLEXO c2) c3 = c1 + c2;


{
c1.real = c1.real + c2.real; cout << endl<< "c3:" << c3 <<endl;
c1.imag = c1.imag + c2.imag;
cout << c3 + 2.0;
return c1;
} cin >> c1;
cout << c3 + c1;
COMPLEXO operator + (COMPLEXO c1, double val) }
{
c1.real = c1.real + val; Resultado:

return c1; c3(5.2,1.7)


}
(7.2,1.7)
ostream &operator<<(ostream &os, COMPLEXO c1) Real: -1
{ Imag: 4
os << '(' <<c1.real<< ',' <<c1.imag<< ')';
(4.2,5.7)
return os;
}

istream &operator>>(istream &is, COMPLEXO &c1)


{
cout << endl << "Real: ";
is >> c1.real;
cout << "Imag: ";
is >> c1.imag;

return is;
}

LISTAGEM 11 Redefinição de operadores

24

You might also like