Lớp và đối tượng (phần 2)
Một destructor là một hàm thành viên đặc biệt của một lớp. Tên của destructor đối với một lớp là ký tự ngã ( ~ ) theo sau bởi tên lớp. Destructor của một lớp được gọi khi đối tượng được hủy bỏ nghĩa là khi sự thực hiện chương trình ...
Một destructor là một hàm thành viên đặc biệt của một lớp. Tên của destructor đối với một lớp là ký tự ngã (~) theo sau bởi tên lớp.
Destructor của một lớp được gọi khi đối tượng được hủy bỏ nghĩa là khi sự thực hiện chương trình rời khỏi phạm vi mà trong đó đối tượng của lớp đó được khởi tạo. Destructor không thực sự hủy bỏ đối tượng – nó thực hiện "công việc nội trợ kết thúc" trước khi hệ thống phục hồi không gian bộ nhớ của đối tượng để nó có thể được sử dụng giữ các đối tượng mới.
Một destructor không nhận các tham số và không trả về giá trị. Một lớp chỉ có duy nhất một destructor – đa năng hóa destructor là không cho phép.
Nếu trong một lớp không có định nghĩa một destructor thì trình biên dịch sẽ tạo một destructor mặc định không làm gì cả.
Ví dụ 3.8: Lớp có hàm destructor
#include <iostream.h> class Simple {private: int *X; public: Simple(); //Constructor ~Simple(); //Destructor void SetValue(int V); int GetValue(); }; Simple::Simple() { X = new int; //Cấp phát vùng nhớ cho X } Simple::~Simple() { delete X; //Giải phóng vùng nhớ khi đối tượng bị hủy bỏ. } void Simple::SetValue(int V) { *X = V; } int Simple::GetValue() { return *X; } int main() { Simple S; int X; cout<<"Enter a number:"; cin>>X; S.SetValue(X); cout<<"The value of this number:"<<S.GetValue(); return 0; }
Chúng ta chạy ví dụ 3.8, kết quả ở hình 3.8
Kết quả của ví dụ 3.8 (Hình 3.8)Các constructor và destructor được gọi một cách tự động. Thứ tự các hàm này được gọi phụ thuộc vào thứ tự trong đó sự thực hiện vào và rời khỏi phạm vi mà các đối tượng được khởi tạo. Một cách tổng quát, các destructor được gọi theo thứ tự ngược với thứ tự của các constructor được gọi.
Các constructor được gọi của các đối tượng khai báo trong phạm vi toàn cục trước bất kỳ hàm nào (bao gồm hàm main()) trong file mà bắt đầu thực hiện. Các destructor tương ứng được gọi khi hàm main() kết thúc hoặc hàm exit() được gọi.
Các constructor của các đối tượng cục bộ tự động được gọi khi sự thực hiện đến điểm mà các đối tượng được khai báo. Các destructor tương ứng được gọi khi các đối tượng rời khỏi phạm vi (nghĩa là khối mà trong đó chúng được khai báo). Các constructor và destructor đối với các đối tượng cục bộ tự động được gọi mỗi khi các đối tượng vào và rời khỏi phạm vi.
Các constructor được gọi của các đối tượng cục bộ tĩnh (static) khi sự thực hiện đến điểm mà các đối tượng được khai báo lần đầu tiên. Các destructor tương ứng được gọi khi hàm main() kết thúc hoặc hàm exit() được gọi.
Ví dụ 3.9: Chương trình sau minh họa thứ tự các constructor và destructor được gọi.
#include <iostream.h> class CreateAndDestroy { public: CreateAndDestroy(int); //Constructor ~CreateAndDestroy(); //Destructor private: int Data; }; CreateAndDestroy::CreateAndDestroy(int Value) { Data = Value; cout << "Object " << Data << " constructor"; } CreateAndDestroy::~CreateAndDestroy() { cout << "Object " << Data << " destructor " << endl; } void Create(void); //Prototype CreateAndDestroy First(1); //Doi tuong toan cuc int main() { cout << " (global created before main)" << endl; CreateAndDestroy Second(2); //Doi tuong cuc bo cout << " (local automatic in main)" << endl; static CreateAndDestroy Third(3); //Doi tuong cuc bo cout << " (local static in main)" << endl; Create(); //Goi ham de tao cac doi tuong CreateAndDestroy Fourth(4); //Doi tuong cuc bo cout << " (local automatic in main)" << endl; return 0; } //Ham tao cac doi tuong void Create(void) { CreateAndDestroy Fifth(5); cout << " (local automatic in create)" << endl; static CreateAndDestroy Sixth(6); cout << " (local static in create)" << endl; CreateAndDestroy Seventh(7); cout << " (local automatic in create)" << endl; }
Chương trình khai báo First ở phạm vi toàn cục. Constructor của nó được gọi khi chương trình bắt đầu thực hiện và destructor của nó được gọi lúc chương trình kết thúc sau tất cả các đối tượng khác được hủy bỏ. Hàm main() khai báo ba đối tượng. Các đối tượng Second và Fourth là các đối tượng cục bộ tự động và đối tượng Third là một đối tượng cục bộ tĩnh. Các constructor của các đối tượng này được gọi khi chương trình thực hiện đến điểm mà mỗi đối tượng được khai báo. Các destructor của các đối tượng Fourth và Second được gọi theo thứ tự này khi kết thúc của main() đạt đến. Vì đối tượng Third là tĩnh, nó tồn tại cho đến khi chương trình kết thúc. Destructor của đối tượng Third được gọi trước destructor của First nhưng sau tất cả các đối tượng khác được hủy bỏ.
Hàm Create() khai báo ba đối tượng – Fifth và Seventh là các đối tượng cục bộ tự động và Sixth là một đối tượng cục bộ tĩnh. Các destructor của các đối tượng Seventh và Fifth được gọi theo thứ tự này khi kết thúc của create() đạt đến. Vì đối tượng Sixth là tĩnh, nó tồn tại cho đến khi chương trình kết thúc. Destructor của đối tượng Sixth được gọi trước các destructor của Third và First nhưng sau tất cả các đối tượng khác được hủy bỏ.
Chúng ta chạy ví dụ 3.9, kết quả ở hình 3.9
Kết quả của ví dụ 3.9 (Hình 3.9)Các thành viên dữ liệu private chỉ có thể được xử lý bởi các hàm thành viên (hay hàm friend) của lớp. Các lớp thường cung cấp các hàm thành viên public để cho phép các client của lớp để thiết lập (set) (nghĩa là "ghi") hoặc lấy (get) (nghĩa là "đọc") các giá trị của các thành viên dữ liệu private. Các hàm này thường không cần phải được gọi "set" hay "get", nhưng chúng thường đặt tên như vậy. Chẳng hạn, một lớp có thành viên dữ liệu private có tên InterestRate, hàm thành viên thiết lập giá trị có tên là SetInterestRate() và hàm thành viên lấy giá trị có tên là GetInterestRate(). Các hàm "Get" cũng thường được gọi là các hàm chất vấn (query functions).
Nếu một thành viên dữ liệu là public thì thành viên dữ liệu có thể được đọc hoặc ghi tại bất kỳ hàm nào trong chương trình. Nếu một thành viên dữ liệu là private, một hàm "get" public nhất định cho phép các hàm khác để đọc dữ liệu nhưng hàm get có thể điều khiển sự định dạng và hiển thị của dữ liệu. Một hàm "set" public có thể sẽ xem xét cẩn thận bất kỳ cố gắng nào để thay đổi giá trị của thành viên dữ liệu. Điều này sẽ bảo đảm rằng giá trị mới thì tương thích đối với mục dữ liệu. Chẳng hạn, một sự cố gắng thiết lập ngày của tháng là 37 sẽ bị loại trừ.
Các lợi ích của sự toàn vẹn dữ liệu thì không tự động đơn giản bởi vì các thành viên dữ liệu được tạo là private – lập trình viên phải cung cấp sự kiểm tra hợp lệ. Tuy nhiên C++ cung cấp một khung làm việc trong đó các lập trình viên có thể thiết kế các chương trình tốt hơn.
Client của lớp phải được thông báo khi một sự cố gắng được tạo ra để gán một giá trị không hợp lệ cho một thành viên dữ liệu. Chính vì lý do này, các hàm "set" của lớp thường được viết trả về các giá trị cho biết rằng một sự cố gắng đã tạo ra để gán một dữ liệu không hợp lệ cho một đối tượng của lớp. Điều này cho phép các client của lớp kiểm tra các giá trị trả về để xác định nếu đối tượng mà chúng thao tác là một đối tượng hợp lệ và để bắt giữ hoạt động thích hợp nếu đối tượng mà chúng thao tác thì không phải hợp lệ.
Ví dụ 3.10: Chương trình mở rộng lớp Time ở ví dụ 3.2 bao gồm hàm get và set đối với các thành viên dữ liệu private là hour, minute và second.
1: #include <iostream.h> 2: 3: class Time 4: { 5: public: 6: Time(int = 0, int = 0, int = 0); //Constructor 7: //Các hàm set 8: void SetTime(int, int, int); //Thiết lập Hour, Minute, Second 9: void SetHour(int); //Thiết lập Hour 10: void SetMinute(int); //Thiết lập Minute 11: void SetSecond(int); //Thiết lập Second 12: //Các hàm get 13: int GetHour(); //Trả về Hour 14: int GetMinute(); //Trả về Minute 15: int GetSecond(); //Trả về Second 16: 17: void PrintMilitary(); //Xuất thời gian theo dạng giờ quânđội 18: void PrintStandard(); //Xuất thời gian theo dạng chuẩn 19: 20: private: 21: int Hour; //0 - 23 22: int Minute; //0 - 59 23: int Second; //0 – 59 24: }; 25: 26: //Constructor khởiđộng dữ liệu private 27: //Gọi hàm thành viên SetTime() để thiết lập các biến 24: //Các giá trị mặc định là 0 25: Time::Time(int Hr, int Min, int Sec) 26: { 27: SetTime(Hr, Min, Sec); 28: } 29: 30: //Thiết lập các giá trị của Hour, Minute, và Second 31: void Time::SetTime(int H, int M, int S) 32: { 33: Hour = (H >= 0 && H < 24) ? H : 0; 34: Minute = (M >= 0 && M < 60) ? M : 0; 35: Second = (S >= 0 && S < 60) ? S : 0; 36: } 37: 38: //Thiết lập giá trị của Hour 39: void Time::SetHour(int H) 40: { 41: Hour = (H >= 0 && H < 24) ? H : 0; 42: } 43: 44: //Thiết lập giá trị của Minute 45: void Time::SetMinute(int M) 46: { 47: Minute = (M >= 0 && M < 60) ? M : 0; 48: } 49: 50: //Thiết lập giá trị của Second 51: void Time::SetSecond(int S) 52: { 53: Second = (S >= 0 && S < 60) ? S : 0; 54: } 55: 56: //Lấy giá trị của Hour 57: int Time::GetHour() 58: { 59: return Hour; 60: } 61: 62: //Lấy giá trị của Minute 63: int Time::GetMinute() 64: { 65: return Minute; 66: } 67: 68: //Lấy giá trị của Second 69: int Time::GetSecond() 70: { 71: return Second; 72: } 73: 74: //Hiển thị thời gian dạng giờ quânđội: HH:MM:SS 75: void Time::PrintMilitary() 76: { 77: cout << (Hour < 10 ? "0" : "") << Hour << ":" 78: << (Minute < 10 ? "0" : "") << Minute << ":" 79: << (Second < 10 ? "0" : "") << Second; 80: } 81: 83: //Hiển thị thời gian dạng chuẩn: HH:MM:SS AM (hay PM) 84: void Time::PrintStandard() 85: { 86: cout << ((Hour == 0 || Hour == 12) ? 12 : Hour % 12) << ":" 87: << (Minute < 10 ? "0" : "") << Minute << ":" 88: << (Second < 10 ? "0" : "") << Second 89: << (Hour < 12 ? " AM" : " PM"); 90: } 91: 92: void IncrementMinutes(Time &, const int); //prototype 93: 94: int main() 95: { 96: Time T; 97: 99: T.SetHour(17); 100: T.SetMinute(34); 101: T.SetSecond(25); 102 cout << "Result of setting all valid values:" << endl 103: << " Hour: " << T.GetHour() 104: << " Minute: " << T.GetMinute() 105: << " Second: " << T.GetSecond() << endl << endl; 106: T.SetHour(234); //Hour không hợp lệđược thiết lập bằng 0 107: T.SetMinute(43); 108: T.SetSecond(6373); //Second không hợp lệđược thiết lập bằng 0 109: cout << "Result of attempting to set invalid Hour and" 110: << " Second:" << endl << " Hour: " << T.GetHour() 111: << " Minute: " << T.GetMinute() 112: << " Second: " << T.GetSecond() << endl << endl; 113: T.SetTime(11, 58, 0); 114: IncrementMinutes(T, 3); 115: return 0; 116: } 117: 118: void IncrementMinutes(Time &TT, const int Count) 119: { 120: cout << "Incrementing Minute " << Count 121: << " times:" << endl << "Start time: "; 122: TT.PrintStandard(); 123: for (int I = 1; I <= Count; I++) 124: { 125: TT.SetMinute((TT.GetMinute() + 1) % 60); 126: if (TT.GetMinute() == 0) 127: TT.SetHour((TT.GetHour() + 1) % 24); 128: cout << endl << "Minute + 1: "; 129: TT.PrintStandard(); 130: } 131: cout << endl; 132: }
Trong ví dụ trên chúng ta có hàm IncrementMinutes() là hàm dùng để tăng Minite. Đây là hàm không thành viên mà sử dụng các hàm thành viên get và set để tăng thành viên Minite.
Chúng ta chạy ví dụ .10, kết quả ở hình 3.10
Kết quả của ví dụ 3.10 (Hình 3.10)Một tham chiếu tới một đối tượng là một bí danh của chính đối tượng đó và do đó có thể được sử dụng ở vế trái của phép gán. Trong khung cảnh đó, tham chiếu tạo một lvalue được chấp nhận hoàn toàn mà có thể nhận một giá trị. Một cách để sử dụng khả năng này (thật không may!) là có một hàm thành viên public của lớp trả về một tham chiếu không const tới một thành viên dữ liệu private của lớp đó.
Ví dụ 3.11: Chương trình sau sử dụng một phiên bản đơn giản của lớp Time để minh họa trả về một tham chiếu tới một dữ liệu private.
1: #include <iostream.h> 2: 3: class Time 4: { 5: public: 6: Time(int = 0, int = 0, int = 0); 7: void SetTime(int, int, int); 8: int GetHour(); 9: int &BadSetHour(int); //Nguy hiểm trả về tham chiếu !!! 10: private: 11: int Hour; 12: int Minute; 13: int Second; 14: }; 15: 16: //Constructor khởiđộng dữ liệu private 17: //Gọi hàm thành viên SetTime()để thiết lập các biến 18: //Các giá trị mặcđịnh là 0 19: Time::Time(int Hr, int Min, int Sec) 20: { 21: SetTime(Hr, Min, Sec); 22: } 23: //Thiết lập các giá trị của Hour, Minute, và Second 24: void Time::SetTime(int H, int M, int S) 25: { 26: Hour = (H >= 0 && H < 24) ? H : 0; 27: Minute = (M >= 0 && M < 60) ? M : 0; 28: Second = (S >= 0 && S < 60) ? S : 0; 29: } 30: 31: //Lấy giá trị của Hour 32: int Time::GetHour() 33: { 34: return Hour; 35: } 36: 37: //KHÔNG NÊN LẬP TRÌNH THEO KIỂU NÀY !!! 38: //Trả về một tham chiếu tới một thành viên dữ liệu private 39: int &Time::BadSetHour(int HH) 40: { 41: Hour = (HH >= 0 && HH < 24) ? HH : 0; 42: return Hour; //Nguy hiểm trả về tham chiếu !!! 43: } 44: 45: int main() 46: { 47: Time T; 48: int &HourRef = T.BadSetHour(20); 49: 50: cout << "Hour before modification: " << HourRef << endl; 51: HourRef = 30; //Thayđổi với giá trị không hợp lệ 52: cout << "Hour after modification: " << T.GetHour() << endl; 53: // Nguy hiểm: Hàm trả về một tham chiếu 54: //có thểđược sử dụng như một lvalue 55: T.BadSetHour(12) = 74; 56: cout << endl << "*********************************" << endl 57: << "BAD PROGRAMMING PRACTICE!!!!!!!!!" << endl 58: << "BadSetHour as an lvalue, Hour: " 59: << T.GetHour() 60: << endl << "*********************************" << endl; 61: return 0; 62: }
Trong chương trình hàm BadSetHour() trả về một tham chiếu tới thành viên dữ liệu Hour.
Chúng ta chạy ví dụ 3.11, kết quả ở hình 3.11
Kết quả của ví dụ 3.11 (Hình 3.11)Toán tử gán (=) được sử dụng để gán một đối tượng cho một đối tượng khác của cùng một kiểu. Toán tử gán như thế bình thường được thực hiện bởi toán tử sao chép thành viên (Memberwise copy) – Mỗi thành viên của một đối tượng được sao chép riêng rẽ tới cùng thành viên ở đối tượng khác (Chú ý rằng sao chép thành viên có thể phát sinh các vấn đề nghiêm trọng khi sử dụng với một lớp mà thành viên dữ liệu chứa vùng nhớ cấp phát động).
Các đối tượng có thể được truyền cho các tham số của hàm và có thể được trả về từ các hàm. Như thế việc truyền và trả về được thực hiện theo truyền giá trị – một sao chép của đối tượng được truyền hay trả về.:
Ví dụ 3.12: Chương trình sau minh họa toán tử sao chép thành viên mặc định
2: #include <iostream.h> 3: //Lớp Date đơn giản 4: class Date 5: { 6: public: 7: Date(int = 1, int = 1, int = 1990); //Constructor mặc định 8: void Print(); 9: private: 10: int Month; 11: int Day; 12: int Year; 13: }; 14: 15: //Constructor Date đơn giản với việc không kiểm tra miền 16: Date::Date(int m, int d, int y) 17: { 18: Month = m; 19: Day = d; 20: Year = y; 21: } 22: 23: //In Date theo dạng mm-dd-yyyy 24: void Date::Print() 25: { 26: cout << Month << '-' << Day << '-' << Year; 27: } 28: 29: int main() 30: { 31: Date Date1(7, 4, 1993), Date2; //Date2 mặc định là 1/1/90 32: cout << "Date1 = "; 33: Date1.Print(); 34: cout << endl << "Date2 = "; 35: Date2.Print(); 36: Date2 = Date1; //Gán bởi toán tử sao chép thành viên mặc định 37: cout << endl << endl 38: << "After default memberwise copy, Date2 = "; 39: Date2.Print(); 40: cout << endl; 41: return 0; 42: }
Chúng ta chạy ví dụ 3.12, kết quả ở hình 3.12
Kết quả của ví dụ 3.12 (Hình 3.12)Một vài đối tượng cần được thay đổi và một vài đối tượng thì không. Lập trình viên có thể sử dụng từ khóa const để cho biết đối tượng không thể thay đổi được, và nếu có cố gắng thay đổi đối tượng thì xảy ra lỗi. Chẳng hạn:
const Time Noon(12,0,0); //Khai báo một đối tượng const
Các trình biên dịch C++ lưu ý đến các khai báo const vì thế các trình biên dịch cấm hoàn toàn bất kỳ hàm thành viên nào gọi các đối tượng const (Một vài trình biên dịch chỉ cung cấp một cảnh báo). Điều này thì khắc nghiệt bởi vì các client của đối tượng hầu như chắc chắn sẽ muốn sử dụng các hàm thành viên "get" khác nhau với đối tượng, và tất nhiên không thể thay đổi đối tượng. Để cung cấp cho điều này, lập trình viên có thể khai báo các hàm thành viên const; điều này chỉ có thể thao tác trên các đối tượng const. Dĩ nhiên các hàm thành viên const không thể thay đổi đối tượng - trình biên dịch cấm điều này.
Một hàm được mô tả như const khi cả hai trong phần khai báo và trong phần định nghĩa của nó được chèn thêm từ khóa const sau danh sách các tham số của hàm, và trong trường hợp của định nghĩa hàm trước dấu ngoặc móc trái ({) mà bắt đầu thân hàm. Chẳng hạn, hàm thành viên của lớp A nào đó:
int A::GetValue() const { return PrivateDataMember; }
Nếu một hàm thành viên const được định nghĩa bên ngoài định nghĩa của lớp thì khai báo hàm và định nghĩa hàm phải bao gồm const ở mỗi phần.
Một vấn đề nảy sinh ở đây đối với các constructor và destructor, mỗi hàm thường cần thay đổi đối tượng. Khai báo const không yêu cầu đối với các constructor và destructor của các đối tượng const. Một constructor phải được phép thay đổi một đối tượng mà đối tượng có thể được khởi tạo thích hợp. Một destructor phải có khả năng thực hiện vai trò "công việc kết thúc nội trợ" trước khi đối tượng được hủy.
Ví dụ 3.13: Chương trình sau sử dụng một lớp Time với các đối tượng const và các hàm thành viên const.
2: #include <iostream.h> 3: class Time 4: { 5: public: 6: Time(int = 0, int = 0, int = 0); //Constructor mặc định 7: //Các hàm set 8: void SetTime(int, int, int); //Thiết lập thời gian 9: void SetHour(int); //Thiết lập Hour 10: void SetMinute(int); //Thiết lập Minute 11: void SetSecond(int); //Thiết lập Second 12: //Các hàm get 13: int GetHour() const; //Trả về Hour 14: int GetMinute() const; //Trả về Minute 15: int GetSecond() const; //Trả về Second 16: //Các hàm in 17: void PrintMilitary() const; //In t.gian theo dạng giờ quân đội 18: void PrintStandard() const; //In thời gian theo dạng giờ chuẩn 19: private: 20: int Hour; //0 - 23 21: int Minute; //0 - 59 22: int Second; //0 – 59 23: }; 24: 25: //Constructor khởi động dữ liệu private 26: //Các giá trị mặc định là 0 27: Time::Time(int hr, int min, int sec) 28: { 29: SetTime(hr, min, sec); 30: } 31: 32: //Thiết lập các giá trị của Hour, Minute, và Second 33: void Time::SetTime(int h, int m, int s) 34: { 35: Hour = (h >= 0 && h < 24) ? h : 0; 36: Minute = (m >= 0 && m < 60) ? m : 0; 37: Second = (s >= 0 && s < 60) ? s : 0; 38: } 39: 40: //Thiết lập giá trị của Hour 41: void Time::SetHour(int h) 42: { 43: Hour = (h >= 0 && h < 24) ? h : 0; 44: } 45: 46: //Thiết lập giá trị của Minute 47: void Time::SetMinute(int m) 48: { 49: Minute = (m >= 0 && m < 60) ? m : 0; 50: } 51: 52: //Thiết lập giá trị của Second 53: void Time::SetSecond(int s) 54: { 55: Second = (s >= 0 && s < 60) ? s : 0; 56: } 57: 58: //Lấy giá trị của Hour 59: int Time::GetHour() const 60: { 61: return Hour; 62: } 63: 64: //Lấy giá trị của Minute 65: int Time::GetMinute() const 66: { 67: return Minute; 68: } 69: 70: //Lấy giá trị của Second 71: int Time::GetSecond() const 72: { 73: return Second; 74: } 75: 76: //Hiển thị thời gian dạng giờ quân đội: HH:MM:SS 77: void Time::PrintMilitary() const 78: { 79: cout << (Hour < 10 ? "0" : "") << Hour << ":" 80: << (Minute < 10 ? "0" : "") << Minute << ":" 81: << (Second < 10 ? "0" : "") << Second; 82: } 83: 84: //Hiển thị thời gian dạng chuẩn: HH:MM:SS AM (hay PM) 85: void Time::PrintStandard() const 86: { 87: cout << ((Hour == 12) ? 12 : Hour % 12) << ":" 88: << (Minute < 10 ? "0" : "") << Minute << ":" 89: << (Second < 10 ? "0" : "") << Second 90: << (Hour < 12 ? " AM" : " PM"); 91: } 92: 93: int main() 94: { 95: const Time T(19, 33, 52); //Đối tượng hằng 96: T.SetHour(12); //ERROR: non-const member function 97: T.SetMinute(20); //ERROR: non-const member function 98: T.SetSecond(39); //ERROR: non-const member function 99: return 0; 100: }
Chương trình này khai báo một đối tượng hằng của lớp Time và cố gắng sửa đổi đối tượng với các hàm thành viên không hằng SetHour(), SetMinute() và SetSecond(). Các lỗi cảnh báo được phát sinh bởi trình biên dịch (Borland C++) như hình 3.13.
Các cảnh báo của chương trình ở ví dụ 3.13 (Hình 3.13)Lưu ý: Hàm thành viên const có thể được đa năng hóa với một phiên bản non-const. Việc lựa chọn hàm thành viên đa năng hóa nào để sử dụng được tạo một cách tự động bởi trình biên dịch dựa vào nơi mà đối tượng được khai báo const hay không.
Một đối tượng const không thể được thay đổi bởi phép gán vì thế nó phải được khởi động. Khi một thành viên dữ liệu của một lớp được khai báo const, một bộ khởi tạo thành viên (member initializer) phải được sử dụng để cung cấp cho constructor với giá trị ban đầu của thành viên dữ liệu đối với một đối tượng của lớp.
Ví dụ 3.14: C.trình sau sử dụng một bộ khởi tạo thành viên để khởi tạo một hằng của kiểu dữ liệu có sẵn.
2: #include <iostream.h> 3: class IncrementClass 4: { 5: public: 6: IncrementClass (int C = 0, int I = 1); 7: void AddIncrement() 8: { 9: Count += Increment; 10: } 11: void Print() const; 12: private: 13: int Count; 14: const int Increment; //Thành viên dữ liệu const 15: }; 16: 17: //Constructor của lớp IncrementClass 18: //Bộ khởi tạo với thành viên const 19: IncrementClass::IncrementClass (int C, int I) : Increment(I) 20: { 21: Count = C; 22: } 23: 24: //In dữ liệu 25: void IncrementClass::Print() const 26: { 27: cout << "Count = " << Count 28: # # << ", Increment = " << Increment << endl; 30: } 31: 32: int main() 33: { 34: IncrementClass Value(10, 5); 35: 36: cout << "Before incrementing: "; 37: Value.Print(); 38: for (int J = 1; J <= 3; J++) 40: { 41: Value.AddIncrement(); 42: cout << "After increment " << J << ": "; 43: Value.Print(); 44: } 45: return 0; 46: }
Chương trình này sử dụng cú pháp bộ khởi tạo thành viên để khởi tạo thành viên dữ liệu const Increment của lớp IncrementClass ở dòng 19.
Chúng ta chạy ví dụ 3.14, kết quả ở hình 3.14
Kết quả của ví dụ 3.14 (Hình 3.14 )Ký hiệu : Increment(I) (ở dòng 19 của ví dụ 3.14) sinh ra Increment được khởi động với giá trị là I. Nếu nhiều bộ khởi tạo thành viên được cần, đơn giản bao gồm chúng trong danh sách phân cách dấu phẩy sau dấu hai chấm. Tất cả các thành viên dữ liệu có thể được khởi tạo sử dụng cú pháp bộ khởi tạo thành viên.
Nếu trong ví dụ 3.14 chúng ta cố gắng khởi tạo Increment với một lệnh gán hơn là với một bộ khởi tạo thành viên như sau:
IncrementClass::IncrementClass (int C, int I) {Count = C; Increment = I; }
Khi đó trình biên dịch (Borland C++) sẽ có thông báo lỗi như sau:
Thông báo lỗi khi cố gắng khởi tạo một thành viên dữ liệu const bằng phép gán (Hình 3.15)