Esercizio - Quiz su include guard, librerie dinamiche, explicit e function object (primo appello 2021-22, quiz)
Quiz del primo appello del corso di Laboratorio di Programmazione, Ing. Informatica UniPD, a.a. 2021-22 (25 gennaio 2022, 90 minuti). Dieci domande a quattro affermazioni vero/falso: risposta giusta , sbagliata , non data (il totale è normalizzato). Gli altri esercizi dello stesso appello: Esercizio - Polimorfismo dinamico e statico (primo appello 2021-22, teoria), Esercizio - Smart pointer, unique_ptr e shared_ptr (primo appello 2021-22, teoria), Esercizio - Aritmetica dei puntatori (primo appello 2021-22, teoria), Esercizio - Ordinare le parole di un file con sort e lambda (primo appello 2021-22, programmazione).
1) Le include guards:
2) Le librerie dinamiche:
| affermazione | V/F | motivo |
|---|---|---|
| Permettono di generare eseguibili self-contained | F | è il contrario: sono le statiche a essere self-contained; la dinamica deve essere presente a run-time |
| Possono essere ricompilate senza ricompilare gli eseguibili che le utilizzano | V | l'eseguibile contiene solo il riferimento alla libreria |
| Hanno lo svantaggio di creare software che occupa molto spazio su disco | F | è il vantaggio opposto: una sola copia condivisa |
In Windows sono caratterizzate dall'estensione .so e in Linux .dll |
F | invertito: .dll su Windows, .so su Linux |
3) Il record di attivazione (RA):
4) In C++ la keyword explicit:
5) In una classe C++, il costruttore di spostamento:
| affermazione | V/F | motivo |
|---|---|---|
| Accetta una lvalue reference come argomento | F | accetta una rvalue reference, T&& |
| Compie una copia ottimizzata dei dati | F | non copia: cede i dati, lasciando la sorgente vuota |
| Può accettare argomenti sia const che non const | F | la sorgente va modificata (svuotata): non è const |
| Può accettare uno o più argomenti a seconda della classe | F | ha un solo argomento, T&& (altri parametri solo se hanno un valore di default) |
6) Il costruttore di copia di default di una classe:
| affermazione | V/F | motivo |
|---|---|---|
Copia l'intero contenuto di uno std::vector membro della classe |
V | copia membro a membro: usa il costruttore di copia di vector, che è profondo |
| Copia il contenuto di un vettore allocato dinamicamente e puntato da un puntatore membro | F | copia il puntatore, non il buffer: shallow copy |
| È disabilitato dalla presenza del costruttore di copia definito dal programmatore | V | se se ne scrive uno, quello generato non esiste |
| È disabilitato dalla presenza del costruttore di spostamento definito dal programmatore | V | dichiarare un costruttore di spostamento fa sì che il costruttore di copia implicito sia deleted |
7) Una funzione template:
8) Gli iteratori begin e end di un container STL:
9) Classe base B, derivata D, entrambe con void f(); oggetto b di tipo B, d di tipo D, puntatore p di tipo B* che punta a d:
10) Un function object:
Versione ripasso
Risposte (V/F in ordine) e motivo.
- 1 Include guard: V, F, V, V. Direttive del preprocessore (
#ifndef/#define/#endif) che includono l'header una volta per unità di traduzione, evitando doppie dichiarazioni/definizioni; il linker non c'entra. 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 Librerie dinamiche: F, V, F, F. Non sono self-contained (lo sono le statiche), si ricompilano senza toccare gli eseguibili, risparmiano spazio,
.dllWindows e.soLinux (non il contrario). - 3 RA: V, V, F, F. Copia dei parametri + locali, creato a ogni chiamata, impilato nello stack (non free store), inserimento a tempo 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 →.
- 4
explicit: V, V, V, F. Vieta le conversioni implicite; va nella dichiarazione, non nella definizione fuori dalla classe. 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 → . - 5 Costruttore di spostamento: F, F, F, F. Prende
T&&(rvalue reference), non copia ma cede i dati, non èconst, ha un solo argomento. - 6 Copia di default: V, F, V, V. Copia membro a membro:
vectormembro copiato in profondità, un puntatore nudo copiato come indirizzo (shallow); sparisce se si scrive un costruttore di copia o di spostamento. - 7 Function template: F, V, V, V. Una versione per tipo istanziato, deduzione dagli argomenti, parametri template tipi o interi. 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 → - 8
begin/end: V, F, V, V.endè una dopo l'ultima; sequenza ; uguali sul container vuoto. - 9 Slicing e virtual: F, V, V, F. Puntatore in
vector<B*>: nessuno slicing; oggettodinvector<B>: slicing;p->f()chiamaD::f()solo se virtuale;d.f()chiama sempreD::f(). C++ - ereditarietà, funzioni virtuali e layout degli oggettiL'ereditarietà pubblica (class D : public B) esprime la relazione "is a": D eredita da B i membri public e protected e li estende; la composizione esprime invece "has a". Una classe base di una gerarchia disabilita copia e assegnamento (=delete) perché copiando una derivata in una base si ha lo slicing. Una funzione virtuale può essere ridefinita nelle derivate (override: stesso nome, tipi e constness) e la funzione chiamata si decide a run-time in base al tipo dell'oggetto, non del puntatore o del riferimento; override esplicito con la keyword override; una funzione virtuale pura (= 0) obbliga le derivate a ridefinirla e rende astratta la classe; il distruttore di una classe base polimorfa è virtuale. Il meccanismo è realizzato con una vtbl per classe (tabella di puntatori alle funzioni) e un vptr in ogni oggetto; i dati della derivata seguono quelli della base.C++ - ereditarietà, funzioni virtuali e layout degli oggetti →. - 10 Function object: F, V, F, F. Oggetto con
operator(), esprime predicati per gli algoritmi STL, non una funzione, la copia non è inibita. 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 → - Strategia: il rende conveniente rispondere solo alle affermazioni di cui si è sicuri; le trappole ricorrenti sono
.so/.dll,stack/free store,endcome ultimo elemento,operator()controoperator[].
Teoria collegata
- C++ - scope, namespace e librerie
- C++ - chiamate a funzione e layout della memoria
- C++ - costruttori, inizializzazione, copia e spostamento
- C++ - template e programmazione generica
- STL - sequenze, iteratori e container (vector, list, string)
- C++ - ereditarietà, funzioni virtuali e layout degli oggetti
- STL - algoritmi, predicati e lambda