Nạp chồng hàm là gì

Trong C++, hoàn toàn có thể thay đổi cách toán tử hoạt động (đối với các kiểu người dùng định nghĩa). Trong bài này, bạn sẽ được học cách lập trình tính năng nạp chồng toán tử.

Nạp chồng hàm là gì

Ý nghĩa của một toán tử luôn luôn là như nhau đối với các biến thuộc kiểu cơ bản như int, float, double…Ví dụ khi cộng hai số nguyên, toán tử + được sử dụng.

Tuy nhiên, đối với kiểu người dùng định nghĩa (như đối tượng), bạn có thể định nghĩa lại cách toán tử hoạt động. Ví dụ:

Nếu có hai đối tượng của một lớp chứa chuỗi là dữ liệu thành viên. Bạn có thể định nghĩa lại ý nghĩa của toán tử + và dùng nó để nối các chuỗi đó.

Tính năng trong lập trình C++ cho phép lập trình viên định nghĩa lại ý nghĩa của một toán tử (khi họ thao tác trên các đối tượng lớp) được gọi là nạp chồng toán tử.

Tại sao nạp chồng toán tử được sử dụng?

Bạn có thể viết bất kỳ chương trình C++ nào mà không cần biết nạp chồng toán tử. Tuy nhiên, nạp chồng toán tử được các lập trình viên rất hay sử dụng để khiến chương trình trở nên có nghĩa hơn. Ví dụ:

Bạn có thể thay thế đoạn mã nguồn dưới đây:

calculation = add(multiply(a, b),divide(a, b));

thành

calculation = (a*b)+(a/b);

Làm thế nào để nạp chồng toán tử trong lập trình C++?

Để nạp chồng một toán tử, một hàm toán tử đặc biệt cần được định nghĩa trong lớp như sau:

class tên_lớp { ... .. ... public kiểu_trả_về operator ký_hiệu (đối_số) { ... .. ... } ... .. ... };
  • Ở đây, kiểu_trả_về là kiểu giá trị hàm sẽ trả về
  • kiểu_trả_về của hàm được theo sau bởi từ khóa operator
  • ký_hiệu là ký hiệu của toán tử bạn muốn nạp chồng. Ví dụ: +, <, -, ++
  • Bạn có thể truyền vào tham số cho hàm toán tử tương tự như với các hàm khác.

Ví dụ: Nạp chồng toán tử trong lập trình C++

#include using namespace std; class Test { private: int count; public: Test(): count(5){} void operator ++() { count = count+1; } void Display() { cout<<"Count: "<Đầu ra

Hàm này được gọi khi toán tử ++ thao tác trên các đối tượng của lớp Test(đối tượng t trong trường hợp này).

Trong chương trình, hàm toán tử void operator ++ () được định nghĩa (trong lớp Test).

Hàm này sẽ tăng giá trị của count lên 1 cho đối tượng t.

Những điều cần ghi nhớ

  1. Nạp chồng toán tử cho phép bạn định nghĩa lại cách mà toán tử hoạt động chỉ đối với các kiểu do người dùng định nghĩa (đối tượng, cấu trúc). Nó không thể được sử dụng cho các kiểu dựng sẵn (int, float, char…).
  2. Hai toán tử = và & đã mặc định được nạp chồng trong C++. Ví dụ: để sao chép đối tượng thuộc cùng lớp, bạn có thể trực tiếp sử dụng toán tử =. Bạn không cần tạo một hàm toán tử.
  3. Nạp chồng toán tử không thể thay đổi thứ tự thực hiện và khả năng kết hợp của các toán tử. Tuy nhiên, nếu bạn muốn thay đổi thứ tự của việc đánh giá, bạn có thể sử dụng dấu ngoặc.
  4. Có 4 toán tử không thể bị nạp chồng trong C++. Chúng là :: (phân giải phạm vi), . (lựa chọn thành viên), .* (lựa chọn thành viên thông qua con trỏ tới hàm) và ?: (toán tử tam nguyên).

Những trường hợp khuyến khích sử dụng khi dùng nạp chồng toán tử

Nạp chồng toán tử cho phép bạn định nghĩa cách toán tử hoạt động (theo cách bạn muốn).

Trong ví dụ trên, toán tử ++ sẽ thao tác trên đối tượng để tăng giá trị của dữ liệu thành viên count lên 1.

void operator ++() { count = count+1; }

Tuy nhiên, nếu bạn sử dụng đoạn mã sau. Nó sẽ giảm giá trị của count đi 100 khi toán tử ++ được sử dụng.

void operator ++() { count = count-100; }

Về mặt kỹ thuật, đây là câu lệnh hợp cách. Nhưng đoạn mã này sẽ khiến việc hiểu và gỡ lỗi trở nên rắc rối và phức tạp hơn.

Công việc của lập trình viên đó là sử dụng nạp chồng toán tử một cách hợp lý và đồng nhất.

Trong ví dụ trên, giá trị của count được tăng lên 1 khi toán tử ++ được sử dụng. Tuy nhiên, chương trình này lại không hoàn thiện vì bạn sẽ không thể sử dụng đoạn mã như:

t1 = ++t

Đó là do kiểu trả về của hàm toán tử là void.

Việc tồn tại hai hoặc nhiều hơn các hàm có cùng tên nhưng khác số lượng đối số được gọi là nạp chồng hàm (Function Overloading). Trong bài này, bạn sẽ học về nạp chồng hàm có kèm theo ví dụ.

Nạp chồng hàm là gì

Hàm được dùng để chỉ một đoạn mã nguồn được nhóm lại nhằm thực thi một tác vụ nhất định.

Trong lập trình C++, hai hàm có thể có cùng tên nếu số lượng và/hoặc kiểu đối số truyền vào là khác nhau.

Các hàm có số lượng hoặc kiểu (hoặc cả hai) đối số khác nhau đó được gọi là các hàm bị nạp chồng. Ví dụ:

int test() { } int test(int a) { } float test(double a) { } int test(int a, double b) { }

Ở đây, toàn bộ 4 hàm đều là hàm bị nạp chồng vì đối số truyền vào các hàm đó là khác nhau.

Lưu ý rằng, kiểu trả về của toàn bộ 4 hàm là không giống nhau. Hàm bị nạp chồng có thể có hoặc không có kiểu trả về khác nhau, nhưng nó nên có số lượng đối số khác nhau.

//mã nguồn không hợp lệ int test(int a) { } double test(int b){ }

Số lượng và kiểu của các đối số được truyền vào hai hàm trên là giống nhau mặc dù kiểu trả về là khác nhau. Vì vậy trình biên dịch sẽ báo lỗi.

#include using namespace std; void display(int); void display(float); void display(int, float); int main() { int a = 5; float b = 5.5; display(a); display(b); display(a, b); return 0; } void display(int var) { cout << "Integer number: " << var << endl; } void display(float var) { cout << "Float number: " << var << endl; } void display(int var1, float var2) { cout << "Integer number: " << var1; cout << " and float number:" << var2; }

Đầu ra

Integer number: 5 Float number: 5.5

Integer number: 5 and float number: 5.5

Ở đây, hàm display() được gọi 3 lần với các kiểu và số lượng đối số khác nhau.

Kiểu trả về của cả ba hàm này là giống nhau, nhưng điều này là không cần thiết.

// Chương trình để tính giá trị tuyệt đối // Dùng được với cả số nguyên và số thực #include using namespace std; int absolute(int); float absolute(float); int main() { int a = -5; float b = 5.5; cout << "Absolute value of " << a << " = " << absolute(a) << endl; cout << "Absolute value of " << b << " = " << absolute(b); return 0; } int absolute(int var) { if (var < 0) var = -var; return var; } float absolute(float var){ if (var < 0.0) var = -var; return var; }

Đầu ra

Absolute value of -5 = 5

Absolute value of 5.5 = 5.5

Trong ví dụ trên, hai hàm absoluate() đã được nạp chồng.

Cả hai hàm đều nhận vào một đối số duy nhất. Tuy nhiên, một hàm nhận số nguyên làm đối số trong khi hàm còn lại nhận đối số là số thực.

Khi hàm absolute() được gọi với số nguyên là đối số, hàm này sẽ được gọi:

int absolute(int var) { if (var < 0) var = -var; return var; }

Khi hàm absolute() được gọi với đối số là số thực, hàm này sẽ được gọi:

float absolute(float var){ if (var < 0.0) var = -var; return var; }

Bài viết này sẽ nói về nạp chồng hàm (Function Overloading), nạp chồng toán tử (Operation Overloading).

Nạp chồng hàm

Trong C ++, hai hàm khác nhau có thể có cùng tên nếu các tham số của chúng khác nhau hoặc bất kỳ tham số nào của chúng có kiểu dữ liệu khác nhau. Ví dụ, chương trình C++ dưới đây sẽ minh hoạ thế nào là nạp chồng hàm:

#include using namespace std; int operate (int a, int b) { return (a*b); } double operate (double a, double b) { return (a/b); } int main () { int x=5,y=2; double n=5.0,m=2.0; cout << operate (x,y) << '\n'; cout << operate (n,m) << '\n'; return 0; }

Kết quả:

10 2,5

Trong ví dụ này, có hai hàm được gọi là operate, một có kiểu là int một có kiểu double. Trình biên dịch biết được cái nào để gọi trong mỗi trường hợp bằng cách kiểm tra kiểu dữ liệu  được truyền như là đối số khi hàm được gọi. Nếu nó được gọi với hai đối số kiểu int, nó gọi hàm có 2 tham số kiểu int, và nếu nó được gọi với hai đối số double, nó sẽ gọi hàm còn lại.

Trong ví dụ này, cả hai hàm có các hành vi và chức năng khá khác nhau, phiên bản int sẽ nhân các đối số của nó, trong khi phiên bản double sẽ chia chúng. Đây không phải là một ý tưởng hay. Hai chức năng với cùng tên thường được dự kiến ​​sẽ có ít nhất một chức năng tương tự, nhưng ví dụ này chứng tỏ rằng hoàn toàn có thể không cần. Hai hàm nạp chồng Overload (tức là hai chức năng có cùng tên) có các định nghĩa và chức năng hoàn toàn khác nhau, sử dụng cho các mục đích khác nhau nhưng có chỉ có cùng tên.

Lưu ý rằng một hàm không thể nạp chồng bởi kiểu trả về của nó. Ít nhất một trong các tham số của nó phải có một kiểu dữ liệu khác.

Khi gọi một hàm nạp chồng hoặc một toán tử nạp chồng, thì trình biên dịch sẽ quyết định hàm thích hợp nhất để sử dụng bằng việc so sánh các kiểu tham số bạn đã sử dụng để gọi hàm hoặc toán tử với các kiểu tham số đã được xác định trong các định nghĩa. Tiến trình lựa chọn hàm nạp chồng hoặc toán tử nạp chồng thích hợp nhất này được gọi là phân giải nạp chồng (overload resolution).

Một ví dụ khác, cùng một hàm printOverload được sử dụng để in các kiểu dữ liệu khác nhau:

#include using namespace std; class printOver { public: void printOverload(int i) { cout << "In so nguyen: " << i << endl; } void printOverload(double f) { cout << "In so thuc: " << f << endl; } void printOverload(char* c) { cout << "In ky tu: " << c << endl; } }; int main(void) { printOver ptr; //Đối số truyền vào kiểu int ptr.printOverload(227); //Đối số truyền vào kiểu int ptr.printOverload(10.0f); //Đối số là một chuỗi ptr.printOverload("Kien thuc co ban C++ - DNMTechs"); getchar(); return 0; }

Kết quả:

In so nguyen: 227 In so thuc: 10 In ky tu: Kien thuc co ban C++ - DNMTechs

Dùng hàm mặc định getchar() để xem kết quả hiện thị trên màn hình sau khi được build

Nạp chồng toán tử

Nạp chồng toán tử (Operator overloading), thỉnh thoảng còn được gọi đa hình tùy biến toán tử (operator ad hoc polymorphism), là một trường hợp đặc biệt của đa hình, trong đó các toán tử khác nhau có cách hiện thực khác nhau dựa vào tham số của chúng.

Nạp chồng toán tử trong C++ là các hàm với tên đặc biệt: Tên hàm là từ khóa operator theo sau là ký hiệu của toán tử đang được định nghĩa. Giống như bất kỳ hàm khác, một toán tử nạp chồng có một kiểu trả về và một danh sách tham số.

Box operator+(const Box&);

Khai báo toán tử + để cộng hai đối tượng Box và trả về đối tượng Box cuối cùng. Hầu hết toán tử nạp chồng có thể được định nghĩa dưới dạng: các hàm không có thành viên (non-member) hoặc các hàm thành viên lớp. Trong trường hợp trên, chúng ta định nghĩa hàm ở dạng non-member của một lớp, thì sau đó chúng ta phải truyền hai tham số cho mỗi đối số, như sau:

Box operator+(const Box&, const Box&);

Ví dụ sau minh họa khái niệm nạp chồng toán tử bởi sử dụng một hàm thành viên. Ở đây, một đối tượng được truyền như là một tham số mà các thuộc tính của nó sẽ được truy cập bởi sử dụng đối tượng này, đối tượng mà sẽ gọi toán tử này có thể được truy cập bởi sử dụng toán tử this, như sau:

#include using namespace std; class Box { public: double tinhTheTich(void) { return chieudai * chieurong * chieucao; } void setChieuDai(double dai) { chieudai = dai; } void setChieuRong(double rong) { chieurong = rong; } void setChieuCao(double cao) { chieucao = cao; } // Nap chong toa tu + de cong hai doi tuong Box. Box operator+(const Box& b) { Box box; box.chieudai = this->chieudai + b.chieudai; box.chieurong = this->chieurong + b.chieurong; box.chieucao = this->chieucao + b.chieucao; return box; } private: double chieudai; // chieu dai cua mot box double chieurong; // Chieu rong cua mot box double chieucao; // Chieu cao cua mot box }; // ham main cua chuong trinh int main() { Box Box1; // Khai bao Box1 la cua kieu Box Box Box2; // Khai bao Box2 la cua kieu Box Box Box3; // Khai bao Box3 la cua kieu Box double thetich = 0.0; // Luu giu the tich cua mot box tai day // thong tin chi tiet cua box 1 Box1.setChieuDai(2.0); Box1.setChieuRong(3.0); Box1.setChieuCao(4.0); // thong tin chi tiet cua box 2 Box2.setChieuDai(5.0); Box2.setChieuRong(6.0); Box2.setChieuCao(7.0); // the tich cua box 1 thetich = Box1.tinhTheTich(); cout << "The tich cua Box1 la: " << thetich << endl; // the tich cua box 2 thetich = Box2.tinhTheTich(); cout << "The tich cua Box2 la: " << thetich << endl; // cong hai doi tuong nhu sau: Box3 = Box1 + Box2; // the tich cua box 3 thetich = Box3.tinhTheTich(); cout << "The tich cua Box3 la: " << thetich << endl; getchar(); return 0; }

Kết quả

The tich cua Box1 la: 24 The tich cua Box2 la: 210 The tich cua Box3 la: 693