C Cpp — Cheat Sheet

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.

Concetti chiave

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.

Domande tipiche e risposte pronte

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.

Insidie e errori comuni

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.

Snippet e comandiC++

// 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

Da ripassare

[ ] 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

Jarvis Interviews
Jarvis