Bỏ qua để đến nội dung

Các cách truyền đối số vào hàm

C++ có 3 cách truyền đối số vào hàm: theo giá trị, theo tham chiếu, và theo con trỏ - mỗi cách có ảnh hưởng khác nhau đến việc biến gốc có bị thay đổi hay không. Bài này giới thiệu tổng quan; chi tiết về con trỏ và tham chiếu sẽ có các bài riêng.

1. Truyền theo giá trị (Pass by Value) - mặc định

Phần tiêu đề “1. Truyền theo giá trị (Pass by Value) - mặc định”

Hàm nhận một bản sao (copy) của đối số. Mọi thay đổi bên trong hàm không ảnh hưởng đến biến gốc bên ngoài:

#include <iostream>
void increase(int x) {
x = x + 1; // Chỉ thay đổi bản sao cục bộ
std::cout << "Ben trong ham: " << x << std::endl;
}
int main() {
int number = 5;
increase(number);
std::cout << "Ben ngoai ham: " << number << std::endl;
// Ben trong ham: 6
// Ben ngoai ham: 5 (KHÔNG đổi!)
return 0;
}

2. Truyền theo tham chiếu (Pass by Reference) - dùng &

Phần tiêu đề “2. Truyền theo tham chiếu (Pass by Reference) - dùng &”

Hàm nhận một “bí danh” (alias) của biến gốc, không phải bản sao. Mọi thay đổi bên trong hàm sẽ ảnh hưởng đến biến gốc:

#include <iostream>
void increase(int& x) { // Lưu ý dấu & sau kiểu dữ liệu
x = x + 1; // Thay đổi TRỰC TIẾP biến gốc
std::cout << "Ben trong ham: " << x << std::endl;
}
int main() {
int number = 5;
increase(number);
std::cout << "Ben ngoai ham: " << number << std::endl;
// Ben trong ham: 6
// Ben ngoai ham: 6 (ĐÃ đổi!)
return 0;
}

Đây là khả năng mà truyền theo giá trị không thể làm được.

Hàm nhận địa chỉ bộ nhớ của biến gốc, và dùng toán tử * để truy cập/thay đổi giá trị tại địa chỉ đó:

#include <iostream>
void increase(int* x) { // Nhận con trỏ (địa chỉ)
*x = *x + 1; // "Đi tới" địa chỉ đó và thay đổi giá trị
}
int main() {
int number = 5;
increase(&number); // Truyền ĐỊA CHỈ của number, không phải giá trị
std::cout << number << std::endl; // 6
return 0;
}

Về hiệu quả, truyền theo con trỏ và tham chiếu tương tự nhau (đều tránh sao chép, đều có thể thay đổi giá trị gốc) - chi tiết về sự khác biệt sẽ được trình bày ở bài “Con trỏ vs Tham chiếu”.

Cách truyền Khi nào dùng
Theo giá trị Kiểu dữ liệu nhỏ (int, char, bool…) và không muốn hàm thay đổi giá trị gốc
Theo tham chiếu & Muốn hàm thay đổi giá trị gốc, hoặc tránh sao chép dữ liệu lớn (std::string, std::vector…)
Theo tham chiếu hằng const& Muốn tránh sao chép dữ liệu lớn NHƯNG không muốn hàm thay đổi nó (phổ biến nhất cho tham số kiểu lớn)
Theo con trỏ * Khi giá trị truyền vào có thể là “không có gì” (nullptr), hoặc làm việc với mảng C-style
// Cách phổ biến nhất khi truyền dữ liệu lớn chỉ để đọc: const&
void print_message(const std::string& message) {
std::cout << message << std::endl;
}
  • Theo giá trị: sao chép dữ liệu, an toàn nhưng tốn bộ nhớ với dữ liệu lớn
  • Theo tham chiếu (&): không sao chép, có thể thay đổi biến gốc
  • Theo con trỏ (*): tương tự tham chiếu nhưng tường minh hơn về địa chỉ bộ nhớ, cho phép giá trị nullptr
  • Quy tắc thường dùng: dùng const& cho tham số kiểu lớn chỉ đọc, dùng & khi cần thay đổi giá trị gốc