2010-01-25 50 views
20

Trong C++, sự hiểu biết của tôi là chức năng ảo có thể được gạch chân, nhưng nói chung, gợi ý cho nội tuyến bị bỏ qua. Dường như các hàm ảo nội tuyến không có ý nghĩa quá nhiều.chức năng ảo nội tuyến

Đúng không?

Ai đó có thể đưa ra trường hợp trong đó chức năng ảo nội tuyến tốt không?

+0

Tôi không thấy một câu hỏi thực sự ở đây ... –

+0

hi, những gì làm bạn nghĩa là? Tôi đã không giải quyết vấn đề rõ ràng? Tôi lấy làm tiếc vì điều đó. – skydoor

+4

Các chức năng nội tuyến không bao giờ cần thiết, vì vậy sẽ rất khó để hiển thị cho bạn một nội tuyến ảo cần thiết. – zneak

Trả lời

15

Trong các trường hợp bình thường, một hàm ảo sẽ được gọi thông qua một con trỏ tới hàm (có trong lớp 'vtable). Trong trường hợp đó, một cuộc gọi hàm ảo chỉ có thể được tạo nội tuyến nếu trình biên dịch có thể xác định tĩnh loại thực tế mà hàm sẽ được gọi, chứ không phải chỉ là lớp X hoặc thứ gì đó có nguồn gốc từ X.

Lần đầu tiên một chức năng ảo nội tuyến có nghĩa là nếu bạn có một tình huống quan trọng về hiệu năng và biết rằng một lớp sẽ thường được sử dụng theo cách cho phép trình biên dịch xác định loại thực tế tĩnh (và ít nhất một trình biên dịch đích tối ưu hóa gọi qua con trỏ).

+1

Tôi cũng tìm thấy rất các hàm ngắn sẽ rõ ràng hơn khi chúng nằm trong nội dung lớp. – Omnifarious

+2

Câu hỏi giải quyết từ khóa 'nội tuyến'. –

+0

Ngoài ra, tôi nghĩ rằng nếu tất cả các chức năng ảo của bạn làm là gọi một số chức năng khác (có thể xảy ra với nhiều thừa kế, hoặc vì lý do khác) trình biên dịch chỉ có thể đặt thunking cho quyền đó trong vtable. – Omnifarious

4

Bạn có thể có các chức năng ảo như nội dòng. Quyết định thực hiện hàm gọi nội dòng không chỉ được thực hiện tại thời gian biên dịch. Nó có thể là bất cứ lúc nào giữa quá trình biên dịch thành thời gian. Bạn có thể tham khảo bài viết này từ Herb Sutter. Inline Redux

37

Để trả lời câu hỏi này đầy đủ, bạn cần hiểu rằng thuộc tính là virtual áp dụng độc lập với chính hàm đó và các cuộc gọi được thực hiện cho chức năng đó. Có các chức năng ảo và phi ảo. Có các cuộc gọi ảo và không ảo đến các chức năng này.

Điều tương tự cũng đúng về thuộc tính là inline. Có các hàm iniline và non-inline. Và có được inlinekhông được gọi cuộc gọi đến các chức năng này.

Các thuộc tính này - virtualinline - khi được áp dụng cho chính hàm đó, không xung đột. Họ chỉ đơn giản là không có lý do và không có cơ hội xung đột. Điều duy nhất mà các thay đổi chỉ định cho chính hàm đó là sửa đổi Quy tắc Một Định nghĩa cho hàm đó: hàm có thể được định nghĩa trong nhiều đơn vị dịch (và nó phải được định nghĩa trong mọi đơn vị dịch mà nó được sử dụng). Điều duy nhất virtual thay đổi chỉ định là lớp chứa hàm đó trở nên đa hình. Nó không có ảnh hưởng thực sự lên bản thân hàm.

Vì vậy, hoàn toàn không có vấn đề khi khai báo hàm virtualinline cùng một lúc. Không có cơ sở cho cuộc xung đột nào cả. Nó hoàn toàn hợp pháp trong ngôn ngữ C++.

struct S { 
    virtual void foo(); 
}; 

inline void S::foo() // virtual inline function - OK, whatever 
{ 
} 

Tuy nhiên, khi mọi người hỏi câu hỏi này, họ thường không quan tâm đến các thuộc tính của hàm, mà là đặc điểm của các cuộc gọi được thực hiện cho hàm.

Tính năng định nghĩa của một cuộc gọi ảo là nó đã được giải quyết tại thời gian chạy, có nghĩa là nó nói chung là không thể nội tuyến các cuộc gọi ảo đúng:

S *s = new SomeType; 
s->foo(); // virtual call, in general case cannot be inlined 

Tuy nhiên, nếu một cuộc gọi là do bản thân không ảo (mặc dù nó đi vào một hàm ảo), nội tuyến không phải là một vấn đề gì cả:

S *s = new SomeType; 
s->S::foo(); // non-virtual call to a virtual function, can easily be inlined 

Tất nhiên, trong một số trường hợp một trình biên dịch tối ưu hóa có thể có thể tìm ra các mục tiêu của một cuộc gọi ảo tại thời gian biên dịch và nội tuyến ngay cả cuộc gọi ảo như vậy. Trong một số trường hợp nó rất dễ dàng:

S ss; 
ss.foo(); // formally a virtual call, but in practice it can easily be inlined 

Trong một số trường hợp nó là phức tạp hơn, nhưng vẫn có thể làm được:

S *s = new S; 
s->foo(); // virtual call, but a clever compiler might be able 
      // to figure out that it can be inlined 
+0

Làm thế nào bạn có thể nói rằng 'ss.foo();' chính thức là một cuộc gọi ảo? – Belloc

+0

@Belloc: Bởi vì trong C++ tất cả các cuộc gọi đến các chức năng ảo là "cuộc gọi ảo", tức là được giải quyết theo loại động của đối tượng. Không có gì trong các đặc tả ngôn ngữ nói rằng 'ss.foo()' là bằng cách nào đó đặc biệt hoặc khác nhau. Cuộc gọi duy nhất mà "loại động" bị bỏ qua là cuộc gọi có tên đủ điều kiện. – AnT

+0

Đó không phải là những gì tôi có thể thấy [ở đây] (https://godbolt.org/g/PCjpqW). Việc tháo gỡ mã cho thấy 'f-> bar()' là một cuộc gọi ảo, như mong đợi, và 'd.bar()' là một cuộc gọi không phải ảo. – Belloc

Các vấn đề liên quan