2012-01-08 19 views
8

वास्तव में यह एक साक्षात्कार प्रश्न है, मैं जवाब नहीं समझ सकता। किसी को इसके बारे में पता है? आप किसी भी अंतर के बारे में बात कर सकते हैं, उदाहरण के लिए, स्टैक में धक्का देने वाला डेटा।वर्चुअल फ़ंक्शन और गैर आभासी फ़ंक्शन को कॉल करने के बीच अंतर?

+0

वास्तव में अपने प्रश्न क्या है? –

+1

एक वर्चुअल फ़ंक्शन गतिशील प्रेषण से गुजरता है। इसे बेहतर सीखने के लिए आपको [एक अच्छी सी ++ पुस्तक] (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) चुननी चाहिए। – GManNickG

+0

मुझे लगता है कि सवाल थोड़ा अस्पष्ट है। इसका क्या मतलब है? कोड अर्थशास्त्र स्तर, कंपाइलर स्तर या मशीन स्तर पर अंतर? – erenon

उत्तर

24

हालांकि virtualism/गतिशील प्रेषण सख्ती से कार्यान्वयन परिभाषित किया गया है, सबसे अधिक (पढ़ा सभी ज्ञात) compilers vptr और vtable का उपयोग करके इसे लागू।

कहा करने के बाद कि, एक गैर आभासी समारोह और आभासी समारोह बुला बीच का अंतर है:

गैर आभासी कार्यों Compile-time पर statically हल कर रहे हैं, जबकि आभासी कार्यों Run-time पर dynamically हल कर रहे हैं।

रन-टाइम पर कॉल करने के लिए कौन सा फ़ंक्शन कॉल करने में सक्षम होने की यह लचीलापन प्राप्त करने के लिए, वर्चुअल फ़ंक्शंस के मामले में थोड़ा ओवरहेड है।

एक अतिरिक्त fetch कॉल जिसे करने की आवश्यकता है और यह गतिशील प्रेषण का उपयोग करने के लिए आपके द्वारा भुगतान किए जाने वाले ओवरहेड/मूल्य है।

गैर आभासी समारोह के मामले में कॉल की अनुक्रम है:

fetch-call 

संकलक और समारोह के fetch पता करने की जरूरत है तो यह call

जबकि आभासी कार्यों के मामले में अनुक्रम है:

fetch-fetch-call 

संकलक this से vptr, तो fetchvptr से समारोह का पता और उसके बाद call समारोह fetch की जरूरत है।

यह एक सरल सरलीकरण है जो वास्तव में वास्तविक अनुक्रम से कहीं अधिक जटिल है, लेकिन आपको वास्तव में यह जानने की ज़रूरत है कि किसी को वास्तव में कार्यान्वयन nitty gritty के बारे में जानने की आवश्यकता नहीं है।

अच्छा पढ़ें:

Inheritance & Virtual Functions

+0

क्या आप fetch-call और fetch-fetch-call की प्रक्रिया के बारे में अधिक जानकारी प्रदान कर सकते हैं? या आप कुछ रीडिंग प्रदान कर सकते हैं? धन्यवाद। – cheng

+1

@cheng: एक लिंक जोड़ा गया, जो विवरणों का एक अच्छा स्पष्टीकरण प्रदान करता है। –

+0

यह वास्तव में मदद करता है। धन्यवाद। – cheng

3

वर्चुअल विधि को कॉल करते समय, इसे वर्चुअल फ़ंक्शन तालिका में कॉल करने के लिए कौन सा फ़ंक्शन देखना होगा।

+0

तो वर्चुअल फ़ंक्शन और गैर वर्चुअल फ़ंक्शन को कॉल करने के बीच क्या अंतर है? उदाहरण के लिए, उनमें से प्रत्येक के लिए कितनी मेमोरी एक्सेस आवश्यक है? – cheng

+0

@cheng: मुझे नहीं पता कि वर्चुअल विधि कॉल में गैर-वर्चुअल विधि कॉल की तुलना में कितनी अधिक पहुंच है (मैं एक या दो मानता हूं), लेकिन वर्चुअल विधि कॉल के लिए थोड़ा और काम करने की आवश्यकता होती है। – icktoofay

+0

ठीक है, मैं इसे समझने की कोशिश करूंगा। धन्यवाद। – cheng

3

वर्चुअल विधि को कॉल करने का ओवरहेड महत्वपूर्ण है।

Also this.

+4

"है" होना चाहिए "हो सकता है"। –

5

आप एक आधार वर्ग 'बेस' और व्युत्पन्न वर्ग 'व्युत्पन्न' है और आप एक समारोह है, तो 'समारोह()' बेस क्लास में आभासी रूप में परिभाषित किया। यह func व्युत्पन्न वर्ग द्वारा ओवरराइड किया गया है।

आप

 Base obj = new Derived(); 
     obj.func(); 

फिर व्युत्पन्न वर्ग की 'समारोह' कहा जाता है परिभाषित मान लीजिए।जबकि 'func()' को बेस में आभासी के रूप में परिभाषित नहीं किया गया था, तो इसे 'बेस' कक्षा से बुलाया जाएगा। यह अंतर है कि फ़ंक्शन कॉलिंग विटुअल और गैर वर्चुअल फ़ंक्शन

+1

को कॉल करने के बाद चित्र में आ रहा है कुछ सूक्ष्म बिंदु: 1. 'obj' को एक सूचक होने की आवश्यकता है। 2. यह उल्लेख करने के लिए कि ओवरसीड फ़ंक्शन 'व्युत्पन्न :: func()' को 'बेस :: func() ' –

+0

को ओवरराइड करने के लिए समान तर्क लेना चाहिए, यह एक सूक्ष्म बिंदु नहीं है, बल्कि एक महत्वपूर्ण है। "obj.func()" बेस क्लास विधि को कॉल नहीं करेगा लेकिन किसी को पॉइंटर्स का उपयोग करना होगा और वर्चुअल विधियों का उपयोग करने के लिए "obj-> func()" को कॉल करना होगा। – user463035818

+0

ध्यान दें कि कोई पॉइंटर्स के बजाय संदर्भों का उपयोग कर सकता है, हालांकि हमेशा एक ही परिस्थितियों में नहीं। उदाहरण के लिए, किसी संदर्भ को किसी भिन्न ऑब्जेक्ट को संदर्भित करने के लिए रीबाउंड नहीं किया जा सकता है। – Alan

1

गैर वर्चुअल सदस्य फ़ंक्शंस को स्थिर रूप से हल किया गया है। सदस्य फ़ंक्शन ऑब्जेक्ट पर पॉइंटर (या संदर्भ) के प्रकार के आधार पर संकलित समय पर स्थिर रूप से बाध्यकारी हैं।

इसके विपरीत, आभासी सदस्य कार्य हैंरन-टाइम में गतिशील रूप से बाध्यकारी। यदि कक्षा में कम से कम एक वर्चुअल सदस्य फ़ंक्शन है तो संकलक ऑब्जेक्ट के निर्माण के दौरान vptr (वर्चुअल टेबल पता) नामक ऑब्जेक्ट में एक छुपा सूचक डालता है।

कंपाइलर प्रत्येक वर्ग के लिए एक v-table बनाता है जिसमें कम से कम एक वर्चुअल फ़ंक्शन होता है। वर्चुअल टेबल में वर्चुअल फ़ंक्शन का पता होता है। वर्चुअल फ़ंक्शन पॉइंटर के
वर्चुअल फ़ंक्शन के प्रेषण के दौरान सरणी या सूची (कंपाइलर पर निर्भर) हो सकती है, रन-टाइम सिस्टम ऑब्जेक्ट के वी-पॉइंटर (क्लास ऑब्जेक्ट से पता लाता है) कक्षा के वी- तालिका, फिर ऑफ़सेट बेस पता (vptr) में जोड़ा जाता है और फ़ंक्शन को कॉल करता है।

ऊपर तकनीक के अंतरिक्ष लागत वाली भूमि के ऊपर नाममात्र है: (लेकिन केवल वस्तुओं है कि गतिशील बाध्यकारी करने की आवश्यकता होगी के लिए) वस्तु प्रति एक अतिरिक्त सूचक, प्लस विधि के अनुसार एक अतिरिक्त सूचक (लेकिन केवल आभासी तरीकों के लिए) । समय-लागत ओवरहेड भी काफी नाममात्र है: सामान्य फ़ंक्शन कॉल की तुलना में, वर्चुअल फ़ंक्शन कॉल के लिए दो अतिरिक्त fetches (v-pointer का मान प्राप्त करने के लिए, विधि के पते को प्राप्त करने के लिए एक सेकंड) की आवश्यकता होती है। ।

इस रनटाइम गतिविधि में से कोई भी गैर वर्चुअल फ़ंक्शंस के साथ होता है, क्योंकि संकलक पॉइंटर के प्रकार के आधार पर संकलन-समय पर गैर वर्चुअल फ़ंक्शंस को हल करता है।

मैंने बेहतर तरीके से समझने के लिए सरल उदाहरण लिया है, कैसे बाध्यकारी गैर वर्चुअल फ़ंक्शन & वर्चुअल फ़ंक्शन और वर्चुअल फ़ंक्शन तंत्र कैसे काम करता है।

#include<iostream> 
using namespace std; 
class Base 
{ 
     public: 
       virtual void fun() 
       {} 
       virtual void fun1() 
       {} 

       void get() 
       { 
         cout<<"Base::get"<<endl; 
       } 
       void get1() 
       { 
         cout<<"Base::get1"<<endl; 
       } 
}; 

class Derived :public Base 
{ 
     public: 
       void fun() 
       { 
       } 
       virtual void fun3(){} 
       void get() 
       { 
         cout<<"Derived::get"<<endl; 
       } 
       void get1() 
       { 
         cout<<"Derived::get1"<<endl; 
       } 

}; 
int main() 
{ 
    Base *obj = new Derived(); 
    obj->fun(); 
    obj->get(); 
} 

कैसे vtable बेस & के लिए बनाया व्युत्पन्न वर्ग

विधानसभा कोड बेहतर समझ के लिए उत्पन्न होता है।

$ g++ virtual.cpp -S -o virtual.s 

मैं बेस के लिए virtual.s से vtable की जानकारी दिलवाया है और क्रमशः वर्ग व्युत्पन्न:

_ZTV4Base: 
     .quad _ZN4Base3funEv 
     .quad _ZN4Base4fun1Ev 
_ZTV7Derived: 
     .quad _ZN7Derived3funEv 
     .quad _ZN4Base4fun1Ev 
     .quad _ZN7Derived4fun3Ev 

आप मज़ा & fun1 बेस कक्षा में केवल दो आभासी कार्य हैं देख सकते हैं। बेस क्लास (_ZTV4Base) के Vtable में वर्चुअल फ़ंक्शन दोनों की प्रविष्टियां हैं। Vtable में गैर वर्चुअल फ़ंक्शन का प्रवेश नहीं है। कृपया मज़े के नाम से भ्रमित न हों (ZN4Base3funEv) & fun1 (ZN4Base4fun1Ev), उनका नाम उलझ गया।

व्युत्पन्न वर्ग vtable है पेड़ प्रविष्टियों

  1. मज़ा (_ZN7Derived3funEv) ओवरराइड समारोह
  2. fun1 (_ZN4Base4fun1Ev) व्युत्पन्न वर्ग में बेस वर्ग
  3. fun3 (_ZN7Derived4fun3Ev) नए कार्य से विरासत में मिली

गैर वर्चुअल फ़ंक्शन & वर्चुअल फ़ंक्शन कैसे कहा जाता है?

गैर आभासी समारोह

Derived d1; 
    d1.get(); 

    subq $16, %rsp 
    leaq -16(%rbp), %rax 
    movq %rax, %rdi 
    call _ZN7DerivedC1Ev //call constructor 
    leaq -16(%rbp), %rax 
    movq %rax, %rdi 
    call _ZN7Derived3getEv //call get function 

बस बता लाने और फोन मिल के लिए गैर आभासी समारोह

Base *obj = new Derived(); 
obj->fun(); 
pushq %rbx 
subq $24, %rsp 
movl $8, %edi 
call _Znwm //call new to allocate memory 
movq %rax, %rbx 
movq $0, (%rbx) 
movq %rbx, %rdi 
call _ZN7DerivedC1Ev //call constructor 
movq %rbx, -24(%rbp) 
movq -24(%rbp), %rax 
movq (%rax), %rax 
movq (%rax), %rax 
movq -24(%rbp), %rdx 
movq %rdx, %rdi 
call *%rax //call fun 
के लिए

(बंधन संकलन समय पर हुआ)

vptr लाएं, फ़ंक्शन जोड़ें ऑफसेट, फ़ंक्शन को कॉल

64 की सभा (बंधन रन समय में हुआ) C++ प्रोग्रामर के सबसे भ्रामक है, लेकिन यदि किसी भी चर्चा करना चाहते हैं तो आपका स्वागत है

+1

एक गतिशील रूप से जुड़े मॉड्यूल में एक सामान्य फ़ंक्शन को कॉल करने में सूचक संकेत शामिल हो सकता है। – curiousguy

संबंधित मुद्दे