1. Trang chủ
  2. » Giáo án - Bài giảng

chương 3 lập trình hướng đối tượng

82 662 5
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Lập trình hướng đối tượng
Thể loại Chương
Định dạng
Số trang 82
Dung lượng 229,5 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

- Lớp dẫn xuất sẽ thừa kế các thành phần dữ liệu, hàm của lớp cơ sở, đồng thời thêm vào các thành phần mới, bao hàm cả việc làm “tốt hơn” hoặc làm lại những công việc mà trong lớp cơ sở

Trang 1

Chương 4:

SỰ KẾ THỪA

Trang 2

Nội dung chương 4

1.Giới thiệu về sự kế thừa

1.1.Khái niệm

1.2.Ví dụ

1.3.Ý nghĩa

1.4.Chương trình minh họa

2 Kế thừa đơn

2.1.Khái niệm

2.2.Ví dụ

2.3.Phương thức thiết lập và hủy bỏ

2.4.Sự tự động kế thừa các đặc tính lớp cha

2.5.Định nghĩa lại thao tác ở lớp con

2.6.Ràng buộc ngữ nghĩa ở lớp con

Trang 3

Nội dung chương 4 (tt)

2.7.Phạm vi truy xuất

2.7.1.Truy xuất theo chiều dọc 2.7.2.Truy xuất theo chiều ngang 2.8.Con trỏ và sự kế thừa

3.Đa kế thừa

Trang 4

1.Giới thiệu về sự kế thừa

Trang 5

1.1.Khái niệm về sự kế thừa

- Sự kế thừa là một đặc điểm của ngôn ngữ dùng để

biểu diễn mối quan hệ đặc biệt giữa các lớp Các lớp được trừu tượng hóa và tổ chức thành một sơ đồ phân cấp lớp

- Sự kế thừa là một mức cao hơn của trừu tượng hóa, cung cấp một cơ chế gom chung các lớp có liên quan với nhau thành một mức khái quát hóa đặc trưng cho toàn bộ các lớp nói trên

- Các lớp với các đặc điểm tương tự nhau có thể được tổ chức thành một sơ đồ phân cấp kế thừa Lớp ở trên cùng là trừu tượng hóa của toàn bộ các lớp ở bên dưới nó

Trang 6

1.1.Khái niệm về sự kế thừa

- Thừa kế cho phép ta định nghĩa 1 lớp mới , gọi là lớp con (subclass) hay lớp dẫn xuất (derived class) từ một lớp đã có, gọi là lớp cha (superclass) hay lớp cơ sở (base class).

- Lớp dẫn xuất sẽ thừa kế các thành phần (dữ liệu, hàm) của lớp cơ sở, đồng thời thêm vào các thành phần mới, bao hàm cả việc làm “tốt hơn” hoặc làm lại những công việc mà trong lớp cơ sở chưa làm tốt hoặc không còn phù hợp với lớp dẫn xuất

Trang 7

1.1.Khái niệm về sự kế thừa

- Thừa kế cho phép nhiều lớp có thể dẫn xuất từ 1 lớp cơ sở

- Thừa kế cũng cho phép một lớp có thể là dẫn xuất của nhiều lớp cơ sở

- Thừa kế không chỉ giới hạn ở 1 mức: Một lớp dẫn xuất có thể là lớp cơ sở cho các lớp dẫn xuất khác

Trang 8

1.2.Ví dụ về sự kế thừa

• Ta tổ chức lớp sinh viên kế thừa từ lớp người Lớp người được gọi là lớp cha (superclass) hay lớp cơ sở (base class) Lớp sinh viên được gọi là lớp con

(subclass) hay lớp dẫn xuất (derived class)

• Ngoài ra, ta có thể tổ chức 2 lớp nam sinh và lớp nữ sinh là 2 lớp con (lớp dẫn xuất) của lớp sinh viên Trường hợp này, lớp sinh viên trở thành lớp cha (lớp

cơ sở) của 2 lớp này

Trang 9

1.2.Ví dụ về sự kế thừa

Hình 4.1: Sơ đồ phân cấp kế thừa

Trang 10

1.2.Ví dụ về sự kế thừa

Trang 11

1.2.Ví dụ về sự kế thừa

Hình 4.2: Sơ đồ phân cấp kế thừa

Trang 12

1.3.Ý nghĩa sự kế thừa

• Kế thừa tạo khả năng xây dựng lớp mới từ lớp đã có, trong đó hàm thành phần được thừa hưởng từ

lớp cha Trong C++, kế thừa còn định nghĩa sự

tương thích, nhờ đó ta có cơ chế chuyển kiểu tự

động

• Kế thừa vừa có khả năng tạo cơ chế khái quát hoá vừa có khả năng chuyên biệt hoá.

• Kế thừa cho phép tổ chức các lớp chia sẻ mã

chương trình chung nhờ vậy có thể dễ dàng sửa

chữa, nâng cấp hệ thống

Trang 13

1.4.Chương trình minh họa

~Nguoi() {delete [] HoTen;}

void An() const { cout<<HoTen<<" an 3 chen com";} void Ngu() const { cout<<HoTen<< " ngu ngay 8

tieng";}

void Xuat() const;

friend ostream& operator << (ostream &os, Nguoi& p);

};

Trang 14

1.4.Chương trình minh họa

class SinhVien : public Nguoi {

~SinhVien() {delete [] MaSo;}

void Xuat() const;

Trang 15

1.4.Chương trình minh họa

cout << "Nguoi, ho ten: " << HoTen

<< " sinh " << NamSinh;

}

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo

<< ", ho ten: " << HoTen;

}

Trang 16

1.4.Chương trình minh họa

void main() {

Nguoi p1(" Le Van Nhan",1980);

SinhVien s1("Vo Vien Sinh", "200002541",1984);

Trang 17

2.Kế thừa đơn

2.1.Khái niệm

- Kế thừa đơn cho phép một lớp là dẫn xuất của chỉ duy nhất 1 lớp cơ sở

- Kế thừa có thể được thực hiện để thể hiện mối

quan hệ 'là một'

Trang 18

2.3.Phương thức thiết lập và hủy

bỏ

• Phương thức thiết lập và huỷ bỏ là các hàm thành phần đặc

biệt dùng để tự động khởi động đối tượng khi nó được tạo ra và tự động dọn dẹp đối tượng khi nó bị hủy đi

• Một đối tượng thuộc lớp con có chứa các thành phần dữ liệu của các lớp cơ sở Có thể xem lớp con có các thành phần ngầm định ứng với các lớp cơ sở Vì vậy khi một đố tượng thuộc lớp con được tạo ra, các thành phần cơ sở cũng được tạo ra, nghĩa là phương thức thiết lập của các lớp cơ sở phải được gọi.

• Trình biên dịch tự động gọi phương thức thiết lập của các lớp

cơ sở cho các đối tượng (cơ sở) nhúng vào đối tượng đạng được tạo ra.

• Đối với phương thức thiết lập của một lớp con, công việc đầu tiên là gọi phương thức thiết lập của các lớp cơ sở.

Trang 19

2.3.Phương thức thiết lập và hủy bỏ

• Nếu mọi phương thức thiết lập của lớp cơ sở đều đòi hỏi phải cung cấp tham số thì lớp con bắt buộc phải có phương thức thiết lập để cung cấp các tham số đó

Trang 20

Cung cấp tham số cho phương thức thiết lập của lớp cha

• Trong trường hợp đó, lớp con bắt buộc phải có phương thức thiết lập để cung cấp tham số cho phương thức thiết lập của lớp cơ sở Cú pháp để gọi phương thức thiết lập của lớp cơ

sở tương tự như cú pháp thiết lập đối tượng thành phần, bản thân tên lớp cơ sở được quan điểm như đối tượng thành

phần nhúng vào lớp con.

void Ve(int color) const;

void TinhTien(double dx, double dy) const;

};

HinhTron t(200,200,50); // Dung

Trang 21

2.3.Phương thức thiết lập và hủy bỏ

SinhVien(char *ht, char *ms, int ns) :

Nguoi(ht,ns) { MaSo = strdup(ms);

}

void Xuat() const;

};

Trang 22

2.3.Phương thức thiết lập và hủy

bỏ

• Sau khi phương thức thiết lập của các lớp cơ sở

được gọi, mã chương trình trong bản thân phương thức của lớp con sẽ được thực hiện Nội dung của phương thức thiết lập ở lớp con chỉ nên thao tác

trên dữ liệu của riêng lớp con, việc khởi động dữ liệu thuộc lớp cha do phương thức thiết lập ở lớp cha đảm nhiệm với các tham số cung cấp bởi lớp con

class SinhVien : public Nguoi

{

char *MaSo;

public:

SinhVien(char *ht, char *ms, int ns) :

Nguoi(ht,ns) { MaSo = strdup(ms); }

void Xuat() const;

};

Trang 23

2.3.Phương thức thiết lập và hủy bỏ

• Ta có thể khởi động các thành phần của lớp cha

bên trong phương thức thiết lập của lớp con Trong trường hợp này đối tượng thuộc lớp cha phải có khả năng tự khởi động:

class Complex {

protected:

double re, im;

public:

Complex(double r = 0, double i = 0):re(r), im(i){}

Complex operator +(Complex b);

Complex operator -(Complex b);

Complex operator *(Complex b);

Complex operator /(Complex b);

double Norm() const {return sqrt(re*re + im*im);}

Trang 24

2.3.Phương thức thiết lập và hủy bỏ

• Hai cách thiết lập đối tượng thuộc lớp con sau đây tương đương:

Trang 25

2.3.Phương thức thiết lập và hủy

Nguoi(char *ht = "Ng Van A", int ns = 1980)

:NamSinh(ns) {HoTen = strdup(ht);}

~Nguoi() {delete [] HoTen;}

//

};

Trang 26

2.3.Phương thức thiết lập và hủy bỏ

// Cach 1

class SinhVien : public Nguoi {

char *MaSo;

public:

SinhVien(char *ht, char *ms, int ns) :

Nguoi(ht,ns) { MaSo = strdup(ms); }

void Xuat() const;

SinhVien(char *ht, char *ms, int ns) {

HoTen = strdup(ht); MaSo = strdup(ms);

NamSinh = ns;

}

};

Trang 27

2.3.Phương thức thiết lập và hủy

bỏ

• Phương thức thiết lập sao chép là cần thiết

trong trường hợp đối tượng có nhu cầu cấp phát tài nguyên.

Trang 28

2.3.Phương thức thiết lập và hủy bỏ

Vấn đề: Ở lớp con có cần phương thức thiết lập sao chép ?

Trường hợp 1:

Không dùng phương thức thiết lập sao chép ở lớp con

class SinhVien : public Nguoi {

Trang 29

2.Phương thức thiết lập và hủy bỏ

Trường hợp 2:

Dùng phương thức thiết lập sao chép ở lớp con

class SinhVien : public Nguoi {

char *MaSo;

public:

SinhVien(char *ht, char *ms, int ns) :

Nguoi(ht,ns) { MaSo = strdup(ms);}

SinhVien(const SinhVien &s) : Nguoi(s)

Trang 30

2.3.Phương thức thiết lập và hủy bỏ

• Khi một đối tượng bị huỷ đi, phương thức huỷ bỏ của nó sẽ được gọi, sau đó phương thức huỷ bỏ của các lớp cơ sở sẽ được gọi một cách tự động Vì vậy lớp con không cần và cũng không được thực hiện các thao tác dọn dẹp cho các thành phần thuộc lớp cha.

class SinhVien : public Nguoi {

Trang 31

2.4 Sự tự động kế thừa các đặc tính của lớp cha

• Về mặt dữ liệu: Mỗi đối tượng sinh viên tự động có thành

phần dữ liệu họ tên và năm sinh của người.

• Về mặt thao tác: Lớp sinh viên được tự động kế thừa các

thao tác của lớp cha Đây chính là khả năng sử dụng lại mã chương trình.

• Riêng phương thức thiết lập không được kế thừa

Trang 32

2.4 Sự tự động kế thừa các đặc tính của lớp cha

Nguoi p1("Le Van Nhan",1980);

SinhVien s1("Vo Vien Sinh", "200002541",1984); p1.An(); cout << "\n";

s1.An(); cout << "\n"; // Tu lop Nguoi

p1.Xuat(); cout << "\n";

Trang 33

2.5.Định nghĩa lại thao tác ở lớp con

• Ta có thể định nghĩa lại các đặc tính ở lớp con đã có ở lớp cha, việc định nghĩa chủ yếu là thao tác, bằng cách khai báo giống hệt như ở lớp cha.

class SinhVien : public Nguoi {

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo << ", ho ten: " << HoTen;

}

Trang 34

• Việc định nghĩa lại thao tác ở lớp con được thực hiện khi thao tác ở lớp con khác thao tác ở lớp cha Thông thường là các thao tác xuất, nhập.

• Ta cũng có thể định nghĩa lại thao tác ở lớp con trong trường hợp giải thuật ở lớp con đơn giản hơn (tô màu đa giác, tính modun của số ảo ).

class DaGiac {

//

void Ve() const;

void ToMau() const;

Trang 35

• Hoặc ở lớp con, thao tác không có tác dụng

Trang 36

2.6.Ràng buộc ngữ nghĩa ở lớp

con

• Kế thừa có thể được áp dụng cho quan hệ kế thừa mang ý nghĩa ràng buộc, đối tượng ở lớp con là đối tượng ở lớp cha nhưng có dữ liệu bị ràng buộc.

– Hình tròn là Ellipse ràng buộc bán kính ngang dọc bằng nhau.

– Số ảo là số phức ràng buộc phần thực bằng 0.

– Hình vuông là hình chữ nhật ràng buộc hai cạnh ngang và dọc bằng nhau…

• Trong trường hợp này, các hàm thành phần phải bảo đảm sự ràng buộc dữ liệu được tôn trọng Lớp số ảo sau đây là một ví dụ minh hoạ.

Trang 37

2.6.Ràng buộc ngữ nghĩa ở lớp

con

class Complex {

friend ostream& operator <<(ostream&, Complex);

friend class Imag;

double re, im;

public:

Complex(double r = 0, double i = 0):re(r), im(i) {}

Complex operator +(Complex b);

Complex operator -(Complex b);

Complex operator *(Complex b);

Complex operator /(Complex b);

double Norm() const {return sqrt(re*re + im*im);} };

Trang 38

class Imag: public Complex {

public:

Imag(double i = 0):Complex(0, i){}

Imag(const Complex &c) : Complex(0, c.im){} Imag& operator = (const Complex &c)

{re = 0; im = c.im; return *this;}

double Norm() const {return fabs(im);}

cout << "z1 = " << z1 << "\n";

cout << "i = " << i << "\n";

cout << "j = " << j << "\n";

}

Trang 39

2.6.Ràng buộc ngữ nghĩa ở lớp

class Imag: public Complex {

public:

//

Imag(const Complex &c) : Complex(0, c.im){}

Imag& operator = (const Complex &c)

{re = 0; im = c.im; return *this;}

};

Trang 40

2.7.Phạm vi truy xuất

• Khi thiết lập quan hệ kế thừa, ta vẫn ph i quan tâm đến ải quan tâm đến tính đóng gói và che dấu thông tin Điều này dẫn đến vấn đề xác định ảnh hưởng của kế thừa đến phạm vi truy xuất các thành phần của lớp Hai vấn đề được đặt ra là:

• Truy xuất theo chiều dọc: Hàm thành phần của lớp con có

quyền truy xuất các thành phần riêng tư của lớp cha hay không ? Vì chiều truy xuất là từ lớp con, cháu lên lớp cha nên ta gọi là truy xuất theo chiều dọc

• Truy xuất theo chiều ngang: Các thành phần của lớp cha,

sau khi kế thừa xuống lớp con, thì thế giới bên ngoài có quyền truy xuất thông qua đối tượng của lớp con hay

không ? Trong trường hợp này, ta gọi là truy xuất theo

chiều ngang.

Trang 41

7.1.Truy xuất theo chiều dọc

• Lớp con có quyền truy xuất các thành phần của lớp cha hay không, hay tổng quát hơn, nơi nào có quyền truy xuất các thành phần của lớp cha, hoàn toàn do lớp cha quyết định Điều đó được xác định bằng

thuộc tính kế thừa

• Trong trường hợp lớp sinh viên kế thừa từ lớp người, truy xuất theo chiều dọc có nghĩa liệu lớp sinh viên có quyền truy xuất các thành phần họ tên, năm sinh của lớp người hay không Chính xác hơn một đối

tượng sinh viên có quyền truy xuất họ tên của chính mình nhưng được khai báo ở lớp người hay không?

Trang 42

2.7.1.Truy xuất theo chiều dọc

• Thuộc tính kế thừa là đặc tính của một thành phần của lớp

cho biết những nơi nào có quyền truy xuất thành phần đó.

- Thuộc tính public: Thành phần nào có thuộc tính public thì

có thể được truy xuất từ bất cứ nơi nào (từ sau khai báo

lớp).

- Thuộc tính private: Thành phần nào có thuộc tính private

thì nó là riêng tư của lớp đó Chỉ có các hàm thành phần của lớp và ngoại lệ là các hàm bạn được phép truy xuất, ngay cả các lớp con cũng không có quyền truy xuất.

- Thuộc tính protected: cho phép qui định một vài thành

phần nào đó của lớp là bảo mật, theo nghĩa thế giới bên ngoài không được phép truy xuất, nhưng tất cả các lớp con, cháu… đều được phép truy xuất

Trang 44

Thuộc tính private

• Trong ví dụ trên, không có hàm thành phần nào của lớp

SinhVien có thể truy xuất các thành phần private HoTen,

NamSinh của lớp Nguoi Nói cách khác, lớp con không có quyền vi phạm tính đóng gói của lớp cha Đoạn chương trình sau gây ra lỗi lúc biên dịch.

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo << ", ho ten: " << HoTen;

}

• Ta có thể khắc phục được lỗi trên nhờ khai báo lớp SinhVien là bạn của lớp Nguoi, như trong ví dụ ở đầu chương:

Trang 46

Thuộc tính private

• Với khai báo hàm bạn như trên, lớp sinh viên có thể truy xuất các thành phần của lớp người

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo

<< ", ho ten: " << HoTen; // Ok }

• Cách làm trên giải quyết được nhu cầu của người sử dụng khi muốn tạo lớp con có quyền truy xuất các thành phần dữ liệu private của lớp cha Tuy nhiên nó đòi hỏi phải sửa đổi lại lớp cha và tất cả các lớp ở cấp cao hơn mỗi khi một lớp con mới ra đời

Trang 47

Thuộc tính private

class Nguoi {

friend class SinhVien;

friend class NuSinh;

class SinhVien : public Nguoi {

friend class NuSinh;

char *MaSo;

public:

//

};

Trang 48

Nguoi p1("Le Van Nhan",1980);

SinhVien s1("Vo Vien Sinh", "200002541",1984);

NuSinh ns("Le Thi Ha Dong", "200002544",1984);

p1.An(); cout << "\n";

s1.An();cout << "\n";

ns.An();cout << "\n";

}

Trang 50

~SinhVien() {delete [] MaSo;}

void Xuat() const; // Co the truy xuat

// Nguoi::HoTen va Nguoi::NamSinh };

class NuSinh : public SinhVien {

Trang 51

Thuộc tính protected

void Nguoi::Xuat() const {

cout << "Nguoi, ho ten: " << HoTen << " sinh " << NamSinh;

}

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo << ", ho ten:

" << HoTen; // Ok: co quyen truy xuat

// Nguoi::HoTen, Nguoi::NamSinh }

void SinhVien::Xuat() const {

cout << "Sinh vien, ma so: " << MaSo

<< ", ho ten: " << HoTen;

}

Trang 52

Thuộc tính protected

• Thuộc tính protected là phương tiện để tránh phải sửa đổi lớp cơ sở khi có lớp con mới ra đời Nhờ đó nó bảo được tính đóng của một lớp Khai báo một thành phần nào có thuộc tính protected tương

đương với qui định trước tất cả các lớp con, cháu sau này đều là bạn của thành phần đó

• Thông thường ta dùng thuộc tính protected cho các thành phần dữ liệu và thuộc tính public cho hàm thành phần

• Các thuộc tính public, private, protected và khai

báo friend cho những nơi nào có quyền truy xuất đến các thành phần của lớp Cho hay không cho ai truy xuất đến (thành phần của) lớp hoàn toàn do lớp quyết định

Ngày đăng: 29/10/2013, 22:11

HÌNH ẢNH LIÊN QUAN

Hình 4.1: Sơ đồ phân cấp kế thừa - chương 3 lập trình hướng đối tượng
Hình 4.1 Sơ đồ phân cấp kế thừa (Trang 9)
Hình 4.2: Sơ đồ phân cấp kế thừa - chương 3 lập trình hướng đối tượng
Hình 4.2 Sơ đồ phân cấp kế thừa (Trang 11)

TỪ KHÓA LIÊN QUAN

w