const în C++

Marchează o valoare care nu mai poate fi modificată după inițializare; compilatorul oprește orice încercare de schimbare.

Sintaxă
const int x = 5;                      // constantă
void f(const std::string& s);          // parametru care nu se modifică
int get() const;                       // metodă care nu modifică obiectul
const int* p;  int* const q = &x;     // pointer la const / pointer constant

Ce face const

O variabilă const primește o valoare la declarare și nu mai poate fi schimbată. Este o promisiune verificată de compilator: dacă încerci x = 6;, primești eroare de compilare. Folosește const peste tot unde valoarea nu trebuie să se schimbe; codul devine mai ușor de înțeles.

Cea mai frecventă folosire este parametrul const T&: funcția primește obiectul fără copie (rapid), dar nu îl poate modifica. Pentru o metodă de clasă, const pus după paranteze înseamnă că metoda nu modifică obiectul și poate fi apelată pe obiecte const.

La pointeri contează poziția: const int* p înseamnă „nu poți modifica valoarea prin p” (dar p poate arăta în altă parte), iar int* const p înseamnă „p arată mereu în același loc” (dar valoarea se poate modifica). Regula: const se aplică la ce are imediat în stânga (sau în dreapta, dacă e primul).

Exemple

Exemplul 1: Constante și parametri const

const std::string& evită copierea textului; funcția îl poate citi, dar nu îl poate modifica.

#include <print>
#include <string>

const double TVA = 0.21;

void afiseaza_factura(const std::string& client, double suma) {
    std::println("Client: {}", client);
    std::println("Total cu TVA: {:.2f} lei", suma * (1 + TVA));
}

int main() {
    const int luni = 12;
    afiseaza_factura("Ana Popescu", 1000);
    std::println("Abonament anual: {} lei", 50 * luni);
}

Rezultat

Client: Ana Popescu
Total cu TVA: 1210.00 lei
Abonament anual: 600 lei

Exemplul 2: Metode const

Pe un obiect const se pot apela doar metodele marcate const. Fără const la sold(), apelul din afiseaza nu s-ar compila.

#include <print>

class Cont {
public:
    void depune(double suma) { sold_ += suma; }
    double sold() const { return sold_; }
private:
    double sold_ = 0;
};

void afiseaza(const Cont& c) {
    std::println("Sold: {} lei", c.sold());
}

int main() {
    Cont c;
    c.depune(250);
    afiseaza(c);
}

Rezultat

Sold: 250 lei

Exemplul 3: Pointer la const și pointer constant

#include <print>

int main() {
    int a = 1, b = 2;

    const int* p = &a;  // nu pot modifica *p
    p = &b;             // dar pot muta p
    std::println("*p = {}", *p);

    int* const q = &a;  // nu pot muta q
    *q = 10;            // dar pot modifica valoarea
    std::println("a = {}", a);
}

Rezultat

*p = 2
a = 10

Greșeli frecvente

  • Transmiți obiecte mari prin valoare (void f(std::vector<int> v)) când doar le citești. Se face o copie la fiecare apel; folosește const std::vector<int>& v.
  • Uiți const la metodele care doar citesc. Apoi nu le poți apela pe obiecte primite ca const& și primești erori greu de înțeles la prima vedere.
  • Confunzi const cu constexpr. Un const poate primi o valoare calculată la rulare (de exemplu citită de la tastatură); constexpr cere o valoare cunoscută la compilare.

Comenzi înrudite

  • constexpr: Cere ca o valoare să fie calculată la compilare; o funcție constexpr poate rula atât la compilare, cât și la rulare.
  • mutable: Permite modificarea unui membru chiar și în metode const sau modificarea variabilelor capturate prin copie într-o lambda.
  • referințe (&, &&): O referință este un alt nume pentru o variabilă existentă; tot ce faci cu referința se face de fapt cu variabila originală.
  • static_cast, dynamic_cast, const_cast, reinterpret_cast: Cele patru conversii explicite: static_cast (obișnuite), dynamic_cast (clase), const_cast (const) și reinterpret_cast (biți).
  • std::string_view: Vedere doar pentru citire asupra unui text existent, fără copiere; se poate micșora la capete.