2010-03-23 3 views
18

Nếu chúng ta định nghĩa một hàm thành viên bên trong định nghĩa lớp, thì nó nhất thiết phải được xử lý nội tuyến hoặc nó chỉ là một yêu cầu tới trình biên dịch mà nó có thể bỏ qua.Chức năng nội tuyến trong C++

+0

Xem thêm http://stackoverflow.com/questions/908830/isnt-cs-inline-totally-optional/910686#910686 –

Trả lời

18

Có, các hàm được xác định bên trong nội dung lớp là hoàn toàn inline.

(Giống như các chức năng khác được khai báo inline, điều đó không có nghĩa là trình biên dịch phải thực hiện mở rộng nội tuyến ở những nơi hàm được gọi, nó chỉ cho phép thư giãn "quy tắc một định nghĩa" được phép định nghĩa phải được bao gồm trong tất cả các đơn vị dịch mà chức năng được sử dụng.)

2

Đó là yêu cầu đối với trình biên dịch mà nó có thể bỏ qua.

+4

Không, đó là một yêu cầu. Nếu một hàm được định nghĩa trong một thân lớp, nó phải được xử lý như thể nó được khai báo là 'inline'. –

+5

giống như bất kỳ yêu cầu 'inline' nào mà trình biên dịch có thể bỏ qua. – shoosh

+1

Vì nó có thể được bỏ qua, và hầu hết các trình biên dịch sẽ nội tuyến khi thích hợp ngay cả khi bạn không yêu cầu nó, đó là một gợi ý khá vô dụng. – marr75

3

Trình biên dịch cần phải được xử lý như một yêu cầu nội tuyến - mà nó có thể bỏ qua. Có một số thành ngữ để xác định một số chức năng trong tiêu đề (ví dụ: các trình phá hủy ảo trống) và một số định nghĩa tiêu đề cần thiết (các hàm mẫu), nhưng khác với xem GotW #33 để biết thêm thông tin. Một số đã lưu ý rằng trình biên dịch thậm chí có thể các hàm nội tuyến mà bạn chưa bao giờ yêu cầu, nhưng tôi không chắc liệu nó có đánh bại mục đích yêu cầu nội tuyến một hàm hay không.

+0

Phụ thuộc vào trình biên dịch. Nói chung, nội tuyến là một gợi ý không làm được gì nhiều. Tôi nghĩ rằng hầu hết các trình biên dịch nhắm vào các nền tảng nhúng lắng nghe các gợi ý chặt chẽ hơn, và đây thực sự là không gian duy nhất mà bạn có thể làm tốt hơn trình biên dịch sau khi thiết kế cẩn thận. – marr75

3

Nó thực sự được gạch chân - nhưng bất kỳ yêu cầu nội dòng nào đều có thể bị bỏ qua bởi trình biên dịch.

2

Tiêu chuẩn ISO 2003 C++ nói

7.1.2/2 Một khai báo hàm (8.3.5, 9.3, 11.4) với một specifier inline tuyên bố một inline chức năng. Thông số nội tuyến cho biết việc triển khai thay thế nội tuyến của hàm nội dung tại thời điểm gọi là được ưu tiên hơn với chức năng gọi thông thường
cơ chế. Triển khai không phải là bắt buộc để thực hiện nội dòng này
thay thế tại thời điểm cuộc gọi; Tuy nhiên, , ngay cả khi nội tuyến này
thay thế bị bỏ qua, các quy tắc khác cho các chức năng nội tuyến được xác định theo 7.1.2 sẽ vẫn được tôn trọng.

7.1.2/3 Hàm được xác định trong định nghĩa lớp là hàm nội tuyến
. Trình định danh nội tuyến phải không xuất hiện trên chức năng phạm vi khối tuyên bố.

7.1.2/4 Một chức năng inline sẽ được xác định trong mỗi đơn vị dịch trong
mà nó được sử dụng và có trách nhiệm chính xác định nghĩa giống nhau ở mọi
trường hợp (3.2). [Lưu ý: một cuộc gọi đến hàm nội tuyến có thể gặp phải
trước khi định nghĩa của nó xuất hiện trong đơn vị dịch . ] Nếu hàm có liên kết bên ngoài được khai báo là nội tuyến trong một đơn vị chuyển tiếp, nó sẽ được khai báo nội dòng trong tất cả đơn vị dịch mà trong đó nó xuất hiện ; không cần chẩn đoán.Hàm nội tuyến với liên kết bên ngoài sẽ có cùng địa chỉ trong tất cả các đơn vị dịch. Biến tĩnh cục bộ trong một hàm bên ngoài
bên ngoài luôn đề cập đến cùng một đối tượng . Một chuỗi ký tự trong một hàm số
bên ngoài là cùng một đối tượng trong các bản dịch khác nhau
đơn vị.

+0

Đây là câu trả lời hữu ích nhất, nhưng cũng gây hiểu nhầm, nói chung, các trình biên dịch chính thống tập trung vào phần tử "Một thực hiện không bắt buộc để thực hiện việc thay thế nội tuyến" này của tiêu chuẩn. – marr75

1

Có hai điều mà không nên gộp lại với nhau:

  1. Làm thế nào bạn đánh dấu một chức năng như là inline: định nghĩa nó với inline ở phía trước của chữ ký hoặc xác định nó vào thời điểm khai;
  2. Trình biên dịch sẽ xử lý việc đánh dấu nội tuyến như thế nào: bất kể cách bạn đánh dấu hàm là nội dòng, nó sẽ được xử lý theo yêu cầu của trình biên dịch.
4

Như đã nêu khác, phương pháp được xác định trong lớp được tự động yêu cầu nội dòng. Rất hữu ích để hiểu tại sao.

Giả sử không. Bạn sẽ phải tạo mã cho một hàm như vậy, và ở khắp mọi nơi nó được gọi, một bước nhảy tới hướng dẫn chương trình con sẽ phải tham chiếu đến vị trí đó, thông qua trình liên kết.

class A { 
public: 
    void f() { ... your code ... } 
}; 

Mỗi lần mã này chỉ được giả định nó phải được tạo, trình biên dịch chỉ có thể giả định nó phải được tạo, vì vậy nó sẽ tạo ra một biểu tượng. Giả sử nó là như thế này:

A__f_v:

Nếu biểu tượng đó là toàn cầu, sau đó nếu bạn xảy ra để bao gồm mã lớp này nhiều lần trong các module khác nhau, bạn sẽ có một lỗi biểu tượng nhân quy định tại thời gian liên kết. Vì vậy, nó không thể là toàn cầu. Thay vào đó, nó là tập tin cục bộ.

Hãy tưởng tượng bạn bao gồm tệp tiêu đề ở trên trong một số mô-đun. Trong mỗi một, nó sẽ tạo ra một bản sao cục bộ của mã đó. Đó là tốt hơn so với không biên dịch ở tất cả, nhưng bạn đang nhận được nhiều bản sao của mã khi bạn thực sự cần chỉ có một.

Điều này dẫn đến kết luận sau: nếu trình biên dịch của bạn không sắp xếp nội tuyến một hàm, bạn tốt hơn đáng kể khai báo nó ở đâu đó một lần và không yêu cầu nó được gạch chân.

Thật không may, nội dung là gì và không nội tuyến không phải là di động. Nó được định nghĩa bởi trình biên dịch. Một nguyên tắc nhỏ là luôn luôn làm cho mỗi một lớp lót, đặc biệt là tất cả các chức năng mà bản thân chúng chỉ gọi một hàm, nội tuyến, khi bạn loại bỏ chi phí. Bất cứ điều gì dưới ba dòng mã tuyến tính là gần như chắc chắn ok. Nhưng nếu bạn có một vòng lặp trong mã, câu hỏi đặt ra là liệu trình biên dịch sẽ cho phép nó nội dòng, và nhiều hơn nữa vào vấn đề, bạn sẽ thấy bao nhiêu lợi ích ngay cả khi nó đã làm những gì bạn muốn.

xem xét mã nội tuyến này:

inline int add(int a, int b) { return a + b; } 

Nó không chỉ gần như là nhỏ như nguyên mẫu sẽ là trong mã nguồn, nhưng ngôn ngữ lắp ráp tạo ra bởi các mã nội tuyến là nhỏ hơn so với các cuộc gọi đến một thói quen sẽ là . Vì vậy, mã này nhỏ hơn và nhanh hơn.

Và, nếu bạn tình cờ được đi qua trong các hằng số:

int c= add(5,4); 

Nó giải quyết tại thời gian biên dịch và không có mã.

Trong gcc, gần đây tôi đã nhận thấy rằng ngay cả khi tôi không có mã nội tuyến, nếu nó là cục bộ cho một tệp, họ vẫn sẽ lén lút ghi lại nó. Chỉ khi tôi khai báo hàm trong một mô-đun nguồn riêng biệt thì chúng không tối ưu hóa cuộc gọi.

Ở đầu kia của quang phổ, giả sử bạn yêu cầu nội tuyến trên một đoạn dòng 1000. Ngay cả khi trình biên dịch của bạn đủ ngớ ngẩn để đi cùng với nó, điều duy nhất bạn lưu là cuộc gọi và chi phí là mỗi khi bạn gọi nó, trình biên dịch phải dán tất cả mã đó vào. Nếu bạn gọi mã đó n lần , mã của bạn phát triển theo kích thước của thường trình * n. Vì vậy, bất cứ điều gì lớn hơn 10 dòng là khá nhiều giá trị không inlining, ngoại trừ trường hợp đặc biệt, nơi nó chỉ được gọi là một số lượng rất nhỏ lần. Một ví dụ về điều đó có thể là một phương thức riêng được gọi bởi chỉ 2 phương pháp khác.

Nếu bạn yêu cầu nội tuyến phương thức chứa vòng lặp, điều đó chỉ có ý nghĩa nếu nó thường thực hiện một số lần nhỏ. Nhưng hãy xem xét một vòng lặp lặp lại một triệu lần. Ngay cả khi mã được inlined, tỷ lệ phần trăm thời gian dành cho cuộc gọi là rất nhỏ. Vì vậy, nếu bạn có phương thức với vòng lặp trong đó, có xu hướng lớn hơn, những giá trị đó sẽ bị loại bỏ khỏi tệp tiêu đề vì chúng a) có xu hướng bị từ chối bởi trình biên dịch và b) ngay cả khi chúng được inlined, nói chung sẽ không cung cấp bất kỳ lợi ích nào

+0

Bạn chỉ cần thổi tâm trí của tôi. Nếu tôi định nghĩa một hàm theo cách thông thường (với định nghĩa ở trên và thực hiện ở dưới), và tôi đặt từ khóa inline ở phía trước (của việc thực thi? Định nghĩa? Cả hai?), Thì trình biên dịch sẽ xem xét nội tuyến nó? Và điều này là tốt hơn? Mối quan tâm chính của tôi với các hàm nội tuyến là chúng xấu và tạo dòng dài. Bạn đang nói rằng tôi có thể có vẻ sexy bình thường của tôi dễ dàng để đọc chức năng nội tuyến ... và không chỉ có vậy, nhưng tôi _should_ đánh dấu các chức năng nhỏ theo cách đó? Tôi hy vọng bạn là người dùng tích cực và trả lời nhận xét này. – Ziggy

+1

Nếu bạn định nghĩa một hàm bằng cách sử dụng một mẫu thử nghiệm, với việc triển khai thực hiện trong cùng một tệp tiêu đề được đánh dấu nội dòng, nó sẽ được tạo nội dòng. Nếu thực hiện của bạn là trong một tập tin khác, trình biên dịch không thể làm cho nó nội tuyến. Nó không phải là một đầu đọc tâm trí, nó không biết mã để tạo ra. Cá nhân tôi không quan tâm thêm một chút mã trong tiêu đề thay vì viết nó hai lần, nhưng bạn chắc chắn có thể làm điều đó nếu bạn thích nó. – Dov

+1

@ziggy nếu tôi hiểu câu hỏi của bạn đúng, có, nếu hàm của bạn ngắn, thì việc xác định nội tuyến trong tệp tiêu đề có thể là tốc độ thời gian chạy rất lớn, đồng thời làm chậm thời gian biên dịch và cũng yêu cầu bất kỳ ký hiệu nào được tham chiếu trong chức năng nội tuyến được xác định. Điều này có nghĩa là nếu mã nội tuyến của bạn đề cập đến các đối tượng khác, tiêu đề của bạn có thể phải bao gồm các tiêu đề khác. Vì vậy, có một chi phí tiềm năng lớn trong thời gian biên dịch lại. Nhưng đối với phương pháp trung bình (getter/setter, tính toán một cái gì đó với các biến riêng), nội tuyến là một chiến thắng lớn. – Dov