24/05/2018, 14:56

Lớp và đối tượng-các hàm truy cập và hàm tiện ích

Không phải tất cả các hàm thành viên đều là public để phục vụ như bộ phận giao diện của một lớp. Một vài hàm còn lại là private và phục vụ như các hàm tiện ích (utility functions) cho các hàm khác của lớp. Các hàm truy cập có thể ...

Không phải tất cả các hàm thành viên đều là public để phục vụ như bộ phận giao diện của một lớp. Một vài hàm còn lại là private và phục vụ như các hàm tiện ích (utility functions) cho các hàm khác của lớp.

Các hàm truy cập có thể đọc hay hiển thị dữ liệu. Sử dụng các hàm truy cập để kiểm tra tính đúng hoặc sai của các điều kiện – các hàm như thế thường được gọi là các hàm khẳng định (predicate functions). Một ví dụ của hàm khẳng định là một hàm IsEmpty() của lớp container - một lớp có khả năng giữ nhiều đối tượng - giống như một danh sách liên kết, một stack hay một hàng đợi. Một chương trình sẽ kiểm tra hàm IsEmpty() trước khi thử đọc mục khác từ đối tượng container.

Một hàm tiện ích không là một phần của một giao diện của lớp. Hơn nữa nó là một hàm thành viên private mà hỗ trợ các thao tác của các hàm thành viên public. Các hàm tiện ích không dự định được sử dụng bởi các client của lớp.

Ví dụ 3.6:  Minh họa cho các hàm tiện ích.

CT3_6.CPP1: #include <iostream.h>2: #include <iomanip.h>3:4: class SalesPerson5: {6: public:7: SalesPerson();             //constructor8: void SetSales(int, double);//Người dùng cung cấp các hình của9:                            #9; #9; //những hàng bán của một tháng10: void PrintAnnualSales();11:12: private:13: double Sales[12]; //12 hình của những hàng bán hằng tháng14: double TotalAnnualSales(); //Hàm tiện ích15: };16:17: //Hàm constructor khởi tạo mảng18: SalesPerson::SalesPerson()19: {20: for (int I = 0; I < 12; I++)21: Sales[I] = 0.0;22: }23:24: //Hàm thiết lập một trong 12 hình của những hàng bán hằng tháng25: void SalesPerson::SetSales(int Month, double Amount)26: {27: if (Month >= 1 && Month <= 12 && Amount > 0)28: Sales[Month - 1] = Amount;29: else30: cout << "Invalid month or sales figure" << endl;31: }32:33: //Hàm tiện íchđể tính tổng hàng bán hằng năm34: double SalesPerson::TotalAnnualSales()35: {36: double Total = 0.0;37:38: for (int I = 0; I < 12; I++)39: Total += Sales[I];40: return Total;41: }42:43: //In tổng hàng bán hằng năm44: void SalesPerson::PrintAnnualSales()45: {46: cout << setprecision(2)47:       << setiosflags(ios::fixed | ios::showpoint)48:       << endl << "The total annual sales are: $"49:       << TotalAnnualSales() << endl;50: }51:52: int main()53: {54: SalesPerson S;55: double salesFigure;56:57: for (int I = 1; I <= 12; I++)58: {59: cout << "Enter sales amount for month "<< I << ": ";60: cin >> salesFigure;61: S.SetSales(I, salesFigure);62: }63: S.PrintAnnualSales();64: return 0;65: }

Chúng ta chạy ví dụ 3.6 , kết quả ở hình 3.6

Hình 3.6: Kết quả của ví dụ 3.6

Khi một đối tượng được tạo, các thành viên của nó có thể được khởi tạo bởi một hàm constructor. Một constructor là một hàm thành viên với tên giống như tên của lớp. Lập trình viên cung cấp constructor mà được gọi tự động mỗi khi đối tượng của lớp đó được tạo. Các thành viên dữ liệu của một lớp không thể được khởi tạo trong định nghĩa của lớp. Hơn nữa, các thành viên dữ liệu phải được khởi động hoặc trong một constructor của lớp hoặc các giá trị của chúng có thể được thiết lập sau sau khi đối tượng được tạo. Các constructor không thể mô tả các kiểu trả về hoặc các giá trị trả về. Các constructor có thể được đa năng hóa để cung cấp sự đa dạng để khởi tạo các đối tượng của lớp.

Constructor có thể chứa các tham số mặc định. Bằng cách cung cấp các tham số mặc định cho constructor, ngay cả nếu không có các giá trị nào được cung cấp trong một constructor thì đối tượng vẫn được bảo đảm để trong một trạng thái phù hợp vì các tham số mặc định. Một constructor của lập trình viên cung cấp mà hoặc tất cả các tham số của nó có giá trị mặc định hoặc không có tham số nào được gọi là constructor mặc định (default constructor). Chỉ có thể có một constructor mặc định cho mỗi lớp.

Ví dụ 3.7: Constructor với các tham số mặc định

 

CT3_7.CPP1: #include <iostream.h>2:3: class Time4: {5: public:6: Time(int = 0, int = 0, int = 0); //Constructor mặc định7: void SetTime(int, int, int);8: void PrintMilitary();9: void PrintStandard();10:11: private:12: int Hour;13: int Minute;14: int Second;15: };16:17: //Hàm constructor để khởi động dữ liệu private18: //Các giá trị mặc định là 019: Time::Time(int Hr, int Min, int Sec)20: {21: SetTime(Hr, Min, Sec);22: }23:24: //Thiết lập các giá trị của Hour, Minute và Second25: //Giá trị không hợp lệ được thiết lập là 026: void Time::SetTime(int H, int M, int S)27: {28: Hour = (H >= 0 && H < 24) ? H : 0;29: Minute = (M >= 0 && M < 60) ? M : 0;30: Second = (S >= 0 && S < 60) ? S : 0;31: }32:33: //Hiển thị thời gian theo dạng giờ quân đội: HH:MM:SS34: void Time::PrintMilitary()35: {36: cout << (Hour < 10 ? "0" : "") << Hour << ":"37:       << (Minute < 10 ? "0" : "") << Minute << ":"38:       << (Second < 10 ? "0" : "") << Second;39: }40:41: // Hiển thị thời gian theo dạng chuẩn: HH:MM:SS AM (hoặc PM)42: void Time::PrintStandard()43: {44: cout << ((Hour == 0 || Hour == 12) ? 12 : Hour % 12)45:       << ":" << (Minute < 10 ? "0" : "") << Minute46:       << ":" << (Second < 10 ? "0" : "") << Second47:       << (Hour < 12 ? " AM" : " PM");48: }49: 50: int main()51: {52: Time T1,T2(2),T3(21,34),T4(12,25,42),T5(27,74,99);53: 54: cout << "Constructed with:" << endl55:       << "all arguments defaulted:" << endl << " ";56: T1.PrintMilitary();57: cout << endl << " ";58: T1.PrintStandard();59: cout << endl << "Hour specified; Minute and Second defaulted:"60: << endl << " ";61: T2.PrintMilitary();62: cout << endl << " ";63: T2.PrintStandard();64: cout << endl << "Hour and Minute specified; Second defaulted:"65: << endl << " ";66: T3.PrintMilitary();67: cout << endl << " ";68: T3.PrintStandard();69: cout << endl << "Hour, Minute, and Second specified:"70: << endl << " ";71: T4.PrintMilitary();72: cout << endl << " ";73: T4.PrintStandard();74: cout << endl << "all invalid values specified:"75: << endl << " ";76: T5.PrintMilitary();77: cout << endl << " ";78: T5.PrintStandard();79: cout << endl;80: return 0;81: }

Chương trình ở ví dụ 3.7 khởi tạo năm đối tượng của lớp Time (ở dòng 52). Đối tượng T1 với ba tham số lấy giá trị mặc định, đối tượng T2 với một tham số được mô tả, đối tượng T3 với hai tham số được mô tả, đối tượng T4 với ba tham số được mô tả và đối tượng T5 với các tham số có giá trị không hợp lệ.

Chúng ta chạy ví dụ 3.7, kết quả ở hình 3.7

Hình 3.7: Kết quả của ví dụ 3.7

Nếu không có constructor nào được định nghĩa trong một lớp thì trình biên dịch tạo một constructor mặc định. Constructor này không thực hiện bất kỳ sự khởi tạo nào, vì vậy khi đối tượng được tạo, nó không bảo đảm để trong một trạng thái phù hợp.

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

CT3_8.CPP1: #include <iostream.h>2:3: class Simple4: {5: private:6: int *X;7: public:8: Simple(); //Constructor9: ~Simple(); //Destructor10: void SetValue(int V);11: int GetValue();12: };13:14: Simple::Simple()15: {16: X = new int; //Cấp phát vùng nhớ cho X17: }18:19: Simple::~Simple()20: {21: delete X; //Giải phóng vùng nhớ khi đối tượng bị hủy bỏ.22: }23:24: void Simple::SetValue(int V)25: {26: *X = V;27: }28:29: int Simple::GetValue()30: {31: return *X;32: }33:34: int main()35: {36: Simple S;37: int X;38:39: cout<<"Enter a number:";40: cin>>X;41: S.SetValue(X);42: cout<<"The value of this number:"<<S.GetValue();43: return 0;44: }

Chúng ta chạy ví dụ 3.8, kết quả ở hình 3.8

Hình 3.8: Kết quả của ví dụ 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.

CT3_9.CPP1: #include <iostream.h>2:3: class CreateAndDestroy4: {5: public:6: CreateAndDestroy(int); //Constructor7: ~CreateAndDestroy(); //Destructor8: private:9: int Data;10: };11:12: CreateAndDestroy::CreateAndDestroy(int Value)13: {14: Data = Value;15: cout << "Object " << Data << " constructor";16: }17:18: CreateAndDestroy::~CreateAndDestroy()19: {20: cout << "Object " << Data << " destructor " << endl;21: }22:23: void Create(void); //Prototype 24: CreateAndDestroy First(1); //Đối tượng toàn cục25:26: int main()27: {28: cout << " (global created before main)" << endl;29:30: CreateAndDestroy Second(2); //Đ ối tượng cục bộ31: cout << " (local automatic in main)" << endl;32:33: static CreateAndDestroy Third(3); //Đối tượng cục bộ34: cout << " (local static in main)" << endl;35:36: Create(); //Gọi hàm để tạo các đ ối tượng 37:38: CreateAndDestroy Fourth(4); //Đối tượng cục bộ39: cout << " (local automatic in main)" << endl;40: return 0;41: }42:43: //Hàm tạo cácđối tượng44: void Create(void)45: {46: CreateAndDestroy Fifth(5);47: cout << " (local automatic in create)" << endl;48:49: static CreateAndDestroy Sixth(6);50: cout << " (local static in create)" << endl;51:52: CreateAndDestroy Seventh(7);53: cout << " (local automatic in create)" << endl;54: }

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 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 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 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 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

Hình 3.9: Kết quả của ví dụ 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 second.

CT3_10.CPP1: #include <iostream.h>2: 3: class Time4: {5: public:6: Time(int = 0, int = 0, int = 0); //Constructor7: //Các hàm set8: void SetTime(int, int, int); //Thiết lập Hour, Minute, Second9: void SetHour(int); //Thiết lập Hour10: void SetMinute(int); //Thiết lập Minute11: void SetSecond(int); //Thiết lập Second12: //Các hàm get13: int GetHour();    //Trả về Hour14: int GetMinute();    //Trả về Minute15: int GetSecond();    //Trả về Second16:17: void PrintMilitary(); //Xuất thời gian theo dạng giờ quânđội18: void PrintStandard(); //Xuất thời gian theo dạng chuẩn19:20: private:21: int Hour; //0 - 2322: int Minute; //0 - 5923: int Second; //0 – 5924: };25:26: //Constructor khởiđộng dữ liệu private27: //Gọi hàm thành viên SetTime() để thiết lập các biến24: //Các giá trị mặc định là 025: 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à Second31: 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 Hour39: 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 Minute45: 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 Second51: void Time::SetSecond(int S)52: {53: Second = (S >= 0 && S < 60) ? S : 0;54: }55:56: //Lấy giá trị của Hour57: int Time::GetHour()58: {59: return Hour;60: }61:62: //Lấy giá trị của Minute63: int Time::GetMinute()64: {65: return Minute;66: }67:68: //Lấy giá trị của Second69: int Time::GetSecond()70: {71: return Second;72: }73:74: //Hiển thị thời gian dạng giờ quânđội: HH:MM:SS75: 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" : "") << Second89:       << (Hour < 12 ? " AM" : " PM");90: }91: 92: void IncrementMinutes(Time &, const int); //prototype93: 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:" << endl103:       << " 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 0107: T.SetMinute(43);108: T.SetSecond(6373); //Second không hợp lệđược thiết lập bằng 0109: 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 " << Count121:       << " 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

Hình 3.10: Kết quả của ví dụ 3.10

0