Trang chủ » Bài 8 – Hàm trong C++ (Phần 2)
Lập trình C/C++

Bài 8 – Hàm trong C++ (Phần 2)

cplusplus

Xin chào mọi người!

Mấy ngày nay ở quê bận bịu với việc nhà quá 😛 một phần vì ở đây không có Internet :(( Ở bài trước, mình cùng nhau tìm hiểu về hàm trong C++ rồi phải không? Vậy thì ở bài này, mình cùng tìm hiểu sâu hơn về hàm nhé 😀

4. Tham số

Tham số trong C++ đóng vai trò cực kì quan trọng, nó giống như việc đưa cây mía (tham số) vào xe ép nước mía (hàm), và sau đó sẽ có xác mía và nước mía (giá trị trả về). Mình ví dụ như vậy vì hiện tại mình đang thèm uống nước mía thôi 😛

Tham số có 2 loại: tham số giá trị (tham trị) và tham số dạng tham chiếu (tham biến). Ví dụ sau sẽ phân biệt rõ ràng hai loại tham số này:

#include <iostream>
using namespace std;

void Ham1(int a)
{
  a++;
  cout << "Thay doi trong ham lan 1: << a << endl;
}

void Ham2(int& a)
{
  a++;
  cout << "Thay doi trong ham lan 2: << a << endl;
}

void main()
{
  int a = 5;
  Ham1(a);
  cout << "Thay doi ngoai ham lan 1: " << a << endl;
  Ham2(a);
  cout << "Thay doi ngoai ham lan 2: " << a << endl;
}

Nhìn tổng quan, hai hàm trên không có gì khác biệt mấy, nhưng cùng đến với kết quả của nó, ta thấy một điều kì lạ sau:

Thay doi trong ham lan 1: 6
Thay doi ngoai ham lan 1: 5
Thay doi trong ham lan 2: 6
Thay doi ngoai ham lan 2: 6

Giá trị ở lần 2 có gì đó khác lạ!?!? Ở ví dụ trên, Ham1 thay đổi giá trị biến trong hàm, nhưng sau đó biến a vẫn không thay đổi, còn Ham2 thay đổi giá trị biến a cả trong và ngoài hàm. Sự khác biệt lớn nhất giữa 2 loại tham số này là: tham trị không thay đổi giá trị biến gửi đến hàm, còn tham biến thì ngược lại. Lý do của nó thì ở bài Con trỏ mình sẽ nói rõ thêm! 😀

5. Giá trị mặc định của tham số

Xét ví dụ sau

#include <iostream>
using namespace std;

int Cong(int a, int b, int c = 5)
{
  return a + b + c;
}

void main()
{
  cout << "Goi ham lan 1: " << Cong(1,2,3) << endl; // = 6
  cout << "Goi ham lan 2: " << Cong(1,2) << endl; // = 8
}

Điều gì kì lạ vậy? Số lượng tham số của hàm Cong ở hai lần gọi là khác nhau, và kết quả ở hai lần gọi dĩ nhiên là khác nhau. Lý do là vì tham số thứ 3 có giá trị mặc định bằng 5, nếu ta không truyền tham số vào, nó sẽ tự lấy giá trị mặc định để tính toán. Tuỳ vào yêu cầu bài toán, ta cần sử dụng giá trị mặc định hay không 😀

6. Hàm trùng tên

Ngôn ngữ lập trình C++ cho phép chúng ta đặt tên hàm trùng với nhau, phân biệt bằng kiểu dữ liệu tham số và  số lượng tham số. Khái niệm này được gọi là chồng hàm (function overloading). Trong thực tế, có rất nhiều tình huống cần phải sử dụng khái niệm này. Mình lấy ví dụ trên cho trường hợp này nhé:

#include <iostream>
using namespace std;

int Cong(int a, int b)
{
  return a + b;
}

int Cong(int a, int b, int c)
{
  return a + b + c;
}

void main()
{
  cout << "Goi ham lan 1: " << Cong(1,2) << endl; // = 3
  cout << "Goi ham lan 2: " << Cong(1,2,3) << endl; // = 6
}

Lần này đã có sự khác biệt, hàm Cong được định nghĩa 2 lần, với số lượng tham số khác nhau. Dĩ nhiên lần gọi hàm thứ 1 sẽ gọi hàm Cong đầu tiên, còn lần gọi hàm thứ 2 sẽ gọi hàm Cong thứ 2. Trong nhiều trường hợp, ta sẽ phải sử dụng hàm trùng tên để phù hợp với yêu cầu 😀

7. Hàm có tham số là hàm

Tham số trong C++ không chỉ dừng lại ở biến, nó còn có thể là 1 hàm! Xét ví dụ sau:

#include <iostream>
using namespace std;

int Cong2So(int a, int b)
{
  return a + b;
}

int Cong(int Ham(int a, int b), int x1, int x2)
{
  return Ham(x1, x2) + x2;
}

void main()
{
  cout << "Ket qua: " << Cong(Cong2So,1,2) << endl; // = 5
}

Có lẽ ở ví dụ này, mình không cần phải nói quá nhiều nhỉ? Sự thật thì bản thân mình rất ít sử dụng tham số là hàm, có lẽ vì nó làm cho đoạn code của mình “không trong sáng” cho lắm :D, mình cũng không khuyên các bạn có nên sử dụng tham số là hàm hay không, vì mỗi người có 1 phong cách code khác nhau mà 😀

8. Khuôn hàm

Khuôn hàm (template) là một công cụ rất mạnh của C++ và được sử dụng khá rộng rãi. Template thật sự rất mạnh mẽ trong việc lập trình tổng quát (generic programming). Thư viện STL (Standard Template Library) được xây dựng hoàn toàn bằng template đấy 😀 (kiểu vector là một ví dụ)

Ta xét ví dụ sau:

#include <iostream>
using namespace std;

int CongSoNguyen(int a, int b)
{
  return a + b;
}

double CongSoThuc(double a, double b)
{
  return a + b;
}

void main()
{
  cout << "2 + 5 = " << CongSoNguyen(2,5) << endl;
  cout << "2.2 + 5.2 = " << CongSoThuc(2.2,5.2) << endl;
}

Ở ví dụ trên, ta thấy 2 hàm nó có nội dung code hoàn toàn giống nhau, chỉ khác nhau ở kiểu dữ liệu tham số và giá trị trả về. Vậy làm cách nào để có thể viết 1 hàm, nhưng sử dụng được cho nhiều kiểu dữ liệu? Lúc này là lúc template toả sáng 😀

#include <iostream>
using namespace std;

template <class T>
T Cong(T a, T b)
{
return a + b;
}

void main()
{
// Có thể bỏ và , nhưng mình khuyên không nên
cout << "2 + 5 = " << Cong(2,5) << endl;
cout << "2.2 + 5.2 = " << Cong(2.2,5.2) << endl;
}

Ở ví dụ trên, ta đã gộp 2 hàm CongSoNguyen và CongSoThuc lại làm 1 hàm Cong kiểu T (kiểu dữ liệu trừu tượng), nhưng sau đó khi gọi hàm,  và  giúp ta phân biệt kiểu dữ liệu và làm việc với nó. Trong 1 template ta có thể sử dụng cho các kiểu dữ liệu khác nhau (U, V gì đó chẳng hạn). Nhưng chúng ta cần phải lưu ý về kiểu dữ liệu để tránh bị lỗi không đáng có 😀

Vậy là đã kết thúc 2 phần về hàm trong C++, còn rất nhiều những phần khác nhưng mình sẽ đề cập vào những bài sau. Cảm ơn các bạn đã quan tâm theo dõi! Xin chào~

Tags

About the author

Võ Hoài Sơn

Tính tình bất định
Chọc vào là bịnh
Rất yêu lập trình
Luôn code hết mình
Mình hiện đang là sinh viên của trường ĐH Khoa học tự nhiên TPHCM. Bản thân rất thích code, kiêm luôn cả mần thơ nên thường hơi hâm hâm dở dở. Ngoài ra chém gió, chém chuối, chém trái cây các kiểu cũng là sở trường của mình. Rất mong được làm quen với các bạn :D

Add Comment