Esercizio - Quiz su constexpr, ordine di valutazione, dynamic_cast e lambda (primo appello 2022-23, quiz)
Quiz del primo 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 - Ereditarietà, is-a e has-a, slicing (primo appello 2022-23, teoria), Esercizio - Allocazione dinamica e layout della memoria (primo appello 2022-23, teoria), Esercizio - Predicati e algoritmi STL (primo appello 2022-23, teoria), Esercizio - La classe Complex (primo appello 2022-23, programmazione).
1) In C++, constexpr identifica:
2) In C++, le espressioni:
| affermazione | V/F | motivo |
|---|---|---|
| Hanno un ordine di valutazione definito dalla standard library | F | l'ordine di valutazione degli operandi non è definito (e non lo stabilisce la libreria) |
| Possono modificare i loro operandi | V | gli operatori con side effect (++, +=, =) |
| Sono il più piccolo elemento usato per esprimere la computazione | V | |
| Possono essere combinate con operatori per formare espressioni più complesse | V | (length + width) * 2 |
3) v[i] = ++i;
| affermazione | V/F | motivo |
|---|---|---|
Il valore assegnato all'-esimo elemento di v è sempre pari a |
F | l'ordine di valutazione di v[i] e ++i non è definito: l'indice può essere il vecchio o il nuovo valore |
| Il valore assegnato all'-esimo elemento non è conoscibile a priori | V | comportamento non definito |
L'operatore ++ non ha side effect |
F | modifica i |
| Può generare segmentation fault | V | se l'indice calcolato esce dal vettore |
4) In C++, una definizione:
5) std::vector<int> vi(10); std::vector<int> vi2 {1, 2, 3, 4}; const int x2 {9};
| affermazione | V/F | motivo |
|---|---|---|
vi di 10 elementi a 0, vi2 di 4 elementi a 0, x2 a 9 |
F | vi2 ha i valori 1, 2, 3, 4 |
vi di 1 elemento inizializzato a 10, vi2 di 4 elementi non inizializzati, x2 costante a 9 |
F | le parentesi tonde (10) danno 10 elementi, non uno; vi2 è inizializzato |
vi di 10 elementi a 0, vi2 di 4 elementi inizializzati ai valori 1, 2, 3, 4 e x2 a 9 |
V | tonde = dimensione, graffe = lista di valori |
| Dà errore in fase di compilazione | F | tre inizializzazioni lecite |
6) Una funzione che implementa l'overloading di un operatore:
7) Il dynamic_cast:
8) L'iteratore di un container STL:
9) Una lambda expression (o lambda function):
10) Uno std::unique_ptr:
Versione ripasso
- 1
constexpr: V, F, F, F. Costante nota in compilazione, di qualunque tipo idoneo; con tipo. - 2 Espressioni: F, V, V, V. Ordine di valutazione non definito, side effect, elemento minimo della computazione, combinabili. C++ - tipi, variabili, espressioni e dichiarazioniun tipo definisce contenuto, intervallo di valori e operazioni; un oggetto è una regione di memoria con un tipo, il valore è la configurazione di bit letta in base al tipo, una variabile è un oggetto con un nome; inizializzazione (dare il primo valore) e assegnamento (cambiare il valore) sono diversi; le conversioni verso un tipo più capiente sono sicure, quelle verso un tipo meno capiente (narrowing, double->int, int->char) non lo sono e il compilatore le segnala di rado, ma la notazione con graffe {} le vieta; un'espressione calcola un valore da operandi e operatori, un lvalue è ciò che può stare a sinistra di =, constexpr vuole un valore noto in compilazione mentre const solo un valore che non cambia; gli operatori con side effect (++, --, +=) non vanno mischiati con altri usi della stessa variabile nella stessa espressione perché l'ordine di valutazione non è definito; una dichiarazione introduce un nome e un tipo, una definizione fornisce anche l'entità (memoria per una variabile, corpo per una funzione).C++ - tipi, variabili, espressioni e dichiarazioni →.
- 3
v[i] = ++i;: F, V, F, V. Indice non determinabile (non sempre ),++ha side effect, può uscire dal vettore. - 4 Definizione: V, V, V, V. Fornisce tutto: memoria per la variabile, corpo per la funzione, membri per la classe.
- 5
vector<int> vi(10),vi2 {1,2,3,4},const int x2 {9}: F, F, V, F. Tonde = 10 elementi a 0; graffe = lista. - 6 Overloading di operatore: V, F, V, F. Solo per UDT, stessa arità, argomenti diversi tra membro e helper, non generato dal compilatore. 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
dynamic_cast: F, V, F, F. È per il downcasting, con verifica a run-time (nullptr/bad_cast), non cambia il tipo dell'oggetto. C++ - polimorfismoPolimorfismo = abilità di associare comportamenti diversi a un'unica notazione generica. In C++ ci sono due forme principali: dinamico (ereditarietà più funzioni virtuali: vincolato dall'interfaccia della classe base, binding a run-time, permette collezioni eterogenee) e statico (template: svincolato, basta che i tipi abbiano la stessa sintassi, binding a tempo di compilazione, nessun costo a run-time ma una funzione per ogni tipo); il polimorfismo statico è lo strumento della programmazione generica. Anche l'overloading di funzioni e di operatori può essere visto come polimorfismo. Nella gerarchia si naviga con l'upcasting (implicito, sempre lecito) e il downcasting con dynamic_cast, l'unico cast con verifica a run-time: su un puntatore dà nullptr se non lecito, su un riferimento lancia bad_cast.C++ - polimorfismo →. - 8 Iteratore STL: F, V, V, V. Indica un elemento (
*), può indicareend()(inesistente), tipo definito dal container template. - 9 Lambda: V, V, F, F. Funzione anonima, con argomenti, con tipo di ritorno (dedotto o
-> T);[=]per valore,[&]per riferimento. 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 → - 10
unique_ptr: V, F, F, F. Classe template; copia vietata, move permesso; il distruttore fadelete(non azzera); il costruttore non alloca, riceve il puntatore. C++ - smart pointerGli smart pointer (classi template di) sono oggetti che detengono un puntatore e lo deallocano automaticamente all'uscita dallo scope (RAII), eliminando memory leak e dangling pointer con un overhead minimo. unique_ptr ha un solo proprietario: non si copia (costruttore e assegnamento di copia cancellati) ma si sposta (std::move, ritorno da funzione), ha reset(), release(), get(), * e ->; due unique_ptr costruiti con lo stesso puntatore grezzo sono un errore (doppia delete) che il compilatore non vede, e si evita con make_unique. shared_ptr ha più proprietari: si copia, tiene un contatore di riferimenti (use_count) e libera l'oggetto quando l'ultimo shared_ptr viene distrutto, a un costo maggiore; weak_ptr osserva senza possedere, per rompere i cicli. auto_ptr è deprecato (rimosso in C++17).C++ - smart pointer → . - Punteggio: giusta, sbagliata, non data, totale normalizzato: conviene rispondere solo alle affermazioni di cui si è sicuri. Trappole:
[=]per valore e[&]per riferimento;dynamic_castnon fa l'upcasting; costruttore diunique_ptrche non alloca.