1. Trang chủ
  2. » Công Nghệ Thông Tin

Tính thừa kế

43 569 0
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 đề Tính kế thừa
Trường học University of Information Technology
Chuyên ngành Computer Science
Thể loại bài giảng
Thành phố Hồ Chí Minh
Định dạng
Số trang 43
Dung lượng 71,25 KB

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

Nội dung

Bài giảng lập trình hướng đối tượng - Thầy Cường Học viện bưu chính viễn thông TP HCM

Trang 1

Chương 6

Tính kế thừa

• Giới thiệu tính kế thừa

• Điều khiển truy cập lớp cơ sở

• Sử dụng các thành viên được bảo vệ

• Hàm tạo, hàm hủy và tính kế thừa

• Tính đa kế thừa

• Lớp cơ sở ảo

Trang 3

I/ Giới thiệu tính kế thừa (inheritance)

Tính kế thừa là cơ chế nhờ đó một lớp có thể kế thừa các đặc điểm của một lớp khác

Tính kế thừa hổ trợ khái niệm phân loại theo thứ bậc (hierachical classification) của lớp, ngoài ra còn hổ trợ tính đa hình (polymorphism)

Lớp cơ sở (base class) là lớp được kế thừa bởi một lớp khác

Lớp dẫn xuất (derive class) là lớp kế thừa từ một lớp cơ sở

Lớp cơ sở xác định các tính chất mà sẽ trở nên thông dụng cho các lớp dẫn xuất Nghiã là lớp cơ sở hiển thị mô tả tổng quát nhất một tập hợp các đặc điểm

Một lớp dẫn xuất kế thừa các đặc điểm tổng quát này và bổ sung thêm các tính chất riêng của lớp dẫn xuất

Cú pháp khai báo cho lớp dẫn xuất

class derived_class_name : access_specifier base_class_name {

// body of class

} ;

base_class_name Tên lớp cơ sở

derived_class_name Tên lớp dẫn xuất

access_specifier chỉ định truy cập bao gồm : public, private và protected

Từ khoá public báo cho trình biên dịch biết rằng lớp cơ sở sẽ được kế thừa sao

cho mọi thành viên chung của lớp cơ sở cũng sẽ là các thành viên chung của lớp

dẫn xuất Tuy nhiên, mọi thành viên riêng của lớp cơ sở vẫn còn riêng đối với nó

và không được truy cập trực tiếp bởi lớp dẫn xuất

Trang 4

// Define base class

// Define derived class

class derived : public base {

// Return value of i in base

int base::get_i() { return i; }

// Set value of j in derived

Trang 5

int main()

{

derived ob;

ob.set_i(10); // load i in base

ob.set_j(4); // load j in derived

cout << ob.mul(); // displays 40

return 0;

}

Một lớp cơ sở là không thuộc riêng về một lớp dẫn xuất Lớp cơ sở có thể được kế

thừa bởi nhiều lớp khác

Ví dụ 1.2 Lớp cơ sở chung Fruit có 2 lớp dẫn xuất Apple và Orange

// An example of class inheritance

#include <iostream.h.h>

#include <string.h>

enum yn {no, yes};

enum color {red, yellow, green, orange};

void out(enum yn x);

char *c[] = {"red", "yellow", "green", "orange"};

// Generic fruit class

Trang 6

// Derive Apple class

class Apple : public fruit {

// Derive orange class

class Orange : public fruit {

Trang 7

cout << name << " apple is: " << "\n";

cout << "Annual: "; out(annual);

cout << "Perennial: "; out(perennial);

cout << "Tree: "; out(tree);

cout << "Tropical: "; out(tropical);

cout << "Color: " << c[clr] << "\n";

cout << "Good for cooking: "; out(cooking);

cout << "Crunchy: "; out(crunchy);

cout << "Good for eating: "; out(eating); cout << "\n";

}

void Orange::show()

{

cout << name << " orange is: " << "\n";

cout << "Annual: "; out(annual);

cout << "Perennial: "; out(perennial);

cout << "Tree: "; out(tree);

cout << "Tropical: "; out(tropical);

cout << "Color: " << c[clr] << "\n";

cout << "Good for juice: "; out(juice);

cout << "Sour: "; out(sour);

cout << "Good for eating: "; out(eating); cout << "\n";

}

Trang 8

void out(enum yn x)

{

if(x==no) cout << "no\n";

else cout << "yes\n";

}

int main()

{

Apple a1, a2;

Orange o1, o2;

a1.seta("Red Delicious", red, no, yes, yes);

a2.seta("Jonathan", red, yes, no, yes);

o1.seto("Navel", orange, no, no, yes);

o2.seto("Valencia", orange, yes, yes, no);

Trang 9

II/ Điều khiển truy cập lớp cơ sở

Chỉ định truy cập (access_specifier) xác định cách mà các phần tử của lớp cơ sở

được kế thừa bởi lớp dẫn xuất

Từ khoá private chỉ định các thành viên chung lớp cơ sở trở thành các thành

viên riêng của lớp dẫn xuất, nhưng những thành viên này vẫn còn được truy cập bởi các hàm thành viên của lớp dẫn xuất

ob.setx(10); // access member of base class

ob.sety(20); // access member of derived class

ob.showx(); // access member of base class

ob.showy(); // access member of derived class

Trang 10

// Inherit as public - this has an error!

class derived : public base {

int y;

public:

void sety(int n) { y = n; }

/* Cannot access private member of base class

x is a private member of base and not available within derived */

void show_sum() { cout << x + y << '\n'; } // Error!

void showy() { cout << y << '\n'; }

};

Ví dụ 2.3 Lớp dẫn xuất kế thừa lớp cơ sở với chỉ định private

// This program contains an error

Trang 11

// Inherit base as private

class derived : private base {

ob.setx(10); // ERROR - now private to derived class

ob.sety(20); // access member of derived class - OK

ob.showx(); // ERROR - now private to derived class

ob.showy(); // access member of derived class - OK

base_ob.setx(1); // is legal because base_ob is of type base

Ví dụ 2.4 Lớp dẫn xuất kế thừa lớp cơ sở với chỉ định private, các thành viên vẫn

còn được truy cập bên trong lớp dẫn xuất

// This program is fixed

Trang 12

// Inherit base as private

class derived : private base {

int y;

public:

// setx is accessible from within derived

void setxy(int n, int m) { setx(n); y = m; }

// showx is accessible from within derived

void showxy() { showx(); cout << y << '\n'; }

Trang 13

void setab(int i, int j) { a = i; b = j; }

void getab(int &i, int &j) { i = a; j = b; }

Trang 14

III/ Sử dụng các thành viên được bảo vệ (protected members)

Với các chỉ định truy cập public và private, một lớp dẫn xuất không truy cập được

các thành viên riêng của lớp cơ sở Tuy nhiên, sẽ có những lúc muốn một thành viên của lớp cơ sở vẫn là riêng nhưng vẫn cho phép lớp dẫn xuất truy cập tới nó

Chỉ định truy cập protected, tương đương với chỉ định private với một ngoại lệ

duy nhất là các thành viên được bảo vệ (protected members) của lớp cơ sở có thể

được truy cập đối với các thành viên của một lớp dẫn xuất từ lớp cơ sở đó

Bên ngoài lớp cơ sở hoặc lớp dẫn xuất, các thành viên được bảo vệ không thể

được truy cập

Dạng tổng quát của khai báo lớp :

Khi một thành viên được bảo vệ của một lớp cơ sở được kế thừa bởi lớp dẫn xuất với

chỉ định public, nó trở thành thành viên được bảo vệ của lớp dẫn xuất

Nếu lớp dẫn xuất được kế thừa với chỉ định private, thì một thành viên được bảo vệ

của lớp cơ sở trở thành thành viên riêng của lớp dẫn xuất

Lớp cơ sở có thể được kế thừa với chỉ định protected bởi lớp dẫn xuất, khi đó các

thành viên chung và được bảo vệ của lớp cơ sở trở thành các thành viên được bảo vệ của lớp dẫn xuất

Còn các thành viên riêng của lớp cơ sở vẫn còn riêng đối với lớp cơ sở và không được truy cập bởi lớp dẫn xuất

Trang 15

Ví dụ 3.1 Truy cập các thành viên chung, riêng và được bảo vệ của lớp cơ sở

int geta() { return a; }

int getb() { return b; }

};

int main()

{

samp ob(10, 20);

// ob.b = 99; Error! b is protected and thus private

ob.c = 30; // OK, c is public

protected : // private to base

int a, b; // but still accessible by derived

public:

Trang 16

void setab(int n, int m) { a = n; b = m; }

Ví dụ 3.3 Các thành viên được bảo vệ được kế thừa bởi chỉ định protected

// This program will not compile

#include <iostream.h>

class base {

protected : // private to base

int a, b; // but still accessible by derived

public:

Trang 17

void setab(int n, int m)

// ERROR: setab() is now a protected member of base

ob.setab(1, 2); // setab() is not accessible here

ob.setc(3);

ob.showabc();

return 0;

}

Trang 18

Bài tập III

1 Lập bảng tổng kết về quyền truy cập của lớp dẫn xuất với sự kế thừa đơn

Thuộc tính thành viên

trong lớp cơ sở Chỉ định truy cập của lớp dẫn xuất Quyền truy cập của lớp dẫn xuất Public Public

Private Protected

?

?

? Private Public

Private Protected

?

?

? Protected Public

Private Protected

Trang 19

IV/ Hàm tạo, hàm hủy và tính kế thừa

Lớp cơ sở, lớp dẫn xuất hoặc cả hai có thể có các hàm tạo và/hoặc hàm hủy

• Khi cả lớp cơ sở lẫn lớp dẫn xuất có các hàm tạo và hàm hủy, các hàm tạo được thi hành theo thứ tự dẫn xuất Cáùc hàm hủy được thi hành theo thứ tự ngược lại

Cú pháp truyền đối số từ lớp dẫn xuất đến lớp cơ sở :

base() { cout << "Constructing base class \n"; }

~base() { cout << "Destructing base class \n"; }

};

class derived : public base {

public:

derived() { cout << "Constructing derived class \n"; }

~derived() { cout << "Destructing derived class \n"; }

Trang 20

Constructing base class

Constructing derived class

Destructing derived class

Destructing base class

Ví dụ 4.2 Truyền một đối số cho hàm tạo của lớp dẫn xuất

#include <iostream.h>

class base {

public:

base() { cout << "Constructing base class\n"; }

~base() { cout << "Destructing base class\n"; }

~derived() { cout << "Destructing derived class\n"; }

void showj() { cout << j << '\n'; }

Kết quả của chương trình ?

Ví dụ 4.3 Hàm tạo của lớp dẫn xuất lẫn của lớp cơ sở nhận một đối số,

Trang 21

cả hai sử dụng đối số giống nhau

~base() { cout << "Destructing base class\n"; }

void showi() { cout << i << '\n'; }

};

class derived : public base {

int j;

public:

derived(int n) : base(n) { // pass arg to base class

cout << "Constructing derived class\n";

j = n;

}

~derived() { cout << "Destructing derived class\n"; }

void showj() { cout << j << '\n'; }

Trang 22

Trong hầu hết các trườnghợp, các hàm tạo đối với lớp cơ sở và lớp dẫn xuất sẽ

không dùng đối số giống nhau

Nếu truyền một hay nhiều đối số cho mỗi lớp, cần phải truyền cho hàm tạo của lớp

dẫn xuất tất cả các đối số mà cả hai lớp dẫn xuất và lớp cơ sở cần đến Sau đó lớp

dẫn xuất chỉ truyền cho lớp cơ sở những đối số nào mà lớp cơ sở cần đến

~base() { cout << "Destructing base class\n"; }

void showi() { cout << i << '\n'; }

};

class derived : public base {

int j;

public:

derived(int n, int m) : base(m) { // pass arg to base class

cout << "Constructing derived class\n";

j = n;

}

~derived() { cout << "Destructing derived class\n"; }

void showj() { cout << j << '\n'; }

};

int main()

{

derived o(10, 20);

Trang 23

o.showi();

o.showj();

return 0;

}

Kết quả của chương trình ?

• Đối với hàm tạo của lớp dẫn xuất không cần phải nhận một số để truyền cho lớp cơ sở Nếu lớp dẫn xuất không cần đối số, nó bỏ qua đối số và chỉ truyền cho lớp cơ sở

~base() { cout << "Destructing base class\n"; }

void showi() { cout << i << '\n'; }

};

class derived : public base {

int j;

public:

derived(int n) : base(n) { // pass arg to base class

cout << "Constructing derived class\n";

j = 0; // n not used here

}

~derived() { cout << "Destructing derived class\n"; }

void showj() { cout << j << '\n'; }

};

Bài tập IV

Trang 24

1 Cho đoạn chương trình sau, hãy bổ sung hàm tạo cho lớp myderived Cho myderived truyền cho lớp mybase một con trỏ về một chuỗi khởi đầu Hãy cho hàm tạo myderived() khởi đầu len với độ dài chuổi

// add myderived() here

int getlen() { return len; }

void show() { cout << get() << '\n'; }

Trang 25

car ob(passengers, wheels, range) ;

truck ob(loadlimit, wheels, range) ;

cout << "Wheels: " << num_wheels << '\n';

cout << "Range: " << range << '\n';

Trang 27

1/ Khái niệm

• Có hai cách để một lớp dẫn xuất kế thừa hơn một lớp

Thứ nhất, lớp dẫn xuất được dùng như một lớp cơ sở cho một lớp dẫn xuất khác,

tạo ra thứ bậc lớp nhiều cấp Khi đó cấp cơ sở gốc được gọi là lớp cơ sở giáng

tiếp của lớp cơ sở thứ hai (Hình 6.2)

Hình 6.2 Kế thừa đa mức Hình 6.3 Kế thừa phân cấp

(Multi level Inheritance) (Hierarchical Inheritance)

Thứ hai, lớp dẫn xuất có thể kế thừa trực tiếp hơn một lớp cơ sở Trường hợp này, hai hay nhiều lớp cơ sở được kết hợp để tạo ra lớp dẫn xuất (Hình 6.4)

Hình 6.4 Đa kế thừa Hình 6.5 Kế thừa lai

(Multiple Inheritance) (Hybrid Inheritance)

2/ Tính chất

• Khi một lớp cơ sở được dùng để dẫn ra một lớp mà lớp này lại được dùng làm

lớp cơ sở cho một lớp dẫn xuất khác, thì các hàm tạo của các lớp được gọi theo

thứ tự dẫn xuất Cũng vậy, tất cả các hàm hủy được gọi theo thứ tự ngược lại

• Khi một lớp dẫn xuất kế thừa trực tiếp hơn một lớp cơ sở, nó dùng cách khai báo mở rộng như sau :

class derived_class_name : access base1 , access base2, , access baseN {

Trang 28

}

base1 , base2, , baseN tên các lớp cơ sở

access chỉ định truy cập, có thể khác nhau đối với mỗi lớp

Khi nhiều lớp cơ sở được kế thừa, các hàm tạo được thi hành theo thứ tự từ trái

qua phải mà các lớp cơ sở đã được chỉ định rõ Các hàm hủy được thi hành

theo thứ tự ngược lại

• Khi một lớp kế thừa nhiều lớp cơ sở có các hàm tạo cần nhiều đối số, lớp dẫn xuất sẽ truyền các đối số cần thiết cho các hàm tạo này bằng cách dùng dạng mở rộng của hàm tạo của lớp dẫn xuất như sau :

derived_constructor(arg_list) : base1(arg_list) , base2(arg_list), , baseN(arg_list) {

// body of class

}

Khi một lớp dẫn xuất kế thừa một hệ thống thứ bậc các lớp, mỗi lớp dẫn xuất trong chuỗi các lớp này phải truyền cho lớp cơ sở đứng trước các tham số mà lớp dẫn xuất này cần

Trang 29

// geta() and getb() are still public here

cout << ob.geta() << ' ' << ob.getb() << '\n';

return 0;

}

Kết quả của chương trình ?

Ví dụ 5.2 Lớp dẫn xuất kế thừa trực tiếp hai lớp cơ sở

#include <iostream.h>

Trang 30

// Create first base class

// Directly inherit two base classes

class D : public B1, public B2 {

int c;

public:

// here, z and y are passed directly to B1 and B2

D(int x, int y, int z) : B1(z) , B2(y) { c = x; }

// Because bases inherited as public, D has access to public elements

Trang 31

Kết quả của chương trình ?

Ví dụ 5.3 Minh họa thứ tự gọi các hàm tạo và hàm hủy khi lớp dẫn xuất

kế thừa trực tiếp nhiều lớp cơ sở

// Inherit two base classes

class D : public B1 , public B2 {

Trang 32

A() { cout << "Constructing A\n"; }

~A() { cout << "Destructing A\n"; }

Trang 33

2 Sử dụng hệ thống thứ bậc lớp sau đây, hãy lập hàm tạo cua lớp C để cho nó khởi đầu k và truyền các đối số cho A() và B()

Trang 34

Khi nhiều lớp cơ sở được kế thừa trực tiếp bởi một lớp dẫn xuất, tồn tại một vấn đề : xuất hiện nhiều bản sao của lớp cơ sở cùng hiện diện trong đối tượng dẫn xuất Chẳng hạn, xét hệ thống thứ bậc lớp sau

Bời vì có hai bản sao của B trong D3, nên một tham chiếu đến một thành viên của B sẽ tham chiếu về B được kế thừa gián tiếp thông qua D1 hay tham chiếu về B được kế thừa gián tiếp thông qua D2 ?

Để giải quyết tính không rõ ràng này, C++ có một cơ chế mà nhờ đó chỉ có một bản

sao của B ở trong D3 Đặc điểm này được gọi là lớp cơ sở ảo

Có thể ngăn chặn được hai bản sao của lớp cơ sở cùng hiện diện trong đối tượng dẫn

xuất bằng cách cho lớp cơ sở đó được kế thừa như virtual bởi bất kỳ các lớp dẫn

xuất nào

Từ khoá virtual đứng trước chỉ định truy cập lớp cơ sở khi nó được kế thừa bởi

một lớp dẫn xuất

D3

Ngày đăng: 21/08/2012, 15:34

HÌNH ẢNH LIÊN QUAN

Hình 6.2   Kế thừa đa mức   Hình 6.3   Kế thừa phân cấp - Tính thừa kế
Hình 6.2 Kế thừa đa mức Hình 6.3 Kế thừa phân cấp (Trang 27)

TỪ KHÓA LIÊN QUAN

w