Esercizio - Quiz su contenitori STL, override, sizeof, void e virtual table (secondo appello 2021-22, quiz)
Quiz del secondo appello del corso di Laboratorio di Programmazione, Ing. Informatica UniPD, a.a. 2021-22 (11 febbraio 2022, 90 minuti): dieci domande a quattro affermazioni vero/falso (giusta , sbagliata , non data ). Gli altri esercizi dell'appello: Esercizio - Chiamata a funzione e layout della memoria (secondo appello 2021-22, teoria), Esercizio - I tipi di scope in C++ (secondo appello 2021-22, teoria), Esercizio - Confronto tra map e vector (secondo appello 2021-22, teoria), Esercizio - Distruttore, copia e spostamento della classe Elements (secondo appello 2021-22, programmazione).
1) Un contenitore STL:
2) L'override di una funzione in una gerarchia di classi in C++:
3) Quando manda in esecuzione un programma, il sistema operativo riserva spazio per:
| affermazione | V/F | motivo |
|---|---|---|
| Codice eseguibile/codice macchina | V | text segment |
| Free store | V | lo heap: la regione per new/delete, che parte piccola e cresce |
| Stack | V | i record di attivazione |
| Variabili globali | V | initialized data e BSS |
4) L'operatore sizeof():
| affermazione | V/F | motivo |
|---|---|---|
| Se applicato a un contenitore STL restituisce il numero di elementi contenuti | F | dà la dimensione in byte dell'oggetto contenitore (puntatori e contatori), non degli elementi: per quelli size() |
| Funziona con un nome di tipo o di variabile/espressione | V | sizeof(int), sizeof(x), sizeof x |
| Il valore ritornato può dipendere dall'architettura utilizzata | V | un puntatore è 4 o 8 byte, long 4 o 8 |
| Ritorna la dimensione in bit dell'argomento | F | in byte |
5) Il puntatore void*:
6) L'istruzione int age = 42;:
| affermazione | V/F | motivo |
|---|---|---|
| Contiene un'inizializzazione | V | = 42 dà il valore alla nascita |
| È una definizione di variabile | V | dichiara e riserva memoria |
| Può riservare spazio nel free store | F | un int di questo tipo sta nello stack (locale) o nei dati (globale); il free store è solo per new |
| Può riservare spazio nell'initialized data segment | V | se è una variabile globale o static (inizializzata a un valore diverso da 0) |
7) La classe DataBuffer (char* data_buffer allocato con new char[kLength]; std::vector<int> data_vector; std::shared_ptr<double> data_smart_buffer costruito con new double[kLength]; il distruttore fa solo delete[] data_buffer;), con l'assegnamento di copia generato dal compilatore:
| affermazione | V/F | motivo |
|---|---|---|
L'assegnamento di copia effettua una shallow copy corretta di data_smart_buffer |
V | la copia di uno shared_ptr condivide il buffer e aumenta il contatore: è la semantica voluta |
L'assegnamento di copia effettua una shallow copy corretta di data_buffer |
F | copia il puntatore: shallow, ma non corretta (due delete[] sullo stesso buffer, e il buffer vecchio va perso: leak) |
L'assegnamento di copia effettua una deep copy corretta di data_vector |
V | la copia di vector duplica gli elementi |
Il distruttore è incompleto: serve chiamare reset dello smart pointer per non avere memory leak |
F | lo smart pointer libera da solo alla distruzione |
(Un'osservazione sul materiale: shared_ptr<double> inizializzato con new double[kLength] userebbe delete e non delete[]: è un errore latente della classe d'esame; dal C++17 si scrive shared_ptr<double[]>. Non incide sulle risposte.)
8) Gerarchia con classe base B e derivata D, virtual table (VT):
| affermazione | V/F | motivo |
|---|---|---|
| È creata una virtual table per ogni oggetto istanziato | F | una per classe; ogni oggetto ha un vptr che la raggiunge |
| Una volta create, le VT sono modificabili dal programmatore per migliorare l'efficienza | F | le costruisce il compilatore |
| Contengono un riferimento a ciascun record di attivazione presente nello stack | F | contengono indirizzi di funzioni virtuali |
Una funzione membro chiamata tramite un B* che punta a un oggetto di classe D utilizza la VT di D |
V | il vptr dell'oggetto punta alla VT della sua classe vera |
9) La classe MyRecords (costruttore senza argomenti, costruttore MyRecords(int i) con records.reserve(i), membro std::vector<std::string> records):
| affermazione | V/F | motivo |
|---|---|---|
Il codice è incompleto: deve essere definito un distruttore per pulire records prima che l'oggetto sia distrutto |
F | vector ha il suo distruttore, chiamato da quello generato |
L'istruzione MyRecords m = 5; compila |
V | il costruttore a un argomento non è explicit: conversione implicita |
| È dotata di un costruttore di copia definito dal compilatore il cui funzionamento è corretto | V | copia il vector membro, che è profonda |
| È dotata di un costruttore di spostamento definito dal compilatore il cui funzionamento è corretto | V | non ci sono costruttori di copia né distruttore dichiarati: si genera, e sposta il vector |
10) Un errore di linking:
Versione ripasso
- 1 Contenitore STL: F, F, F, V. Sequenza (le due forme date sono sbagliate), copia/spostamento supportati, tipi
iteratoreconst_iterator. 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) →. - 2 Override: F, F, F, V. Stessi nome, tipi e constness (parametri diversi = funzione nuova,
constdiversa = altra funzione);overridefacoltativa;virtualnella base necessaria. 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 →. - 3 Il sistema operativo riserva: V, V, V, V. Text, free store (heap), stack, dati globali (data + BSS). 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
sizeof: F, V, V, F. Byte dell'oggetto (non il numero di elementi), vale per tipi e espressioni, dipende dall'architettura, non è in bit. - 5
void*: V, V, F, V. Memoria grezza; accetta ogni puntatore; il template non recupera il tipo; non si dereferenzia. - 6
int age = 42;: V, V, F, V. Inizializzazione e definizione; mai nel free store; può stare nell'initialized data se globale/static. - 7
DataBuffer: V, F, V, F.shared_ptrcondiviso (corretto), puntatore nudo (shallow non corretto: doppiadelete[]),vectordeep, nessunresetnel distruttore. Nota:shared_ptr<double>connew double[...]userebbedelete. - 8 Virtual table: F, F, F, V. Una VT per classe, non modificabile, contiene indirizzi di funzioni; con
B*che punta aDsi usa la VT diD. - 9
MyRecords: F, V, V, V. Nessun distruttore da scrivere;MyRecords m = 5;compila (costruttore nonexplicit); copia e spostamento generati corretti (membrovector). - 10 Linking: F, V, F, F.
undefined referenceper dichiarata-usata-non definita; sintassi e#includemancante sono errori del compilatore. C++ - introduzione, compilazione e linkingC++ è un linguaggio compilato e standard che dà controllo totale sulla macchina (basso livello) e strumenti di astrazione (alto livello); un programma parte da main, usa gli stream (std::cout << ...) e le direttive del preprocessore (#include); il passaggio da sorgente a eseguibile ha tre fasi, preprocessore (espande #include e macro), compilatore (controlla sintassi e tipi e produce un file oggetto in codice macchina) e linker (unisce i file oggetto e le librerie risolvendo i nomi); gli errori di sintassi e di tipo sono del compilatore, i simboli dichiarati ma non definiti (undefined reference) sono del linker; si compila da riga di comando con g++ -o eseguibile sorgente.cpp -lLibreria.C++ - introduzione, compilazione e linking →. - Criterio generale: la copia/spostamento generati vanno bene se tutti i membri gestiscono già le proprie risorse (
vector,shared_ptr) e non vanno bene se c'è un puntatore nudo.