Showing posts with label Override. Show all posts
Showing posts with label Override. Show all posts

Tuesday, May 3, 2011

Khái niệm về Overloading fuction trong C++

Khái niệm về Overloading fuction trong C++
The concept of Overloading function in C++


Trong phần trước ta đã nói về Override và hide function. Phần này ta sẽ nói về Overloading fuction.

So với Override và Hide thì Overload function đơn giản hơn nhiều. Overloading một hàm tức là khai báo nhiều hơn một hàm f() trong cùng một scope. Các hàm f() khai báo phải khác nhau về các tham số. Khi gọi thì trình biên dịch sẽ so sánh và chọn ra hàm nào phù hợp với loại tham số được khai báo.
Ta có thể thấy khái niệm Overload rất đơn giản. Chỉ cần xem xét ví dụ sau:

  1. #include  
  2. using namespace std; 
  3.  
  4. void print(int i) { 
  5. cout << " Here is int " << i << endl; 
  6.  
  7. void print(double f) { 
  8. cout << " Here is float " << f << endl; 
  9.  
  10. void print(char* c) { 
  11. cout << " Here is char* " << c << endl; 
  12.  
  13. int main() { 
  14. print(10); 
  15. print(10.10); 
  16. print("ten"); 
  17. }

và kết quả sẽ là:

 Here is int 10
 Here is float 10.1
 Here is char* ten

Khái niệm Static binding, Dynamic Binding, Virtual function, Polymorphism, Override và Hide trong C++

Khái niệm Static binding, Dynamic Binding, Virtual function, Polymorphism, Override và Hide trong C++.
The concept of Static binding, Dynamic Binding, Virtual function, Polymorphism, Override and Hide a function in C++.


Lý do để ta đề cập đến nhiều vấn đề một lúc là bởi vì chúng có liên quan đến nhau một cách mật thiết. Bên cạnh đó đây đều là các khái niệm mang tính cơ sở của C++. Các khái niệm này cần thiết để đọc sách về C++ vì ta có thể bắt gặp trong bất kỳ cuốn sách nào về C++.

Về cơ bản mặc định trong C++ sử dụng Static binding, trừ khi muốn sử dụng riêng dynamic binding cho 1 hàm nào đó thì ta phải khai báo thêm từ khóa Virtual ở lời khai báo hàm, như vậy compiler sẽ hiểu cần thực hiên dynamic binding cho hàm đã nêu.
Để rõ hơn ta xem xét ví dụ về static binding sau:
  1. #include  
  2. using namespace std; 
  3.  
  4. struct A { 
  5. void f() { cout << "Class A" << endl; } 
  6. }; 
  7.  
  8. struct B: A {
  9. void f() { cout << "Class B" << endl; } 
  10. }; 
  11.  
  12. void g(A& arg) { 
  13. arg.f(); 
  14.  
  15. int main() { 
  16. B x; 
  17. g(x); 
  18. }
  19.  
Đây là một ví dụ về Static binding trong đó ta nhận thấy, khi hàm g(x) được gọi thìhàm A::f() sẽ được gọi dù ta đã định nghĩa kiểu của x là B. Do tại thời điểm biên dịch (compile-time) thì trình biên dịch chỉ có thể xác định được tham số của hàm g() sẽ là 1 tham chiếu tới biển có kiểu A hoặc là kiểu thừa kế của A. Như vậy trình biên dịch sẽ không thể xác định kiểu dữ liệu chính xác là kiểu A hay là kiểu B (ở đây B cũng là kế thừa của A). Như vậy kết quả của chương trình trên sẽ là

Class A

Tuy vậy tại thời điểm đang chay (runtime) thì chương trình có thể xác định được là kiểu dữ liệu nào. Muốn để thông báo cho chương trình làm việc này, hãy dùng từ khóa Virtual như ví dụ sau:
  1. #include  
  2. using namespace std; 
  3.  
  4. struct A { 
  5. virtual void f() { cout << "Class A" << endl; } 
  6. }; 
  7.  
  8. struct B: A { 
  9. void f() { cout << "Class B" << endl; } 
  10. }; 
  11.  
  12. void g(A& arg) { arg.f(); } 
  13.  
  14. int main() { 
  15. B x; 
  16. g(x); 
  17. }
 Ví dụ trên hàm A::f() đã được thêm từ khóa Virtual và kết quả phải là

Class B

Nên nhớ: Nếu A::f() được khai là Virtual thì mặc nhiên B::f() cũng là Virtual khi B là kế thừa của A và f() là identical (identical theo nghĩa là có cùng tên và cùng tham số). Như vậy dù không tường minh nhưng B::f() cũng là một hàm virtual và B::f() đã override A::f(). Tuy vậy hãy xem xét ví dụ sau để thấy được sự khác nhau giữa Override và Hide:
  1. #include  
  2. using namespace std; 
  3.  
  4. struct A { 
  5. virtual void f() { cout << "Class A" << endl; } 
  6. }; 
  7.  
  8. struct B: A { 
  9. void f(int) { cout << "Class B" << endl; } 
  10. }; 
  11.  
  12. struct C: B { 
  13. void f() { cout << "Class C" << endl; } 
  14. }; 
  15.  
  16. int main() { 
  17. B b; 
  18. C c; 
  19. A* pa1 = &b; 
  20. A* pa2 = &c; 
  21. // b.f(); 
  22. pa1->f(); 
  23. pa2->f(); 
  24. }
  25.  
Trong ví dụ trên kết quả sẽ phải là
Class A
Class C


Lý do vì B::f() không còn là hàm virtual do nó có tham số khác với hàm A::f(). Như vậy khi B::f() không còn là hàm Virtual thì trình biên dịch không thực hiện dynamic binding.Ở đây B::f() đã Hide A::f().
Tuy vậy C::f() lại là hàm Virtual vì nó giống nguyên so với A::f() dù C::f() không thể nhìn thấy A::f() hay A::f() là không visible đối với C. Ở đây C::f() đã Override A::f().
Có một hàm rất dễ nhận thấy là hàm Destructor. Hàm này thường được khai báo là Virtual chính do tính chất trên.

Một lớp có chứa hàm Virtual hay kế thừa hàm Virtual được gọi là một lớp có tính chất đa hình.

Còn một vấn đề nữa là giá trị trả về của các hàm Override. Tuy nhiên vấn đề này sẽ được thảo luận ở một bài khác.