Esercizio - Quiz su operatore =, dangling pointer, enumerazioni e at() (quarto appello 2021-22, quiz)
Quiz del quarto appello del corso di Laboratorio di Programmazione, Ing. Informatica UniPD, a.a. 2021-22 (12 settembre 2022, 90 minuti): dieci domande a quattro affermazioni vero/falso (giusta , sbagliata , non data ). Gli altri esercizi dell'appello: Esercizio - Shallow copy e deep copy (quarto appello 2021-22, teoria), Esercizio - Template, specializzazione e deduzione degli argomenti (quarto appello 2021-22, teoria), Esercizio - RAII e garanzie sulle eccezioni (quarto appello 2021-22, teoria), Esercizio - Spostare i valori di una list in un array dinamico senza delete (quarto appello 2021-22, programmazione).
1) L'operatore =:
2) Il problema del dangling pointer:
3) int v[4] = {0, 1, 2, 3}; int *p = &v[2]; int *q = p + 2;
4) Un'enumerazione:
5) Una variabile data in input a una funzione che ne modifica il valore:
6) Il move constructor in C++:
7) La classe STL std::vector:
8) Il distruttore di una classe in C++:
| affermazione | V/F | motivo |
|---|---|---|
| Viene chiamato quando l'oggetto esce dallo scope | V | |
Viene chiamato quando si usa l'operatore delete |
V | delete invoca il distruttore e poi libera la memoria |
| Se non definito, viene implementato automaticamente dal compilatore | V | e distrugge i membri |
| Viene chiamato quando si usa il move constructor della classe | F | il costruttore di spostamento non distrugge nulla; il distruttore della sorgente (svuotata) sarà chiamato più tardi, quando esce dallo scope |
9) Le include guards in C++:
10) La compilazione di un progetto C++:
Versione ripasso
- 1 Operatore
=: F, V, F, V. Assegna (non confronta), ha side effect, generato per le classi, a destra può esserci un lvalue (usato come rvalue). - 2 Dangling pointer: F, F, V, F. Non è un leak; è un errore a run-time (non di compilazione); non spreca memoria; avviene ovunque si usi un puntatore dopo
delete/uscita dallo scope. C++ - allocazione dinamica della memoria con new e deletela memoria dinamica si prende dal free store (heap) con new, che restituisce un puntatore all'oggetto o al primo elemento dell'array (new T, new T[n], con inizializzatori {}), e si restituisce con delete (per new) e delete[] (per new[]); il puntatore non sa quanti elementi ci sono, la memoria non ha nome e non ha controllo dei limiti; in C++ non c'è garbage collector (scelta per efficienza e controllo), quindi un'area che perde ogni riferimento diventa garbage e produce un memory leak, e gli errori tipici sono la doppia cancellazione e il dangling pointer (si mette il puntatore a nullptr dopo delete); il distruttore ~T() è chiamato dal compilatore quando l'oggetto esce dallo scope o con delete e libera le risorse, per cui una classe come vector acquisisce la memoria nel costruttore e la rilascia nel distruttore; new alloca e chiama il costruttore, delete chiama il distruttore e libera; operator[] con due versioni (T& e const) dà accesso in lettura e scrittura al buffer; si alloca dinamicamente solo se serve davvero (memoria grande, dimensione nota a run-time, durata controllata).C++ - allocazione dinamica della memoria con new e delete →. - 3
p + 2suint v[4]: F, F, V, F. Nessun errore (q = &v[4], oltre la fine); indirizzo diq=p + 2*sizeof(int);q - p= 2. 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 →. - 4 Enumerazione: F, V, F, V. Non è una classe;
enum classcrea uno scope; nessuna efficienza in più; più espressivo. - 5 Argomento modificato: V, V, F, V. Visibile con reference e puntatore; con
const&non si può modificare. 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 →. - 6 Move constructor: F, F, V, V.
&&= rvalue reference; nonconst; sposta invalidando la sorgente; scelto dal compilatore. - 7
std::vector: V, F, V, V.[]non controlla,at()lanciaout_of_range; entrambi leggono,[]scrive. 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) →. - 8 Distruttore: V, V, V, F. Uscita dallo scope,
delete, generato se mancante; non è chiamato dal move constructor. - 9 Include guard: F, V, F, V.
#ifndef/#define/#endif(non#pragma once), negli header, evitano inclusioni multiple e ricorsive. 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 →. - 10 Compilazione: F, V, F, V. Sintassi = compilatore; libreria mancante = linker; i commenti non finiscono nel programma; i tipi non validi sono errori del compilatore.
- Trappole: leak contro dangling;
q - pin elementi;[]controat();&&rvalue.
Teoria collegata
- C++ - tipi, variabili, espressioni e dichiarazioni
- C++ - allocazione dinamica della memoria con new e delete
- C++ - puntatori, array e cast
- C++ - enumerazioni e overloading degli operatori
- C++ - funzioni, reference e passaggio dei parametri
- C++ - costruttori, inizializzazione, copia e spostamento
- STL - sequenze, iteratori e container (vector, list, string)
- C++ - scope, namespace e librerie
- C++ - introduzione, compilazione e linking