24/05/2018, 22:12

Tính đa hình-các thành viên ảo của một lớp

Toán tử ảo Toán tử thực chất cũng là một hàm nên chúng ta có thể tạo ra các toán tử ảo trong một lớp. Tuy nhiên do đa năng hóa khi tạo một toán tử cần chú ý đến các kiểu của các toán hạng phải sử dụng kiểu của lớp cơ sở ...

Toán tử ảo

Toán tử thực chất cũng là một hàm nên chúng ta có thể tạo ra các toán tử ảo trong một lớp. Tuy nhiên do đa năng hóa khi tạo một toán tử cần chú ý đến các kiểu của các toán hạng phải sử dụng kiểu của lớp cơ sở gốc có toán tử ảo.

Ví dụ 6.4: Đa năng hóa toán tử với hàm toán tử là phương thức ảo.

CT6_4.CPP1: //Chương trình 6.4: Toán tử ảo2: #include <iostream.h>3:4: class A5: {6: protected:7: int X1;8: public:9: A(int I)10: {11: X1=I;12: }13: virtual A& operator + (A& T);14: virtual A& operator = (A& T);15: virtual int GetA()16: {17: return X1;18: }19: virtual int GetB()20: {21: return 0;22: }23: virtual int GetC()24: {25: return 0;26: }27: void Print(char *St)28: {29: cout<<St<<":X1="<<X1<<endl;30: }31: };32:33: class B : public A34: {35: protected:36: int X2;37: public:38: B(int I,int J):A(I)39: {40: X2=J;41: }42: virtual A& operator + (A& T);43: virtual A& operator = (A& T);44: virtual int GetB()45: {46: return X2;47: }48: void Print(char *St)49: {50: cout<<St<<":X1="<<X1<<",X2="<<X2<<endl;51: }52: };53:54: class C : public B55: {56: protected:57: int X3;58: public:59: C(int I,int J,int K):B(I,J)60: {61: X3=K;62: }63: virtual A& operator + (A& T);64: virtual A& operator = (A& T);65: virtual int GetC()66: {67: return X3;68: }69: void Print(char *St)70: {71: cout<<St<<":X1="<<X1<<",X2="<<X2<<",X3="<<X3<<endl;72: }73: };74:75: A& A::operator + (A& T)76: {77: X1+=T.GetA();78: return *this;79: }80:81: A& A::operator = (A& T)82: {83: X1=T.GetA();84: return *this;85: }86:87: A& B::operator + (A& T)88: {89: X1+=T.GetA();90: X2+=T.GetB();91: return *this;92: }93:94: A& B::operator = (A& T)95: {96: X1=T.GetA();97: X2=T.GetB();98: return *this;99: }100:101:A& C::operator + (A& T)102: {103: X1+=T.GetA();104: X2+=T.GetB();105: X3+=T.GetC();106: return *this;107 }108:109: A& C::operator = (A& T)110: {111: X1=T.GetA();112: X2=T.GetB();113: X3=T.GetC();114: return *this;115: }116:117: void AddObject(A& T1,A& T2)118: {119: T1=T1+T2;120: }121:122: int main()123: {124: A a(10);125: B b(10,20);126: C c(10,20,30);127: a.Print("a");128: b.Print("b");129: c.Print("c");130: AddObject(a,b);131: a.Print("a");132: AddObject(b,c);133: b.Print("b");134: AddObject(c,a);135: c.Print("c");136: a=b+c;137: a.Print("a");138: c=c+a;139: c.Print("c");140: return 0;141: }

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

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

Có constructor và destructor ảo hay không?:

Khi một đối tượng thuộc lớp có phương thức ảo, để thực hiện cơ chế kết nối động, trình biên dịch sẽ tạo thêm một con trỏ vptr như một thành viên của lớp, con trỏ này có nhiệm vụ quản lý địa chỉ của phương thức ảo. Một lớp chỉ có một bảng phương thức ảo, trong khi đó có thể có nhiều đối tượng thuộc lớp, nên khi một đối tượng khác thuộc cùng lớp tạo ra thì con trỏ vptr đã còn tại. Chính vì vậy bảng phương thức ảo phải được tạo ra trước khi gọi thực hiện constructor, nên constructor không thể là phương thức ảo. Ngược lại do một lớp chỉ có một bảng phương thức ảo nên khi một đối tượng thuộc lớp bị hủy bỏ, bảng phương thức ảo vẫn còn đó, và con trỏ vptr vẫn còn đó. Hơn nữa, destructor được gọi thực hiện trước khi vùng nhớ dành cho đối tượng bị thu hồi, do đó destructor có thể là phương thức ảo. Tuy nhiên, constructor của một lớp có thể gọi phương thức ảo khác. Điều này hoàn toàn không có gì mâu thuẫn với cơ chế kết nối động.

Ví dụ 6.5:

CT6_5.CPP1: //Chương trình 6.5: Destructor ảo2: #include <iostream.h>3:4: class Base5: {6: public:7: virtual ~Base()8: {9: cout<<"~Base"<<endl;10: }11: };12:13: class Derived:public Base14: {15: public:16: virtual ~Derived()17: {18: cout<<"~Derived"<<endl;18: }19: };20:21: int main()22: {23: Base *B;24: B = new Derived;25: delete B;26: return 0;27: }

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

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

Nếu destructor không là phương thức ảo thì khi giải phóng đối tượng B chỉ có destructor của lớp cơ sơ được gọi mà thôi nhưng khi destructor là phương thức ảo thì khi giải phóng đối tượng B (ở dòng 25) destructor của lớp dẫn xuất được gọi thực hiện rồi đến destructor của lớp cơ sơ.

: Chúng ta sử dụng các phương thức ảo và tính đa hình để thực hiện bảng lương dựa trên loại người lao động. Chúng ta sử dụng lớp cơ sở Employ. Các lớp dẫn xuất của EmployBoss mà người được trả tiền lương hàng tuần cố định bất chấp số giờ làm việc, CommissionWorker mà người có tiền lương cơ sở cộng thêm phần trăm của hàng bán, PieceWorker mà người được trả bởi số các mục sản xuất, và HourlyWorker mà người được trả bởi theo giờ và nhận ngoài giờ.

Ví dụ 6.6:

File EMPLOY.H

EMPLOY.H1: //EMPLOY.H2: //Lớp cơ sở trừu tượng Employee3: #ifndef EMPLOY_H4: #define EMPLOY_H5: class Employee6: {7: private:8: char *FirstName;9: char *LastName;10: public:11: Employee(const char *First, const char *Last);12: ~Employee();13: const char *GetFirstName() const;14: const char *GetLastName() const;15: virtual float Earnings() const = 0;16: virtual void Print() const = 0;17: };18:19: #endif

File EMPLOY.CPP

EMPLOY.CPP1: //EMPLOY.CPP2: //Định nghĩa hàm thành viên của lớp cơ sở trừu tượng3: #include <iostream.h>4: #include <string.h>5: #include <assert.h>6: #include "employ.h"7:8: Employee::Employee(const char *First, const char *Last)9: {10: FirstName = new char[ strlen(First) + 1 ];11: assert(FirstName != 0);12: strcpy(FirstName, First);13: LastName = new char[ strlen(Last) + 1 ];14: assert(LastName != 0);15: strcpy(LastName, Last);16: }17:18: Employee::~Employee()19: {20: delete [] FirstName;21: delete [] LastName;22: }23:24: const char *Employee::GetFirstName() const25: {26: return FirstName; //Nơi gọi phải xóa vùng nhớ27: }28:29: const char *Employee::GetLastName() const30: {31: return LastName; //Nơi gọi phải xóa vùng nhớ32: }

File BOSS.H

BOSS.H1: //BOSS.H2: //Lớp Boss dẫn xuất từ Employee3: #ifndef BOSS_H4: #define BOSS_H5: #include "employ.h"6:7: class Boss : public Employee8: {9: private:10: float WeeklySalary;11: public:12: Boss(const char * First, const char *Last, float S = 0.0);13: void SetWeeklySalary(float S);14: virtual float Earnings() const;15: virtual void Print() const;16: };17:18: #endif

File BOSS.CPP

BOSS.CPP1: //BOSS.CPP2: //Định nghĩa hàm thành viên của lớp Boss3: #include <iostream.h>4: #include "boss.h"5:6: Boss::Boss(const char *First, const char *Last, float S) : Employee(First, Last)7: {8: WeeklySalary = S > 0 ? S : 0;9: }10:11: void Boss::SetWeeklySalary(float S)12: {13: WeeklySalary = S > 0 ? S : 0;14: }15:16: float Boss::Earnings() const17: {18: return WeeklySalary;19: }20:21: void Boss::Print() const22: {23: cout << endl << " Boss: " << GetFirstName()24:          << ' ' << GetLastName();25: }

File COMMIS.H

COMMIS.H1: //COMMIS.H2: //Lớp CommissionWorker dẫn xuất từ Employee3: #ifndef COMMIS_H4: #define COMMIS_H5: #include "employ.h"6:7: class CommissionWorker : public Employee8: {9: private:10: float Salary; ; //Lương cơ bản mỗi tuần11: float Commission; //Khối lượng các mục được bán12: unsigned Quantity; //Tổng các mục bán mỗi tuần13: public:14: CommissionWorker(const char *First, const char *Last,15: float S = 0.0, float C = 0.0, unsigned Q = 0);16: void SetSalary(float S);17: void SetCommission(float C);18: void SetQuantity(unsigned Q);19: virtual float Earnings() const;20: virtual void Print() const;21: };22: 23: #endif

File COMMIS.CPP

COMMIS.CPP1: //COMMIS.CPP2: //Định nghĩa hàm thành viên của lớp CommissionWorker3: #include <iostream.h>4: #include "commis.h"5:6: CommissionWorker::CommissionWorker(const char *First,const char *Last,7: float S, float C, unsigned Q)8: : Employee(First, Last)9: {10: Salary = S > 0 ? S : 0;11: Commission = C > 0 ? C : 0;12: Quantity = Q > 0 ? Q : 0;13: }14:15: void CommissionWorker::SetSalary(float S)16: {17: Salary = S > 0 ? S : 0;18: }19:20: void CommissionWorker::SetCommission(float C)21: {22: Commission = C > 0 ? C : 0;23: }24:25: void CommissionWorker::SetQuantity(unsigned Q)26: {27: Quantity = Q > 0 ? Q : 0;28: }29:30: float CommissionWorker::Earnings() const31: {32: return Salary + Commission * Quantity;33: }34:35: void CommissionWorker::Print() const36: {37: cout << endl << "Commission worker: " << GetFirstName()38:          << ' ' << GetLastName();39: }

File HOURLY.H

HOURLY.H1: //HOURLY.H2: //Định nghĩa lớp HourlyWorker3: #ifndef HOURLY_H4: #define HOURLY_H5: #include "employ.h"6:7: class HourlyWorker : public Employee8: {9: private:10: float Wage; //Tiền lương mỗi giờ11: float Hours; //Số giờ làm việc mỗi tuần12: public:13: HourlyWorker(const char *First, const char * Last,14:               #9; #9; #9; float = 0.0, float = 0.0);15: void SetWage(float W);16: void SetHours(float H);17: virtual float Earnings() const;18: virtual void Print() const;19: };20:21: #endif

File HOURLY.CPP

HOURLY.CPP1: //HOURLY.CPP2: //Định nghĩa hàm thành viên của lớp HourlyWorker3: #include <iostream.h>4: #include "hourly.h"5:6: HourlyWorker::HourlyWorker(const char *First, const char *Last,7:       float W, float H)8: : Employee(First, Last)9: {10: Wage = W > 0 ? W : 0;11: Hours = H >= 0 && H < 168 ? H : 0;12: }13:14: void HourlyWorker::SetWage(float W)15: {16: Wage = W > 0 ? W : 0;17: }18:19: void HourlyWorker::SetHours(float H)20: {21: Hours = H >= 0 && H < 168 ? H : 0;22: }23:24: float HourlyWorker::Earnings() const25: {26: return Wage * Hours;27: }28:29: void HourlyWorker::Print() const30: {31: cout << endl << " Hourly worker: " << GetFirstName()32:          << ' ' << GetLastName();33: }

File PIECE.H

PIECE.H1: //PIECE.H2: //Lớp PieceWorker dẫn xuất từ Employee3: #ifndef PIECE_H4: #define PIECE_H5: #include "employ.h"6:7: class PieceWorker : public Employee8: {9: private:10: float WagePerPiece; //Lương cho mỗi mãnh11: unsigned Quantity; //Đầu ra mỗi tuần12: public:13: PieceWorker(const char *First, const char * Last, float W=0.0, 14:              unsigned Q=0);15: void SetWage(float W);16: void SetQuantity(unsigned Q);17: virtual float Earnings() const;18: virtual void Print() const;19: };20: 21: #endif

File PIECE.CPP

PIECE.CPP1: //PIECE.CPP2: //Định nghĩa hàm thành viên của lớp PieceWorker3: #include <iostream.h>4: #include "piece.h"5: 7: PieceWorker::PieceWorker(const char *First, const char *Last,8:                       float W, unsigned Q)9: : Employee(First, Last)10: {11: WagePerPiece = W > 0 ? W : 0;12: Quantity = Q > 0 ? Q : 0;13: }14:15: void PieceWorker::SetWage(float W)16: {17: WagePerPiece = W > 0 ? W : 0;18: }19:20: void PieceWorker::SetQuantity(unsigned Q)21: {22: Quantity = Q > 0 ? Q : 0;23 }24:25: float PieceWorker::Earnings() const26: {27: return Quantity * WagePerPiece;28: }29:30: void PieceWorker::Print() const31: {32: cout << endl << " Piece worker: " << GetFirstName()33:          << ' ' << GetLastName();34: }

File CT6_6.CPP

CT6_6.CPP1: //CT6_6.CPP2: //Chương trình 6.63: #include <iostream.h>4: #include <iomanip.h>5: #include "employ.h"6: #include "boss.h"7: #include "commis.h"8: #include "piece.h"9: #include "hourly.h"10:11: int main()12: {13: //Ấn định dạng xuất14: cout << setiosflags(ios::showpoint) << setprecision(2);15: Employee *Ptr;16: Boss B("John", "Smith", 800.00);17: Ptr = &B;18: Ptr->Print();19: cout << " earned $" << Ptr->Earnings();20: B.Print();21: cout << " earned $" << B.Earnings();22: CommissionWorker C("Sue", "Jones", 200.0, 3.0, 150);23: Ptr = &C;24: Ptr->Print();25: cout << " earned $" << Ptr->Earnings();26: C.Print();27: cout << " earned $" << C.Earnings();28: PieceWorker P("Bob", "Lewis", 2.5, 200);29: Ptr = &P;30: Ptr->Print();31: cout << " earned $" << Ptr->Earnings();32: P.Print();33: cout << " earned $" << P.Earnings();34: HourlyWorker H("Karen", "Price", 13.75, 40);35: Ptr = &H;36: Ptr->Print();37: cout << " earned $" << Ptr->Earnings();38: H.Print();39: cout << " earned $" << H.Earnings();40: cout << endl;41: return 0;42: }

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

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

Ví dụ 6.7: Viết lại chương trình ở ví dụ 5.6 với lớp cơ sở trừu tượng Shape.

File SHAPE.H

SHAPE.H1: //SHAPE.H2: //Định nghĩa lớp cơ sở trừu tượng Shape3: #ifndef SHAPE_H4: #define SHAPE_H5:6: class Shape7: {8: public:9: virtual float Area() const10: {11: return 0.0;12: }13: virtual float Volume() const14: {15: return 0.0;16: }17: virtual void PrintShapeName() const = 0;18: virtual void Print() const = 0;19: };20:21: #endif

File POINT.H

POINT.H1: //POINT.H2: //Định nghĩa lớp Point3: #ifndef POINT_H4: #define POINT_H5: #include <iostream.h>6: #include "shape.h"7:8: class Point : public Shape9: {10: private:11: float X, Y;12: public:13: Point(float A = 0, float B = 0);14: void SetPoint(float A, float B);15: float GetX() const16: {17: return X;18: }19: float GetY() const20: {21: return Y;22: }23: virtual void PrintShapeName() const24: {25: cout << "Point: ";26: }27: virtual void Print() const;28: friend ostream &operator <<(ostream &Ouput, const Point &P);29: };30:31: #endif

File POINT.CPP

POINT.CPP1: //POINT.CPP2: //Định nghĩa hàm thành viên cho lớp Point3: #include <iostream.h>4: #include "point.h"5:6: Point::Point(float A, float B)7: {8: SetPoint(A, B);9: }10:11: void Point::SetPoint(float A, float B)12: {13: X = A;14: Y = B;15: }16:17: void Point::Print() const18: {19: cout << '[' << X << ", " << Y << ']';20: }21:22: ostream &operator<<(ostream &Output, const Point &P)23: {24: P.Print();25: return Output;26: }
0