Salta al contenuto
Note per Studenti Esercizio - Quiz su inizializzazione, free store, polimorfismo e passaggio di argomenti (quarto appello 2022-23, quiz)

Esercizio - Quiz su inizializzazione, free store, polimorfismo e passaggio di argomenti (quarto appello 2022-23, quiz)

Esame

Quiz del quarto appello del corso di Laboratorio di Programmazione, Ing. Informatica UniPD, a.a. 2022-23 (90 minuti): dieci domande a quattro affermazioni vero/falso (giusta +1+1, sbagliata −0.25-0.25, non data 00). Gli altri esercizi dell'appello: Esercizio - Costruttore di default, di copia e di spostamento (quarto appello 2022-23, teoria), Esercizio - Template di classe (quarto appello 2022-23, teoria), Esercizio - Aritmetica dei puntatori (quarto appello 2022-23, teoria), Esercizio - Array dinamico ordinato come classe template (quarto appello 2022-23, programmazione).

1) L'istruzione C++11 double vector{7};

affermazione V/F motivo
Dà errore in fase di compilazione in quanto 7 non è rappresentato in forma decimale F 7 si converte esattamente a double
Inizializza la variabile vector con il valore 7 V vector == 7.0: vector è soltanto il nome di una variabile double
Crea un vettore di lunghezza 7 F non è std::vector: il tipo è double e le graffe sono l'inizializzatore
Crea una variabile vector costante F manca const

2) Il Free store:

affermazione V/F motivo
È un'area di memoria in cui vengono memorizzate le variabili libere di un programma F non esistono "variabili libere": locali nello stack, globali nei dati
È un'area di memoria gestibile utilizzando new e delete V
Rappresenta un'area di memoria utilizzata durante l'allocazione dinamica della memoria V
Contiene memoria che deve essere deallocata per evitare fenomeni di memory leak V la memoria presa con new va restituita con delete/delete[]. Vedi C++ - allocazione dinamica della memoria con new e deletela memoria dinamica si prende dal free store (heap) con new, che restituisce un puntatore all'oggetto o al primo elemento dell'array (new T, new T[n], con inizializzatori {}), e si restituisce con delete (per new) e delete[] (per new[]); il puntatore non sa quanti elementi ci sono, la memoria non ha nome e non ha controllo dei limiti; in C++ non c'è garbage collector (scelta per efficienza e controllo), quindi un'area che perde ogni riferimento diventa garbage e produce un memory leak, e gli errori tipici sono la doppia cancellazione e il dangling pointer (si mette il puntatore a nullptr dopo delete); il distruttore ~T() è chiamato dal compilatore quando l'oggetto esce dallo scope o con delete e libera le risorse, per cui una classe come vector acquisisce la memoria nel costruttore e la rilascia nel distruttore; new alloca e chiama il costruttore, delete chiama il distruttore e libera; operator[] con due versioni (T& e const) dà accesso in lettura e scrittura al buffer; si alloca dinamicamente solo se serve davvero (memoria grande, dimensione nota a run-time, durata controllata).C++ - allocazione dinamica della memoria con new e delete →

3) Il polimorfismo dinamico in C++:

4) I tipi che implementano una deep copy in C++:

affermazione V/F motivo
Copiano i dati senza copiare i puntatori e le reference V duplicano i dati puntati invece dell'indirizzo
Permettono la copia dei soli membri pubblici della classe F si copia tutto lo stato
Hanno un tempo di esecuzione più lento rispetto ai tipi che implementano la shallow copy V si duplicano i dati
Devono definire in modo esplicito il costruttore di copia e l'assegnamento in caso utilizzino una allocazione dinamica della memoria V quelli generati copiano il puntatore. Vedi C++ - costruttori, inizializzazione, copia e spostamentouna buona interfaccia è completa e minimale, ha i costruttori giusti, supporta o proibisce la copia, usa tipi adeguati, dichiara const ciò che non modifica e libera le risorse nel distruttore; il costruttore di default (senza argomenti) esiste quando c'è un valore iniziale sensato, si ottiene con valori di default nei membri (in-class initializer) o argomenti di default, e explicit vieta la conversione implicita da un argomento; initializer_list permette la lista {a, b, c} e con le graffe ha la precedenza sul costruttore a un intero (vector{3} è un elemento, vector(3) sono tre); la copia di default è membro a membro (shallow copy) e con un puntatore a un buffer duplica il puntatore invece del contenuto (due oggetti che condividono i dati e una doppia delete), quindi servono il costruttore di copia e l'assegnamento di copia che fanno la deep copy (l'assegnamento prima alloca e copia, poi libera i vecchi dati, e deve tollerare l'auto-assegnamento); this è il puntatore all'oggetto corrente, parametro implicito delle funzioni membro; il costruttore e l'assegnamento di spostamento (T&&, rvalue reference) rubano il buffer della sorgente lasciandola vuota ma valida, il compilatore li sceglie da solo (con la copy elision a volte non servono nemmeno); i cinque (distruttore, copia, spostamento) vanno definiti insieme.C++ - costruttori, inizializzazione, copia e spostamento →

5) Un function object in C++:

affermazione V/F motivo
È implementato tramite una classe che deve contenere l'overloading dell'operatore [] F serve operator()
Permette di esprimere un predicato da fornire a un algoritmo STL V find_if(..., Larger_than(31))
È una funzione in cui è definito un oggetto F è un oggetto utilizzabile come una funzione
Prevede che sia inibito il costruttore di copia F viene passato per copia. Vedi STL - algoritmi, predicati e lambdaLa STL ha circa 80 algoritmi generici (header e ) che lavorano su sequenze [first, last) di qualunque contenitore tramite iteratori: find/find_if, count/count_if, sort, copy/copy_if/unique_copy, merge, equal_range, equal, accumulate, inner_product. find cerca un valore e restituisce l'iteratore alla prima occorrenza o end(); find_if verifica un predicato (funzione che restituisce bool) passato come puntatore a funzione, function object (classe con operator()) o lambda expression. Una variabile globale per passare la soglia al predicato è da evitare; il function object la memorizza in un dato membro. Una lambda cattura -> tipo { corpo } definisce la funzione sul posto e può catturare le variabili locali per valore ([=], [x]) o per riferimento ([&], [&x]). sort(b, e) ordina con < e sort(b, e, cmp) con un criterio, solo su iteratori random access; copy richiede spazio a destinazione.STL - algoritmi, predicati e lambda →

6) Una funzione che implementa l'overloading di un operatore:

affermazione V/F motivo
È implementabile solo per gli UDT V almeno un operando UDT
È introdotta dalla keyword explicit F si chiama operator+ ecc.; explicit è dei costruttori
Se non implementata dal programmatore, è generata automaticamente dal compilatore se l'operatore è utilizzato nel codice F
Se implementata per un tipo built-in, permette di modificare il numero di operandi rispetto all'uso standard dell'operatore F non si possono ridefinire gli operatori per i soli built-in, né cambiarne l'arità. Vedi C++ - enumerazioni e overloading degli operatoriun'enumerazione è un tipo definito dall'utente che specifica un insieme di valori simbolici (enumeratori); enum class crea uno scope (Month::jan), non si converte implicitamente in int, e i valori partono da 0 o dal valore del precedente più uno; l'overloading degli operatori permette di definire gli operatori esistenti per tipi definiti dall'utente (almeno un operando UDT, stesso numero di operandi e stessa sintassi) scrivendo una funzione di nome operator+, operator++, operator[], ...; si può fare con una helper function (due argomenti per un operatore binario, il primo può essere di qualunque tipo) o con una funzione membro (un argomento in meno, perché il primo operando è l'oggetto, quindi l'UDT deve stare a sinistra: T + int sì, int + T no); << e >> sono helper function che prendono e restituiscono ostream& e istream&; ++ ha due forme (pre e post, quest'ultima con un int fittizio); gli operatori più utili sono =, ==, !=, <, [], ().C++ - enumerazioni e overloading degli operatori →

7) Le librerie dinamiche:

affermazione V/F motivo
Permettono di generare eseguibili self-contained F lo sono le statiche
Possono non essere nel sistema in fase di compilazione/linking di un sorgente che le linka V nel modello del corso il codice della libreria non viene copiato nell'eseguibile: serve a run-time, quando il caricatore la cerca. (In pratica con GNU ld il file .so o uno stub serve comunque al momento del link; la risposta attesa è quella del modello)
Permettono di generare eseguibili di dimensioni ridotte rispetto alle librerie statiche V la libreria non è dentro l'eseguibile
Possono essere usate solo da un eseguibile alla volta F sono condivise: una sola copia per tutti. Vedi C++ - scope, namespace e librerielo scope è la regione di testo in cui un nome è valido, dal punto di dichiarazione alla fine dello scope; i tipi di scope sono globale (di file), di classe, locale (blocco o lista argomenti di una funzione), di statement (for) e di namespace; funzioni dentro funzioni sono illegali, le classi locali da evitare; i namespace raggruppano nomi senza creare un tipo e si accedono col nome qualificato namespace::membro oppure con using (la using directive va evitata negli header); un header file è un insieme di dichiarazioni che chi lo include può usare (<> per i file di sistema, "" per quelli del progetto), dev'essere protetto dalle include guard (#ifndef/#define/#endif) per non includere due volte le stesse definizioni; il linker unisce i file oggetto con le librerie, statiche (copiate in ogni eseguibile, self-contained) o dinamiche (una sola copia condivisa, .so su Linux, .dll su Windows, ricompilabili senza toccare gli eseguibili).C++ - scope, namespace e librerie →

8) Una variabile locale automatica definita in una funzione f:

9) Il passaggio di argomenti a una funzione:

10) std::vector<int> vi(10); std::vector<int> vi2 {1, 2, 3, 4}; const int vi3 {9};

Versione ripasso

Teoria collegata