static_cast, dynamic_cast, const_cast, reinterpret_cast în C++

Cele patru conversii explicite: static_cast (obișnuite), dynamic_cast (clase), const_cast (const) și reinterpret_cast (biți).

Sintaxă
static_cast<TipNou>(expresie)
dynamic_cast<Derivata*>(pointer_baza)
const_cast<Tip&>(obiect_const)
reinterpret_cast<TipNou>(expresie)

Ce face static_cast, dynamic_cast, const_cast, reinterpret_cast

static_cast este conversia de zi cu zi: double în int, int în enum, pointer la clasa de bază în pointer la clasa derivată (fără verificare). Se verifică la compilare.

dynamic_cast transformă un pointer sau o referință la o clasă de bază polimorfică (cu funcții virtuale) într-un pointer la o clasă derivată și verifică la rulare dacă obiectul chiar este de acel tip. Pentru pointeri returnează nullptr dacă nu este; pentru referințe aruncă std::bad_cast.

const_cast adaugă sau scoate const, de obicei ca să apelezi o funcție veche care nu a fost scrisă cu const. reinterpret_cast privește aceiași biți ca alt tip (pointer ca număr, obiect ca octeți); este periculos și rar necesar. Vechea conversie în stil C, (int)x, încearcă pe rând toate acestea, deci ascunde ce se întâmplă de fapt.

Exemple

Exemplul 1: static_cast pentru numere și enumerări

#include <print>

enum class Luna { ianuarie = 1, februarie, martie };

int main() {
    int suma = 27;
    int numar = 4;
    double media = static_cast<double>(suma) / numar;
    std::println("Media: {}", media);
    std::println("Partea întreagă: {}", static_cast<int>(media));
    Luna l = static_cast<Luna>(3);
    std::println("Luna 3 este martie: {}", l == Luna::martie);
}

Rezultat

Media: 6.75
Partea întreagă: 6
Luna 3 este martie: true

Exemplul 2: dynamic_cast verifică tipul real

Primul obiect este un Student, deci conversia reușește. Al doilea este doar o Persoana: varianta cu pointer dă nullptr, iar cea cu referință aruncă std::bad_cast.

#include <print>
#include <string>
#include <typeinfo>

struct Persoana {
    std::string nume;
    virtual ~Persoana() = default;
};

struct Student : Persoana {
    int an = 2;
};

void verifica(Persoana* p) {
    if (auto* s = dynamic_cast<Student*>(p)) {
        std::println("{} este student în anul {}", s->nume, s->an);
    } else {
        std::println("{} nu este student", p->nume);
    }
}

int main() {
    Student ana;
    ana.nume = "Ana";
    Persoana mihai;
    mihai.nume = "Mihai";
    verifica(&ana);
    verifica(&mihai);
    try {
        Student& s = dynamic_cast<Student&>(mihai);
        std::println("an {}", s.an);
    } catch (const std::bad_cast&) {
        std::println("Conversia referinței a eșuat: std::bad_cast");
    }
}

Rezultat

Ana este student în anul 2
Mihai nu este student
Conversia referinței a eșuat: std::bad_cast

Exemplul 3: const_cast și reinterpret_cast

const_cast permite apelul unei funcții vechi care nu modifică textul, dar nu cere const. reinterpret_cast la unsigned char* permite citirea octeților unui număr (aici ordinea este little-endian, ca pe Apple M și pe procesoarele Intel/AMD).

#include <cstdint>
#include <print>

int lungime_veche(char* text) {  // funcție veche, fără const
    int n = 0;
    while (text[n] != '\0') ++n;
    return n;
}

int main() {
    const char* mesaj = "Salut";
    std::println("Lungime: {}", lungime_veche(const_cast<char*>(mesaj)));

    std::uint32_t numar = 0x0A0B0C0D;
    auto* octeti = reinterpret_cast<unsigned char*>(&numar);
    for (int i = 0; i < 4; ++i) {
        std::print("{:02X} ", octeti[i]);
    }
    std::println("");
}

Rezultat

Lungime: 5
0D 0C 0B 0A

Greșeli frecvente

  • Folosești const_cast ca să modifici un obiect declarat inițial const (const int x = 5;). Este comportament nedefinit; const_cast e sigur doar dacă obiectul original nu era const.
  • Folosești static_cast în loc de dynamic_cast pentru a coborî în ierarhie fără să fii sigur de tip. Dacă obiectul nu este de tipul cerut, accesul ulterior este comportament nedefinit.
  • Folosești dynamic_cast pe o clasă fără nicio funcție virtuală. Nu se compilează: tipul trebuie să fie polimorfic.

Comenzi înrudite

  • virtual: Marchează o metodă care poate fi suprascrisă în clasele derivate; apelul printr-un pointer sau o referință la bază alege varianta tipului real.
  • typeid: Dă informații despre tipul unei expresii la rulare; pentru clase cu funcții virtuale arată tipul real al obiectului.
  • const: Marchează o valoare care nu mai poate fi modificată după inițializare; compilatorul oprește orice încercare de schimbare.
  • pointeri (*, &): Un pointer este o variabilă care ține adresa altei variabile; prin el poți citi și modifica valoarea de la acea adresă.
  • std::bit_cast: Reinterpretează biții unui obiect ca alt tip de aceeași mărime, în mod sigur (de exemplu biții unui float ca uint32_t).