Variáveis, tipos, literais, expressões, novidades e Cia

Este material trata de *alguns* aspectos da linguagem C++ que não estão presentes nas versões mais antigas da linguagem C.

A entrada e saída “usual” de C é feita via funções printf e scanf que utiliza os fluxos stdout e stdin, em C++ o mecanismo “usual” de entrada e saída (teclado/tela) é a utilização dos fluxos cout e cin (character output e character input) e as funções “implícitas” (operator<<() e operator>>() existem dezenas, centenas destas funções, este aspecto da linguagem C++ é tratado em material mais à frente). Além destas funções implícitas podem ser usados “manipuladores” (C++ manipulators).

Alguns dos manipuladores de C++ #include <iomanip>

endl
atua na hora escreve "nova linha"(new line)

setw(int) o mesmo que width(int)
atua no próximo elemento a ser impresso, largura do campo

setfill(char)
é útil no contexto de um setw(), se um valor não esgota a largura o caractere preenche ao invés do default "branco".

right ou left
é útil no contexto de um setw(), default right, onde posicionar os caracteres do elemento a ser impresso

ex:  cout<<setfill('*')<<setw(20)<<34<<"@@@"<<endl;

a saida deve ser
******************34@@@

ex:  cout<<setfill('*')<<setw(20)<<left<<34<<"@@@"<<endl;

a saida deve ser
34******************@@@

 

setprecision(int)
numero de dígitos à direita do ponto de elementos do tipo ponto flutuante.

fixed
exibir a parte inteira do ponto flutuante

scientific
notação científica.

ex: cout<<123456.7890123<<endl;
    cout<<fixed<<123456.7890123<<endl;
    cout<<scientific<<123456.7890123<<endl;

saida:
123457
123456.789012
1.234568e+005

boolalpha  noboolalpha

imprimir os strings true ou false

 

outros
skipws noskipws showpoint, noshowpoint, uppercase, nouppercase, dec, oct, hex, setbase(8|10|16), showbase, noshowbase, ends, showpos, noshowpos, internal, flush, unitbuf, nounitbuf, setiosflags(), resetiosflags()

-------
variáveis & tipos

A linguagem C++ define vários tipos. Assim como na linguagem C alguns tipos são básicos ou primitivos (p.ex. int, double, char) e outros tipos são definidos a partir dos tipos primitivos e algumas vezes são denominados tipos complexos. A linguagem C também permite a definição de tipos complexos, por exemplo através do mecanismo de definição de estruturas (struct), mas a linguagem C++ estende o mecanismo de definição de estruturas e define vários outros mecanismos. Vários tipos da linguagem C++ são definidos utilizando bibliotecas, que são conjuntos de unidades précompiladas de dados e códigos.

 

Tipos aritméticos

Tipo

Significado

bool

domínio de dois valores que podem ser representados por dois literais:
true e false

char

dominio dos caracteres usualmente codificados via ASCII 8 bits

wchar_t

domínio de caracteres com codificação em 16 bits

short

(short int) inteiros com pelo menos 16 bits

int

inteiros com pelo menos 16 bits

long

(long int) inteiros com pelo menos 32 bits

float

ponto flutuante com pelo menos 6 digitos de precisão na base 10

double

ponto flutuante com pelo menos 10 dígitos de precisão na base 10

long double

ponto flutuante com extensão (pelo menos 10 dígitos)

A linguagem C++ permite um trecho de programa como este:

bool b;
b=true;
if(b) cout<<”b e’ verdadeiro”<<endl;

ou ainda

bool aceitar(){
     cout<<"Voce deseja prosseguir?(s/n)\n";
     char resposta=0;
     cin>>resposta;
     return resposta=='s';
}

A linguagem C++ define tipos apropriados para as variáveis prédefinidas cout e cin (cout e cin de certo modo equivalem ao stdout e stdin da linguagem C; deve ser observado que a “ênfase” em C é nas funções printf() e scanf() e os objetos destino e origem stdin/stdout ficam implícitos a estas funções, a ênfase em C++ desloca-se para os objetos cout e cin e as dezenas/centenas de funções operator<<() ficam obnubiladas como simples operadores). Os tipos de cout e cin redefinem os operadores << e >> como (“inserir no fluxo” e “extrair do fluxo”) ou (“enviar” e “obter”) ou (“stream insertion” and “stream extraction”) respectivamente. As variáveis (os objetos) cin e cout correspondem aos fluxos padrão de entrada e saída respectivamente e serão vistos mais à frente na disciplina.

A linguagem C++ introduz o tipo bool como sendo “aritmético” (existe o tipo “booleano”, mas o falso corresponde ao int 0 e true corresponde ao int 1) e utiliza as operações lógicas e aritméticas que já eram definidas em C (além de vários novos aspectos).

Operadores aritméticos:
 +(unário), +(binário), -(unário), -(binário), *, /, %

Operadores de comparação
==, !=, <, >, <=, >=

Nas atribuições e operações aritméticas, C++ faz  conversões entre os tipos básicos e portanto eles podem ser misturados de forma livre.

 

-------

Escopo, espaço de nomes e operador de resolução de escopo

O espaço de nomes em C se reduz “grosso modo” ao escopo global (fora das funções) e escopo local (dentro das funções) [se desconsiderarmos que o espaço de nomes de cada arquivo pode ser explorado via definições globais com o modificador “static”]. A linguagem C++ possui diretivas de definição (namespace <nome>)  e uso (p.ex. using namespace <nome>) de escopos de nomes. Não vamos discutir todas as características destas diretivas, mas vamos ver como funciona o conceito. Em um programa C++ podemos definir variáveis em um escopo similar ao escopo global e local de C. Mas podemos definir novos escopos. Uma vez definido um certo escopo podemos identificá-lo usando o operador de resolução de escopo “dois pontos dois pontos”.

#include <cstdlib>
#include <iostream>

using namespace std;

namespace A{ int i=10;}
namespace B{ int i=20;}

int main(int argc, char *argv[]){
    cout<<A::i<<endl;
    cout<<B::i<<endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}
No programa acima é definido um espaço de nomes identificado por A e um espaço de nomes identificado por B. Os espaços A e B definem duas variáveis distintas mas com mesmo nome. Na função main() distinguimos as duas variáveis usando o operador de resolução de escopo. A linguagem C++ permite ainda um espaço de nomes “unnamed” definido via omissão do nome do espaço! A diretiva “using namespace <nome>” instrui o compilador a tentar resolver a identificação de nomes através de escopos. Compare o programa acima com este programa:
#include <cstdlib>
#include <iostream>


using namespace std;

namespace A{ int i=10;}
namespace B{ int i=20;}

int main(int argc, char *argv[]){
    using namespace B;
    cout<<A::i<<endl;
    cout<<i<<endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}
O uso da diretiva “using namespace” também obedece questões de escopo, seu uso no escopo global ou no escopo de definição de um espaço de nomes é diferente do uso no escopo local. A definição de escopo de nomes pode ser aninhada, conforme programa abaixo.

#include <cstdlib>
#include <iostream>

using namespace std;

namespace A{
  int i=10;
  namespace X{ int i=30;} //aninhamento
}
namespace B{ int i=20;}

int main(int argc, char *argv[]){
    using namespace B;
    cout<<A::i<<endl;
    cout<<i<<endl;
    cout<<A::X::i<<endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}

Em função de todos estes aspectos os identificadores em programas C++ são mais estruturados do que em programas C. Boa parte da biblioteca padrão do C++ (p.ex. iostream) é definida com base no espaço de nomes std.

-----

Cadeias de caracteres: arranjos/ponteiros, strings etc.

A linguagem C++ define uma classe padrão “string”, além disso os projetistas não removeram de C++ nenhuma das caracteristicas da linguagem C relacionadas ao tratamento de arranjos de caracteres. A linguagem C++ não definiu novos literais para strings e, portanto, o único literal para string em C++ continua correspondendo a uma cadeia de caracteres terminada com zero/nulo apesar de ter havido uma certa “briga” relacionada a um literal string corresponder ao tipo “const char *” ou ao tipo “char *”. O uso de strings no estilo da linguagem C corresponde a inclusão do cabecalho <cstring>(strlen, strcpy, strcmp, etc).

A linguagem C++ define um tipo (class)  string baseada em uma classe definida via “templates”(“template” é visto no material mais à frente da disciplina). Este uso corresponde a inclusão do cabeçalho <string>. A classe string é baseada no uso de caracteres do tipo char e a classe wstring é baseada no uso de caracteres do tipo wchar_t.

As instâncias de string em C++ são objetos “dinâmicos” no seguinte sentido: uma vez instanciado este objeto pode conter uma cadeia que pode ser modificada. Podemos inserir, trocar, remover caracteres da cadeia de caracteres correspondente à instância.

A classe string “sobrecarrega”os seguintes operadores: = + += == != < <= > >= [] << >> e podemos usá-los de forma razoavelmente intuitiva. A sobrecarga permite o uso de == != < <= > >= ao invés de usar uma função que corresponderia à strcmp das cadeias no estilo da linguagem C.

---

Ponteiros, referências, etc

Além dos ponteiros a linguagem C++ define “referências” (será visto mais a frente). A recomendação é não usar alocação dinâmica via malloc() & free(), C++ define os operadores new e delete (e também new[] e delete[]). O operador new é polimórfico e devolve um ponteiro do mesmo tipo do argumento. Veja estes exemplos:

int *pini=new int; //não inicializado

int *pii=new int(10); //int inicializado com valor 10

double *pd=new double(3.14);

delete pini; // torna disponivel a area que estava sendo apontada por pini

int *pai=new int[10];

delete [] pai; // operador delete[] considera o arranjo

endereços mais ao final da memória

Sistema; código e dados diversos

Registros de ativação (PILHA) vvvvvvvvvvvvvvvvv

Espaço da PILHA vvvvvvvvvvvvvvvvvvvvvvvvvv

 

Código das funções de código ligado dinâmicamente

 

Espaço do HEAP ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Dados HEAP [malloc calloc new etc ]^^^^^^^^

Dados não inicializados

Dados inicializados

Código da função main() e outras funções estáticas

Sistema; código e dados diversos

Endereços no início da memória

Uma figura melhor definida pode ser conseguida na Internet, p.ex. www.linuxjournal.com

Parâmetros default (default parameters)

A linguagem C++ permite na hora de *definir* uma função que os parâmetros tenham valores default. Na invocação das funções o programador pode omitir a expressão correspondente ao parâmetro e será então usado o valor default. Observe que a ordem dos parâmetros é importante porque não há como expressar, na invocação, que deseja-se utilizar apenas o valor default de um parâmetro específico, além disso se um parâmetro tem valor default na definição então todos os parâmetros que o seguem devem ter valor default.

Observe a seguinte definição de função:

void f(int p1=10, int p2=20, int p3=30){
     cout<<p1<<" "<<p2<<endl;
}

podemos invocar esta função sem especificar expressões para os parâmetros:

....f();//equivale a f(10,20,30)
    f(40); // f(40, 20, 30)
    f(50,100); //f(50,100, 30)

 

A linguagem C++ permite um estilo de inicialização idiomaticamente diferente da linguagem C:

Uma definição e inicialização em C como esta

int var=10;

pode ser escrita em C++ desta forma:

int var(10);

 

 

Exercícios

 

reescrever alguns dos programas em C já discutidos em aula para utilizar recursos da linguagem C++ (por exemplo bool).