24/05/2018, 20:12

Tính kế thừa-2

Một lớp dẫn xuất có thể định nghĩa lại một hàm thành viên lớp cơ sở. Điều này được gọi là overriding. Khi hàm đó được đề cập bởi tên trong lớp dẫn xuất, phiên bản của lớp dẫn xuất được chọn một cách tự động. Toán tử định phạm vi có thể sử ...

Một lớp dẫn xuất có thể định nghĩa lại một hàm thành viên lớp cơ sở. Điều này được gọi là overriding. Khi hàm đó được đề cập bởi tên trong lớp dẫn xuất, phiên bản của lớp dẫn xuất được chọn một cách tự động. Toán tử định phạm vi có thể sử dụng để truy cập phiên bản của lớp cơ sở từ lớp dẫn xuất.

Ví dụ 5.2: Định nghĩa một hàm thành viên lớp cơ sở trong lớp dẫn xuất và project có tên là CT5_2.PRJ

File EMPLOY.H

EMPLOY.H1: //EMPLOY.H2: //Định nghĩa lớp Employee3: #ifndef EMPLOY_H4: #define EMPLOY_H5:6: class Employee7: {8: private:9: char *FirstName;10: char *LastName;11: public:12: Employee(const char *First, const char *Last);13: void Print() const;14: ~Employee();15: };16:17: #endif

File EMPLOY.CPP

EMPLOY.CPP1: //EMPLOY.CPP2: //Định nghĩa các hàm thành viên của lớp Employee3: #include <string.h>4: #include <iostream.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: void Employee::Print() const19: {20: cout << FirstName << ' ' << LastName;21: }22:23: Employee::~Employee()24: {25: delete [] FirstName;26: delete [] LastName;27: }

File HOURLY.H

HOURLY.H1: //HOURLY.H2: //Định nghĩa lớp HourlyWorker3: #ifndef HOURLY_H4: #define HOURLY_H5:6: #include "employ.h"7:8: class HourlyWorker : public Employee9: {10: private:11: float Wage; //Tiền lương cho mỗi giờ12: float Hours; //Số giờ làm việc cho một tuần13: public:14: HourlyWorker(const char *First, const char *Last,15:              float InitHours, float InitWage);16: float GetPay() const; //Tính toán và trả về lương17: void Print() const; //Định nghĩa lại Print() của lớp cơ sở18: };19:20: #endif

File HOURLY.CPP

HOURLY.CPP1: //HOURLY.CPP2: //Định nghĩa các hàm thành viên của lớp HourlyWorker3: #include <iostream.h>4: #include <iomanip.h>5: #include "hourly.h"6:7: HourlyWorker::HourlyWorker(const char *First, const char *Last,8:       float InitHours, float InitWage)9: : Employee(First, Last)10: {11: Hours = InitHours;12: Wage = InitWage;13: }14:15: float HourlyWorker::GetPay() const16: {17: return Wage * Hours;18: }19:20: void HourlyWorker::Print() const21: {22: cout << "HourlyWorker::Print()" << endl;23: Employee::Print(); //Gọi hàm Print() của lớp cơ sở24: cout << " is an hourly worker with pay of"25:            << " $" << setiosflags(ios::showpoint)26:            << setprecision(2) << GetPay() << endl;27: }

File CT5_2.CPP

CT5_2.CPP1: //CT5_2.CPP2: #include <iostream.h>3: #include "hourly.h"4:5: int main()6: {7: HourlyWorker H("Bob", "Smith", 40.0, 7.50);8: H.Print(); //Gọi hàm HourlyWorker::Print()9 cout<<endl<<"Employee::Print()<<endl;10: H.Employee::Print();11: return 0;12: }

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

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

Ví dụ 5.3: Định nghĩa một thành viên dữ liệu của lớp cơ sở trong lớp dẫn xuất.

CT5_3.CPP1: //Chương trình 5.32: #include <iostream.h>3: class Base4: {5: protected:6: int Value;7: public:8: Base(int X)9: {10: Value = X;11: }12: };13:14: class Derived : public Base15: {16: private:17: int Value; //Định nghĩa lại thành viên dữ liệu18: public:19: Derived(int X):Base(X-1)20: {21: Value=X;22: }23: void Print() const24: {25: cout<<"Base::Value="<<Base::Value<<endl;26: cout<<"Derived::Value="<<Value<<endl;27: }28: };29:30:31: int main()32: {33: Derived D(30);34: D.Print();35: return 0;36: }

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

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

Khi dẫn xuất một lớp từ một lớp cơ sở, lớp cơ sở có thể được kế thừa là public, protectedprivate.

class <drived_class_name> : <type_of_inheritance> <base_class_name>

{

………………..

};

Trong đó type_of_inheritancepublic, protected hoặc private. Mặc định là private.

Khi dẫn xuất một lớp từ một lớp cơ sở public, các thành viên public của lớp cơ sở trở thành các thành viên public của lớp dẫn xuất, và các thành viên protected của lớp cơ sở trở thành các thành viên protected của lớp dẫn xuất. Các thành viên private của lớp cơ sở không bao giờ được truy cập trực tiếp từ một lớp dẫn xuất.

Khi dẫn xuất một lớp từ một lớp cơ sở protected, các thành viên public protected của lớp cơ sở trở thành các thành viên protected của lớp dẫn xuất. Khi dẫn xuất một lớp từ một lớp cơ sở private, các thành viên public protected của lớp cơ sở trở thành các thành viên private của lớp dẫn xuất.

Bảng sau (hình 5.6)tổng kết khả năng truy cập các thành viên lớp cơ sở trong một lớp dẫn xuất dựa trên thuộc tính xác định truy cập thành viên của các thành viên trong lớp cơ sở và kiểu kế thừa.

 

Kiểu kế thừa
Kế thừa public Kế thừa protected Kế thừa private
public public trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend và các hàm không thành viên. protected trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend. private trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend.
protected protected trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend. protected trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend. private trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend.
private Dấu trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend thông qua các hàm thành viên public protected của lớp cơ sở. Dấu trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend thông qua các hàm thành viên public protected của lớp cơ sở. Dấu trong lớp dẫn xuất.Có thể truy cập trực tiếp bởi các hàm thành viên không tĩnh, các hàm friend thông qua các hàm thành viên public protected của lớp cơ sở.

Hình 5.7: Tổng kết khả năng truy cập thành viên lớp cơ sở trong lớp dẫn xuất.

Bởi vì một lớp dẫn xuất kết thừa các thành viên lớp cơ sở của nó (ngoại trừ constructor và destructor), khi một đối tượng của lớp dẫn xuất được khởi động, constructor lớp cơ sở phải được gọi để khởi động các thành viên lớp cơ sở của đối tượng lớp dẫn xuất. Một bộ khởi tạo lớp cơ sở (sử dụng cú pháp giống như bộ khởi tạo thành viên) có thể được cung cấp trong constructor lớp dẫn xuất để gọi tường minh constructor lớp cơ sở, mặt khác constructor lớp dẫn xuất sẽ gọi constructor mặc định lớp cơ sở.

Các constructor lớp cơ sở và các toán tử gán lớp cơ sở không được kế thừa bởi lớp dẫn xuất.Tuy nhiên, các constructor và các toán tử gán lớp dẫn xuất có thể gọi các constructor và các toán tử gán lớp cơ sở.

Một constructor lớp dẫn xuất luôn gọi constructor lớp cơ sở của nó đầu tiên để khởi tạo các thành viên lớp cơ sở của lớp dẫn xuất. Nếu constructor lớp dẫn bị bỏ qua, constructor mặc định lớp dẫn gọi constructor lớp cơ sở. Các destructor được gọi theo thứ tự ngược lại thứ tự gọi các constructor, vì thế destructor lớp dẫn xuất được gọi trước destructor lớp cơ sở của nó.

Ví dụ 5.4: Minh họa thứ tự các contructor và destructor lớp cơ sở và lớp dẫn xuất được gọi và project có tên là CT5_4.PRJ

File POINT.H

POINT.H1: //POINT.H2: //Định nghĩa lớp Point3: #ifndef POINT_H4: #define POINT_H5:6: class Point7: {8: public:9: Point(float A= 0.0, float B= 0.0);10: ~Point();11: protected:12: float X, Y;13: };14:15: #endif

File POINT.CPP

POINT.CPP1: //POINT.CPP2: //Định nghĩa các hàm thành viên lớp Point3: #include <iostream.h>4: #include "point.h"5:6: Point::Point(float A, float B)7: {8: X = A;9: Y = B;10: cout << "Point constructor: "11:            << '[' << X << ", " << Y << ']' << endl;12: }13:14: Point::~Point()15: {16: cout << "Point destructor: "17:            << '[' << X << ", " << Y << ']' << endl;18: }

File CIRCLE.H

CIRCLE.H1: //CIRCLE.H2: //Định nghĩa lớp Circle3: #ifndef CIRCLE_H4: #define CIRCLE_H5:6: #include "point.h"7: #include <iomanip.h>8:9: class Circle : public Point10: {11: public:12: Circle(float R = 0.0, float A = 0, float B = 0);13: ~Circle();14: private:15: float Radius;16: };17:18: #endif

File CIRCLE.CPP

CIRCLE.CPP1: //CIRCLE.CPP2: //Định nghĩa các hàm thành viên lớp Circle3: #include "circle.h"4:5: Circle::Circle(float R, float A, float B): Point(A, B)6: {7: Radius = R;8: cout << "Circle constructor: Radius is "9:            << Radius << " [" << A << ", " << B << ']' << endl;10: }11:12: Circle::~Circle()13: {14: cout << "Circle destructor: Radius is "15:            << Radius << " [" << X << ", " << Y << ']' << endl;16: }

File CT5_4.CPP

CT5_4.CPP1: //CT5_4.CPP2: //Chương trình 5.43: #include <iostream.h>4: #include "point.h"5: #include "circle.h"6: int main()7: {8: {9: Point P(1.1, 2.2);10: }11: cout << endl;12: Circle C1(4.5, 7.2, 2.9);13: cout << endl;14: Circle C2(10, 5, 5);15: cout << endl;16: return 0;17: }

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

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

0