Giao diện
⚡ 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
| Pattern | When to Use |
|---|---|
Init list : member_(val) | Luôn luôn (default) |
Default member init int x_ = 0; | Khi có sensible default |
| Delegating constructor | Avoid code duplication |
| Body assignment | Hầu như không bao giờ |
Rules:
- ✅ Luôn dùng initialization list
- ✅ const/ref members bắt buộc init list
- ✅ Viết init list theo thứ tự khai báo
- ✅ 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.