Vietnamese Developers’ Blog

[C++] Const and mutable members

Posted in C/C++ by Hoang Tran on September 17, 2007

Chúng ta sẽ bắt đầu bằng cách lược qua lại một vài khái niệm cơ bản của C++.

1. Const member function

class Date
{
    int d, m, y;
public:
    int day() const { return d; }
    int month() const { return m; }
    int year() const { return y; }
    // …
};

Những member function của class này là const. Điều đó có nghĩa là những function đó không được thay đổi lớp đó. Ví dụ trong hàm year() bạn làm như sau sẽ gây ra lỗi biên dịch

inline int year() const
{
    return y++; // error: attempt to change member value in const function
}

Một const member function có thể được thực hiện bởi cả non-const hay const object, trong khi một non-const member function chỉ có thể thực hiện bởi non-const object.

void f( Date &d, const Date& cd )
{
    int i = d.year(); // ok
    d.add_year(1); // ok
    int j = cd.year(); // ok
    cd.add_year(1); // error: can not change value of const cd
}

2. Physical and logical constness

Có những trường hợp một const member function nhưng vẫn cần thay đổi giá trị của một vài member. Đối với user thì hàm này không làm thay đổi “trạng thái” của object, tuy nhiên thì nó vẫn cần thay đổi một vài member mà user không thấy được. Ví dụ như là trong lớp Date của chúng ta có thể có hàm string-representation trả về một string (“dd/mm/ccyy”). Khởi tạo nên chuỗi này có thể rất expensive, và sẽ tốt hơn nếu giữ một copy của string này để mỗi khi có yêu cầu lặp lại thì ta chỉ cần trả về một copy trừ phi khi lớp Date thay đổi thì ta phải update lại string này. Kỹ thuật caching value này khá phổ biến trong những cấu trúc phức tạp. Hãy xem chúng ta làm thế nào trong ví dụ lớp Date này:

class Date
{
    bool cache_valid;
    string cache;
    void compute_cache_value(); // fill cache
public:
    string string_rep() const; // string representation
};

Từ phía user thì string_rep rõ ràng là const vì nó sẽ không thay đổi gì lớp này cả. Mặc dù vậy thì cache vẫn phải được update (nếu có) trước khi nó được sử dụng.

string Date::string_rep() const
{
    if ( cache_valid == false ) {
        Date *th = const_cast< Date *> (this); // cast away const
        compute_cache_value();
        cache_valid = true;
    }
    return cache;
}

Nhớ rằng kiểu của con trỏ this trong hàm string_rep là const X *const nên cần sử dụng const_cast để ép kiểu sang X *const.

Mặc dù vậy toán tử const_cast không đảm bảo là nó bỏ đi const với những object được khai báo const. Ví dụ

Date d1;
const Date d2;
string s1 = d1.string_rep(); // ok
string s2 = d2.string_rep(); // undefined behavior

3. Mutable

Mutable giúp cho ta giải quyết trường hợp trên.

class Date
{
    mutable bool cache_valid;
    mutable string cache;
    void compute_cache_value(); // fill (mutable) cache
public:
    string string_rep() const; // string representation
};

Toán tử mutable chỉ ra rằng member được store theo cách cho phép update mặc dù nó là member của một const object. Nói một cách khác thì mutable có nghĩa là “can never be const”. Như vậy thì ta có một hàm string_rep() rất đơn giản.

string Date::string_rep() const
{
    if ( !cache_valid ) {
        compute_cache_value();
        cache_valid = true;
    }
    return cache;
}

Date d3;
const Date d4;
string s1 = d3.string_rep(); // ok
string s2 = d4.string_rep(); // ok

Khai báo những member là mutable rất thích hợp khi chỉ một phần thể hiện được phép thay đổi. Khi phần lớn một object là thay đổi trong khi object đó vẫn được coi là const thì tốt hơn là đưa phần thay đổi ra một object riêng rẽ và truy nhập nó gián tiếp. Ví dụ

struct cache {
    bool valid;
    string rep;
}

class Date
{
    cache *c;
    void compute_cache_value() const;// fill what cache refers to
public:
    string string_rep() const; // string representation
};

string Date::string_rep() const
{
    if ( !c-> valid ) {
        compute_cache_value();
        c-> valid = true;
    }
    return c->rep;
}

Reference: The C++ Programming Language – Special Edition

One Response

Subscribe to comments with RSS.

  1. kiennguyen said, on October 4, 2007 at 2:03 pm

    Ôi trời, màu mè ở đâu ra mà kinh thế :-D


Leave a Reply