0

[C++ OOP Thực Chiến] Bài 14: "Constructor chồng Constructor" và Bí thuật ngăn chặn ép kiểu lỗi

Chào anh em! Chúng ta lại gặp nhau ở phần 2 của chủ đề Parameterized Constructor.

Khi thiết kế một hệ thống lớn, một Class thường có rất nhiều cách để khởi tạo. Ví dụ, Class Product (Sản phẩm) có thể được tạo chỉ với Tên, hoặc Tên + Giá, hoặc Tên + Giá + Giảm giá.

Nếu mỗi Constructor bạn đều viết lại đống logic gán giá trị, code sẽ rất "rác". C++11 đã mang đến một giải pháp cực kỳ thanh lịch: Delegating Constructors (Hàm khởi tạo ủy quyền). Bên cạnh đó, chúng ta sẽ học cách dùng từ khóa explicit để "khóa mõm" những pha ép kiểu tự động tai hại.

1. Delegating Constructors - Đừng lặp lại chính mình!

Thay vì viết 3-4 cái Initializer List giống hệt nhau, bạn có thể cho phép một Constructor "gọi" một Constructor khác trong cùng một Class.

Nguyên tắc: Constructor ít tham số sẽ ủy quyền cho Constructor nhiều tham số nhất xử lý. Điều này giúp logic khởi tạo tập trung về một mối, cực kỳ dễ quản lý.

class Product {
private:
    string name;
    double price;
    int discount;

public:
    // 1. Constructor đầy đủ nhất (Làm "gốc")
    Product(string n, double p, int d) : name(n), price(p), discount(d) {
        cout << "[LOG] Khoi tao day du: " << name << "\n";
    }

    // 2. Constructor chi co Ten và Gia -> Uy quyen cho thang (1)
    // Truyen mac dinh discount = 0
    Product(string n, double p) : Product(n, p, 0) {
        cout << "[LOG] Uy quyen tu Constructor 2 tham so\n";
    }

    // 3. Constructor chi co Ten -> Uy quyen cho thang (2)
    Product(string n) : Product(n, 0.0) {}
};

2. Từ khóa explicit - Ngăn chặn "ép kiểu ma quái"

Trong C++, nếu một Constructor chỉ nhận duy nhất 1 tham số, C++ sẽ mặc định coi đó là một phép chuyển đổi kiểu dữ liệu (Implicit Conversion).

Giả sử bạn có class Order nhận vào int orderID. Nếu bạn lỡ tay viết Order myOrder = 100;, C++ sẽ tự hiểu là bạn muốn tạo Object Order có ID là 100. Nghe thì tiện, nhưng trong các hệ thống Backend phức tạp, việc tự ý "đoán ý" này của C++ thường dẫn đến những lỗi logic cực kỳ khó debug.

Giải pháp: Thêm từ khóa explicit trước Constructor. Lúc này, C++ sẽ bắt bạn phải gọi đúng tên Class thì mới cho tạo Object.

class Order {
private:
    int id;
public:
    // Thêm explicit để chặn ép kiểu tự động
    explicit Order(int id) : id(id) {}
};

// --- Trong hàm main ---
// Order o1 = 100; // LỖI COMPILER! (Việc này rất tốt để tránh nhầm lẫn)
Order o2(100);     // OK! Đúng cú pháp rõ ràng.

3. Bài tập thực chiến (Homework)

Để nắm vững 14 bài học vừa qua, mình có một thử thách nhỏ cho anh em:

Yêu cầu: Thiết kế Class Account cho một hệ thống ngân hàng.

  1. Thuộc tính: ownerName (string), balance (double), accountType (string - ví dụ: "Saving", "Credit").
  2. Yêu cầu Constructor:
  • Sử dụng Delegating Constructor để hỗ trợ 3 cách tạo:
  • Chỉ truyền Tên (mặc định balance=0, type="Normal").
  • Truyền Tên + Số dư (mặc định type="Normal").
  • Truyền đủ cả 3 thông tin.
  • Sử dụng Initializer List để khởi tạo các giá trị.
  • Thêm một Constructor nhận 1 tham số là balance và dùng explicit để bảo vệ.
  1. Phương thức: Viết hàm display() để in thông tin tài khoản.

Tạm kết & Gợi mở

Vậy là chúng ta đã "phá đảo" thế giới của Constructor - cánh cổng đầu tiên mà mọi Object đều phải bước qua. Các bạn đã biết cách đúc ra những Object vừa tối ưu hiệu năng, vừa an toàn, vừa dễ bảo trì.

Nhưng xuyên suốt các bài học trước, các bạn thấy mình liên tục nhắc đến dấu & (Tham chiếu) khi truyền Object vào hàm: void attack(Character& target).

Tại sao dấu & nhỏ xíu đó lại có sức mạnh giúp hệ thống không bị tràn RAM? Tại sao Senior C++ lại nói: "Nếu không hiểu về Tham chiếu, bạn đừng bao giờ nhận mình biết code C++"?

Hẹn gặp lại các bạn ở Bài 15: Tham chiếu (Reference) là gì? - Sợi dây liên kết sống còn trong C++. Nhớ làm bài tập và Upvote để mình có động lực chấm bài nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí