Dđiều nào sau đây mô tả tính da kế thừa năm 2024

Một trong những đặc tính quan trọng nhất của OOP là kế thừa. Ưu điểm của đặc tính kế thừa: sử dụng lại các đoạn code đã có trong chương trình 1 cách hiệu quả. Khi tạo 1 class, thay vì việc viết 1 class mới hoàn toàn, người lập trình viên có thể kế thừa một số thuộc tính và phương thức từ 1 class đã có trong project. Class đã có trước đấy gọi là lớp cơ sở (Base Class), class kế thừa từ Base Class (hay superclass) gọi là lớp dẫn xuất (Derived Class).

Do tính kế thừa khá rõ ràng và dễ hiểu nên mình sẽ không giải thích nhiều về kế thừa nữa. Bạn nào muốn tìm hiểu thêm thì tham khảo link này nhé: https://www.cppdeveloper.com/c-co-ban/6-1-1-dinh-nghia-mot-phan-lop-don-gian-1/

Tính đa hình – Polymorphism

Tính Đa hình – Polymorphism là một tính chất đặc trưng rất thần thánh của OOP. Trong bài này mình sẽ chủ yếu tập trung giải thích về Polymorphism cho các bạn.

Đầu tiên mình sẽ đưa ví dụ về Polymorphism ở ngoài đời thực để anh em dễ hình dung đã. Ví dụ, cùng là một người nhưng tuỳ từng ngữ cảnh sẽ đóng vai trò khác nhau, một người đàn ông vừa là nhân viên (khi đi làm), vừa là một người chồng (đối với vợ) và là người cha (đối với con),… nói chung là anh ta sẽ biến hình thành con người khác nhau tuỳ từng ngữ cảnh.

Trong OOP và cụ thể là trọng ngôn ngữ C++ thì Polymorphism có 2 dạng:

  • Dạng 1 – Compile time Polymorphism: Một class có nhiều hàm cùng tên nhưng khác nhau về số lượng tham số hoặc kiểu dữ liệu của tham số. Khi call hàm cùng tên đó thì trong quá trình biên dịch, compiler sẽ quyết định hàm nào (trong số các hàm cùng tên) sẽ được call dựa trên số lượng tham số và kiểu dữ liệu của tham số truyển vào hàm. Việc định nghĩa các hàm cùng tên được gọi là overloading – nạp chồng hàm.
  • Dạng 2 – Runtime Polymorphism: Cùng một class có thể cho ra nhiều biến thể, không phải được định nghĩa bởi lớp đó, mà bởi các lớp con của nó. Đây là một phương pháp để định nghĩa lại hành vi của lớp cơ sở mà không phải sửa code của lớp cơ sở. Nếu call hàm của đối tượng của lớp dẫn xuất thông qua con trỏ của lớp cơ sở thì việc hàm nào được call sẽ được quyết định lúc Runtime. Runtime Polymorphism được thực hiện bằng phương pháp overriding – ghi đè phương thức.

Sau đây mình sẽ đưa code sample cho 2 dạng Polymorphism ở trên. — Sample 1 – Compile time Polymorphism

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

include

using namespace std;

class PrintData {

public:

void print(int i) {

cout << "Printing int: " << i << endl;

}

void print(double f) {

cout << "Printing float: " << f << endl;

}

void print(char* c) {

cout << "Printing character: " << c << endl;

}

};

int main(void) {

PrintData pd;

pd.print(5);// Call print to print integer

pd.print(500.263);// Call print to print float

pd.print("Hello C++");// Call print to print character

return 0;

}

Ta có class PrintData có 3 phương thức cùng tên là print(), 3 phương thức này cùng có một tham số nhưng kiểu của tham số thì khác nhau, lần lượt là int, double và char*. Trong hàm main gọi đến phương thức print() 3 lần với 3 tham số khác nhau, khi đó trình biên dịch sẽ dựa vào kiểu ‘(hoặc giá trị) của tham số truyền vào và tự quyết định phương thức nào trong 3 phương thức cùng tên sẽ được gọi. Chương trình này chạy sẽ ra kết quả trên console như sau:

Printing int: 5

Printing float: 500.263

Printing character: Hello C++

— Sample 2 – Runtime Polymorphism

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

include

using namespace std;

class Pet {

protected:

string Name;

public:

Pet(string n) { Name \= n; }

virtual string getSound() { return "";};

void makeSound(void) { cout << Name << "says: " << getSound() << endl; }

};

class Cat : public Pet {

public:

Cat(string n) : Pet(n) { }

string getSound() { return "Meow! Meow!";};

};

class Dog : public Pet {

public:

Dog(string n) : Pet(n) { }

string getSound() { return "Woof! Woof!";};

};

int main(void) {

Pet *a_pet \= new Cat("Kitty");;

a_pet-\>makeSound();

delete a_pet;

a_pet \= new Dog("Doggie");

a_pet-\>makeSound();

delete a_pet;

return 0;

}

Chương trình này sẽ xuất ra màn hình nội dung như sau →

Kitty says: Meow! Meow!

Doggie says: Woof! Woof!

Ở đây ta có class cha là Pet định nghĩa phương thức makeSound(), phương thức này đưa ra mô tả về tiếng kêu của con vật. Tiếng kêu được định nghĩa bởi hàm getSound(), hàm này được khai báo là hàm ảo (virtual) ở class cha, điều đó cho phép các class con như Cat, Dog định nghĩa lại (overriding) hàm này cho phù hợp với đặc điểm riêng của chúng. Chính vì vậy ở hàm main dù cùng là con trỏ a_pet nhưng khi gọi hàm makeSound() 2 lần lại ra 2 nội dung khác nhau tuỳ thuộc vào đối tượng đang được trỏ tới lúc đó là đối tượng của class con nào (Dog hay Cat)