Tôi chỉ biết rằng việc xác định hàm C++ bên trong tệp tiêu đề của lớp sẽ làm cho hàm nội tuyến. Nhưng tôi biết rằng việc đặt từ khóa nội tuyến bên cạnh một hàm chỉ là một gợi ý và trình biên dịch sẽ không nhất thiết tuân theo nó. Điều này có giống với các hàm C++ được định nghĩa tiêu đề và có sự khác biệt về hành vi giữa hàm C++ độc lập và hàm C++ là một phần của lớp không?Việc định nghĩa một hàm bên trong một tiêu đề luôn làm cho trình biên dịch coi nó là nội tuyến?
Trả lời
"xác định hàm C++ bên trong tệp tiêu đề của lớp làm cho hàm trực tuyến"
Điều đó không đúng. Định nghĩa một hàm (có nghĩa là, cung cấp phần thân của hàm thay vì chỉ là một khai báo) bên trong một định nghĩa lớp làm cho nó nội tuyến. Bởi "làm cho nó nội tuyến", tôi có nghĩa là nó giống như cho nó từ khóa nội tuyến. Nhưng các định nghĩa lớp không phải nằm trong các tiêu đề và các tiêu đề có thể chứa các thứ khác ngoài các định nghĩa lớp.
Vì vậy, trong ví dụ này, hàm foo
hoàn toàn nội tuyến. Chức năng bar
không ngầm inline:
struct Foo {
void foo() {}
void bar();
};
void Foo::bar() {}
"đặt từ khóa inline bên cạnh một chức năng chỉ là một gợi ý và trình biên dịch sẽ không nhất thiết phải theo nó"
inline
có hai tác dụng. Một trong số đó là một gợi ý cho trình biên dịch mà nó có thể bỏ qua. Cách khác không phải là tùy chọn và luôn có hiệu lực. "Gợi ý" là trình biên dịch được khuyên nên thay thế các cuộc gọi đến hàm đó bằng một bản sao của mã cho chính hàm đó.
Hiệu ứng được đảm bảo là một hàm nội tuyến có thể được xác định trong nhiều đơn vị dịch và các liên kết được liên kết với nhau mà không có lỗi định nghĩa nhiều và tất cả trừ một trong các bản sao bị xóa bởi trình liên kết. Vì vậy, nếu ví dụ trên xuất hiện trong tệp tiêu đề được chia sẻ giữa nhiều đơn vị dịch, thì bar
cần phải được đánh dấu rõ ràng. Nếu không, trình liên kết sẽ khám phá nhiều định nghĩa của bar
, không được phép.
Mặc dù tên, inline
trong C++ chủ yếu là về hiệu ứng bắt buộc thứ hai, không phải là tùy chọn thứ nhất, tùy chọn. Các trình biên dịch tối ưu hóa hiện đại có ý tưởng riêng về các cuộc gọi nào nên được gạch chân và không chú ý nhiều đến inline
khi đưa ra quyết định đó. Ví dụ tôi đã thấy nó có hiệu lực trong gcc ở mức tối ưu vừa phải, nhưng ở mức thấp không có gì là được gạch chân, và ở mức cao gần như tất cả mọi thứ là (nếu định nghĩa có sẵn khi cuộc gọi được biên dịch) trừ khi nó làm cho hàm quá lớn.
Cho dù một hàm được xác định trong tiêu đề hoặc trong tệp cpp có hoàn toàn không ảnh hưởng đến bất kỳ điều gì một mình. Bạn có thể tưởng tượng một cách an toàn rằng những gì #include làm là sao chép và dán tệp tiêu đề vào tệp cpp trong bộ tiền xử lý trước khi trình biên dịch nhìn thấy nó. Nếu một hàm được định nghĩa trong cùng một đơn vị dịch như là một cuộc gọi đến nó, thì mã chức năng có sẵn để được biên dịch bởi trình biên dịch. Nếu họ đang ở các đơn vị dịch thuật khác nhau, sau đó mã không có sẵn và các cuộc gọi chỉ có thể được inline bởi linker, với tối ưu hóa toàn bộ chương trình hoặc tương tự. Một "đơn vị dịch" ít nhiều có nghĩa là "một tệp cpp, sau khi tất cả các tiêu đề đã được sao chép và dán vào nó".
Trình biên dịch C++ được tự do lựa chọn nội dung sẽ là nội tuyến và những gì sẽ không, bất kể gợi ý bạn cung cấp cho chúng là gì. Nó không quan trọng nếu chức năng là một phần của một lớp hay không, hoặc cho dù đó là trong một tập tin tiêu đề hoặc tập tin nguồn; trình biên dịch không chú ý đến những điều đó trong khi đưa ra quyết định.
Để chính xác, trình biên dịch không thể nội tuyến một hàm mà nó không có định nghĩa, đó là trường hợp nếu hàm được khai báo trong tiêu đề được bao gồm nhưng được định nghĩa trong một đơn vị biên dịch khác. Tuy nhiên, trình liên kết có thể chọn nội tuyến hàm, nếu nó hỗ trợ loại tối ưu hóa đó. –
@Carl: Đúng, nhưng câu hỏi là về điều kiện ngược lại. Tôi cho rằng câu trả lời của tôi có thể được nói một cách cẩn thận hơn một chút. –
Nếu bạn đặt định nghĩa của một chức năng không mẫu miễn phí trong tệp tiêu đề, bạn sẽ kết thúc với định nghĩa hàm trong mỗi tệp .cpp bao gồm tiêu đề (trực tiếp hoặc gián tiếp). Điều này có thể dẫn đến các vấn đề khi liên kết.
Tuy nhiên, nếu bạn khai báo hàm là nội dòng, trình liên kết sẽ đảm bảo bạn chỉ sử dụng một định nghĩa duy nhất ngay cả khi nó được bao gồm ở nhiều vị trí.
Không, không phải lúc nào. Trình biên dịch xử lý nó như một gợi ý, giống như từ khóa inline
, nhưng nó chủ yếu tự quyết định, bởi vì nó biết rõ hơn bạn về chi phí và lợi ích có thể là gì. Việc nội tuyến mã tránh chi phí cuộc gọi hàm, nhưng làm cho mã lớn hơn, có tác động tiêu cực đến hiệu suất trên bộ nhớ cache lệnh.
Những gợi ý về hiệu suất này từ người lập trình nói chung thường bị trình biên dịch bỏ qua nhiều hơn. Những gì nó không bỏ qua (hoặc đúng hơn, những gì mà trình liên kết không bỏ qua) là một hàm được khai báo inline
có thể xuất hiện trong một số đơn vị biên dịch và sẽ được coi là nhiều bản sao của cùng một hàm mà không dẫn đến lỗi liên kết.
+1 để đề cập đến hiệu ứng thứ hai của từ khóa 'inline'. –