Diễn Đàn Teen Việt -Thế Giới 9x pro
Bạn có muốn phản ứng với tin nhắn này? Vui lòng đăng ký diễn đàn trong một vài cú nhấp chuột hoặc đăng nhập để tiếp tục.

giáo trình ngôn ngữ lập trinh C ++ <phần 7>

Go down

giáo trình ngôn ngữ lập trinh C ++ <phần 7> Empty giáo trình ngôn ngữ lập trinh C ++ <phần 7>

Bài gửi by luongbv93 25th November 2011, 10:05

CHƯƠNG 6

TÍNH KẾ THỪA VÀ ĐA HÌNH

Nội dung chương này tập trung trình bày các vấn đề liên quan đến tính kế thừa và tương ứng bội
(đa hình) trong ngôn ngữ C++:

• Khái niệm kế thừa, dẫn xuất và các kiểu dẫn xuất

• Khai báo, định nghĩa các hàm khởi tạo và hàm hủy bỏ trong lớp dẫn xuất

• Truy nhập tới các thành phần của lớp cơ sở và các lớp dẫn xuất

• Việc một lớp được kế thừa từ nhiều lớp cơ sở khác nhau

• Khai báo và sử dụng các lớp cơ sở trừu tượng trong kế thừa

• Tính đa hình trong kế thừa

6.1 KHÁI NIỆM KẾ THỪA

Lập trình hướng đối tượng có hai đặc trưng cơ bản:

• Đóng gói dữ liệu, được thể hiện bằng cách dùng khái niệm lớp để biểu diễn đối tượng với
các thuộc tính private, chỉ cho phép bên ngoài truy nhập vào thông qua các phương thức
get/set.

• Dùng lại mã, thể hiện bằng việc thừa kế giữa các lớp. Việc thừa kế cho phép các lớp thừa
kế (gọi là lớp dẫn xuất) sử dụng lại các phương thức đã được định nghĩa trong các lớp gốc
(gọi là lớp cơ sở).

6.1.1 Khai báo thừa kế

Cú pháp khai báo một lớp kế thừa từ một lớp khác như sau:

class <Tên lớp dẫn xuất>: <Từ khóa dẫn xuất> <Tên lớp cơ sở>{

… // Khai báo các thành phần lớp

};

Trong đó:

• Tên lớp dẫn xuất: là tên lớp được cho kế thừa từ lớp khác. Tên lớp này tuân thủ theo quy
tắc đặt tên biến trong C++.

• Tên lớp cở sở: là tên lớp đã được định nghĩa trước đó để cho lớp khác kế thừa. Tên lớp
này cũng tuân thủ theo quy tắc đặt tên biến của C++.

• Từ khóa dẫn xuất: là từ khóa quy định tính chất của sự kế thừa. Có ba từ khóa dẫn xuất
là private, protected và public. Mục tiếp theo sẽ trình bày ý nghĩa của các từ khóa dẫn xuất
này.

Ví dụ:

class Bus: public Car{

… // Khai báo các thành phần

};

là khai báo một lớp Bus (xe buýt) kế thừa từ lớp Car (xe ô tô) với tính chất kế thừa là public.

6.1.2 Tính chất dẫn xuất

Sự kế thừa cho phép trong lớp dẫn xuất có thể sử dụng lại một số mã nguồn của các phương thức
và thuộc tính đã được định nghĩa trong lớp cơ sở. Nghĩa là lớp dẫn xuất có thể truy nhập trực tiếp
đến một số thành phần của lớp cơ sở. Tuy nhiên, phạm vi truy nhập từ lớp dẫn xuất đến lớp cơ sở
không phải bao giờ cũng giống nhau: chúng được quy định bởi các từ khóa dẫn xuất private,
protected và public.

Dẫn xuất private

Dẫn xuất private quy định phạm vi truy nhập như sau:

• Các thành phần private của lớp cơ sở thì không thể truy nhập được từ lớp dẫn xuất.

• Các thành phần protected của lớp cơ sở trở thành các thành phần private của lớp dẫn xuất

• Các thành phần public của lớp cơ sở cũng trở thành các thành phần private của lớp dẫn
xuất.

• Phạm vi truy nhập từ bên ngoài vào lớp dẫn xuất được tuân thủ như quy tắc phạm vi lớp
thông thường.

Dẫn xuất protected

Dẫn xuất protected quy định phạm vi truy nhập như sau:

• Các thành phần private của lớp cơ sở thì không thể truy nhập được từ lớp dẫn xuất.

• Các thành phần protected của lớp cơ sở trở thành các thành phần protected của lớp dẫn
xuất

• Các thành phần public của lớp cơ sở cũng trở thành các thành phần protected của lớp dẫn
xuất.

• Phạm vi truy nhập từ bên ngoài vào lớp dẫn xuất được tuân thủ như quy tắc phạm vi lớp
thông thường.

Dẫn xuất public

Dẫn xuất public quy định phạm vi truy nhập như sau:

• Các thành phần private của lớp cơ sở thì không thể truy nhập được từ lớp dẫn xuất.

• Các thành phần protected của lớp cơ sở trở thành các thành phần protected của lớp dẫn
xuất.

• Các thành phần public của lớp cơ sở vẫn là các thành phần public của lớp dẫn xuất.

• Phạm vi truy nhập từ bên ngoài vào lớp dẫn xuất được tuân thủ như quy tắc phạm vi lớp
thông thường.

Bảng 6.1 tóm tắt lại các quy tắc truy nhập được quy định bới các từ khóa dẫn xuất.

Kiểu dẫn xuất Tính chất ở lớp c ơ sở Tính chất ở lớp d ẫn xuất

private private Không truy nhập được


protected private

public private

private Không truy nhập được

protected protected protected

public protected

private Không truy nhập được

public protected protected

public public

6.2 HÀM KHỞI TẠO VÀ HUỶ BỎ TRONG KẾ THỪA

6.2.1 Hàm khởi tạo trong kế thừa

Khi khai báo một đối tượng có kiểu lớp được dẫn xuất từ một lớp cơ sở khác. Chương trình sẽ tự
động gọi tới hàm khởi tạo của lớp dẫn xuất. Tuy nhiên, thứ tựđược gọi sẽ bắt đầu từ hàm khởi tạo
tương ứng của lớp cơ sở, sau đó đến hàm khởi tạo của lớp dẫn xuất. Do đó, thông thường, trong
hàm khởi tạo của lớp dẫn xuất phải có hàm khởi tạo của lớp cơ sở.

Cú pháp khai báo hàm khởi tạo như sau:

<Tên hàm khởi tạo dẫn xuất>([<Các tham số>]):

<Tên hàm khởi tạo cơ sở>([<Các đối số>]){

… // Khởi tạo các thuộc tính mới bổ sung của lớp dẫn xuất

};

Vì tên hàm khởi tạo là trùng với tên lớp, nên có thể viết lại thành:

<Tên lớp dẫn xuất>([<Các tham số>]):

<Tên lớp cơ sở>([<Các đối số>]){

… // Khởi tạo các thuộc tính mới bổ sung của lớp dẫn xuất

};

Ví dụ:

Bus():Car(){

… // Khởi tạo các thuộc tính mới bổ sung của lớp Bus

}

là một định nghĩa một hàm khởi tạo của lớp Bus kế thừa từ lớp Car. Định nghĩa này được thược
hiện trong phạm vi khai báo lớp Bus. Đây là một hàm khởi tạo không tham số, nó gọi tới hàm
khởi tạo không tham số của lớp Car.

Lưu ý:

• Nếu định nghĩa hàm khởi tạo bên ngoài phạm vi lớp thì phải thêm tên lớp dẫn xuất và toán
tử phạm vi “::” trước tên hàm khởi tạo.

• Giữa tên hàm khởi tạo của lớp dẫn xuất và hàm khởi tạo của lớp cơ sở, chỉ có môt dấu hai
chấm “:”, nếu là hai dấu “::” thì trở thành toán tử phạm vi lớp.
• Nếu không chỉ rõ hàm khởi tạo của lớp cơ sở sau dấu hai chấm “:” chương trình sẽ tự
động gọi hàm khởi tạo ngầm định hoặc hàm khởi tạo không có tham số của lớp cơ sở nếu
hàm đó được định nghĩa tường minh trong lớp cơ sở.

Ví dụ, định nghĩa hàm khởi tạo:

Bus():Car(){

… // Khởi tạo các thuộc tính mới bổ sung của lớp Bus

};

Có thể thay bằng:

Bus(){ // Gọi hàm khởi tạo không tham số của lớp Car

… // Khởi tạo các thuộc tính mới bổ sung của lớp Bus

};

Chương trình 6.1 định nghĩa lớp Car có 3 thuộc tính với hai hàm khởi tạo, sau đó định nghĩa lớp
Bus có thêm thuộc tính label là số hiệu của tuyến xe buýt. Lớp Bus sẽđược cài đặt hai hàm khởi
tạo tường minh, gọi đến hai hàm khởi tạo tương ứng của lớp Car.

Chương trình 6.1

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

Car(); // Khởi tạo không tham số

Car(int, char[ ], float); // Khởi tạo đủ tham số

};

Car::Car(){ // Khởi tạo không tham số

speed = 0;

strcpy(mark, “”);

price = 0;

}

// Khởi tạo đủ tham số

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

int label; // Số hiệu tuyến xe

public:

Bus(); // Khởi tạo không tham số

Bus(int, char[ ], float, int); // Khởi tạo đủ tham số

};

Bus::Bus():Car(){ // Khởi tạo không tham số

label = 0;

}

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, int lIn):Car(sIn, mIn, pIn){

label = lIn;

}

Trong hàm khởi tạo của lớp Bus, muốn khởi tạo các thuộc tính của lớp Car, ta phải khởi tạo gián
tiếp thông qua hàm khởi tạo của lớp Car mà không thể gán giá trị trực tiếp cho các thuộc tính
speed, mark và price. Lí do là các thuộc tính này có tính chất private, nên lớp dẫn xuất không thể
truy nhập trực tiếp đến chúng.

6.2.2 Hàm hủy bỏ trong kế thừa

Khi một đối tượng lớp dẫn xuất bị giải phóng khỏi bộ nhớ, thứ tự gọi các hàm hủy bỏ ngược với
thứ tự gọi hàm thiết lập: gọi hàm hủy bỏ của lớp dẫn xuất trước khi gọi hàm hủy bỏ của lớp cơ sở.

Vì mỗi lớp chỉ có nhiều nhất là một hàm hủy bỏ, nên ta không cần phải chỉ ra hàm hủy bỏ nào của
lớp cơ sở sẽđược gọi sau khi hủy bỏ lớp dẫn xuất. Do vậy, hàm hủy bỏ trong lớp dẫn xuất được
khai báo và định nghĩa hoàn toàn giống với các lớp thông thường:

<Tên lớp>::~<Tên lớp>([<Các tham số>]){

… // giải phóng phần bộ nhớ cấp phát cho các thuộc tính bổ sung

}

Lưu ý:

• Hàm hủy bỏ của lớp dẫn xuất chỉ giải phóng phần bộ nhớđược cấp phát động cho các
thuộc tính mới bổ sung trong lớp dẫn xuất, nếu có, mà không được giải phóng bộ nhớ
được cấp cho các thuộc tính trong lớp cơ sở (phần này là do hàm hủy bỏ của lớp cơ sở
đảm nhiệm).

• Không phải gọi tường minh hàm hủy bỏ của lớp cơ sở trong hàm hủy bỏ của lớp dẫn xuất.

• Ngay cả khi lớp dẫn xuất không định nghĩa tường minh hàm hủy bỏ (do không cần thiết)
mà lớp cơ sở lại có định nghĩa tường minh. Chương trình vẫn gọi hàm hủy bỏ ngầm định
của lớp dẫn xuất, sau đó vẫn gọi hàm hủy bỏ tường minh của lớp cơ sở.

Chương trình 6.2 cài đặt lớp Bus kế thừa từ lớp Car: lớp Car có một thuộc tính có dạng con trỏ
nên cần giải phóng bằng hàm hủy bỏ tường minh. Lớp Bus có thêm một thuộc tính có dạng con
trỏ là danh sách các đường phố mà xe buýt đi qua (mảng động các chuỗi kí tự *char[]) nên cũng
cần giải phóng bằng hàm hủy bỏ tường minh.

Chương trình 6.2

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

char *mark; // Nhãn hiệu xe

public:

~Car(); // Hủy bỏ tường minh

};

Car::~Car(){ // Hủy bỏ tường minh

delete [ ] mark;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

char *voyage[ ]; // Hành trình tuyến xe

public:

~Bus(); // Hủy bỏ tường minh

};

Bus::~Bus(){ // Hủy bỏ tường minh

delete [ ] voyage;

}

Trong hàm hủy bỏ của lớp Bus, ta chỉđược giải phóng vùng nhớđược cấp phát cho thuộc tính
voyage (hành trình của xe buýt), là thuộc tính được bổ sung thêm của lớp Bus. Mà không được
giải phóng vùng nhớ cấp phát cho thuộc tính mark (nhãn hiệu xe), việc này là thuộc trách nhiệm
của hàm hủy bỏ của lớp Car vì thuộc tính mark được khai báo trong lớp Car.

6.3 TRUY NHẬP TỚI CÁC THÀNH PHẦN TRONG KẾ THỪA LỚP

6.3.1 Phạm vi truy nhập

Mối quan hệ giữa các thành phần của lớp cơ sở và lớp dẫn xuất được quy định bởi các từ khóa
dẫn xuất, nhưđã trình bày trong mục 6.1.2, được tóm tắt trong bảng 6.2

Kiểu dẫn xuất Tính chất ở lớp c ơ sở Tính chất ở lớp d ẫn xuất

private Không truy nhập được
private
protected private

public private

private Không truy nhập được

protected protected protected

public protected

private Không truy nhập được

public protected protected

public public

Ta xét phạm vi truy nhập theo hai loại:

• Phạm vi truy nhập từ các hàm bạn, lớp bạn của lớp dẫn xuất

• Phạm vi truy nhập từ các đối tượng có kiểu lớp dẫn xuất

Truy nhập t ừ các hàm bạn và lớp b ạn của lớp d ẫn xuất

Nhìn vào bảng tổng kết 6.2, phạm vi truy nhập của hàm bạn, lớp bạn của lớp dẫn xuất vào lớp cơ
sở như sau:

• Với dẫn xuất private, hàm bạn có thể truy nhập được các thành phần protected và public
của lớp cơ sở vì chúng trở thành các thành phần private của lớp dẫn xuất, có thể truy nhập
được từ hàm bạn.

• Với dẫn xuất protected, hàm bạn cũng có thể truy nhập được các thành phần protected và
public của lớp cơ sở vì chúng trở thành các thành phần protected của lớp dẫn xuất, có thể
truy nhập được từ hàm bạn.

• Với dẫn xuất public, hàm bạn cũng có thể truy nhập được các thành phần protected và
public của lớp cơ sở vì chúng trở thành các thành phần protected và public của lớp dẫn
xuất, có thể truy nhập được từ hàm bạn.

• Đối với cả ba loại dẫn xuất, hàm bạn đều không truy nhập được các thành phần private
của lớp cơ sở, vì các thành phần này cũng không truy nhập được từ lớp dẫn xuất.

Truy nhập t ừ các đối tượng tạo bởi lớp d ẫn xuất

Nhìn vào bảng tổng kết 6.2, phạm vi truy nhập của các đối tượng của lớp dẫn xuất vào lớp cơ sở
như sau:

• Với dẫn xuất private, đối tượng của lớp dẫn xuất không truy nhập được bất cứ thành phần
nào của lớp cơ sở vì chúng trở thành các thành phần private của lớp dẫn xuất, không truy
nhập được từ bên ngoài.

• Với dẫn xuất protected, đối tượng của lớp dẫn xuất không truy nhập được bất cứ thành
phần nào của lớp cơ sở vì chúng trở thành các thành phần protected của lớp dẫn xuất,
không truy nhập được từ bên ngoài.

• Với dẫn xuất public, đối tượng của lớp dẫn xuất có thể truy nhập được các thành phần
public của lớp cơ sở vì chúng trở thành các thành phần public của lớp dẫn xuất, có thể truy
nhập được từ bên ngoài.

Bảng 6.3 tổng kết phạm vi truy nhập từ hàm bạn và đối tượng của lớp dẫn xuất vào các thành
phần của lớp cơ sở, được quy định bởi các từ khóa dẫn xuất.

Kiểu dẫn xuất Tính chất ở lớp c ơ Tính chất ở lớp Truy nhập t ừ hàm Truy nhập t ừđối
sở dẫn xuất bạn của lớp d ẫn tượng của lớp d ẫn
xuất xuất

private --- --- ---

private protected private ok ---

public private ok ---

private --- --- ---

protected protected protected ok ---

public protected ok ---

private --- --- ---

public protected protected ok ---

public public ok ok

6.3.2 Sử dụng các thành phần của lớp cơ sở từ lớp dẫn xuất

Từ bảng tổng kết phạm vi truy nhập, ta thấy rằng chỉ có dẫn xuất theo kiểu public thì đối tượng
của lớp dẫn xuất mới có thể truy nhập đến các thành phần (thuộc loại public) của lớp cơ sở. Khi
đó, việc gọi đến các thành phần của lớp cơ sở cũng tương tự như gọi các thành phần lớp thông
thường:

• Đối với biến đối tượng thông thường:

<Tên đối tượng>.<Tên thành phần>([Các đối số]);

• Đối với con trỏđối tượng:

<Tên đối tượng>-><Tên thành phần>([Các đối số]);

Lưu ý:

• Cách gọi hàm thành phần này được áp dụng khi trong lớp dẫn xuất, ta không định nghĩa
lại các hàm thành phần của lớp cơ sở. Trường hợp định nghĩa lại hàm thành phần của lớp
cơ sở sẽđược trình bày trong mục 6.3.3.

Chương trình 6.3 minh họa việc sử dụng các thành phần lớp cơ sở từđối tượng lớp dẫn xuất: lớp
Bus kế thừa từ lớp Car. Lớp Bus có định nghĩa bổ sung một số phương thức và thuộc tính mới.
Khi đó, đối tượng của lớp Bus có thể gọi các hàm public của lớp Bus cũng như của lớp Car.

Chương trình 6.3

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

private:

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

void setSpeed(int); // Gán tốc độ cho xe

int getSpeed(); // Đọc tốc độ xe

void setMark(char); // Gán nhãn cho xe

char[] getMark(); // Đọc nhãn xe

void setPrice(float); // Gán giá cho xe

float getPrice(); // Đọc giá xe

// Khởi tạo thông tin về xe

Car(int speedIn=0, char markIn[ ]=””, float priceIn=0);

void show(); // Giới thiệu xe

};

/* Khai báo phương thức bên ngoài lớp */

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

void Car::setSpeed(int speedIn){ // Gán tốc độ cho xe

speed = speedIn;

}

int Car::getSpeed(){ // Đọc tốc độ xe

return speed;

}

void Car::setMark(char markIn){ // Gán nhãn cho xe

strcpy(mark, markIn);

}

char[ ] Car::getMark(){ // Đọc nhãn xe

return mark;

}

void Car::setPrice(float priceIn){ // Gán giá cho xe

price = priceIn;

}

float Car::getPrice(){ // Đọc giá xe

return price;

}

void Car::show(){ // Phương thức giới thiệu xe

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h and its price is $” << price << endl;

return;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

int label; // Số hiệu tuyến xe

public:

// Khởi tạo đủ tham số

Bus(int sIn=0, char mIn[ ]=””, float pIn=0, int lIn=0);

void setLabel(int); // Gán số hiệu tuyến xe

int getLabel(); // Đọc số hiệu tuyến xe

};

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, int lIn):Car(sIn, mIn, pIn){

label = lIn;

}

void Bus::setLabel(int labelIn){ // Gán số hiệu tuyến xe

label = labelIn;

}

int Bus::getLabel(){ // Đọc số hiệu tuyến xe

return label;

}

// Chương trình chính

void main(){

clrscr();

Bus myBus; // Biến đối tượng của lớp Bus

int speedIn, labelIn;

float priceIn;

char markIn[20];

// Nhập giá trị cho các thuộc tính

cout << “Toc do xe bus:”;

cin >> speedIn;

cout << “Nhan hieu xe bus:”;

cin >> markIn;

cout << “Gia xe bus:”;

cin >> priceIn;

cout << “So hieu tuyen xe bus:”;

cin >> labelIn;

myBus.setSpeed(speedIn); // Phương thức của lớp Car

myBus.setMark(markIn); // Phương thức của lớp Car

myBus.setPrice(priceIn); // Phương thức của lớp Car

myBus.setLabel(labelIn); // Phương thức của lớp Bus

myBus.show(); // Phương thức của lớp Car

return;

}

Trong chương trình 6.3, đối tượng myBus có kiểu lớp Bus, là lớp dẫn xuất của lớp cơ sở Car, có
thể sử dụng các phương thức của lớp Car và lớp Bus một cách bình đẳng. Khi đó, lệnh
myBus.show() sẽ gọi đến phương thức show() của lớp Car, do vậy, chương trình trên sẽ in ra màn
hình kết quả như sau (tùy theo dữ liệu nhập vào ở 4 dòng đầu):

Toc do xe bus: 80

Nhan hieu xe bus: Mercedes

Gia xe bus: 5000

So hieu tuyen xe bus: 27

This is a Mercedes having a speed of 80km/h and its price is $5000

Trong dòng giới thiệu xe bus (vì ta đang dùng đối tượng myBus của lớp Bus), không có giới thiệu
số hiệu tuyến xe. Lí do là vì ta đang dùng hàm show của lớp Car. Muốn có thêm phần giới thiệu
về số hiệu tuyến xe buýt, ta phải định nghĩa lại hàm show trong lớp Bus. Mục 6.3.3 sẽ trình bày
nội dung này.

6.3.3 Định nghĩa chồng các phương thức của lớp cơ sở

Định nghĩa chồng phương thức của lớp c ơ sở

Một phương thức của lớp cơ sở bị coi là nạp chồng nếu ở lớp dẫn xuất cũng định nghĩa một
phương thức có cùng tên.

Ví dụ, trong lớp Car, đã có phương thức show(), bây giờ, trong lớp Bus kế thừa từ lớp Car, ta
cũng định nghĩa lại phương thức show():

class Car{



public:



void show(); // Phương thức của lớp cơ sở

};

class Bus: public Car{



public:



void show(); // Phương thức nạp chồng

};

khi đó, phương thức show() của lớp Bus được coi là phương thức nạp chồng từ phương thức
show() của lớp Car.
Sử dụng các phương thức nạp ch ồng

Từ một đối tượng của lớp dẫn xuất, việc truy nhập đến phương thức đã được định nghĩa lại trong
lớp dẫn xuất được thực hiện như lời gọi một phương thức thông thường:

• Đối với biến đối tượng thông thường:

<Tên đối tượng>.<Tên thành phần>([Các đối số]);

• Đối với con trỏđối tượng:

<Tên đối tượng>-><Tên thành phần>([Các đối số]);

Ví dụ:

Bus myBus;

myBus.show();

sẽ gọi đến phương thức show() được định nghĩa trong lớp Bus.

Trong trường hợp, từ một đối tượng của lớp dẫn xuất, muốn truy nhập đến một phương thức của
lớp cơ sở (đã bịđịnh nghĩa lại ở lớp dẫn xuất) thì phải sử dụng chỉ thị phạm vi lớp trước phương
thức được gọi:

• Đối với biến đối tượng thông thường:

<Tên đối tượng>.<Tên lớp cơ sở>::<Tên thành phần>([Các đối số]);

• Đối với con trỏđối tượng:

<Tên đối tượng>-><Tên lớp cơ sở>::<Tên thành phần>([Các đối số]);

Ví dụ:

Bus myBus;

myBus.Car::show();

sẽ gọi đến phương thức show() được định nghĩa trong lớp Car từ một đối tượng của lớp Bus.

Chương trình 6.4 minh họa việc định nghĩa chồng hàm show() trong lớp Bus và việc sử dụng hai
phương thức show() của hai lớp từ một đối tượng của lớp dẫn xuất.

Chương trình 6.4

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

private:

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

int getSpeed(); // Đọc tốc độ xe

char[ ] getMark(); // Đọc nhãn xe

float getPrice(); // Đọc giá xe

// Khởi tạo thông tin về xe
Car(int speedIn=0, char markIn[ ]=””, float priceIn=0);

void show(); // Giới thiệu xe

};

/* Khai báo phương thức bên ngoài lớp */

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

void Car::setSpeed(int speedIn){ // Gán tốc độ cho xe

speed = speedIn;

}

int Car::getSpeed(){ // Đọc tốc độ xe

return speed;

}

char[ ] Car::getMark(){ // Đọc nhãn xe

return mark;

}

float Car::getPrice(){ // Đọc giá xe

return price;

}

void Car::show(){ // Phương thức giới thiệu xe

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h and its price is $” << price << endl;

return;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

int label; // Số hiệu tuyến xe

public:

// Khởi tạo đủ tham số

Bus(int sIn=0, char mIn[ ]=””, float pIn=0, int lIn=0);

void show(); // Giới thiệu xe bus

};

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, int lIn):Car(sIn, mIn, pIn){

label = lIn;

}
// Định nghĩa nạp chồng phương thức

void Bus::show(){ // Giới thiệu xe bus

cout << “This is a bus of type ” << getMark() << “, on the line “

<< label << “, having a speed of ” << getSpeed()

<< “km/h and its price is $” << getPrice() << endl;

return;

}

// Chương trình chính

void main(){

clrscr();

Bus myBus(80, “Mercedes”, 5000, 27);// Biến đối tượng của lớp Bus

cout << “Gioi thieu xe:” << endl;

myBus.Car::show(); // Phương thức của lớp Car

cout << “Gioi thieu xe bus:” << endl;

myBus.show(); // Phương thức của lớp Bus

return;

}

Chương trình 6.4 sẽ hiển thị các thông báo như sau:

Gioi thieu xe:

This is a Mercedes having a speed of 80km/h and its price is $5000

Gioi thieu xe bus:

This is a bus of type Mercedes, on the line 27, having a speed of
80km/h and its price is $5000

Lưu ý:

• Trong phương thức show() của lớp Bus, ta phải dùng các hàm get để truy nhập đến các
thuộc tính của lớp Car. Không được truy nhập trực tiếp đến tên các thuộc tính (speed,
mark và price) vì chúng có dạng private của lớp Car.

6.3.4 Chuyển đổi kiểu giữa lớp cơ sở và lớp dẫn xuất

Về mặt dữ liệu, một lớp dẫn xuất bao giờ cũng chứa toàn bộ dữ liệu của lớp cơ sở: Ta luôn tìm
thấy lớp cơ sở trong lớp dẫn xuất, nhưng không phải bao giờ cũng tìm thấy lớp dẫn xuất trong lớp
cơ sở. Do vậy:

• Có thể gán một đối tượng lớp dẫn xuất cho một đối tượng lớp cơ sở:

<Đối tượng lớp cơ sở> = <Đối tượng lớp dẫn xuất>; // Đúng

• Nhưng không thể gán một đối tượng lớp cơ sở cho một đối tượng lớp dẫn xuất:

<Đối tượng lớp dẫn xuất> = <Đối tượng lớp cơ sở>; // Không được

Ví dụ, ta có lớp Bus kế thừa từ lớp Car và:

Bus myBus;

Car myCar;

khi đó, phép gán:

myCar = myBus; // đúng

thì chấp nhận được, nhưng phép gán:

myBus = myCar; // không được

thì không chấp nhận được.

Lưu ý:

• Nguyên tắc chuyển kiểu này cũng đúng với các phép gán con trỏ: một con trỏđối tượng
lớp cơ sở có thể trỏđến địa chỉ của một đối tượng lớp dẫn xuất. Nhưng một con trỏđối
tượng lớp dẫn xuất không thể trỏđến địa chỉ một đối tượng lớp cơ sở.

• Nguyên tắc chuyển kiểu này cũng đúng với truyền đối số cho hàm: có thể truyền một đối
tượng lớp dẫn xuất vào vị trí của tham số có kiểu lớp cơ sở. Nhưng không thể truyền một
đối tượng lớp cơ sở vào vị trí một tham số có kiểu lớp dẫn xuất.

Chương trình 6.5 minh họa việc chuyển kiểu giữa các đối tượng của lớp cơ sở và lớp dẫn xuất.

Chương trình 6.5

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

private:

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

int getSpeed(); // Đọc tốc độ xe

char[] getMark(); // Đọc nhãn xe

float getPrice(); // Đọc giá xe

// Khởi tạo thông tin về xe

Car(int speedIn=0, char markIn[]=””, float priceIn=0);

void show(); // Giới thiệu xe

};

/* Khai báo phương thức bên ngoài lớp */

Car::Car(int speedIn, char markIn[], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

int Car::getSpeed(){ // Đọc tốc độ xe

return speed;

}

char[] Car::getMark(){ // Đọc nhãn xe

return mark;

}

float Car::getPrice(){ // Đọc giá xe

return price;

}

void Car::show(){ // Phương thức giới thiệu xe

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h and its price is $” << price << endl;

return;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

int label; // Số hiệu tuyến xe

public:

// Khởi tạo đủ tham số

Bus(int sIn=0, char mIn[]=””, float pIn=0, int lIn=0);

void show(); // Định nghĩa chồng phương thức

};

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, int lIn):Car(sIn, mIn, pIn){

label = lIn;

}

// Định nghĩa nạp chồng phương thức

void Bus::show(){ // Giới thiệu xe bus

cout << “This is a bus of type ” << getMark() << “, on the line “

<< label << “, having a speed of ” << getSpeed()

<< “km/h and its price is $” << getPrice() << endl;

return;

}

// Chương trình chính

void main(){

clrscr();

Car myCar(100, “Ford”, 3000); // Biến đối tượng lớp Car

Bus myBus(80, “Mercedes”, 5000, 27);// Biến đối tượng lớp Bus
cout << “Gioi thieu xe o to lan 1:” << endl;

myCar.show();

cout << “Gioi thieu xe o to lan 2:” << endl;

myCar = myBus;

myCar.show();

cout << “Gioi thieu xe bus:” << endl;

myBus.show();

return;

}

Chương trình 6.5 sẽ in ra kết quả thông báo như sau:

Goi thieu xe o to lan 1:

This is a Ford having a speed of 100km/h and its price is $3000

Gioi thieu xe lan o to lan 2:

This is a Mercedes having a speed of 80km/h and its price is $5000

Gioi thieu xe bus:

This is a bus of type Mercedes, on the line 27, having a speed of
80km/h and its price is $5000

Ở thông báo thứ nhất, đối tượng myCar gọi phương thức show() của lớp Car với các dữ liệu được
khởi đầu cho myCar: (100, “Ford”, 3000). Ở thông báo thứ hai, myCar gọi phương thức show()
của lớp Car, nhưng với dữ liệu vừa được gán từđối tượng myBus: (80, “Mercedes”, 5000). Ở
thông báo thứ ba, myBus gọi phương thức show() của lớp Bus với các dữ liệu của myBus: (80,
“Mercedes”, 5000, 27).

6.4 ĐA KẾ THỪA

C++ cho phép đa kế thừa, tức là một lớp có thểđược dẫn xuất từ nhiều lớp cơ sở khác nhau, với
những kiểu dẫn xuất khác nhau.

6.4.1 Khai báo đa kế thừa

Đa kế thừa được khai báo theo cú pháp:

class <Tên lớp dẫn xuất>: <Từ khoá dẫn xuất> <Tên lớp cơ sở 1>,

<Từ khoá dẫn xuất> <Tên lớp cơ sở 2>,



<Từ khoá dẫn xuất> <Tên lớp cơ sở n>{

… // Khai báo thêm các thành phần lớp dẫn xuất

};

Ví dụ:

class Bus: public Car, public PublicTransport{

… // Khai báo các thành phần bổ sung

};
là khai báo lớp Bus (xe buýt) kế thừa từ hai lớp xe Car (ô tô) và PublicTransport (phương tiện
giao thông công cộng) theo cùng một kiểu dẫn xuất là public.

Lưu ý:

• Trong đa kế thừa, mỗi lớp cơ sởđược phân cách nhau bởi dấu phẩy “,”.

• Mỗi lớp cơ sở cơ thể có một kiểu dẫn xuất bởi một từ khoá dẫn xuất khác nhau.

• Nguyên tắc truy nhập vào các thành phần lớp cơ sở cũng hoàn toàn tương tự như trong kế
thừa đơn.

6.4.2 Hàm khởi tạo và hàm huỷ bỏ trong đa kế thừa

Hàm khởi tạo trong đa kế thừa

Hàm khởi tạo trong đa kế thừa được khai báo tương tự như trong đơn kế thừa, ngoại trừ việc phải
sắp xếp thứ tự gọi tới hàm khởi tạo của các lớp cơ sở: thông thường, thứ tự gọi đến hàm khởi tạo
của các lớp cơ sở nên tuân theo thứ tự dẫn xuất từ các lớp cơ sở trong đa kế thừa.

Chương trình 6.6 minh hoạ việc định nghĩa hàm khởi tạo tường minh trong đa kế thừa: thứ tự gọi
hàm khởi tạo của các lớp cơ sở trong hàm khởi tạo của lớp Bus là tương tự thứ tự dẫn xuất: hàm
khởi tạo của lớp Car trước hàm khởi tạo của lớp PublicTransport.

Chương trình 6.6

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

Car(); // Khởi tạo không tham số

Car(int, char[ ], float); // Khởi tạo đủ tham số

};

Car::Car(){ // Khởi tạo không tham số

speed = 0;

strcpy(mark, “”);

price = 0;

}

// Khởi tạo đủ tham số

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;
}

/* Định nghĩa lớp PublicTransport */

class PublicTransport{

float ticket; // Giá vé phương tiện

public:

PublicTransport(); // Khởi tạo không tham số

PublicTransport(float); // Khởi tạo đủ tham số

};

PublicTransport::PublicTransport(){ // Khởi tạo không tham số

ticket = 0;

}

// Khởi tạo đủ tham số

PublicTransport::PublicTransport(float ticketIn){

ticket = ticketIn;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car và PublicTransport */

class Bus: public Car, public PublicTransport{ // Thứ tự khai báo

int label; // Số hiệu tuyến xe

public:

Bus(); // Khởi tạo không tham số

Bus(int, char[], float, float, int);// Khởi tạo đủ tham số

};

// Khởi tạo không tham số

Bus::Bus(): Car(), Transport(){ // Theo thứ tự dẫn xuất

label = 0;

}

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, float tIn, int lIn):

Car(sIn, mIn, pIn), PublicTransport(tIn){ // Theo thứ tự dẫn xuất

label = lIn;

}

Lưu ý:

• Trong trường hợp dùng hàm khởi tạo ngầm định hoặc không có tham số, ta có thể không
cần gọi tường minh các hàm khởi tạo của các lớp cơ sở, trình biên dịch sẽ tựđộng gọi tới
chúng theo đúng thứ tự dẫn xuất
Ví dụ, trong chương trình 6.6, hai cách định nghĩa hàm khởi tạo không tham số của lớp Bus sau là
tương đương:

Bus::Bus(): Car(), Transport(){// Theo thứ tự dẫn xuất

label = 0;

}

là tương đương với:

Bus::Bus(){ // Theo thứ tự dẫn xuất ngầm định

label = 0;

}

Hàm huỷ bỏ trong đa kế thừa

Vì hàm huỷ bỏ là duy nhất của mỗi lớp, hơn nữa hàm huỷ bỏ của lớp cơ sở sẽđược tựđộng gọi
đến khi giải phóng đối tượng của lớp dẫn xuất. Cho nên hàm huỷ bỏ trong đa kế thừa hoàn toàn
tương tự hàm huỷ bỏ trong đơn kế thừa:

• Hàm huỷ bỏ của lớp dẫn xuất chỉ giải phóng bộ nhớ cho các thành phần bổ sung, nếu có,
của lớp dẫn xuất.

• Hàm huỷ bỏ của lớp dẫn xuất sẽđược gọi đến sớm nhất. Sau đó các hàm huỷ bỏ của các
lớp cơ sở sẽđược gọi đến.

• Quá trình này được trình biên dịch thực hiện tựđộng.

6.4.3 Truy nhập các thành phần lớp trong đa kế thừa

Việc truy nhập đến các thành phần của các lớp trong đa kế thừa được dựa trên các nguyên tắc sau:

• Việc truy nhập từđối tượng lớp dẫn xuất đến các thành phần của mỗi lớp cơ sởđược tuân
theo quy tắc phạm vi tương tự như trong đơn kế thừa.

• Trong trường hợp các lớp cơ sởđều có các thành phần cùng tên, việc truy xuất đến thành
phần của lớp nào phải được chỉ rõ bằng toán tử phạm vi: “<Tên lớp>::” đối với thành
phần lớp cơ sởđó.

Ví dụ, ta định nghĩa lớp Bus kế thừa từ hai lớp cơ sở: Car và PublicTransport. Nhưng cả ba lớp
này đều định nghĩa một phương thức show() để tự giới thiệu:

class Car{

public:

void show();

};

class PublicTransport{

public:

void show();

};

class Bus: public Car, public PublicTransport{

public:

void show();

};
Khi đó, khai báo:

Bus myBus;

và lời gọi hàm:

myBus.show(); // Gọi đến hàm của lớp Bus

myBus.Car::show(); // Gọi đến hàm của lớp Car

myBus.PublicTransport::show();// Gọi đến hàm của lớp PublicTransport

Chương trình 6.7 minh hoạ việc truy nhập đến các thành phần trùng nhau trong các lớp cơ sở và
được định nghĩa lại trong lớp dẫn xuất.

Chương trình 6.7

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

Car(); // Khởi tạo không tham số

Car(int, char[ ], float); // Khởi tạo đủ tham số

void show(); // Giới thiệu

float getSpeed(){return speed;};

char[] getMark(){return mark;};

float getPrice(){return price;};

};

Car::Car(){ // Khởi tạo không tham số

speed = 0;

strcpy(mark, “”);

price = 0;

}

// Khởi tạo đủ tham số

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

// Giới thiệu

void Car::show(){

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h and its price is $” << price << endl;

return;

}

/* Định nghĩa lớp PublicTransport */

class PublicTransport{

float ticket; // Giá vé phương tiện

public:

PublicTransport(); // Khởi tạo không tham số

PublicTransport(float); // Khởi tạo đủ tham số

void show(); // Giới thiệu

float getTicket(){return ticket;};

};

PublicTransport::PublicTransport(){ // Khởi tạo không tham số

ticket = 0;

}

// Khởi tạo đủ tham số

PublicTransport::PublicTransport(float ticketIn){

ticket = ticketIn;

}

// Giới thiệu

void PublicTransport::show(){

cout << “This public transport had a ticket of $”

<< ticket << endl;

return;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car và PublicTransport */

class Bus: public Car, public PublicTransport{ // Thứ tự khai báo

int label; // Số hiệu tuyến xe

public:

Bus(); // Khởi tạo không tham số

Bus(int, char[ ], float, float, int);// Khởi tạo đủ tham số

void show(); // Giới thiệu

};

// Khởi tạo không tham số

Bus::Bus(): Car(), Transport(){ // Theo thứ tự dẫn xuất

label = 0;

}

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[], float pIn, float tIn, int lIn):

Car(sIn, mIn, pIn), PublicTransport(tIn){ // Theo thứ tự dẫn xuất

label = lIn;

}

// Giới thiệu

void Bus::show(){

cout << “This is a bus on the line ” << label

<< “, its speed is ” << getSpeed()

<< “km/h, mark is ” << getMark()

<< “, price is $” << getPrice()

<< “ and ticket is ” << getTicket() << endl;

return;

}

// phương thức main

void main(){

clrscr();

Bus myBus(100, “Mercedes”, 3000, 1.5, 27);

myBus.Car::show(); // Hàm của lớp Car

myBus.PublicTransport:: show(); // Hàm của lớp PublicTransport

myBus.show(); // Hàm của lớp Bus

return;

}

Chương trình 6.7 sẽ in ra thông báo như sau:

This is a Mercedes having a speed of 100km/h and its price is $3000

This public transport had a ticket of $1.5

This is a bus on the line 27, its speed is 100km/h, mark is Mercedes,
price is $3000 and ticket is $1.5

Dòng thứ nhất là kết quả của phương thức show() của lớp Car, dòng thứ hai, tương ứng là kết quả
phương thức show() của lớp PublicTransport, dòng thứ ba là kết quả phương thức show() của lớp
Bus.

6.5 LỚP CƠ SỞ TRỪU TƯỢNG

6.5.1 Đặt vấn đề

Sự cho phép đa kế thừa trong C++ dẫn đến một số hậu quả xấu, đó là sựđụng độ giữa các thành
phần của các lớp cơ sở, khi có ít nhất hai lớp cơ sở lại cùng được kế thừa từ một lớp cơ sở khác.
Xét trường hợp:

• Lớp Bus kế thừa từ lớp Car và lớp PublicTransport.

• Nhưng lớp Car và lớp PublicTransport lại cùng được thừa kế từ lớp Engine (động cơ).
Lớp Engine có một thuộc tính là power (công suất của động cơ).

Engine

Car PublicTransport

Bus

Khi đó, nảy sinh một số vấn đề như sau:

• Các thành phần dữ liệu của lớp Engine bị lặp lại trong lớp Bus hai lần: một lần do kế thừa
theo đường Bus::Car::Engine, một lần theo đường Bus::PublicTransport::Engine. Điều
này là không an toàn.

• Khi khai báo một đối tượng của lớp Bus, hàm khởi tạo của lớp Engine cũng được gọi hai
lần: một lần do gọi truy hồi từ hàm khởi tạo lớp Car, một lần do gọi truy hồi từ hàm khởi
tạo lớp PublicTransport.

• Khi giải phóng một đối tượng của lớp Bus, hàm huỷ bỏ của lớp Engine cũng sẽ bị gọi tới
hai lần.

Để tránh các vấn đề này, C++ cung cấp một khái niệm là kế thừa từlớp c ơ sở trừu tượng. Khi đó,
ta cho các lớp Car và PublicTransport kế thừa trừu tượng từ lớp Engine. Bằng cách này, các thành
phần của lớp Engine chỉ xuất hiện trong lớp Bus đúng một lần. Lớp Engine được gọi là lớp cơ sở
trừu tượng của các lớp Car và PublicTransport.

6.5.2 Khai báo lớp cơ sở trừu tượng

Việc chỉ ra một sự kế thừa trừu tượng được thực hiện bằng từ khoá virtual khi khai báo lớp cơ sở:

class <Tên lớp cơ sở>: <Từ khoá dẫn xuất> virtual <Tên lớp cơ sở>{

… // Khai báo các thành phần bổ sung

};

Ví dụ:

class Engine{

… // Các thành phần lớp Engine
};

class Car: public virtual Engine{

… // Khai báo các thành phần bổ sung

};

là khai báo lớp Car, kế thừa từ lớp cơ sở trừu tượng Engine, theo kiểu dẫn xuất public.

Lưu ý:

• Từ khoá virtual được viết bằng chữ thường.

• Từ khoá virtual không ảnh hưởng đến phạm vi truy nhập thành phần lớp cơ sở, phạm vi
này vẫn được quy định bởi từ khoá dẫn xuất như thông thường.

• Từ khoá virtual chỉ ra một lớp cơ sở là trừu tượng nhưng lại được viết trong khi khai báo
lớp dẫn xuất.

• Một lớp dẫn xuất có thểđược kế thừa từ nhiều lớp cơ sở trừu tượng

6.5.3 Hàm khởi tạo lớp cơ sở trừu tượng

Khác với các lớp cơ sở thông thường, khi có một lớp dẫn xuất từ một lớp cơ sở trừu tượng, lại
được lấy làm cơ sở cho một lớp dẫn xuất khác thì trong hàm khởi tạo của lớp dẫn xuất cuối cùng,
vẫn phải gọi hàm khởi tạo tường minh của lớp cơ sở trừu tượng. Hơn nữa, hàm khởi tạo của lớp
cơ sở trừu tượng phải được gọi sớm nhất.

Ví dụ, khi lớp Car và lớp PublicTransport được kế thừa từ lớp cơ sở trừu tượng Engine. Sau đó,
lớp Bus được kế thừa từ hai lớp Car và PublicTranport. Khi đó, hàm khởi tạo của lớp Bus cũng
phải gọi tường minh hàm khởi tạo của lớp Engine, theo thứ tự sớm nhất, sau đó mới gọi đến hàm
khởi tạo của các lớp Car và PublicTransport.

class Engine{

public:

Engine(){… };

};

class Car: public virtual Engine{ //Lớp cơ sở virtual

public:

Car(): Engine(){… };

};

class PublicTransport: public virtual Engine{ //Lớp cơ sở virtual

public:

PublicTransport():Engine(){… };

};

class Bus: public Car, public PublicTransport{

public:

// Gọi hàm khởi tạo tường minh của lớp cơ sở trừu tượng

Bus():Engine(), Car(), PublicTransport(){… };

};

Lưu ý:

• Trong trường hợp lớp Engine không phải là lớp cơ sở trừu tượng của các lớp Car và
PublicTransport, thì trong hàm khởi tạo của lớp Bus không cần gọi hàm khởi tạo của lớp
Engine, mà chỉ cần gọi tới các hàm khởi tạo của các lớp cơ sở trực tiếp của lớp Bus là lớp
Car và lớp PublicTransport.

Chương trình 6.8 minh hoạ việc khai báo và sử dụng lớp cơ sở trừu tượng: lớp Engine là lớp cơ sở
trừu tượng của các lớp Car và lớp PublicTransport. Hai lớp này, sau đó, lại làm lớp cơ sở của lớp
Bus.

Chương trình 6.8

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Engine */

class Engine{

int power; // Công suất

public:

Engine(){power = 0;}; // Khởi tạo không tham số

Engine(int pIn){power = pIn;};// Khởi tạo đủ tham số

void show(); // Giới thiệu

float getPower(){return power;};

};

// Giới thiệu

void Engine::show(){

cout << “This is an engine having a power of ”

<< power << “KWH” << endl;

return;

}

/* Định nghĩa lớp Car dẫn xuất từ lớp cơ sở trừu tượng Engine*/

class Car: public virtual Engine{

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

Car(); // Khởi tạo không tham số

Car(int, int, char[ ], float); // Khởi tạo đủ tham số

void show(); // Giới thiệu

float getSpeed(){return speed;};

char[ ] getMark(){return mark;};

float getPrice(){return price;};

};
v
Car::Car(): Engine(){ // Khởi tạo không tham số

speed = 0;

strcpy(mark, “”);

price = 0;

}

// Khởi tạo đủ tham số

Car::Car(int pwIn, int sIn, char mIn[], float prIn): Engine(pwIn){

speed = sIn;

strcpy(mark, mIn);

price = prIn;

}

// Giới thiệu

void Car::show(){

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h, its power is” << getPower()

<< “KWh and price is $” << price << endl;

return;

}

/* Định nghĩa lớp PublicTransport dẫn xuất trừu tượng từ lớp Engine */

class PublicTransport: public virtual Engine{

float ticket; // Giá vé phương tiện

public:

PublicTransport(); // Khởi tạo không tham số

PublicTransport(int, float); // Khởi tạo đủ tham số

void show(); // Giới thiệu

float getTicket(){return ticket;};

};

// Khởi tạo không tham số

PublicTransport::PublicTransport(): Engine(){

ticket = 0;

}

// Khởi tạo đủ tham số

PublicTransport::PublicTransport(int pwIn, float tIn): Engine(pwIn){

ticket = tIn;

}

// Giới thiệu

void PublicTransport::show(){
cout << “This is a public transport havìn a ticket of $”

<< ticket << “ and its power is ” << getPower()

<< “KWh” << endl;

return;

}

/* Định nghĩa lớp Bus kế thừa từ lớp Car và PublicTransport */

class Bus: public Car, public PublicTransport{ // Thứ tự khai báo

int label; // Số hiệu tuyến xe

public:

Bus(); // Khởi tạo không tham số

Bus(int,int,char[],float,float,int);// Khởi tạo đủ tham số

void show(); // Giới thiệu

};

// Khởi tạo không tham số

Bus::Bus(): Engine(), Car(), Transport(){ // Theo thứ tự dẫn xuất

label = 0;

}

// Khởi tạo đủ tham số

Bus::Bus(int pwIn, int sIn, char mIn[ ], float prIn, float tIn, int lIn):

Engine(pwIn), Car(sIn, mIn, prIn), PublicTransport(tIn){

label = lIn;

}

// Giới thiệu

void Bus::show(){

cout << “This is a bus on the line ” << label

<< “, its speed is ” << getSpeed()

<< “km/h, power is” << Car::getPower()

<< “KWh, mark is ” << getMark()

<< “, price is $” << getPrice()

<< “ and ticket is ” << getTicket() << endl;

return;

}

// phương thức main

void main(){

clrscr();

Bus myBus(250, 100, “Mercedes”, 3000, 1.5, 27);

myBus.Car::Engine::show(); // Hàm của lớp Engine

myBus.PublicTransport::Engine::show();// Hàm của lớp Engine

myBus.Car::show(); // Hàm của lớp Car

myBus.PublicTransport:: show(); // Hàm của lớp PublicTransport

myBus.show(); // Hàm của lớp Bus

return;

}

Chương trình 6.8 sẽ in ra thông báo như sau:

This is an engine having a power of 250KWh

This is an engine having a power of 250KWh

This is a Mercedes having a speed of 100km/h, its power is 250KWh and
price is $3000

This is a public transport having a ticket of $1.5 and its power is
250KWh

This is a bus on the line 27, its speed is 100km/h, power is 250KWh,
mark is Mercedes, price is $3000 and ticket is $1.5

Hai dòng đầu là kết quả của phương thức show() của lớp Engine: một lần gọi qua lớp Car, một lần
gọi qua lớp PublicTransport, chúng cho kết quả như nhau. Dòng thứ ba là kết quả phương thức
show() của lớp Car. Dòng thứ tư, tương ứng là kết quả phương thức show() của lớp
PublicTransport. Dòng thứ năm là kết quả phương thức show() của lớp Bus.
luongbv93
luongbv93
Admin

Tổng số bài gửi : 117
Points : 354
Reputation : 0
Join date : 18/11/2011
Age : 30
Đến từ : Hà Nội

https://diendanteenviet.forumvi.com

Về Đầu Trang Go down

Về Đầu Trang


 
Permissions in this forum:
Bạn không có quyền trả lời bài viết