1.
int Add (int a, int b = 3);
int Add (int a, int b)
{
}
2.
int Add (int a, int b);
int Add (int a, int b = 3)
{
}
Cả hai đều làm việc; Đó là cách tiêu chuẩn và tại sao ?
1.
int Add (int a, int b = 3);
int Add (int a, int b)
{
}
2.
int Add (int a, int b);
int Add (int a, int b = 3)
{
}
Cả hai đều làm việc; Đó là cách tiêu chuẩn và tại sao ?
Câu trả lời:
Nếu bạn đặt khai báo trong một tệp tiêu đề và định nghĩa trong một .cpp
tệp riêng biệt và #include
tiêu đề từ một .cpp
tệp khác , bạn sẽ có thể thấy sự khác biệt.
Cụ thể, giả sử:
int Add(int a, int b);
int Add(int a, int b = 3) {
...
}
#include "lib.h"
int main() {
Add(4);
}
Việc biên dịch test.cpp
sẽ không thấy khai báo tham số mặc định và sẽ bị lỗi.
Vì lý do này, định nghĩa tham số mặc định thường được chỉ định trong khai báo hàm :
int Add(int a, int b = 3);
b
được xác định một lần cho mỗi .cpp
tệp bao gồm tiêu đề. Nhưng không sao, vì bạn chỉ có một tuyên bố về Add
chức năng.
Trong C ++, các yêu cầu áp đặt cho các đối số mặc định liên quan đến vị trí của chúng trong danh sách tham số như sau:
Đối số mặc định cho một tham số đã cho phải được chỉ định không quá một lần. Chỉ định nó nhiều lần (ngay cả với cùng một giá trị mặc định) là bất hợp pháp.
Các tham số với các đối số mặc định phải tạo thành một nhóm liền kề ở cuối danh sách tham số.
Bây giờ, hãy nhớ rằng, trong C ++, bạn được phép "phát triển" tập hợp các tham số có đối số mặc định từ một khai báo của hàm sang tiếp theo, miễn là các yêu cầu trên được thỏa mãn liên tục.
Ví dụ: bạn có thể khai báo một hàm không có đối số mặc định
void foo(int a, int b);
Để gọi hàm đó sau khi khai báo như vậy, bạn sẽ phải chỉ định rõ ràng cả hai đối số.
Sau này (tiếp tục xuống) trong cùng một đơn vị dịch, bạn có thể khai báo lại lần nữa, nhưng lần này với một đối số mặc định
void foo(int a, int b = 5);
và từ thời điểm này, bạn có thể gọi nó chỉ bằng một đối số rõ ràng.
Xa hơn nữa bạn có thể khai báo lại một lần nữa thêm một đối số mặc định
void foo(int a = 1, int b);
và từ thời điểm này, bạn có thể gọi nó mà không có đối số rõ ràng.
Ví dụ đầy đủ có thể trông như sau
void foo(int a, int b);
int main()
{
foo(2, 3);
void foo(int a, int b = 5); // redeclare
foo(8); // OK, calls `foo(8, 5)`
void foo(int a = 1, int b); // redeclare again
foo(); // OK, calls `foo(1, 5)`
}
void foo(int a, int b)
{
// ...
}
Đối với mã trong câu hỏi của bạn, cả hai biến thể đều hoàn toàn hợp lệ, nhưng chúng có nghĩa là những thứ khác nhau. Biến thể đầu tiên khai báo một đối số mặc định cho tham số thứ hai ngay lập tức. Biến thể thứ hai ban đầu khai báo hàm của bạn không có đối số mặc định và sau đó thêm một đối số cho tham số thứ hai.
Hiệu ứng ròng của cả hai khai báo của bạn (tức là cách mã được nhìn thấy sau khai báo thứ hai) hoàn toàn giống nhau: hàm có đối số mặc định cho tham số thứ hai của nó. Tuy nhiên, nếu bạn quản lý để nén một số mã giữa khai báo thứ nhất và khai báo thứ hai, hai biến thể này sẽ hoạt động khác nhau. Trong biến thể thứ hai, hàm không có đối số mặc định giữa các khai báo, vì vậy bạn sẽ phải chỉ định rõ ràng cả hai đối số.
void foo(int a = 1, int b)
để làm việc, nó phải được khai báo sau void foo(int a, int b = 5)
. Vâng, nó sẽ làm việc. Và không, nó không phải là một lỗi cú pháp. g ++ 4.5.3 sẽ biên dịch nó hoàn toàn tốt.
int foo(int)
. Tôi thấy rằng tôi có thể viết int foo(int=5)
lại, bỏ qua các tên tham số. Không ai có vẻ đã đề cập đến điều đó.
Cách thứ nhất sẽ được ưu tiên cho cách thứ hai.
Điều này là do tệp tiêu đề sẽ hiển thị rằng tham số là tùy chọn và giá trị mặc định của nó sẽ là gì. Ngoài ra, điều này sẽ đảm bảo rằng giá trị mặc định sẽ giống nhau, bất kể việc triển khai tệp .cpp tương ứng.
Theo cách thứ hai, không có gì đảm bảo giá trị mặc định cho tham số thứ hai. Giá trị mặc định có thể thay đổi, tùy thuộc vào cách tệp .cpp tương ứng được triển khai.
b
sẽ được xác định nhiều lần, một lần cho mỗi đơn vị biên dịch bao gồmlib.h
, điều đó có đúng không?