Kế thừa constructor trong c++

Một trong những khái niệm quan trọng nhất trong lập trình hướng đối tượng là Tính kế thừa (Inheritance). Kế thừa trong C++ là sự liên quan giữa hai class với nhau, trong đó có class cơ sở (Base Class) và class con (Derived Class). Khi kế thừa class con được hưởng tất cả các phương thức và thuộc tính của class cơ sở. Tuy nhiên, nó chỉ được truy cập các thành viên public và protected của class cơ sở. Nó không được phép truy cập đến thành viên private của class cơ sở.

Tư tưởng của kế thừa trong C++ là có thể tạo ra một class mới được xây dựng trên các lớp đang tồn tại. Khi kế thừa từ một lớp đang tồn tại bạn có sử dụng lại các phương thức và thuộc tính của lớp cơ sở, đồng thời có thể khai báo thêm các phương thức và thuộc tính khác.



Lớp cơ sở (Base Class) và Lớp thừa kế (Derived Class) trong C++

Một lớp có thể được kế thừa từ hơn một lớp khác, nghĩa là, nó có thể kế thừa dữ liệu và hàm từ nhiều lớp cơ sở. Để định nghĩa một lớp kế thừa (Derived Class), chúng ta sử dụng một danh sách để xác định các lớp cơ sở. Danh sách này liệt kê một hoặc nhiều lớp cơ sở và có form sau:

class lop_ke_thua: access_modifier lop_co_so

Ở đây, access_modifier là public, protected hoặc private, và lop_co_so là tên của lớp đã được định nghĩa trước đó. Nếu access_modifier không được sử dụng, thì mặc định là private.

Bạn xem xét ví dụ sau với Hinh là lớp cơ sở và HinhChuNhat là lớp kế thừa:

#include 
 
using namespace std;
// lop co so: Hinh
class Hinh {
   public:
      void setChieuRong(int rong) {
         chieuRong = rong;
      }
      void setChieuDai(int dai) {
         chieuDai = dai;
      }
   protected:
      int chieuRong;
      int chieuDai;
};

// day la lop ke thua: HinhChuNhat
class HinhChuNhat: public Hinh {
   public:
      int tinhDienTich() { 
         return chieuRong * chieuDai; 
      }
};

int main(void) {
   HinhChuNhat hcn;
 
   hcn.setChieuRong(10);
   hcn.setChieuDai(15);
   // in dien tich cua doi tuong.
   cout << "Dien tich HCN la: " << hcn.tinhDienTich() << endl;
   return 0;
}

Biên dịch và chạy chương trình C++ trên sẽ cho kết quả sau:

Kế thừa constructor trong c++

Điều khiển truy cập và Tính kế thừa trong C++

Một lớp kế thừa có thể truy cập tất cả thành viên không phải là private của lớp cơ sở của nó. Vì thế, các thành viên lớp cơ sở, mà là hạn chế truy cập tới các hàm thành viên của lớp kế thừa, nên được khai báo là private trong lớp cơ sở.

Chúng ta tổng kết các kiểu truy cập khác nhau, tương ứng với ai đó có thể truy cập chúng như sau:

Truy cậppublicprotectedprivateTrong cùng lớpCóCóCóLớp kế thừaCóCóKhôngBên ngoài lớpCóKhôngKhông

Một lớp kế thừa (Derived Class) sẽ kế thừa tất cả các phương thức của lớp cơ sở, ngoại trừ:

  • Constructor, destructor và copy constructor của lớp cơ sở.

  • Overloaded operator (toán tử nạp chồng) của lớp cơ sở.

  • Hàm friend của lớp cơ sở.



Kiểu kế thừa trong C++

Khi kế thừa từ một lớp cơ sở, lớp cơ sở đó có thể được kế thừa thông qua kiểu kế thừa là public, protected hoặc private. Kiểu kế thừa trong C++ được xác định bởi Access-specifier đã được giải thíc ở trên.

Chúng ta hiếm khi sử dụng kiểu kế thừa protected hoặc private, nhưng kiểu kế thừa public thì được sử dụng phổ biến hơn. Trong khi sử dụng các kiểu kế thừa khác sau, bạn nên ghi nhớ các quy tắc sau:

  • Kiểu kế thừa Public: Khi kế thừa từ một lớp cơ sở là public, thì 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 kế thừa; 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 kế thừa. Một thành viên là private của lớp cơ sở là không bao giờ có thể được truy cập trực tiếp từ một lớp kế thừa, nhưng có thể truy cập thông qua các lời gọi tới các thành viên public và protected của lớp cơ sở đó.

  • Kiểu kế thừa protected: Khi kế thừa từ một lớp cơ sở là protected, thì các thành viên public và protected của lớp cơ sở trở thành các thành viên protected của lớp kế thừa

  • Kiểu kế thừa private: Khi kế thừa từ một lớp cơ sở là private, thì các thành viên public và protected của lớp cơ sở trở thành các thành viên private của lớp kế thừa


Đa kế thừa trong C++

Một lớp trong C++ có thể kế thừa các thành viên từ nhiều lớp, và đây là cú pháp:

class lop_ke_thua: access_modifier lop_co_so_1, access_modifier lop_co_so_2 ...

Tại đây, access_modifier là public, protected hoặc private và sẽ được cung cấp cho mỗi lớp cơ sở, và chúng sẽ được phân biệt với nhau bởi dấu phảy như trên. Bạn thử ví dụ sau:

Qua bài viết trước, chúng ta đã hiểu được tính kế thừa trong lập trình C#. Trong bài này sẽ bổ sung hoàn chỉnh với nội dung trình bày về cách sử dụng hàm dựng constructor trong kế thừa:

  • Nhắc lại Tính kế thừa
  • Hàm dựng Constructor trong kế thừa
    • #1. Trường hợp hàm constructor không tham số.
    • #2. Trường hợp hàm constructor có tham số.

Nhắc lại Tính kế thừa

Nhắc lại Tính kế thừa

Giả sử chúng ta có một lớp cơ sở Hình Học và hai lớp dẫn xuất là Hình Chữ Nhật và Hình Vuông như sau:

– Lớp cơ sở Hình học : có các thuộc tính Chiều dài, Chiều rộng, và các phương thức Set chiều dài, Set chiều rộng.

– Hai lớp dẫn xuất là Hình Chữ Nhật và Hình Vuông : ngoài việc kế thừa các thuộc tính và phương thức của lớp cha Hình Học, thì hình chữ nhật và hình vuông sẽ có cách tính chu vi và diện tích là khác nhau nên ở mỗi lớp sẽ xây dựng thêm 2 phương thức riêng là Tính chu vi và Tính diện tích.

Kế thừa constructor trong c++

Inheritance: Shape – Rectangle – Square

– Chúng ta sẽ cài đặt như sau:

Kế thừa constructor trong c++
Ví dụ ①

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// ① Tạo đối tượng hình chữ nhật
			HinhChuNhat hcn = new HinhChuNhat();

			// Sử dụng phương thức kế thừa từ lớp cha
			hcn.setChieuRong(8);
			hcn.setChieuDai(9);

			// In Chu vi và Diện tích của hình chữ nhật
			Console.WriteLine("Chu vi hinh chu nhat: {0}", hcn.TinhChuVi());
			Console.WriteLine("Dien tich hinh chu nhat: {0}", hcn.TinhDienTich());

			// ② Tạo đối tượng hình vuông
			HinhVuong hv = new HinhVuong();

			// Sử dụng phương thức kế thừa từ lớp cha
			hv.setChieuDai(19);

			// In Chu vi và Diện tích của hình vuông
			Console.WriteLine("Chu vi hinh vuong: {0}", hv.TinhChuVi());
			Console.WriteLine("Dien tich hinh vuong: {0}", hv.TinhDienTich());

			Console.ReadKey();
		}
	}

	/// 
	/// Class cơ sở Hình Học
	/// 
	class HinhHoc
	{
		// Thuộc tính dùng chung
		protected int ChieuDai;

		protected int ChieuRong;

		// Phương thức dùng chung
		public void setChieuDai(int inChieuDai)
		{
			ChieuDai = inChieuDai;
		}

		public void setChieuRong(int inChieuRong)
		{
			ChieuRong = inChieuRong;
		}
	}

	/// 
	/// Class dẫn xuất Hình Chữ Nhật
	/// 
	class HinhChuNhat : HinhHoc		// Kế thừa
	{
		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai, ChieuRong kế thừa từ lớp cha
			return (ChieuDai + ChieuRong) * 2;
		}

		public int TinhDienTich()
		{
			return (ChieuDai * ChieuRong);
		}
	}

	/// 
	/// Class dẫn xuất Hình Vuông
	/// 
	class HinhVuong : HinhHoc       // Kế thừa
	{
		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai kế thừa từ lớp cha
			return (ChieuDai * 4);
		}

		public int TinhDienTich()
		{
			return (int)Math.Pow(ChieuDai, 2);
		}
	}
}

Kế thừa constructor trong c++

Kết quả chương trình

Hàm dựng Constructor trong kế thừa

Hàm dựng Constructor trong kế thừa

– Khi chúng ta tạo một đối tượng từ lớp con thì hàm dựng Constructor của lớp cha sẽ được gọi trước, được thực hiện trước, sau đó thì hàm dựng Constructor của lớp con mới được gọi thực thi (Nôm na là phải có cha rồi mới có con, nên hàm của cha sẽ được thực hiện trước). Chúng ta xét ví dụ sau:

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng hình chữ nhật
			HinhChuNhat hcn = new HinhChuNhat();

			Console.ReadKey();
		}
	}

	/// 
	/// Class cơ sở Hình Học
	/// 
	class HinhHoc
	{
		/// 
		/// Constructor
		/// 
		public HinhHoc()
		{
			Console.WriteLine( "Constructor base class HinhHoc." );
		}
	}

	/// 
	/// Class dẫn xuất Hình Chữ Nhật
	/// 
	class HinhChuNhat : HinhHoc     // Kế thừa
	{
		/// 
		/// Constructor
		/// 
		public HinhChuNhat()
		{
			Console.WriteLine( "Constructor derived class HinhChuNhat." );
		}
	}
}

・ Kết quả chương trình:

Constructor base class HinhHoc.

Constructor derived class HinhChuNhat.

Trong C#, khi một class được tạo ra, nếu class đó chưa có Constructor, Visual C# sẽ ngầm định tự động generate một constructor mặc định (không tham số), vì nếu không làm vậy sẽ không thể tạo một object mới thuộc class đó rồi, như các bạn thấy ở ví dụ ① chúng ta có thể tạo ra các đối tượng HinhChuNhat, HinhVuong mà không cần tạo hàm Constructor.

Vậy trong trường hợp Constructor có tham số thì sẽ khai báo thế nào?

– Bây giờ chúng ta sẽ xét ví dụ sau có khai báo constructor có tham số ở lớp cha:

Kế thừa constructor trong c++
Ví dụ ②

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng hình chữ nhật, sử dụng constructor không tham số
			HinhChuNhat hcn = new HinhChuNhat();

			// Sử dụng phương thức kế thừa từ lớp cha
			hcn.setChieuRong(8);
			hcn.setChieuDai(9);

			// In Chu vi và Diện tích của hình chữ nhật
			Console.WriteLine("Chu vi hinh chu nhat: {0}", hcn.TinhChuVi());
			Console.WriteLine("Dien tich hinh chu nhat: {0}", hcn.TinhDienTich());

			Console.ReadKey();
		}
	}

	/// 
	/// Class cơ sở Hình Học
	/// 
	class HinhHoc
	{
		// Thuộc tính dùng chung
		protected int ChieuDai;

		protected int ChieuRong;

		/// 
		/// Constructor không tham số
		/// 
		public HinhHoc()
		{
			Console.WriteLine( "Constructor base class HinhHoc." );
		}

		/// 
		/// Constructor có tham số
		/// 
		/// Chiều dài
		/// Chiều rộng
		public HinhHoc(int cd, int cr)
		{
			ChieuDai = cd;
			ChieuRong = cr;
		}

		// Phương thức dùng chung
		public void setChieuDai(int inChieuDai)
		{
			ChieuDai = inChieuDai;
		}

		public void setChieuRong(int inChieuRong)
		{
			ChieuRong = inChieuRong;
		}
	}

	/// 
	/// Class dẫn xuất Hình Chữ Nhật
	/// 
	class HinhChuNhat : HinhHoc		// Kế thừa
	{
		/// 
		/// Constructor không tham số
		/// 
		public HinhChuNhat()
		{
			Console.WriteLine( "Constructor derived class HinhChuNhat." );
		}

		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai, ChieuRong kế thừa từ lớp cha
			return (ChieuDai + ChieuRong) * 2;
		}

		public int TinhDienTich()
		{
			return (ChieuDai * ChieuRong);
		}
	}
}

Đối với trường hợp ở lớp cha KHÔNG khai báo constructor có tham số thì việc khai báo constructor không tham số ở lớp con có hay không cũng không sao cả. Chương trình vẫn chạy bình thường như ví dụ ①.

Đối với trường hợp ở lớp cha CÓ khai báo constructor có tham số như ví dụ ②, thì sẽ có 2 trường hợp xảy ra:

■ Trường hợp 1: Lớp cha có khai báo constructor không tham số
Chương trình ở ví dụ ② sẽ chạy được bình thường, vì khi tạo đối tượng hình chữ nhật, là chúng ta đang sử dụng constructor không tham số:

HinhChuNhat hcn = new HinhChuNhat();

■ Trường hợp 2: Lớp cha không khai báo constructor không tham số

– Nếu comment constructor không tham số ở lớp cha Hình học thì constructor không tham số ở lớp con Hình chữ nhật sẽ báo lỗi, vì lúc này nó không có constructor không tham số ở lớp cha base() để kế thừa.

*** Vì bản chất 2 cách viết bên dưới là tương đương nhau:

/// 
/// Constructor
/// 
public HinhChuNhat()
{
	Console.WriteLine( "Constructor derived class HinhChuNhat" );
}

Hoặc:

/// 
/// Constructor
/// 
public HinhChuNhat() : base()
{
	Console.WriteLine( "Constructor derived class HinhChuNhat" );
}

Kế thừa constructor trong c++

Comment hàm constructor không tham số ở lớp cha thì base() sẽ không còn tồn tại. Do đó constructor không tham số ở lớp con sẽ báo lỗi.

Do đó trong trường hợp ở lớp cha có constructor có tham số, mà không khai báo constructor không tham số thì ở lớp con bắt buộc phải khai báo một constructor có tham số.