A linguagem C++
Conceitos gerais
Novas facilidades da linguagem C++
A linguagem de programação C++ foi concebida para:
 Ser um C melhorado
 Suportar Abstracção de Dados
 Suportar Programação Orientada por Objectos
O suporte mínimo para a programação procedimental consiste em
funções, operadores aritméticos, instruções de selecção, ciclos e
entrada/saída de dados.
O C++ herda todos estes mecanismos básicos e inclui alguns novos
1. Entrada/saída de dados
Tal como em C, o C++ também define streams para os
dispositivos de entrada e saída que são abertos quando um
programa é executado
Os streams são:
1. cout, equivalente ao stdout;
2. cin, equivalente ao stdin;
3. cerr, equivalente ao stderr;
De facto cout é um objecto do tipo ostream.
A classe ostream foi definida com o operador operator<< (“put to”)
para fornecer as saídas dos tipos predefinidos nas linguagens C/C++.
Vamos abordar um exemplo:
#include <iostream.h>
int main()
{
cout << "This is the second argument\n";
}
Isto é o primeiro argumento do operador (da função) operator<<
Isto é o segundo argumento do operador (da função) operator<<
O operador << (“put to”) escreve o valor do segundo argumento
no primeiro argumento. Isto significa que, no exemplo, o texto
This is the second argument\n será escrito no dispositivo de saída
standard cout.
cerr é um outro objecto do tipo ostream.
Por analogia cin é um objecto do tipo istream.
A classe istream foi definida com o operador operator>> (“get from”)
para fornecer as entradas dos tipos predefinidos nas linguagens C/C++.
Vamos considerar o exemplo:
int a;
float b;
char c;
cin >> a >> b >> c;
Os streams são declarados no ficheiro iostream.h
Um programa poderá usar para entradas e saídas tanto a
biblioteca standard de C como a de C++ (ou as duas)
2. Overloading (Sobrecarga) do nome das funções
Funções diferentes têm geralmente nomes diferentes. Contudo, para
funções que realizam tarefas semelhantes em tipos de objectos
diferentes, por vezes é preferível que essas funções mantenham o
mesmo nome. Quando o tipo dos seus argumentos é diferente,
o compilador consegue diferenciá-las e escolher a função correcta
a invocar. Por exemplo, podemos ter uma função potência para
inteiros e outra para variáveis de vírgula flutuante.
Vamos abordar o exemplo:
int pow(int,int);
double pow(double,double);
// ...
x = pow (3,5);
// chamada pow(int,int)
y = pow (2.0, 4.0); // chamada pow(double,double)
Este tipo de uso múltiplo de um nome de uma função é designado
por function-name overloading ou simplesmente overloading.
Os argumentos de uma função podem ser passados quer
“por valor”, quer “por ponteiro”, quer “por referência”.
void swap(int* a, int* b)
{ int temp = *a;
*a = *b;
*b = temp;
}
void swap(int& a, int& b)
{ int temp = a;
a = b;
b = temp;
}
void f(int i, int j)
{
swap(&i,&j);
}
void f(int i, int j)
{
swap(i,j);
}
Usando a passagem por referência podemos trabalhar com
ponteiros implícitos
3. Referências
Uma referência é um nome alternativo para um objecto
Tal como um apontador, uma referência corresponde a um local
onde se situa uma variável
Com o tipo de dados referência, criamos um nome alternativo
para uma variável previamente declarada
Numa declaração, a notação T& significa referência para um valor
do tipo T
Seja, por exemplo:
int i=3;
int &j=i;
j=2;
// .....
A diferença entre os ponteiros e as referências é
a seguinte: o utilizador pode usar uma referência
como um objecto ordinário ainda que o acesso seja
fornecido através do endereço
void main(void)
{
int i=3;
int &j=i;
j=2;
// .....
void main(void)
{
int i=3;
int *j=&i;
* j=2;
// .....
Uma referência é um ponteiro implícito
i=3
ss
bp-2
bp
// mov word ptr [bp-2],3 Isto
// mov [bp-4],ss
// mov [bp-6],ax ; lea ax,[bp-2]
j=2 and i=2
mov es:word ptr [bx],2
es
bx
ss
é um ponteiro explícito
mov word ptr[bp-2],3
lea ax,[bp-2]
mov [bp-4],ss
mov [bp-6],ax
les bx,[bp-6]
mov es:word ptr [bx],2
Como uma referência é um ponteiro podemos obter
um resultado inesperado, por exemplo
int F(int& ri)
{
++ri;
return ri;
}
int main(...)
{
int m1=1;
cout << F(m1) << endl;
cout << m1 << endl;
// o resultado é 2
// o resultado é 2
}
A referência para o objecto m1 é passada à função F. Esta função
alterou o valor da variável m1 indirectamente através da referência
De facto, uma referência é implementada pelo compilador como um
apontador constante que é desreferenciado automaticamente de cada
vez que foi usado
Na maioria dos casos as referências e os apontadores têm uma
relação de equivalência, por exemplo:
O seguinte código usa
uma referência:
O seguinte código usa
um ponteiro:
int ii=5;
int *rii=&ii;
cout << ii << '\t' << *rii << endl;
*rii = 10;
cout << ii << '\t' << *rii << endl;
int i=5;
int &ri=i;
cout << i << '\t' << ri << endl;
ri = 10;
cout << i << '\t' << ri << endl;
Os resultados são iguais:
5
10
5
10
int &a,&b,&c;
As referências devem
ser inicializadas, por exemplo: int *aa,*bb,*cc;
int k;
int &a=k,&b=k,&c=k;
// erro
// Ok
// Ok
// Ok
Vamos considerar o seguinte código:
X = Y;
Esta atribuição está correcta se a expressão esquerda for um
endereço (lvalue - a left value) e se a expressão direita produzir
um valor (rvalue - a right value), que é um valor permissível.
Como resultado o valor do lado direito (Y) vai ser copiado para
a célula da memória que tem o endereço X.
Uma referência é um endereço e por isso pode ser considerada
como lvalue. Então, a referência pode ser escrita no lado esquerdo
da expressão.
Vamos considerar uma função. Podemos passar o argumento
à função como valor. Neste caso este valor (de facto rvalue) vai
ser copiado na pilha da função. A função não tem acesso ao valor
da variável original que foi passada.
Podemos passar o argumento à função como referência. Neste
caso o endereço (de facto lvalue) vai ser copiado na pilha da
função. Usando este endereço (esta referência) podemos alterar
o valor da variável original que foi passada. Por outras palavras
a referência pode ser usada como um apontador implícito.
A referência pode ser devolvida da função. Neste caso a função
pode aparecer no lado esquerdo da expressão.
Vamos abordar das seguintes funções:
1. int F(int& i) { return i; }
2. int& RF(int& j) { return j; }
F(x) = 6;
// erro
RF(x) = 6;
// Ok
podemos, por exemplo
obter um resultado
inesperado
int x=3;
cout << " x = " << x << endl;
RF(x) = 6;
cout << " x = " << x << endl;
// x=3
// x=6
Download

ppt1