Đa năng hóa toán tử-các toán tử chèn dòng
Chúng ta có thể đa năng hóa các toán tử chèn dòng << (stream insertion) và trích dòng >> (stream extraction). Hàm toán tử của toán tử << được đa năng hóa có prototype như sau: ostream & operator ...
Chúng ta có thể đa năng hóa các toán tử chèn dòng << (stream insertion) và trích dòng >> (stream extraction). Hàm toán tử của toán tử << được đa năng hóa có prototype như sau:
ostream & operator << (ostream & stream , ClassName Object );
Hàm toán tử << trả về tham chiếu chỉ đến dòng xuất ostream. Tham số thứ nhất của hàm toán tử << là một tham chiếu chỉ đến dòng xuất ostream, tham số thứ hai là đối tượng được chèn vào dòng. Khi sử dụng, dòng trao cho toán tử << (tham số thứ nhất) là toán hạng bên trái và đối tượng được đưa vào dòng (tham số thứ hai) là toán hạng bên phải. Để bảo đảm cách dùng toán tử << luôn nhất quán, chúng ta không thể định nghĩa hàm toán tử << như là hàm thành viên của lớp đang xét, thông thường nó chính là hàm friend.
Còn hàm toán tử của toán tử >> được đa năng hóa có prototype như sau:
istream & operator >> (istream & stream , ClassName Object );
Hàm toán tử >> trả về tham chiếu chỉ đến dòng nhập istream. Tham số thứ nhất của hàm toán tử này là một tham chiếu chỉ đến dòng nhập istream, tham số thứ hai là đối tượng của lớp đang xét mà chúng ta muốn tạo dựng nhờ vào dữ liệu lấy từ dòng nhập. Khi sử dụng, dòng nhập đóng vai toán hạng bên trái, đối tượng nhận dữ liệu đóng vai toán hạng bên phải. Cũng như trường hợp toán tử <<, hàm toán tử >> không là hàm thành viên của lớp, thông thường nó chính là hàm friend.
Ví dụ 4.17:
CT4_17.CPP1: #include <iostream.h> 2: 3: class Point4: {5: private:6: int X,Y;7: public:8: Point();9: friend ostream & operator << (ostream & Out,Point & P);10: friend istream & operator >> (istream & In,Point & P);11: };12:13: Point::Point()14: {15: X=Y=0;16: }17:18: ostream & operator << (ostream & Out,Point & P)19: {20: Out<<"X="<<P.X<<",Y="<<P.Y<<endl;21: return Out; //Cho phép cout<<a<<b<<c;22: }23:24: istream & operator >> (istream &In,Point & P)25: {26: cout<<"X:";27: In>>P.X;28: cout<<"Y:";29: In>>P.Y;30: return In; //Cho phép cin>>a>>b>>c;31: }32:33: int main()34: {35: Point P;36: cin>>P;37: cout<<"Point:"<<P;38: return 0;39: } | ||
Chúng ta chạy ví dụ 4.17, kết quả ở hình 4.22
Hình 4.22: Kết quả của ví dụ 4.17
Lớp String
Ví dụ 4.18: Chúng ta sẽ xây dựng một lớp xử lý việc tạo và thao tác trên các chuỗi (string). C++ không cài sẵn kiểu dữ liệu chuỗi. Nhưng C++ cho phép chúng ta thêm kiểu chuỗi như một lớp thông qua cơ chế đa năng hóa.
CT4_18.CPP1: #include <iostream.h> 2: #include <iomanip.h>3: #include <string.h>4: #include <assert.h>5: 6: class String7: {8: private:9: char *Ptr; //Con trỏ trỏ đến điểm bắt đầu của chuỗi10: int Length; //Chiều dài chuỗi11: public:12: String(const char * = ""); //Constructor chuyển đổi13: String(const String &); //Constructor sao chép14: ~String(); //Destructor15: const String &operator=(const String &); //Phép gán16: String &operator+=(const String &); //Phép nối17: int operator!() const; //Kiểm tra chuỗi rỗng18: int operator==(const String &) const;19: int operator!=(const String &) const;20: int operator<(const String &) const;21: int operator>(const String &) const;22: int operator>=(const String &) const;23: int operator<=(const String &) const;24: char & operator[](int); //Trả về ký tự tham chiếu25: String &operator()(int, int); //Trả về một chuỗi con26: int GetLength() const;27: friend ostream &operator<<(ostream &, const String &);28: friend istream &operator>>(istream &, String &);29: };30:31: //Constructor sao chép: Chuyển đổi char * thành String32: String::String(const char *S)33: {34: cout << "Conversion constructor: " << S << endl;35: Length = strlen(S);36: Ptr = new char[Length + 1];37: assert(Ptr != 0);38: strcpy(Ptr, S);39: }40:41: String::String(const String &Copy)42: {43: cout << "Copy constructor: " << Copy.Ptr << endl;44: Length = Copy.Length;45: Ptr = new char[Length + 1];46: assert(Ptr != 0);47: strcpy(Ptr, Copy.Ptr);48: }49:50: //Destructor51: String::~String()52: {53: cout << "Destructor: " << Ptr << endl;54: delete [] Ptr;55: }56:57: const String &String::operator=(const String &Right)58: {59: cout << "operator= called" << endl;60: if (&Right != this)61: {62: delete [] Ptr;63: Length = Right.Length;64: Ptr = new char[Length + 1];65: assert(Ptr != 0);66: strcpy(Ptr, Right.Ptr);67: }68: else69: cout << "Attempted assignment of a String to itself" << endl;70: return *this;71: }72:73: String &String::operator+=(const String &Right)74: {75: char *TempPtr = Ptr;76: Length += Right.Length;77: Ptr = new char[Length + 1];78: assert(Ptr != 0);79: strcpy(Ptr, TempPtr);80: strcat(Ptr, Right.Ptr);81: delete [] TempPtr;82: return *this;83: }84:85: int String::operator!() const86: {87: return Length == 0;88: }89:90: int String::operator==(const String &Right) const91: {92: return strcmp(Ptr, Right.Ptr) == 0;93: }94:95: int String::operator!=(const String &Right) const96: {97: return strcmp(Ptr, Right.Ptr) != 0;98: }99:100: int String::operator<(const String &Right) const101: {102: return strcmp(Ptr, Right.Ptr) < 0;103: }104:105: int String::operator>(const String &Right) const106: {107: return strcmp(Ptr, Right.Ptr) > 0;108: }109:110: int String::operator>=(const String &Right) const111: {112: return strcmp(Ptr, Right.Ptr) >= 0;113: }114: 115: int String::operator<=(const String &Right) const116: {117: return strcmp(Ptr, Right.Ptr) <= 0;118: }119:120: char &String::operator[](int Subscript)121: {122: assert(Subscript >= 0 && Subscript < Length);123: return Ptr[Subscript];124: }125:126: String &String::operator()(int Index, int SubLength)127: {128: assert(Index >= 0 && Index < Length && SubLength >= 0);129: String *SubPtr = new String;130: assert(SubPtr != 0);131: if ((SubLength == 0) || (Index + SubLength > Length))132: SubPtr->Length = Length - Index + 1;133: else134: SubPtr->Length = SubLength + 1;135: delete SubPtr->Ptr;136: SubPtr->Ptr = new char[SubPtr->Length];137: assert(SubPtr->Ptr != 0);138: strncpy(SubPtr->Ptr, &Ptr[Index], SubPtr->Length);139: SubPtr->Ptr[SubPtr->Length] = ' ';140: return *SubPtr;141: }142:143: int String::GetLength() const144: {145: return Length;146: }147: ostream &operator<<(ostream &Output, const String &S)148: {149: Output << S.Ptr;150: return Output;151: }152: 153: istream &operator>>(istream &Input, String &S)154: {155: char Temp[100];156: Input >> setw(100) >> Temp;157: S = Temp;158: return Input;159: }160:161: int main()162: {163: String S1("happy"), S2(" birthday"), S3;164: cout << "S1 is "" << S1 << ""; S2 is "" << S2165: << ""; S3 is "" << S3 << '"' << endl166: << "The results of comparing S2 and S1:" << endl167: << "S2 == S1 yields " << (S2 == S1) << endl168: << "S2 != S1 yields " << (S2 != S1) << endl169: << "S2 > S1 yields " << (S2 > S1) << endl170: << "S2 < S1 yields " << (S2 < S1) << endl171: << "S2 >= S1 yields " << (S2 >= S1) << endl172: << "S2 <= S1 yields " << (S2 <= S1) << endl;173: cout << "Testing !S3:" << endl;174: if (!S3)175: {176: cout << "S3 is empty; assigning S1 to S3;" << endl;177: S3 = S1;178: cout << "S3 is "" << S3 << """ << endl;179: }180: cout << "S1 += S2 yields S1 = ";181: S1 += S2;182: cout << S1 << endl;183: cout << "S1 += " to you" yields" << endl;184: S1 += " to you";185: cout << "S1 = " << S1 << endl;186: cout << "The substring of S1 starting at" << endl187: << "location 0 for 14 characters, S1(0, 14), is: "188: << S1(0, 14) << endl;189: cout << "The substring of S1 starting at" << endl190: << "location 15, S1(15, 0), is: "191: << S1(15, 0) <<endl; // 0 is "to end of string"192: String *S4Ptr = new String(S1);193: cout << "*S4Ptr = " << *S4Ptr <<endl;194: cout << "assigning *S4Ptr to *S4Ptr" << endl;195: *S4Ptr = *S4Ptr;196: cout << "*S4Ptr = " << *S4Ptr << endl;197: delete S4Ptr;198: S1[0] = 'H';199: S1[6] = 'B';200: cout <<"S1 after S1[0] = 'H' and S1[6] = 'B' is: "<< S1 << endl;201: cout << "Attempt to assign 'd' to S1[30] yields:" << endl;202: S1[30] = 'd'; //Lỗi: Chỉ số vượt khỏi miền!!!203: return 0;204: } | ||
Chúng ta chạy ví dụ 4.18, kết quả ở hình 4.23
Hình 4.23: Kết quả của ví dụ 4.18
Lớp Date
Ví dụ 4.19:
CT4_19.CPP1: #include <iostream.h> 2: class Date3: {4: private:5: int Month;6: int Day;7: int Year;8: static int Days[]; //Mảng chứa số ngày trong tháng9: void HelpIncrement(); //Hàm tăng ngày lên một10: public:11: Date(int M = 1, int D = 1, int Y = 1900);12: void SetDate(int, int, int);13: Date operator++(); //Tiền tố14: Date operator++(int); //Hậu tố15: const Date &operator+=(int);16: int LeapYear(int); //Kiểm tra năm nhuần17: int EndOfMonth(int); //Kiểm tra cuối tháng18: friend ostream &operator<<(ostream &, const Date &);19: };20:21: int Date::Days[] = {31, 28, 31, 30, 31, 30,31, 31, 30, 31, 30, 31};22:23: Date::Date(int M, int D, int Y)24: {25: SetDate(M, D, Y);26: }27:28: void Date::SetDate(int MM, int DD, int YY)29: {30: Month = (MM >= 1 && MM <= 12) ? MM : 1;31: Year = (YY >= 1900 && YY <= 2100) ? YY : 1900;32: if (Month == 2 && LeapYear(Year))33: Day = (DD >= 1 && DD <= 29) ? DD : 1;34: else35: Day = (DD >= 1 && DD <= Days[Month-1]) ? DD : 1;36: }37:38: Date Date::operator++()39: {40: HelpIncrement();41: return *this;42: }43: Date Date::operator++(int)44: {45: Date Temp = *this;46: HelpIncrement();47: return Temp;48: }49:50: const Date &Date::operator+=(int AdditionalDays)51: {52: for (int I = 1; I <= AdditionalDays; I++)53: HelpIncrement();54: return *this;55: }56:57: int Date::LeapYear(int Y)58: {59: if (Y % 400 == 0 || (Y % 100 != 0 && Y % 4 == 0) )60: return 1; //Năm nhuần61: return 0; //Năm không nhuần62: }63:64: int Date::EndOfMonth(int D)65: {66: if (Month == 2 && LeapYear(Year))67: return D == 29;68: return D == Days[Month-1];69: }70:71: void Date::HelpIncrement()72: {73: if (EndOfMonth(Day) && Month == 12) //Hết năm74: {75: Day = 1;76: Month = 1;77: ++Year;78: }79: else80: if (EndOfMonth(Day)) //Hết tháng81: {82: Day = 1;83: ++Month;84: }85: else86: ++Day;87: }88:89: ostream &operator<<(ostream &Output, const Date &D)90: {91: static char*MonthName[12]={"January","February","March","April","May",92: ; "June","July", "August", "September",93: ; "October","November", "December"94: };95:96: Output << MonthName[D.Month-1] << ' '<< D.Day << ", " << D.Year;97: return Output;98: }99: 100: int main()101: {102: Date D1, D2(12, 27, 1992), D3(0, 99, 8045);103: cout << "D1 is " << D1 << endl104: << "D2 is " << D2 << endl105: << "D3 is " << D3 << endl << endl;106: cout << "D2 += 7 is " << (D2 += 7) << endl << endl;107: D3.SetDate(2, 28, 1992);108: cout << " D3 is " << D3 << endl;109: cout << "++D3 is " << ++D3 << endl << endl;110: Date D4(3, 18, 1969);111: cout << "Testing the preincrement operator:" << endl112: << " D4 is " << D4 << endl;113: cout << "++D4 is " << ++D4 << endl;114: cout << " D4 is " << D4 << endl << endl;115: cout << "Testing the postincrement operator:" << endl116: << " D4 is " << D4 << endl;117: cout << "D4++ is " << D4++ << endl;118: cout << " D4 is " << D4 << endl;119: return 0;120: } | ||
Chúng ta chạy ví dụ 4.19, kết quả ở hình 4.24
Hình 4.24: Kết quả của ví dụ 4.19