Piccolo Cheat Sheet per il C++ parte 7

  • I/O su file

void ifstream::open(NOME_FILE, modalità) // modalità default = ios::in

void ofstream::open(NOME_FILE, modalità) // modalità default = ios:out

void fstream::open(NOME_FILE, modalità) // modalità default = ios::in|ios::out

es:

ofstream out;

out.open("pippo");

// la stessa cosa si può fare banalmente con:

ofstream out("pippo");

out.close();

  • file binari

istream &read(char *buf, streamsize num);

ostream &write(const char *buf, streamsize num);

buf deve essere definito come array di caratteri, oppure (ad esempio se buf è una struttura, deve essere castato esplicitamente ad array di caratteri (unsigned char *)

  • funzioni varie

bool eof(); // in.eof()

istream &ignore(streamsize num=1, int_type delim=EOF); // leggere ed ignora num caratteri

int_t peek(); // restituisce il prossimo carattere dello stream, senza rimuoverlo dallo stream stesso

istream &putback(char c); // rimette nello stream l'ultimo carattere letto

ostream &flush();

istream &seekg(off_type offset, seekdir origine); // sposta il puntatore di lettura del file

ostram &seekp(off_type offset, seekdir origine); // sposta il puntatore di scrittura del file

origine = ios::beg (inizio), ios::cur (corrente), ios::end (fine)

pos_type tellg(); // posizione puntatore in lettura

pos_type tellp(); // posizione puntatore in scrittura

  • Identificazione runtime dei tipi (RTTI)

type_info typeid(oggetto); // restituisce un oggetto di tipo type_info che rappresenta il tipo del parametro, funziona anche con classi template (restituisce tipo1<tipoA>)

typeid(oggetto).name(); // restituisce il nome del tipo dell'oggetto

  • operazioni di cast

dynamic_cast<tipo_destinazione> (espressione) // effettua il cast a runtime verificando se è possibile. tipo_destinazione deve essere un puntatore o un indirizzo, così come "elemento", se non ha successo restituisce null (nel caso di puntatori) o solleva una bad_cast (nel caso di indirizzi)

dynamic_cast<tipo1<tipoA> (espressione) // per classi template

const_cast<tipo> (espressione) // permette di rimuovere "const" e/o "volatile" dalla dichiarazione di una variabile (quindi ad esempio posso modificare una variabile dichiarata con const), tipo deve essere lo stesso di espressione a meno di "const" e/o "volatile"

static_cast<tipo> (espressione) // è la stessa cosa del cast vecchio stile (alla "C"), non effettua verifiche

reinterpret_cast<tipo> (espressione) // converte un tipo in uno completamente scorrelato, può fare grossi danni

  • namespace

namespace NOME {

...

}

NOME::var;

nome::oggetto;

using NOME;

using NOME::var; // solo var diventa visibile senza riportare il nome del namespace

namespace { // namespace senza nome, serve per rendere invisibili all'esterno gli oggetti/variabili/funzioni qui definiti

int k;

}

è possibile dichiarare più volte un namespace, l’unione di queste dichiarazioni rappresenterà il namespace complessivo

using namespace std; // namespace per libreria standard

  • overloading di conversioni a tipi

è possibile dichiarare delle funzioni di conversione tra tipi ad-hoc

class tipo {

int i;

operator int() {return i}

...

}

tipo oggetto;

int j;

j = oggetto; // converto oggetto in int, quindi j = i

  • funzioni const e mutable

una funzione dichiarata con const non permette le modifiche a this (quindi all’oggetto che la contiene)

int f() const;

f() {

...

}

se dichiaro qualche attributo "mutable" allora posso modificarlo anche all’interno di funzioni const

mutable i;

int f() const {

i = X;

}

  • funzioni membro volatile

rendono this un puntatore volatile

void f() volatile;

  • costruttore esplicito

class myclass {

...

explicit myclass(int i);

...

}

myclass b(5); //ok

myclass b = 5; //errore

  • specifiche di linking

extern "linguaggio" prototipo_funzione;

extern "C" prototipo_funzione; // link della funzione secondo regole C

  • I/O su array

come con sprintf e sscanf

ostrstream ostr(char *buf, streamsize size, openmode mode=iso::out);

char str[80];

ostrstream outs(str, sizeof(str));

outs << "pippo";

istrstream istr(const char *buf);

istrstream ins(str);

ins >> var;

strstream iostr(char *buf, streamsize size, openmode mode=ios::in!ios::out);

ostrstream(); // per lunghezza variabile

ostrstream ostr;

char *p;

ostr << "pippo" << endl;

p = ostr.str(); // restituisce l'array;

Annunci

Piccolo Cheat Sheet per il C++ parte 6

Sesta parte del Cheat Sheet per C++

  • stream predefiniti

cin, cout, cerr e clog (versione bufferizzata di cerr)

  • manipolatori per formattazione I/O

cout << manipolatore << valore;

boolalpha, noboolalpha // è possibile utilizzare "true" e "false" come valori booleani (sia in input che in output)

dec // base decimale

endl // fine riga e svuotamento stream

ends // fine stringa ''

fixed // notazione normale per numeri in virgola mobile

flush // svuotamento stream

hex // base 16

internal // spazi riempitivi inseriti fra il segno o il carattere di base

left // allineamento a sinistra

showpoint, noshowpoint // visualizza il punto e i decimali anche se non servono

showbase, noshowbase // visualizza base numerica

showpos, noshowpos // visualizza '+' davanti ai numeri positivi

skipws, noskipws // gli spazi bianchi, le tabulazioni e le fine riga iniziali vengono eliminati dall'input

unitbuf, nounitbuf // buffer svuotato ad ogni operazione

uppercase, nouppercase // scrive tutto maiuscolo

oct // base 8

right // allineamento a destra

scientific // notazione scientifica

setbase(int) // imposta la base

setfill(int) // imposta il carattere riempitivo

setprecision(int) // imposta precisione decimale

setw(int) // imposta ampiezza campo

ws // salta gli spazi bianchi iniziali

  • overload di >> e <<

funzioni esterne alle classi

ostream &operator<<(ostream &stream, tipo_classe oggetto)

{

...

stream << oggetto.field << endl;

...

return stream;

}

istream &operator>>(istream &stream, tipo_classe &oggetto)

{

...

stream >> oggetto.field;

...

return stream;

}

  • manipolatori ad-hoc

funzioni esterne alle classi

ostream &manip(ostream &stream)

{

// codice

return stream;

}

istream &manip(istream &stream)

{

// codice

return stream;

}

Piccolo Cheat Sheet per il C++ parte 5

Quinta parte del Cheat Sheet per C++

  • funzioni template

template <class tipo1, class tipo2,...>

tipo_restituito func(parametri)

{

... // posso usare "tipo1,2" quando devo dichiarare una variabile di tipo parametrico (anche nella lista dei parametri o nel tipo restituito)

}

se ho una funzione template ed una con overloading espicito, quest’ultima ha la precedenza nel caso di invocazione con i tipi per i quali c’è l’overloading.

  • classi template

template <class tipo1, class tipo2,...>

class nomeClasse {

...

}

// per dichiarare un oggetto

nomeClasse<tipo1, tipo2,...> oggetto;

è possibile definire degli argomenti standard nel template

template <class tipo1=int>...

è anche possibile dichiarare degli argomenti non tipi:

template <class tipo1, int size>

... // uso size come una normale variabile

nomeClasse<double, 10> oggetto; //tutte le ricorrenze di size nella classe verranno sostituite con 10

è possibile specificare esplicitamente una classe template:

template <class tipo1, class tipo2,...>

class nomeClasse {

...

}

...

template <>

class nomeClasse<int, double,...> {

...

}

  • typename e export

typename può sostituire class all’interno del template <class tipo> == <typename tipo>

typename assicura che un nome utilizzato in un template si riferisca ad un tipo e non ad un nome di un oggetto (vedi sopra l’esempio con size)

export precede la dichiarazione di un template e consente agli altri file di usare il template senza dover duplicare tutta la definizione, ma indicando solo la dichiarazione.

  • Gestione eccezioni

try {

...

throw valore_tipo_eccezione //es: throw 100

} catch(tipo eccezione_var) { //es: int i

...

}

il tipo di una eccezione può essere sia un tipo base, sia un tipo definito dall’utente (una classe)

non serve il new nella clausola throw

un catch con un tipo base cattura anche eccezioni di tipo derivato

catch(...) // cattura eccezioni di qualsiasi tipo

tipo func(par) throw(tipo1, tipo2, tipo3) // questa funzione può lanciare solo eccezioni di tipo1, tipo2 o tipo3

{

...

}

tipo func(par) throw() // questa funzione non può lanciare eccezioni

{

...

}

se voglio propagare un’eccezione catturata in un catch verso l’esterno:

catch(tipo eccezione) {

...

throw; // rilancia un'eccezione pari a *eccezione*

}

  • terminate() e unexpected()

serve l’header <exception>

terminate() viene richiamata quando nel programma non è presente un catch() in grado di catturare l’eccezione lanciata (normalmente richiama abort())

unexpected() viene lanciata quando una funzione tenta di lanciare non consentita a causa del costrutto throw(tipo1, tipo2, tipo3). Normalmente richiama terminate()

è possibile associare delle nuove funzioni a queste due funzioni attraverso i seguenti metodi:

terminate_handler set_terminate(terminate_handler newhandler) throw(); // typedef void (*terminate_handler)();

unexpected_handler set_unexpected(unexpected_handler newhandler) throw(); // typedef void (*unexpected_handler)();

  • uncaught_exception()

bool uncaught_exception();

restituisce true se è stata lanciata un’eccezione non ancora catturata

  • classe exception e bad_exception

la libreria standard dichiara la classe base exception, il gestore di unexpected() può lanciare un’eccezione di tipo bad_exception

Piccolo Cheat Sheet per il C++ parte 4

Quarta parte del Cheat Sheet per il C++

  • Ereditarietà

class classe-derivata: accesso classe-base {

...

}

se non metto il tipo di accesso:

  • se derivata è una class -> private
  • se derivata è una struct -> public

se public -> tutti i membri public della base sono public anche nella derivata (idem per i membri protected, restano protected), i membri private restano private e quindi non sono accessibili alla classe derivata

se private -> tutti i membri public e protected della base diventano private della derivata

se protected -> (accessibili solo a classe base e sue derivate) tutti i membri public e protected di base diventano protected in derivata

  • Ereditarietà multipla

class derivata: public base1, private base2 {

...

}

  • Passaggio di parametri al costruttore della classe base

costruttore-derivato(par1, par2, par3): costruttore-base1(par1, par2), costruttore-base2(par2)

{

...

}

  • Ripristino livelli di accesso originali

se ho una classe derivata con private su base, ma voglio mantenere public alcuni membri che in base erano public, posso usare la seguente "dichiarazione" nella classe derivata:

base::membro; // se membro è una funzione non metto () ma lascio solo il nome (come se fosse una variabile), non dichiaro nessun tipo.

ovviamente non si può alzare la visibilità di un membro (se è private nella base non posso renderlo public nella derivata)

  • classi base Virtuali

utilizzate per risolvere ambiguità con ereditarietà multipla (in caso di base, der1 e der2 che ereditano da base e der3 che eredità da der1 e der2), se dichiaro una classe virtuale nelle derivate non ho una duplicazione dei membri di base, ma ho una sola copia di tali membri.

class base {

public:

int x;

}

class der1: virtual public base {

...

}

class der2: virtual public base {

...

}

class der3: public der1, public der2 {

...

}

  • funzioni virtuali

funzione membro dichiarata come virtual in base (con tanto di implementazione) e ridefinita in derivata. se accedo a questa funzione tramite puntatore si applica polimorfismo a run-time, ovvero a run-time calcolo il tipo corrente dell’oggetto puntato ed invoco la funzione giusta (quella base oppure quella derivata)

class base {

virtual tipo func() {

...

}

...

}

il polimorfismo a run-time si applica anche utilizzando gli indirizzi invece dei puntatori.

se ho una funzione virtual in base, e der1 eredita da base, in der1 la funzione è ancora virtual, quindi se der2 eredita da der1 la funzione sarà ancora virtual.

  • funzioni virtuali pure

virtual tipo func() = 0;

in questo modo in base non è presente una implementazione della funzione, quindi tutte le classi derivate dovranno fornire un’implementazione, altrimenti ho un errore in compilazione.

se una classe contiene almeno una funzione virtuale pura -> classe astratta

Piccolo Cheat Sheet per il C++ parte 3

Terza parte del Cheat Sheet per il C++

  • Costruttori di copie

classname(const classname &ob) {

...

}

viene invocato solamente nelle inizializzazioni:

myclass x = y; // inizializzazione esplicita

func(y); // crea un oggetto temporaneo inizializzandolo a y

y = func(); // crea un oggetto temporaneo utilizzato per inizializzare y

x = y; // non è un'inizializzazione, quindi non richiama il costruttore di copie

  • Puntatori a funzioni con overloading

se non c’è overloading si può fare:

p = func;

con overloading è necessario esplicitare i parametri:

int func(int a);

int func(int a, int b);

int main()

{

int (*p)(int a);

p = func; // punta a func con un solo parametro

  • Argomenti standard delle funzioni

È possibile dichiarare parametri con valori di default nelle funzioni:

void func(int x=0)

{

...

}

...

func(5);

func();

i parametri standard devono essere specificati una sola volta (ad es. solo nel prototipo), i parametri standard devono comparire alla fine della lista dei parametri.

  • Overloading degli operatori

tipo nomeclasse::operator#(elenco-argomenti) // # va sostituito con l'operatore che si vuole ridefinire

{

...

}

se l’operatore è unario non ci sono parametri (l’unico parametro è l’oggetto stesso, quindi this), se l’operatore è binario c’è un solo parametro (l’operando di destra, quello di sinistra è l’oggetto stesso, this)

forme prefisse e postfisse di ++ e —

tipo operator++(); // per ++var

tipo operator++(tipo o); // per var++

è possibile anche fare l’overloading delle forme abbreviate +=, -=, ecc. ecc.

le funzioni operator#() non possono avere argomenti standard e non si può fare l’overloading di ., ::, .*, ?

tranne che per =, le funzioni operator#() sono ereditate dalle classi derivate

  • Overloading degli operatori tramite funzioni friend

in questo modo si devono esplicitare tutti gli operandi (in ordine da sinistra a destra), non è possibile per =, [], () e ->

per ++ e -- si deve utilizzare un parametro indirizzo, per le forme postfisse si usa un parametro aggiuntivo intero fittizio:

friend tipo operator++(tipo &ob, int x);

con le funzioni operator friend c’è maggiore flessibilità, ad esempio:

ob + 100; // ok sia con funzioni membro che con funzioni friend (il secondo parametro, o il primo nel caso di funzione membro, è dichiarato di tipo int)

100 + ob; // ok per le funzioni membro (basta dichiarare una versione con (int a, tipo b)), non è possibile con le funzioni membro perchè int è un tipo predefinito, quindi non posso ridefinire + per accettare il secondo operando del tipo di ob

  • Overloading di new e delete

È possibile fare l’overloading:

void *operator new(size_t size);

void operator delete(void *p);

void *operator new[](size_t size); // per array

void operator delete[](void *p); // per array

  • Overloading di []

tipo nomeclasse::operator[](int i); // per forza funzione membro

  • Overloading di ()

utilizzata per creare una funzione che accette un certo numero di parametri:

...

tipo nomeclasse::operator()(int a, double b, mclass c);

...

int main()

{

myclass x;

nomeclasse ob1, ob2(10, 3.4, x); //per ob2 chiamo il costruttore, perchè è la forma abbreviata di ob2 = nomeclasse(10, 3.4, x) utilizzata per l'inizializzazione

ob1(2,23.2,x); // equivale a ob1.operator()(2,23.2,x), non chiama il costruttore

  • Overloading di , (virgola)

in C++ è possibile fare l’overloading anche della virgola, operatore binario (il comportamento standard è quello di considerare in ordine i suoi operandi) es:

a = (b,c); // prima assegna b ad a, poi assegna c ad a

Piccolo Cheat Sheet per il C++ parte 2

Seconda parte del Cheat Sheet per il C++

  • Array di oggetti

myclass a[3]; //possibile solo se myclass dichiara un costruttore vuoto

myclass a[] = {1,3,5}; // possibile solo se il costruttore riceve un solo parametro, in questo caso un int, non è necessario specificare la dimensione

myclass a[] = { myclass(1, 2), myclass(2,4), myclass(4,8)}; // se il costruttore ha più di un parametro, in questo caso 2, non è necessario specificare la dimensione

  • Puntatori a oggetti

myclass *p, ob;

p = &ob;

p->funzione(par); // equivale a ob.funzione(par);

this // è un puntatore, quindi si usa ->

i puntatori a classi base non possono richiamare elementi delle classi derivate (cfr. in Java)

  • puntatori a un membro di una classe

in C++ è possibile definire un puntatore a un membro di una classe (il membro, che può essere una funzione o un attributo, deve essere public):

class myclass {

public:

int val;

int f();

}

int main()

{

int myclass::*data;

int (myclass::*func)();

myclass ob;

data = &myclass::val;

func = &myclass::f;

cout << ob.*data;

cout << (ob.*func)();

}

si può accedere ai puntatori a membro solamente con gli operatori speciali .* (vedi sopra) e ->* (se invece di usare ob, si usava un puntatore a un oggetto myclass)

  • Indirizzi

evoluzione del concetto di puntatore, molto più comodi per il passaggio di parametri (appunto) per indirizzo (sono puntatori "impliciti")

int f(int &par)

{

par = 5;

}

int main()

{

int x = 2;

x = f(x); // adesso x vale 5

}

le funzioni possono anche ritornare indirizzi:

char array[10] = "pippo pluto";

char &f(int x)

{

return array[x]; // restituisce l'indirizzo del x-esimo carattere dell'array

}

int main()

{

f(5) = "&"; // assegna "&" all'indirizzo restituito da f

cout << array; // stampa pippo&pluto

}

l’indirizzo ritornato non deve far riferimento ad un oggetto interno alla funzione stessa, altrimenti si restituisce un indirizzo non consistente

un indirizzo di una classe base può essere utilizzato anche per oggetti di classi derivate (come per i puntatori)

  • indirizzi indipendenti

sono variabili dichiarate come indirizzi, in pratica permettono la definizione di un alias di un’altra variabile

int main()

{

int a;

int &ind = a; // ora ind e a sono due nomi che indicano la stessa variabile

}

  • NB

int* a, b; // a è un puntatore a un intero, ma b no, b è un intero

int *a, b; // equivalente a quella sopra ma decisamente più chiara

  • Operatori di allocazione dinamica della memoria

new permette di allocare dinamicamente la memoria:

tipo *a = new tipo;

int *x = new int;

myclass *x = new myclass;

inizializzazione

int *x = new int (5);

allocazione array

tipo *p = new tipo[dim]

allocazione di oggetti

myclass *p = new myclass; // se myclass non definisce un costruttore

myclass *p = new myclass(par1, par2); // se myclass ha un costruttore con eventuali parametri

myclass *p = new myclass[3]; // array di 3 elementi, myclass DEVE avere un costruttore senza parametri, non si può inizializzare un array creato con new

new si usa all’interno di un try-catch:

try {

p =new myclass(a, b);

} catch(bad_alloc xa) {

cout << "errore";

}

delete permette la cancellazione (come free(p)):

delete p; // per variabili singole

delete[] p; // per array

se non si vuole usare il try-catch per new si può usare la seguente forma:

p = new(nothrow) tipo(a,b);

if(!p) { // se la new fallisce p==null

cout << "errore";

}