Skip to content

🛡️ const Best Practices — Const Correctness

const không chỉ là keyword — nó là contract. Sử dụng const đúng cách giúp compiler bắt bugs, tối ưu code, và communicate intent.

Tại sao const quan trọng?

  1. Bug prevention: Compiler bắt lỗi nếu bạn vô tình modify
  2. Self-documentation: Code nói rõ "tôi không thay đổi giá trị này"
  3. Optimization: Compiler có thể optimize aggressive hơn
  4. Thread safety: const objects an toàn đọc từ nhiều threads

const với Variables

cpp
#include <iostream>

int main() {
    const int MAX_SIZE = 100;  // Compile-time constant
    // MAX_SIZE = 200;  // ❌ Error: cannot assign to const
    
    int x = 10;
    const int y = x;  // Runtime constant (giá trị từ x)
    // y = 20;  // ❌ Error
    
    std::cout << "MAX_SIZE = " << MAX_SIZE << std::endl;
    std::cout << "y = " << y << std::endl;
    
    return 0;
}

constexpr (C++11) — Compile-time constant guaranteed

cpp
#include <iostream>

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int size = square(10);  // Computed at compile time!
    int arr[size];  // OK: size là compile-time constant
    
    std::cout << "Array size: " << size << std::endl;  // 100
    
    return 0;
}

const với Function Parameters

Rule 1: const Reference cho Read-only Objects

cpp
#include <iostream>
#include <string>
#include <vector>

// ❌ BAD: Copy toàn bộ string
void printBad(std::string s) {
    std::cout << s << std::endl;
}

// ✅ GOOD: Không copy, không modify
void printGood(const std::string& s) {
    std::cout << s << std::endl;
    // s = "modified";  // ❌ Error: s is const
}

// ✅ Với vector
void processData(const std::vector<int>& data) {
    for (const auto& item : data) {
        std::cout << item << " ";
    }
    // data.push_back(100);  // ❌ Error: data is const
}

Rule 2: const Pointer vs Pointer to const

cpp
#include <iostream>

int main() {
    int x = 10, y = 20;
    
    // Pointer to const: Không thể modify giá trị, có thể rebind pointer
    const int* ptr1 = &x;
    // *ptr1 = 100;  // ❌ Error: cannot modify value
    ptr1 = &y;       // ✅ OK: can rebind pointer
    
    // Const pointer: Có thể modify giá trị, không thể rebind
    int* const ptr2 = &x;
    *ptr2 = 100;     // ✅ OK: can modify value
    // ptr2 = &y;    // ❌ Error: cannot rebind pointer
    
    // Const pointer to const: Không thể làm gì cả!
    const int* const ptr3 = &x;
    // *ptr3 = 100;  // ❌ Error
    // ptr3 = &y;    // ❌ Error
    
    return 0;
}

Đọc từ phải sang trái:

const int* ptr1     → ptr1 is a pointer to (const int)
                      → Giá trị không đổi

int* const ptr2     → ptr2 is a (const pointer) to int
                      → Pointer không đổi

const int* const p  → p is a (const pointer) to (const int)
                      → Cả hai không đổi

const Member Functions

cpp
#include <iostream>
#include <string>

class Person {
private:
    std::string name_;
    int age_;

public:
    Person(const std::string& name, int age) 
        : name_(name), age_(age) {}
    
    // const member function — không modify object state
    std::string getName() const {
        // name_ = "changed";  // ❌ Error: this is const
        return name_;
    }
    
    int getAge() const {
        return age_;
    }
    
    // Non-const — có thể modify
    void setAge(int age) {
        age_ = age;  // ✅ OK
    }
    
    // const function có thể gọi từ const object
    void print() const {
        std::cout << name_ << ", " << age_ << std::endl;
    }
};

int main() {
    const Person alice("Alice", 25);
    
    std::cout << alice.getName() << std::endl;  // ✅ OK
    // alice.setAge(26);  // ❌ Error: setAge is non-const
    
    alice.print();  // ✅ OK: print() is const
    
    return 0;
}

📌 HPN Standard: const Member Functions

  • Getter functions luônconst
  • Nếu function không modify state, đánh dấu const
  • Compiler sẽ bắt lỗi nếu bạn quên

const Return Types

Returning const Reference

cpp
#include <iostream>
#include <string>
#include <vector>

class Container {
private:
    std::vector<int> data_;
    
public:
    Container() : data_{1, 2, 3, 4, 5} {}
    
    // Return const reference — caller không thể modify
    const std::vector<int>& getData() const {
        return data_;
    }
    
    // Return non-const reference — caller CAN modify
    std::vector<int>& getDataMutable() {
        return data_;
    }
};

int main() {
    Container c;
    
    // Read-only access
    const auto& data = c.getData();
    // data.push_back(6);  // ❌ Error: data is const
    
    // Mutable access
    auto& mutableData = c.getDataMutable();
    mutableData.push_back(6);  // ✅ OK
    
    return 0;
}

Best Practices Summary

DO:

cpp
// 1. Dùng const reference cho read-only params
void process(const std::string& input);

// 2. Mark member functions const khi applicable
int getValue() const;

// 3. Dùng constexpr cho compile-time constants
constexpr int MAX_SIZE = 100;

// 4. Return const reference để prevent modification
const std::vector<int>& getData() const;

// 5. Prefer const over #define
const int BUFFER_SIZE = 1024;  // ✅
// #define BUFFER_SIZE 1024    // ❌ Old C-style

DON'T:

cpp
// 1. Đừng return const by value cho primitives (vô nghĩa)
const int getValue();  // ❌ Vô ích, caller vẫn copy

// 2. Đừng quá const-ify (over-engineering)
void func(const int x);  // ❌ Vô nghĩa — int đã là copy

// 3. Đừng cast away const trừ khi absolutely necessary
const int* ptr = &x;
int* mutablePtr = const_cast<int*>(ptr);  // ⚠️ Dangerous!

const-correctness Cheatsheet

SituationPatternExample
Read-only param (large)const T&void f(const std::string& s)
Read-only param (small)T (by value)void f(int x)
Modify paramT&void f(std::string& s)
Optional paramconst T*void f(const Config* cfg)
Getter functionT getX() constint getAge() const
Return referenceconst T&const Data& get() const

🐛 Bug Hunt Challenge

🐛 Bug Hunt

Đoạn code sau compile được không? Tại sao?

cpp
#include <iostream>
#include <string>

class User {
    std::string name_;
public:
    User(const std::string& name) : name_(name) {}
    
    std::string& getName() const {
        return name_;
    }
};

int main() {
    const User alice("Alice");
    alice.getName() = "Bob";  // ???
    return 0;
}
💡 Gợi ý

getName() là const function, nhưng return type là gì?

🔍 Giải thích

Compiler Error!

error: cannot convert 'const std::string' to 'std::string&'

getName() là const function → this là const pointer → name_ được coi là const → không thể return non-const reference!

Fix:

cpp
const std::string& getName() const {  // Return const ref
    return name_;
}

Hoặc nếu cần mutable access:

cpp
std::string& getName() {  // Non-const version
    return name_;
}

const std::string& getName() const {  // Const version
    return name_;
}

📚 Tổng kết

ConceptKey Takeaway
const T& paramsAvoid copy, prevent modification
const member functionsCallable on const objects
constexprCompile-time constants
Pointer to const (const T*)Cannot modify value
Const pointer (T* const)Cannot rebind pointer
Return const T&Prevent caller modification

Mantra: "const is your friend. Use it everywhere applicable."


🎉 Hoàn thành Part 2!

Chúc mừng! Bạn đã hoàn thành Part 2: Control Flow & Functions.

Quay lại C++ Engineering để xem các modules tiếp theo.