Salta al contenuto
Note per Studenti Esercizio - Quiz su include guard, librerie dinamiche, explicit e function object (primo appello 2021-22, quiz)

Esercizio - Quiz su include guard, librerie dinamiche, explicit e function object (primo appello 2021-22, quiz)

Esame

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 +1+1, sbagliata −0.25-0.25, non data 00 (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:

affermazione V/F motivo
Assicurano che il compilatore processi una sola volta il file incluso dalle guards V il contenuto dell'header entra una sola volta in ogni unità di traduzione (lo fa il preprocessore, prima della compilazione)
Permettono che il linker conosca il nome del file incluso F il linker non vede gli header: agiscono prima, e non servono a lui
Sono delle direttive del preprocessore V #ifndef, #define, #endif
Sono usate per evitare la doppia dichiarazione/definizione di funzioni e classi V è lo scopo: redefinition of ... se l'header arriva due volte. 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 →

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:

affermazione V/F motivo
Permette di chiamare un costruttore solo con la semantica standard V formulazione vaga: l'intenzione è che il costruttore si chiami solo in modo esplicito (T x {5};), non attraverso una conversione implicita
Assicura che non ci siano conversioni implicite quando viene chiamato il costruttore V T x = 5; e il passaggio di un 5 dove serve un T non compilano
Viene utilizzato nella dichiarazione del costruttore di una classe V explicit Records(int n);
Viene utilizzato nella definizione del costruttore di una classe F se il costruttore è definito fuori dalla classe, explicit si scrive solo nella dichiarazione. 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) 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:

affermazione V/F motivo
Possiede un campo aggiuntivo in cui è possibile passare argomenti per copia F frase priva di senso: i parametri sono quelli normali, più i parametri template
Forza il compilatore a generarne una versione per ogni tipo per cui il template è specificato V per ogni tipo con cui viene istanziata (specializzazione)
Può essere chiamata senza specificare il template, se il tipo è deducibile dagli argomenti V myMax(i, j) invece di myMax<int>(i, j)
Può avere come parametri template sia tipi che interi V template <typename T, int N>. Vedi 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) Gli iteratori begin e end di un container STL:

affermazione V/F motivo
Sono sempre disponibili V ogni container STL ha begin() e end()
begin punta al primo elemento, end all'ultimo elemento F end punta dopo l'ultimo: la sequenza è [begin,end)[begin, end)
Devono essere dereferenziati (operatore *) per poter accedere al dato contenuto V *it
Possono essere uguali: begin() == end() è true V container vuoto. Vedi STL - sequenze, iteratori e container (vector, list, string)La STL (Standard Template Library) fornisce contenitori (vector, list, map, ...) e algoritmi generici (sort, find, accumulate, ...) tenuti insieme da una interfaccia unificata: la sequenza, un intervallo [begin, end) di elementi gestito tramite iteratori (oggetti che identificano un elemento: * dereferenzia, ++ avanza, == e != confrontano; end è il primo elemento dopo l'ultimo, quindi una sequenza vuota ha begin == end). Gli algoritmi conoscono solo gli iteratori e i contenitori espongono solo iteratori, così i due mondi sono disaccoppiati e cambiare contenitore non cambia l'algoritmo. Un iteratore di lista è una classe annidata con un puntatore al nodo; auto deduce il tipo dell'iteratore. vector (contiguo, [] e at(), insert/erase costosi e che invalidano gli iteratori), string (un vector di char con + e +=), list (nodi concatenati, insert/erase senza spostare gli altri elementi, niente []) e char[] (array C: dimensione fissa, niente iteratori, == confronta i puntatori) a confronto.STL - sequenze, iteratori e container (vector, list, string) →

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:

affermazione V/F motivo
L'inserimento di p in uno std::vector causa lo slicing F un puntatore si copia per intero; lo slicing riguarda gli oggetti inseriti in un vector<B>
L'inserimento di d in uno std::vector causa lo slicing V in un vector<B> d è copiato in uno slot da B: i membri di D sono tagliati. Vedi 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 →
La chiamata p->f() causa la chiamata di D::f() solo se la funzione è virtuale V senza virtual il tipo statico (B*) decide e si chiama B::f()
La chiamata d.f() causa la chiamata di D::f() solo se la funzione è virtuale F d ha tipo D: si chiama D::f() comunque

10) Un function object:

affermazione V/F motivo
È implementato tramite una classe che deve contenere l'overloading dell'operator[] F serve operator()
Permette di esprimere un predicato da fornire a un algoritmo STL V find_if(v.begin(), v.end(), Larger_than(31)). 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 →
È una funzione in cui è definito un oggetto F è un oggetto che si usa come una funzione
Prevede che sia inibito il costruttore di copia F al contrario, viene passato per copia agli algoritmi

Versione ripasso

Risposte (V/F in ordine) e motivo.

Teoria collegata