Tag: c, cpp, pointers, memory-management, raii, templates, stl, compilation
Livello: intermedio
TL;DR: Fondamenti di C e C++ focalizzati su gestione della memoria, ciclo di vita degli oggetti, differenze tra linguaggi e costrutti moderni per colloquio.
C è procedurale e orientato alla manipolazione diretta della memoria.
C++ introduce astrazioni a costo zero, programmazione orientata agli oggetti e programmazione generica.
Lo stack gestisce memoria automatica, rapida e a dimensione fissa.
Lo heap richiede allocazione dinamica manuale o gestita (malloc/free, new/delete).
Un puntatore contiene un indirizzo di memoria; un riferimento è un alias immutabile di un oggetto esistente.
RAII lega l'acquisizione di una risorsa all'inizializzazione e il rilascio alla distruzione dell'oggetto scope-bound.
Gli smart pointer (std::unique_ptr, std::shared_ptr, std::weak_ptr) prevengono leak e dangling pointer.
La vtable abilita il polimorfismo a runtime tramite puntatori a funzioni virtuali (vptr).
Move semantics e rvalue reference (&&) eliminano copie costose trasferendo la proprietà delle risorse.
Il processo di build si articola in: preprocessore, compilazione, assemblaggio e linking.
Q: Qual è la differenza principale tra malloc/free e new/delete?R: malloc alloca solo blocchi grezzi di byte senza invocare costruttori; new alloca memoria tipizzata e chiama il costruttore dell'oggetto. free dealloca senza distruttore, mentre delete invoca prima il distruttore e poi libera la memoria.
Q: Perché il distruttore di una classe base deve essere dichiarato virtual?R: Se distruggo un oggetto derivato tramite un puntatore alla classe base, un distruttore non virtuale causa comportamento indefinito ed esegue solo il distruttore base, provocando resource leak.
Q: Quando si usa std::unique_ptr rispetto a std::shared_ptr?R: Uso std::unique_ptr di default per ownership esclusiva a costo zero; uso std::shared_ptr solo quando la proprietà della risorsa è realmente condivisa tra più componenti.
Q: Cos'è il principio RAII e come lo applichi?R: Incapsulo l'acquisizione di risorse nel costruttore e il rilascio nel distruttore. Quando l'istanza esce dallo scope, il rilascio avviene deterministicamente anche in presenza di eccezioni.
Q: Cosa distingue un Puntatore da una Reference in C++?R: Un puntatore può essere nullo, può essere riassegnato e richiede dereferenziazione (*, ->); una reference non può essere nulla, deve essere inizializzata e non può cambiare bersaglio.
Q: Cos'è la Rule of Three / Rule of Five?R: Se definisco manualmente distruttore, copy constructor o copy assignment, devo implementarli tutti e tre; con C++11 si aggiungono move constructor e move assignment per gestire l'ownership.
Dereferenziazione di puntatore nullo o dangling pointer (memoria già liberata).
Undefined Behavior dovuto ad accesso out-of-bounds in array grezzi.
Memory leak causato da mancata liberazione di memoria allocata sullo heap.
Double free: tentare di liberare lo stesso blocco di memoria due volte.
Slicing di oggetti: passare un'istanza per valore invece che per riferimento/puntatore tronca i dati derivati.
Riferimenti circolari con std::shared_ptr che impediscono la deallocazione (risolvibili con std::weak_ptr).
Omissione dell'inclusione delle guardie #ifndef / #pragma once negli header file.
// Smart pointers e RAII
#include <memory>
#include <iostream>
struct Resource {
Resource() { std::cout << "Acquired\n"; }
~Resource() { std::cout << "Released\n"; }
};
void run() {
auto res = std::make_unique<Resource>(); // Deallocazione automatica all'uscita dello scope
}
C++
// Move semantics essenziale
#include <utility>
#include <vector>
class Buffer {
int* data;
size_t size;
public:
Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
};
Bash
# Compilazione classica con GCC/Clang e flag diagnostici
g++ -std=c++20 -Wall -Wextra -Wpedantic -O2 main.cpp -o app
valgrind --leak-check=full ./app
[ ] Meccanismo interno della vtable e costo prestazionale del dispatch dinamico
[ ] Differenze tra const int*, int* const e const int* const
[ ] Reference collapsing e perfect forwarding con std::forward
[ ] Differenza tra std::move (cast a rvalue) e l'effettivo spostamento nel costruttore
[ ] Regole di memoria ed esecuzione tra Stack e Heap
[x] Sintassi e utilizzo base di std::unique_ptr
[x] Differenza sintattica tra malloc e new