Esercizio - Quiz su scope, riferimenti, conversioni e puntatori (secondo appello 2022-23, quiz)
Quiz del secondo appello del corso di Laboratorio di Programmazione, Ing. Informatica UniPD, a.a. 2022-23 (90 minuti): dieci domande a quattro affermazioni vero/falso (giusta , sbagliata , non data ). Gli altri esercizi dell'appello: Esercizio - Polimorfismo statico e dinamico, bounded e unbounded (secondo appello 2022-23, teoria), Esercizio - Design pattern, elementi e classificazione (secondo appello 2022-23, teoria), Esercizio - Passaggio per copia e per riferimento con schema dello stack (secondo appello 2022-23, teoria), Esercizio - La classe template DynamicStorage (secondo appello 2022-23, programmazione).
1) Lo scope in C++:
2) Il passaggio per riferimento di una variabile a una funzione:
3) Le conversioni fra variabili di tipo diverso in C++11:
4) Il record di attivazione:
5) Il layout della memoria di un programma in esecuzione:
| affermazione | V/F | motivo |
|---|---|---|
| L'uninitialized data segment contiene variabili globali e statiche non inizializzate | V | il BSS |
| L'heap viene gestito dal programmatore tramite allocazione dinamica della memoria | V | new/delete |
| L'initialized data segment è solo read-only | F | lettura/scrittura (può avere una parte read-only) |
| Il text segment contiene unicamente il nome dell'eseguibile | F | contiene le istruzioni eseguibili |
6) La funzione STL std::find():
7) Il costruttore e l'assegnamento di spostamento di una classe:
8) Oggetto b di una classe base B e oggetto d di una sua derivata D:
9) La funzione template template<typename T> bool AreEqual(const T& a, const T& b) { return a == b; }:
10) Il codice int x{3}, y{8}; int *p = &x; int *q = &y; p = &y; *p = *q; (righe 1-5):
Versione ripasso
- 1 Scope: V, F, V, F. Regione di testo; funzioni nello scope di classe lecite; blocco scope locale; le include guard non definiscono scope. 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 →.
- 2 Passaggio per riferimento: V, V, F, V.
&nel parametro, niente copia, non identico al puntatore, lettura e scrittura. C++ - funzioni, reference e passaggio dei parametriuna funzione ha parametri formali (0 o più, input) e un valore di ritorno (0 o 1, output); in C++ gli argomenti si passano per valore, cioè il parametro è una variabile locale inizializzata con una copia dell'argomento, e modificarla non cambia l'originale; per oggetti grandi (vector, immagini) la copia costa, e si passa per reference (T&), un alias dell'oggetto del chiamante che permette lettura e scrittura, oppure per const reference (const T&), alias in sola lettura verificata dal compilatore, che accetta anche literal (oggetto temporaneo); regola: valore per oggetti piccoli, const T& per grandi da non modificare, T& solo se la funzione deve davvero modificare o produrre più di un output; il passaggio converte l'argomento nel tipo del parametro come in un'inizializzazione e un cast esplicito (static_cast) rende visibile l'intenzione.C++ - funzioni, reference e passaggio dei parametri →. - 3 Conversioni: V, F, V, F. Le sicure preservano il valore; le non sicure sono il narrowing;
{}errore,()nessun controllo. - 4 RA: V, V, F, V. Creato a ogni chiamata, parametri e locali, stack, inserimento costante. C++ - chiamate a funzione e layout della memoriauna chiamata a funzione crea un record di attivazione (RA) con una copia dei parametri e le variabili locali, impilato nello stack (LIFO) e tolto all'uscita; la dimensione dipende dalle variabili locali e il tempo per inserirlo è costante; il processo ha cinque regioni di memoria: text (istruzioni, spesso read-only), initialized data (globali e statiche inizializzate), uninitialized data o BSS (globali e statiche non inizializzate, azzerate dal sistema), stack (RA) e heap (allocazione dinamica con new e delete); la durata (storage duration) di una variabile è diversa dallo scope: le globali e le static sono statiche (esistono per tutto il programma, inizializzate una volta), le locali sono automatiche (create all'entrata nel blocco, distrutte in ordine inverso all'uscita); le globali sono inizializzate prima del main ma l'ordine fra file diversi non è definito.C++ - chiamate a funzione e layout della memoria →.
- 5 Layout: V, V, F, F. BSS = globali e static non inizializzate; heap gestito con
new/delete; l'initialized data è lettura/scrittura; il text contiene il codice. - 6
std::find(): F, V, V, F. Una sola funzione template su iteratori; ritornaend()se non trova;Tdeve avere==. STL - algoritmi, predicati e lambdaLa STL ha circa 80 algoritmi generici (headere .) 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 → - 7 Move: V, V, F, V. Generati se non c'è copia/distruttore dichiarati; più economici; argomento
T&&nonconst; sorgente svuotata. 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_listpermette 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 → . - 8
BeD: F, F, V, F. Override = stessa firma;D* pd = &b;non lecito;vector<B*>ammetteBeD; unBinvector<D>non compila (lo slicing èDinvector<B>). - 9
AreEqual: V, F, F, F. Una copia per tipo; vincolo==; niente copia (const T&); non solo built-in. C++ - template e programmazione genericaun template permette di usare un tipo (o un intero noto in compilazione) come parametro di una classe o di una funzione; il compilatore genera il codice per ogni tipo per cui il template è specializzato (template instantiation, a tempo di compilazione): il codice è efficiente ma gli errori sono lunghi e oscuri, e il compilatore controlla sia il template sia il suo argomento; la notazione è template(o class T, equivalenti) davanti alla classe o alla funzione, e le funzioni membro fuori dalla classe si scrivono template .tipo Classe ::nome(...); nelle function template il compilatore deduce gli argomenti dai parametri della chiamata (myMax(i, j) invece di myMax (i, j)), ma due argomenti di tipo diverso per lo stesso T sono un errore; una specializzazione esplicita (template<> ...) fornisce il codice per un tipo particolare; i parametri interi (template<typename T, int N> struct array) danno array di dimensione fissa senza free store, più efficienti e che esprimono concetti come la tripletta RGB; poiché il compilatore deve vedere la definizione completa al punto d'uso, le definizioni si mettono nell'header (spesso in un file .hpp incluso in coda al .h).C++ - template e programmazione generica → - 10 Puntatori: F, V, F, F. La riga 4 sposta
psuy; alla finep == q;x = 3,y = 8; con una reference non si può "spostare". C++ - puntatori, array e castun puntatore è una variabile che contiene un indirizzo di memoria e sa il tipo dell'oggetto puntato (T*); si ottiene l'indirizzo con & e si accede al dato con * (dereference, un lvalue), nullptr indica nessun oggetto e due puntatori a tipi diversi sono tipi diversi; void* è un puntatore a memoria grezza (accetta qualsiasi puntatore ma non si dereferenzia) e i cast (static_cast, reinterpret_cast, const_cast) servono per interfacciarsi con hardware e codice non modificabile; il puntatore, a differenza della reference, si può spostare, può essere nullptr e va dereferenziato, e assegnarlo copia l'indirizzo (shallow copy) mentre assegnare una reference copia l'oggetto; un array C è una sequenza contigua di elementi dello stesso tipo, con dimensione costante, senza controllo dei limiti, che passato a una funzione decade in un puntatore al primo elemento; l'aritmetica dei puntatori somma o sottrae interi spostando di n elementi (non di n byte) e la differenza fra puntatori è in elementi; gli errori classici sono nullptr, puntatori non inizializzati, accessi fuori dai limiti e puntatori a oggetti usciti dallo scope.C++ - puntatori, array e cast →. - Codice della domanda 10:
int x{3}, y{8}; int *p = &x; int *q = &y; p = &y; *p = *q;alla finep == q(entrambi&y),x = 3,y = 8. Una reference non si può spostare:int& r = x; r = y;copia il valore diyinx. - Punteggio: , , ; si risponde solo se si è sicuri.
Teoria collegata
- C++ - scope, namespace e librerie
- C++ - funzioni, reference e passaggio dei parametri
- C++ - tipi, variabili, espressioni e dichiarazioni
- C++ - chiamate a funzione e layout della memoria
- STL - algoritmi, predicati e lambda
- C++ - costruttori, inizializzazione, copia e spostamento
- C++ - ereditarietà, funzioni virtuali e layout degli oggetti
- C++ - template e programmazione generica
- C++ - puntatori, array e cast