Skip to content

Member Initialization — Khởi tạo thành viên

Member initialization list là cách hiệu quả và đúng đắn để khởi tạo members trong constructor. Đây là Modern C++ best practice.

Assignment vs Initialization List

Assignment trong Constructor Body (Old way)

cpp
class OldStyle {
private:
    std::string name_;
    int value_;
    
public:
    OldStyle(const std::string& name, int value) {
        // Assignment — không phải initialization!
        name_ = name;   // 1. Default construct name_ (empty string)
                        // 2. Then assign new value (copy)
        value_ = value; // 1. value_ uninitialized garbage
                        // 2. Then assign
    }
};

Vấn đề:

  • name_ được construct 2 lần (default → assign)
  • Inefficient với complex objects
  • Không hoạt động với const và reference members!

Member Initialization List (Modern way)

cpp
class ModernStyle {
private:
    std::string name_;
    int value_;
    
public:
    ModernStyle(const std::string& name, int value)
        : name_(name),      // Direct initialization — efficient!
          value_(value) {}  // Đã initialize, không cần body
};

Cú pháp Member Initialization List

cpp
class Example {
private:
    int a_;
    double b_;
    std::string c_;
    
public:
    // Colon starts the initializer list
    Example(int a, double b, const std::string& c)
        : a_(a),        // Initialize a_ with a
          b_(b),        // Initialize b_ with b
          c_(c)         // Initialize c_ with c
    {
        // Constructor body (often empty)
    }
};

Khi nào BẮT BUỘC dùng Initialization List?

1. const Members

cpp
class Config {
private:
    const int maxSize_;  // const — phải init trong list
    
public:
    // ❌ Cannot assign to const in body
    // Config(int size) { maxSize_ = size; }  // Error!
    
    // ✅ Must use initializer list
    Config(int size) : maxSize_(size) {}
};

2. Reference Members

cpp
class Logger {
private:
    std::ostream& output_;  // Reference — phải init trong list
    
public:
    // ✅ Initialize reference in list
    explicit Logger(std::ostream& out) : output_(out) {}
    
    void log(const std::string& msg) {
        output_ << "[LOG] " << msg << std::endl;
    }
};

int main() {
    Logger consoleLogger(std::cout);
    consoleLogger.log("Hello!");  // [LOG] Hello!
}

3. Members không có default constructor

cpp
class NoDefault {
public:
    NoDefault(int x) {}  // Không có default constructor
};

class Container {
private:
    NoDefault obj_;  // Không thể default construct!
    
public:
    // ✅ Phải init trong list
    Container(int val) : obj_(val) {}
    
    // ❌ Error: NoDefault không có default constructor
    // Container(int val) { obj_ = NoDefault(val); }
};

4. Base Class Initialization

cpp
class Base {
public:
    Base(int x) {}  // Không có default constructor
};

class Derived : public Base {
private:
    int value_;
    
public:
    // ✅ Base class phải init trước members
    Derived(int x, int v) 
        : Base(x),      // Call base constructor
          value_(v) {}
};

Thứ tự Initialization

⚠️ QUAN TRỌNG

Members được initialize theo thứ tự khai báo, không phải thứ tự trong init list!

cpp
class Order {
private:
    int first_;    // 1st
    int second_;   // 2nd
    int third_;    // 3rd
    
public:
    // ⚠️ Thứ tự trong list KHÔNG ảnh hưởng thứ tự thực tế
    Order(int val)
        : third_(val),     // Nhưng third_ init cuối cùng!
          first_(val),     // Nhưng first_ init đầu tiên!
          second_(val) {}  // second_ init ở giữa
};

Best Practice: Viết init list theo thứ tự khai báo members.


Default Member Initializers (C++11)

cpp
class ModernClass {
private:
    int count_ = 0;                    // Default value
    std::string name_ = "Unknown";
    bool active_ = false;
    std::vector<int> data_ = {1, 2, 3};
    
public:
    // Default constructor uses all defaults
    ModernClass() = default;
    
    // Override some
    explicit ModernClass(const std::string& name)
        : name_(name) {}  // count_, active_, data_ use defaults
    
    // Override all
    ModernClass(const std::string& name, int count)
        : name_(name), count_(count) {}  // active_, data_ use defaults
};

Ưu điểm:

  • ✅ Không quên initialize
  • ✅ Defaults visible ở declaration
  • ✅ Fewer constructor variations needed

Delegating Constructors (C++11)

cpp
class Rectangle {
private:
    double width_;
    double height_;
    std::string color_;
    
public:
    // Primary constructor
    Rectangle(double w, double h, const std::string& color)
        : width_(w), height_(h), color_(color) {}
    
    // Delegate to primary — default color
    Rectangle(double w, double h)
        : Rectangle(w, h, "white") {}
    
    // Delegate — square with default color
    explicit Rectangle(double side)
        : Rectangle(side, side) {}  // Delegates to (double, double)
    
    // Default — unit square
    Rectangle()
        : Rectangle(1.0) {}  // Delegates to (double)
};

Performance: Init List vs Body Assignment

cpp
#include <iostream>
#include <string>

class Heavy {
public:
    Heavy() { std::cout << "Default construct\n"; }
    Heavy(const std::string& s) { std::cout << "Parameterized: " << s << "\n"; }
    Heavy& operator=(const Heavy& other) { std::cout << "Copy assign\n"; return *this; }
};

// ❌ Inefficient
class BadHolder {
    Heavy h_;
public:
    BadHolder(const std::string& s) {
        h_ = Heavy(s);  // Default construct + copy assign
    }
};

// ✅ Efficient
class GoodHolder {
    Heavy h_;
public:
    GoodHolder(const std::string& s) : h_(s) {}  // Direct construct
};

int main() {
    std::cout << "=== Bad ===" << std::endl;
    BadHolder bad("test");
    // Output:
    // Default construct
    // Parameterized: test
    // Copy assign
    
    std::cout << "\n=== Good ===" << std::endl;
    GoodHolder good("test");
    // Output:
    // Parameterized: test
}

📚 Tổng kết

PatternWhen to Use
Init list : member_(val)Luôn luôn (default)
Default member init int x_ = 0;Khi có sensible default
Delegating constructorAvoid code duplication
Body assignmentHầu như không bao giờ

Rules:

  1. ✅ Luôn dùng initialization list
  2. ✅ const/ref members bắt buộc init list
  3. ✅ Viết init list theo thứ tự khai báo
  4. ✅ Dùng default member init cho defaults

➡️ Tiếp theo

Tiếp theo: RPG Character Project — Thực hành xây dựng class hoàn chỉnh.