जीएनयू जीसीसी (जी ++): यह कई डाइवोर्स क्यों उत्पन्न करता है?


89

विकासशील वातावरण: जीएनयू जीसीसी (जी ++) 4.1.2

हालांकि मैं यह जांचने की कोशिश कर रहा हूं कि यूनिट परीक्षण में 'कोड कवरेज - विशेष रूप से फ़ंक्शन कवरेज' को कैसे बढ़ाया जाए, मैंने पाया है कि कई बार कक्षा का डोर कई बार उत्पन्न होता है। क्या आप में से कुछ को इस पर कोई विचार है, कृपया?

मैंने कोशिश की और देखा कि मैंने निम्नलिखित कोड का उपयोग करके क्या उल्लेख किया है।

"Test.h" में

class BaseClass
{
public:
    ~BaseClass();
    void someMethod();
};

class DerivedClass : public BaseClass
{
public:
    virtual ~DerivedClass();
    virtual void someMethod();
};

"Test.cpp" में

#include <iostream>
#include "test.h"

BaseClass::~BaseClass()
{
    std::cout << "BaseClass dtor invoked" << std::endl;
}

void BaseClass::someMethod()
{
    std::cout << "Base class method" << std::endl;
}

DerivedClass::~DerivedClass()
{
    std::cout << "DerivedClass dtor invoked" << std::endl;
}

void DerivedClass::someMethod()
{
    std::cout << "Derived class method" << std::endl;
}

int main()
{
    BaseClass* b_ptr = new BaseClass;
    b_ptr->someMethod();
    delete b_ptr;
}

जब मैंने उपरोक्त कोड बनाया (g ++ test.cpp -o test) और फिर देखें कि किस प्रकार के प्रतीक इस प्रकार उत्पन्न हुए हैं,

एनएम --demangle परीक्षण

मैं निम्नलिखित आउटपुट देख सकता था।

==== following is partial output ====
08048816 T DerivedClass::someMethod()
08048922 T DerivedClass::~DerivedClass()
080489aa T DerivedClass::~DerivedClass()
08048a32 T DerivedClass::~DerivedClass()
08048842 T BaseClass::someMethod()
0804886e T BaseClass::~BaseClass()
080488f6 T BaseClass::~BaseClass()

मेरे प्रश्न इस प्रकार हैं।

1) क्यों कई डाइवोर्स उत्पन्न किए गए हैं (बेसक्लास - 2, डेरेवेडक्लास - 3)?

2) इन डार्टर्स में क्या अंतर हैं? उन एकाधिक डोरर्स का चयन कैसे किया जाएगा?

मुझे अब लग रहा है कि C ++ प्रोजेक्ट के लिए 100% फंक्शन कवरेज प्राप्त करने के लिए, हमें इसे समझने की आवश्यकता होगी ताकि मैं अपने यूनिट परीक्षणों में उन सभी डॉकटरों को आमंत्रित कर सकूं।

मैं बहुत सराहना करूंगा अगर कोई मुझे उपरोक्त पर जवाब दे सके।


5
एक न्यूनतम, पूर्ण नमूना कार्यक्रम को शामिल करने के लिए +1। ( sscce.org )
Robᵩ

2
क्या आपका आधार वर्ग जानबूझकर एक गैर-आभासी विध्वंसक है?
केरेक एसबी

2
एक छोटा सा अवलोकन; आपने पाप किया है, और अपने बेसक्लास विध्वंसक को आभासी नहीं बनाया है।
११:४१ बजे ल्येक

मेरे अधूरे नमूने के लिए क्षमा करें। हां, बेसक्लास में वर्चुअल विध्वंसक होना चाहिए ताकि इन वर्ग की वस्तुओं का उपयोग पॉलीमॉर्फिक रूप से किया जा सके।
स्मॉग

1
@ लाइक: ठीक है, यदि आप जानते हैं कि आप एक पॉइंटर-टू-बेस के माध्यम से व्युत्पन्न को हटाने नहीं जा रहे हैं जो ठीक है, तो मैं सिर्फ यह सुनिश्चित कर रहा था ... मजेदार रूप से, यदि आप आधार के सदस्यों को आभासी बनाते हैं, तो आप भी अधिक विध्वंसक।
केरेक एसबी

जवाबों:


73

सबसे पहले, इन कार्यों के उद्देश्य इटेनियम सी ++ एबीआई में वर्णित हैं ; "बेस ऑब्जेक्ट डिस्ट्रक्टर", "पूर्ण ऑब्जेक्ट डिस्ट्रक्टर", और "डेस्ट्रक्टर को हटाना" के तहत परिभाषाएं देखें। मैंगल्ड नामों की मैपिंग 5.1.4 में दी गई है।

मूल रूप से:

  • डी 2 "बेस ऑब्जेक्ट डिस्ट्रक्टर" है। यह ऑब्जेक्ट को स्वयं नष्ट कर देता है, साथ ही डेटा सदस्यों और गैर-आभासी आधार वर्गों को भी।
  • डी 1 "पूर्ण वस्तु विध्वंसक" है। यह अतिरिक्त रूप से वर्चुअल बेस कक्षाओं को नष्ट कर देता है।
  • D0 "डिलीट ऑब्जेक्ट डिस्ट्रक्टर" है। यह सब कुछ करता है पूरी वस्तु विध्वंसक करता है, साथ ही यह operator deleteवास्तव में स्मृति को मुक्त करने के लिए कहता है।

यदि आपके पास कोई वर्चुअल बेस क्लास नहीं है, तो D2 और D1 समान हैं; जीसीसी पर्याप्त अनुकूलन स्तरों पर, वास्तव में प्रतीकों को दोनों के लिए समान कोड में बदल देगा।


स्पष्ट उत्तर के लिए धन्यवाद। अब मैं इससे संबंधित हो सकता हूं, हालांकि मुझे और अधिक अध्ययन करने की आवश्यकता है क्योंकि मैं आभासी विरासत प्रकार के सामानों से परिचित नहीं हूं।
Smg

@Smg: आभासी विरासत में, "वस्तुतः" विरासत में मिली कक्षाएं सबसे अधिक व्युत्पन्न वस्तु की एकमात्र जिम्मेदारी के तहत हैं। यही है, अगर आपके पास है struct B: virtual Aऔर फिर struct C: B, तब जब Bआप नष्ट करते हैं तो आप आह्वान करते हैं, B::D1जो आह्वान करता है A::D2और जब आप नष्ट करते हैं, तो Cआप आह्वान C::D1करते हैं B::D2और A::D2ध्यान दें कि ( B::D2एक विध्वंसक कैसे नहीं होता है)। इस उपखंड में जो वास्तव में आश्चर्यजनक है वह वास्तव में 3 अवरोधों की एक सरल रैखिक पदानुक्रम के साथ सभी स्थितियों का प्रबंधन करने में सक्षम है ।
मथिउ एम।

हम्म, मैं शायद इस बिंदु को स्पष्ट रूप से समझ नहीं पाया ... मुझे लगा कि पहले मामले में (बी ऑब्जेक्ट को नष्ट करना), ए :: डी 1 के बजाय ए :: डी 1 को लागू किया जाएगा। और दूसरे मामले में भी (सी ऑब्जेक्ट को नष्ट करना), ए :: डी 1 को ए :: डी 2 के बजाय लागू किया जाएगा। क्या मै गलत हु?
Smg

A :: D1 को लागू नहीं किया गया है क्योंकि A यहां शीर्ष-स्तरीय वर्ग नहीं है; ए (जो हो सकता है या नहीं हो सकता है) के आभासी आधार वर्गों को नष्ट करने की जिम्मेदारी ए की नहीं है, बल्कि शीर्ष स्तर के वर्ग के डी 1 या डी 0 की है।
2

37

आमतौर पर कंस्ट्रक्टर ( न-इन-चार्ज / इन-चार्ज ) के दो वेरिएंट होते हैं और डिस्ट्रक्टर के तीन ( न-इन-चार्ज / इन-चार्ज / -इन-डिलीट )।

नहीं प्रभारी ctor और dtor उपयोग किया जाता है जब एक वर्ग का एक उद्देश्य है कि एक और वर्ग से विरासत में मिली का उपयोग कर से निपटने virtualकीवर्ड, जब वस्तु पूरा वस्तु नहीं है (ताकि मौजूदा वस्तु के निर्माण या destructing के "आरोप में नहीं" है आभासी आधार वस्तु)। यह ctor वर्चुअल बेस ऑब्जेक्ट के लिए एक पॉइंटर प्राप्त करता है और इसे स्टोर करता है।

प्रभारी ctor और dtors सभी अन्य मामलों में, यानी अगर कोई आभासी विरासत शामिल है के लिए कर रहे हैं; यदि कक्षा में एक वर्चुअल विध्वंसक होता है, तो प्रभारी डोर पॉइंटर को हटाने वाला वाइबेटर स्लॉट में चला जाता है, जबकि एक स्कोप जो ऑब्जेक्ट के डायनामिक प्रकार को जानता है (अर्थात स्वचालित या स्थैतिक भंडारण अवधि वाली वस्तुओं के लिए), इन-चार्ज डटोर का उपयोग करेगा (क्योंकि इस स्मृति को मुक्त नहीं किया जाना चाहिए)।

कोड उदाहरण:

struct foo {
    foo(int);
    virtual ~foo(void);
    int bar;
};

struct baz : virtual foo {
    baz(void);
    virtual ~baz(void);
};

struct quux : baz {
    quux(void);
    virtual ~quux(void);
};

foo::foo(int i) { bar = i; }
foo::~foo(void) { return; }

baz::baz(void) : foo(1) { return; }
baz::~baz(void) { return; }

quux::quux(void) : foo(2), baz() { return; }
quux::~quux(void) { return; }

baz b1;
std::auto_ptr<foo> b2(new baz);
quux q1;
std::auto_ptr<foo> q2(new quux);

परिणाम:

  • प्रत्येक vtables में dtor प्रविष्टि foo, bazऔर quuxसंबंधित प्रभारी को dtor को हटाने के लिए इंगित करता है।
  • b1और प्रभारीb2 द्वारा निर्मित होते हैं , जिसे baz() प्रभारी कहते foo(1) हैं
  • q1और इसका q2निर्माण quux() प्रभारी द्वारा किया जाता है, जो कि पूर्व में निर्मित वस्तु के लिए एक सूचक के साथ - साथ foo(2) प्रभारी और गिरता हैbaz() foo
  • q2~auto_ptr() प्रभारी द्वारा नष्ट कर दिया जाता है , जो वर्चुअल डोर ~quux() प्रभारी को हटाने को कहता है, जिसे ~baz() प्रभारी नहीं , ~foo() प्रभारी और कहते हैं operator delete
  • q1~quux() प्रभारी द्वारा नष्ट कर दिया जाता है , जिसे ~baz() प्रभारी और प्रभारी नहीं कहते ~foo() हैं
  • b2~auto_ptr() प्रभारी द्वारा नष्ट कर दिया जाता है , जो वर्चुअल डोर ~baz() प्रभारी को हटाने को कहता है, जिसे ~foo() प्रभारी कहते हैं औरoperator delete
  • b1~baz() प्रभारी द्वारा नष्ट कर दिया जाता है , जिसे प्रभारी कहते ~foo() हैं

इससे प्राप्त होने वाला कोई भी व्यक्ति quuxअपने प्रभारी नहीं ctor और dtor का उपयोग करेगा और fooवस्तु बनाने की जिम्मेदारी लेगा ।

सिद्धांत रूप में, नॉट -इन-चार्ज संस्करण की आवश्यकता उस वर्ग के लिए कभी नहीं होती है जिसके पास कोई आभासी आधार नहीं है; उस स्थिति में, प्रभारी संस्करण को कभी-कभी एकीकृत कहा जाता है , और / या प्रभारी और न- प्रभारी दोनों के प्रतीकों को एक ही कार्यान्वयन के लिए अलियास किया जाता है।


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

क्रिस्टल स्पष्ट स्पष्टीकरण के लिए धन्यवाद। मैं अधिक बात पर स्पष्टीकरण प्राप्त करना चाहता था, क्या होगा यदि हम ऑटो_प्रटर का उपयोग नहीं करते हैं और इसके बजाय कंस्ट्रक्टर में मेमोरी आवंटित करते हैं और विध्वंसक में हटाते हैं। उस स्थिति में हमारे पास केवल दो विध्वंसक नहीं होंगे प्रभारी / प्रभारी को हटाना?
नॉनऑन

1
@ भाविन, नहीं, सेटअप बिल्कुल वैसा ही रहता है। एक विध्वंसक के लिए उत्पन्न कोड हमेशा ऑब्जेक्ट को स्वयं और किसी भी उप-ऑब्जेक्ट को नष्ट कर देता है, इसलिए आपको deleteअभिव्यक्ति के लिए कोड या तो अपने स्वयं के विध्वंसक के हिस्से के रूप में मिलता है, या उप-ऑब्जेक्ट विध्वंसक कॉल के हिस्से के रूप में। deleteअगर यह एक आभासी नाशक (जहां हम पाते है अभिव्यक्ति वस्तु की vtable के माध्यम से एक फोन के रूप में या तो लागू किया गया है प्रभारी को हटाने , या वस्तु के लिए एक सीधा फोन के रूप में है के प्रभारी । नाशक
साइमन रिक्टर

एक deleteअभिव्यक्ति कभी - कभी प्रभारी संस्करण को नहीं बुलाती है , जिसका उपयोग केवल अन्य विध्वंसक द्वारा किया जाता है, जो आभासी विरासत का उपयोग करने वाली वस्तु को नष्ट कर देता है।
साइमन रिक्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.