Bài tập đa kế thừa c++

Đ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. 


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

class :  , 
                    , 
                   … 
                    { 
         …  // 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 
}; 

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. 

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. 
Ví dụ: Đị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. 

#include 
#include 
using namespace std;
/* Định nghĩa lớp */
class Car {
private:
	int  speed;               // Tốc độ  
	string  mark;           // Nhãn hiệu
	float price;               // Giá xe 						
							   // Khởi tạo với các giá trị ngầm định cho các tham số 
public:
	Car();
	Car(int speed, string mark, float price);
	
};

/* Khai báo phương thức bên ngoài lớp */
Car::Car() {
	this->speed = 0;
	this->mark = "";
	this->price = 0;

}
Car::Car(int speed, string mark, float price) {
	this->speed = speed;
	this->mark = mark;
	this->price = price;
}
/* Đị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ố  	
	this->ticket = 0; 
}

// Khởi tạo đủ tham số 
PublicTransport::PublicTransport(float ticket) {
	this->ticket = ticket;
}
/* Đị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, string, float, float, int);// Khởi tạo đủ tham số 
};

// Khởi tạo không tham số 
Bus::Bus() : Car(), PublicTransport() { 	 	// Theo thứ tự dẫn xuất  	
	label = 0; 
}

// Khởi tạo đủ tham số 
Bus::Bus(int speed, string mark, float price, float ticket, int label) :
	Car(speed, mark, price), PublicTransport(ticket) { // Theo thứ tự dẫn xuất 
	this->label = label;
}

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 trên 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. 

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: “::” đố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 
 

Ví dụ về 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. 
 

#include 
#include 
using namespace std;
/* Định nghĩa lớp */
class Car {
private:
	int  speed;               // Tốc độ  
	string  mark;           // Nhãn hiệu
	float price;               // Giá xe 						
							   // Khởi tạo với các giá trị ngầm định cho các tham số 
public:
	Car();
	Car(int speed, string mark, float price);
	void show(); 	 	 	// Giới thiệu  
	float getSpeed(){
		return speed;
	};  	
	string getMark(){
		return mark;
	};  	
	float getPrice(){
		return price;
	}; 
	
};

/* Khai báo phương thức bên ngoài lớp */
Car::Car() {
	this->speed = 0;
	this->mark = "";
	this->price = 0;

}
Car::Car(int speed, string mark, float price) {
	this->speed = speed;
	this->mark = mark;
	this->price = price;
}

void Car::show() {                // Phương thức hiển thị xe
	cout << "This is a " << mark << " having a speed of  " << speed << "km/h and its price is $" << price << endl;
}
/* Đị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ố  	
	this->ticket = 0; 
}

// Khởi tạo đủ tham số 
PublicTransport::PublicTransport(float ticket) {
	this->ticket = ticket;
}
// Giới thiệu 
void PublicTransport::show() {
	cout << "This public transport had a ticket of $"<< ticket << endl;  	
}

/* Đị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, string, float, float, int);// Khởi tạo đủ tham số 
	void show();
};

// Khởi tạo không tham số 
Bus::Bus() : Car(), PublicTransport() { 	 	// Theo thứ tự dẫn xuất  	
	label = 0; 
}

// Khởi tạo đủ tham số 
Bus::Bus(int speed, string mark, float price, float ticket, int label) :
	Car(speed, mark, price), PublicTransport(ticket) { // Theo thứ tự dẫn xuất 
	this->label = label;
}
// 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;	
}
int main() {
	
	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 
	system("pause");

	return 0; 
}

Kết quả:

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.