25/05/2018, 13:12

Định nghĩa chồng các hàm (overloading)

Định nghĩa chồng (hay còn gọi sự tải bội) các hàm là dùng cùng một tên để định nghĩa các hàm khác nhau. Đây là một mở rộng rất có ý nghĩa của C++. Như đã biết, trong C và các ngôn ngữ khác (như PASCAL, FOXPRO,...) mỗi hàm đều phải có ...

Định nghĩa chồng (hay còn gọi sự tải bội) các hàm là dùng cùng một tên để định nghĩa các hàm khác nhau. Đây là một mở rộng rất có ý nghĩa của C++.

Như đã biết, trong C và các ngôn ngữ khác (như PASCAL, FOXPRO,...) mỗi hàm đều phải có một tên phân biệt. Đôi khi đây là một sự hạn chế lớn, vì phải dùng nhiều hàm khác nhau để thực hiện cùng một công việc. Ví dụ để lấy giá trị tuyệt đối trong C cần dùng tới 3 hàm khác nhau:

int abs(int i); // Lấy giá trị tuyệt đối giá trị kiểu int
    longt labs(longt l); // Lấy giá trị tuyệt đối giá trị kiểu long
    double fabs(double d); // Lấy giá trị tuyệt đối giá trị kiểu double
    

Nhờ khả năng định nghĩa chồng, trong C++ có thể dùng chung một tên cho cả 3 hàm trên như sau:

int abs(int i) ; // Lấy giá trị tuyệt đối giá trị kiểu int
    longt abs(longt l) ; // Lấy giá trị tuyệt đối giá trị kiểu long
    double abs(double d) ; // Lấy giá trị tuyệt đối giá trị kiểu double
    

Khi dùng cùng một tên để định nghĩa nhiều hàm, Trình biên dịch C++ sẽ dựa vào sự khác nhau về tập đối của các hàm này để đổi tên các hàm. Như vậy, sau khi biên dịch mỗi hàm sẽ có một tên khác nhau.

Từ đó cho thấy: các hàm được định nghĩa trùng tên phải có tập đối khác nhau (về số lượng hoặc kiểu). Nếu 2 hàm hoàn toàn trùng tên và trùng đối thì Trình biên dịch sẽ không có cách nào phân biệt được. Ngay cả khi 2 hàm này có kiểu khác nhau thì Trình biên dịch vẫn báo lỗi. Ví dụ sau xây dựng 2 hàm cùng có tên là f và cùng có một đối nguyên a, nhưng kiểu hàm khác nhau. Hàm thứ nhất kiểu nguyên (trả về a*a), hàm thứ hai kiểu void (in giá trị a). Chương trình sẽ bị thông báo lỗi khi biên dịch (bạn hãy thử xem sao)

#include <conio.h>
    #include <iostream.h>
    int f(int a);
    void f(int a);
    int f(int a)
    {
    return a*a;
    }
    void f(int a)
    {
    cout << "
 " << a ;
    }
    void main()
    {
    int b=f(5);
    f(b);
    getch();
    }
    

Khi gặp một lời gọi, Trình biên dịch sẽ căn cứ vào số lượng và kiểu của các tham số để gọi hàm có đúng tên và đúng bộ đối số tương ứng. Ví dụ:

abs(123); // Tham số kiểu int, gọi hàm int abs(int i) ;
    abs(123L); // Tham số kiểu long, gọi hàm long abs(long l);
    abs(3.14); //Tham số kiểu double, gọi hàm double abs(double d);
    

Khi không có hàm nào có bộ đối cùng kiểu với bộ tham số (trong lời gọi), thì Trình biên dịch sẽ chọn hàm nào có bộ đối gần kiểu nhất (phép chuyển kiểu dễ dàng nhất). Ví dụ:

abs(‘A’) ; // Tham số kiểu char, gọi hàm int abs(int i) ;
    abs(3.14F); // Tham số kiểu float, gọi hàm double abs(double d);
    

Như đã nói ở trên, khi xây dựng cũng như sử dụng các hàm trùng tên, Trình biên dịch C++ đã phải suy đoán và giải quyết nhiều trường hợp khá nhập nhằng. Vì vậy không nên lạm dụng quá đáng khả năng định nghĩa chồng, vì điều đó làm cho chương trình khó kiểm soát và dễ dẫn đến sai sót. Việc định nghĩa chồng sẽ hiệu quả hơn nếu được sử dụng theo các lời khuyên sau:

+ Chỉ nên định nghĩa chồng các hàm thực hiện những công việc như nhau nhưng trên các đối tượng có kiểu khác nhau. Ví dụ trong chương trình cần xây dựng các hàm: cộng 2 ma trận vuông kiểu double, cộng 2 ma trận vuông kiểu int, cộng 2 ma trân chữ nhật kiểu double, cộng 2 ma trận chữ nhật kiểu int, thì 4 hàm trên nên định nghĩa chồng (đặt cùng tên).

+ Nên dùng các phép chuyển kiểu (nếu cần) để bộ tham số trong lời gọi hoàn toàn trùng kiểu với bộ đối số của một hàm được định nghĩa chồng. Vì như thế mới tránh được sự nhập nhằng cho Trình biên dịch và Trình biên dịch sẽ chọn đúng hàm cần gọi.

Giả sử có 4 hàm đều có tên là tinh_max được khai báo như sau:

int tinh_max(int a, int b, int c) ; // Max của 3 số nguyên
    double tinh_max(double a, double b, double c); // Max của 3 số // thực
    int tinh_max(int *a, int n) ; // Max của một dẫy số nguyên
    double tinh_max(double *a, int n) ; //Max của một dẫy số thực
    

Vấn đề đặt ra là làm thế nào lấy được địa chỉ của mỗi hàm. Câu trả lời như sau:

Để lấy địa chỉ của một hàm, ta khai báo một con trỏ hàm có kiểu và bộ đối như hàm cần lấy địa chỉ. Sau đó gán tên hàm cho con trỏ hàm. Ví dụ:

int (*f1)(int , int, int );
    f1 = tinh_max ; // Lấy địa chỉ của hàm thứ nhất
    double (*f2)(double , double, double);
    f2 = tinh_max ; // Lấy địa chỉ của hàm thứ hai
    int (*f3)(int *, int );
    f3 = tinh_max ; // Lấy địa chỉ của hàm thứ ba
    double (*f4)(double *, int );
    f4 = tinh_max ; // Lấy địa chỉ của hàm thứ tư 
    

Chương trình giải bài toán tìm max của một dẫy số nguyên và max của một dẫy số thực. Trong chươmg trình có 6 hàm. Hai hàm dùng để nhập dẫy số nguyên và dẫy số thực có tên chung là nhapds. Bốn hàm: tính max 2 số nguyên, tính max 2 số thực, tính max của dẫy số nguyên, tính max của dẫy số thực được đặt chung một tên là max.

#include <conio.h>

#include <iostream.h>

#include <iomanip.h>

void nhapds(int *x, int n);

void nhapds(double *x, int n);

int max(int x, int y);

double max(double x, double y);

int max(int *x, int n);

double max(double *x, int n);

void nhapds(int *x, int n)

{

for (int i=1;i<=n;++i)

{

cout << "Phan tu " << i << " = " ;

cin >> x[i] ;

}

}

void nhapds(double *x, int n)

{

for (int i=1;i<=n;++i)

{

cout << "Phan tu " << i << " = " ;

cin >> x[i] ;

}

}

int max(int x, int y)

{

return x>y?x:y ;

}

double max(double x, double y)

{

return x>y?x:y ;

}

int max(int *x, int n)

{

int s=x[1];

for (int i=2;i<=n;++i)

s = max(s,x[i]);

return s;

}

double max(double *x, int n)

{

double s=x[1];

for (int i=2;i<=n;++i)

s = max(s,x[i]);

return s;

}

void main()

{

int a[20] , n , ni, nd, maxi ;

double x[20] , maxd ;

clrscr();

cout << "
So phan tu nguyen ni = " ;

cin >> ni ;

cout << "Nhap day so nguyen
 " ;

nhapds(a,ni);

cout << "
So phan tu thuc nd = " ;

cin >> nd ;

cout << "Nhap day so thuc
 " ;

nhapds(x,nd);

maxi = max(a,ni);

maxd = max(x,nd);

cout << "
Max cua day nguyen = " << maxi ;

cout << "
Max cua day thuc = " << maxd ;

getch();

}

Chương trình sau thực hiện phép nhân ma trận:

D = A*B*C

trong đó A, B là các ma trận vuông, C là ma trận chữ nhật. Trong chương trình có 3 cặp hàm trùng tên để thực hiện 3 nhiệm vụ (nhưng trên 2 đối tượng khác nhau là ma trận vuông và chữ nhật): Nhập ma trận, nhân 2 ma trận và in ma trân.

#include <conio.h>

#include <iostream.h>

#include <iomanip.h>

typedef int MT[20][20];

void nhapmt(MT a,char *ten, int m, int n);

void inmt(MT a,char *ten, int m, int n);

void nhanmt(MT a,MT b, MT c, int m, int n, int p);

void nhapmt(MT a,char *ten, int n);

void inmt(MT a,char *ten, int n);

void nhanmt(MT a,MT b, MT c, int n);

void nhapmt(MT a, char *ten, int m, int n)

{

for (int i=1;i<=m;++i)

for (int j=1;j<=n;++j)

{

cout << "
" << ten <<"[" << i << "," << j << "]= " ;

cin >> a[i][j];

}

}

void nhapmt(MT a,char *ten, int n)

{

nhapmt(a,ten,n,n) ;

}

void inmt(MT a,char *ten, int m, int n)

{

cout << "
Ma tran: " << ten;

for (int i=1;i<=m;++i)

{

cout << "
" ;

for (int j=1;j<=n;++j)

cout << setw(6) << a[i][j];

}

}

void inmt(MT a,char *ten, int n)

{

inmt(a,ten,n,n) ;

}

void nhanmt(MT a,MT b, MT c, int m, int n, int p)

{

for (int i=1;i<=m;++i)

for (int j=1;j<=p;++j)

{

c[i][j]=0;

for (int k=1;k<=n;++k)

c[i][j] += a[i][k] * b[k][j];

}

}

void nhanmt(MT a,MT b, MT c, int n)

{

nhanmt(a,b,c,n,n, n) ;

}

void main()

{

MT a,b,c,d; // d= abc

MT u;

clrscr();

nhapmt(a,"A",2);

nhapmt(b,"B",2);

nhapmt(c,"C",2,3);

nhanmt(a,b,u,2);

nhanmt(u,c,d,2,2,3);

inmt(a,"A",2);

inmt(b,"B",2);

inmt(u,"U = A*B",2);

inmt(c,"C",2,3);

inmt(d,"D = U*C",2,3);

getch();

}
0