2012-09-17 10 views
12

मैं समझता हूं कि सार्वजनिक विरासत के साथ यह सामान्य रूप से सुरक्षित है, क्योंकि delete बेस क्लास पॉइंटर में संकलक केवल बेस क्लास के विनाशक को कॉल करने के लिए कोड उत्पन्न करता है, और व्युत्पन्न वर्ग के किसी को नहीं कहा जाता है।क्या यह गैर-वर्चुअल विनाशक वाले वर्ग से निजी रूप से उत्तराधिकारी है?

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

#include <iostream> 

struct BaseVirtual 
{ 
    virtual ~BaseVirtual() 
    { 
     std::cout << "BaseVirtual's dtor" << '\n'; 
    } 
}; 

struct BaseNonVirtual 
{ 
    ~BaseNonVirtual() 
    { 
     std::cout << "BaseNonVirtual's dtor" << '\n'; 
    } 
}; 

struct DerivedPrivVirtual: private BaseVirtual 
{ 
    static void f() 
    { 
     BaseVirtual * p = new DerivedPrivVirtual; 
     delete p; 
    } 

    ~DerivedPrivVirtual() 
    { 
     std::cout << "DerivedPrivVirtual's dtor" << '\n'; 
    } 
}; 

struct DerivedPrivNonVirtual: private BaseNonVirtual 
{ 
    static void f() 
    { 
     BaseNonVirtual * p = new DerivedPrivNonVirtual; 
     delete p; 
    } 

    ~DerivedPrivNonVirtual() 
    { 
     std::cout << "DerivedPrivNonVirtual's dtor" << '\n'; 
    } 
}; 

int main() 
{ 
    std::cout << "With explicit derived pointer type:" << '\n'; 
    { 
     DerivedPrivVirtual * derivedPrivVirtual = new DerivedPrivVirtual; 
     DerivedPrivNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual; 

     delete derivedPrivVirtual; 
     delete derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 

    std::cout << "With base pointer type:" << '\n'; 
    { 
     // Client code can't cast Derived to Base when inherit privately. 
     //BaseVirtual * derivedPrivVirtual = new DerivedPrivVirtual; 
     //BaseNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual; 

     //delete derivedPrivVirtual; 
     //delete derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 

    std::cout << "Inside derived class itself:" << '\n'; 
    { 
     DerivedPrivVirtual::f(); 
     DerivedPrivNonVirtual::f(); 
    } 
    std::cout << '\n'; 

    std::cout << "With non-dynamic variables:" << '\n'; 
    { 
     DerivedPrivVirtual derivedPrivVirtual; 
     DerivedPrivNonVirtual derivedPrivNonVirtual; 
    } 
    std::cout << '\n'; 
} 

दोनों जीसीसी 4.7.1 और बजना 3.1 एक ही आउटपुट दे:

मैं इस परीक्षण किए गए। व्युत्पन्न वर्ग कन्स्ट्रक्टर को तब छोड़ दिया जाता है जब व्युत्पन्न वर्ग स्वयं बेस क्लास के लिए व्युत्पन्न क्लास पॉइंटर और delete पर रखता है।

इस मामले के अलावा जो काफी असामान्य और आसानी से टालने योग्य लगता है (वर्ग का लेखक एकमात्र ऐसा व्यक्ति है जो नुकसान पहुंचा सकता है, लेकिन यह जानता है कि यह किस वर्ग से प्राप्त हुआ है), क्या मैं निष्कर्ष निकाल सकता हूं कि यह सुरक्षित है?

With explicit derived pointer type: 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 
DerivedPrivNonVirtual's dtor 
BaseNonVirtual's dtor 

With base pointer type: 

Inside derived class itself: 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 
BaseNonVirtual's dtor <-- Only a problem inside the class itself 

With non-dynamic variables: 
DerivedPrivNonVirtual's dtor 
BaseNonVirtual's dtor 
DerivedPrivVirtual's dtor 
BaseVirtual's dtor 

बोनस सवाल: संरक्षित विरासत के बारे में क्या? मुझे लगता है कि नुकसान करने की क्षमता अब सीधे वर्ग के लेखक से प्राप्त नहीं है, बल्कि पदानुक्रम में किसी भी वर्ग के लेखकों के लिए है।

+0

यदि मुझे सही याद है, स्कॉट मेयर्स (प्रभावी सी ++ के लेखक, अधिक प्रभावी सी ++) अभी भी यह भी नहीं जानते कि संरक्षित विरासत का क्या अर्थ है। सार्वजनिक एक "एक-एक" संबंध जहाज है, निजी "कार्यान्वित-इन-टर्म" है, लेकिन संरक्षित है? वह डरावना है। – Borgleader

उत्तर

9

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

डिजाइन में समस्या यह है कि आपके प्रश्न के अनुसार, BaseNonVirtual को विस्तारित करने के लिए डिज़ाइन नहीं किया गया है। यदि ऐसा होता है, तो यह या तो सार्वजनिक वर्चुअल विनाशक, या संरक्षित गैर-आभासी होना चाहिए, यह सुनिश्चित करना कि किसी भी कोड को आधार पर पॉइंटर के माध्यम से व्युत्पन्न ऑब्जेक्ट पर डिलीट करने में सक्षम नहीं होगा।

delete reinterpret_cast<BaseNonVirtual*>(new DerivedPrivNonVirtual);

~DerivedPrivNonVirtual() की इस प्रकार लंघन निष्पादन:

+0

किसी भी निर्माण का दुरुपयोग किया जा सकता है। क्या यह एक निजी डेटा सदस्य घोषित करना सुरक्षित है? अरे नहीं, आप गलती से इसे स्थापित कर सकते हैं जहां अपरिपक्व नहीं है! "सुरक्षित" का अर्थ है "दूसरों द्वारा दुरुपयोग के लिए खुला नहीं" और निजी विरासत एक निजी डेटा सदस्य के रूप में सुरक्षित है। –

+0

@ एनएम .: यदि आप सावधानीपूर्वक पहले अनुच्छेद को पढ़ते हैं तो आपको ध्यान रखना चाहिए कि यह उन मामलों का उल्लेख करता है जिनके तहत यह निर्माण विफल हो जाएगा: * यदि आपकी कक्षा [..] आपके प्रकार की वस्तु को ... * तक पास करता है। यह भी बताता है कि इस छेद का समाधान दूसरे पैराग्राफ में क्या है। निजी विरासत एक निजी डेटा सदस्य के रूप में सुरक्षित है जब तक आप सदस्यों को पॉइंटर्स नहीं देते हैं, लेकिन यह असामान्य है। दूसरी तरफ, विरासत को आधार पर पॉइंटर्स देने के लिए अक्सर उपयोग किया जाता है - विरासत (एबी) एक अलग डिज़ाइन समस्या है जो कार्यक्षमता प्राप्त करने के लिए उपयोग की जाती है। –

0

एक मामले में जहां ग्राहक कोड निजी वंशानुक्रम के बावजूद बेस व्युत्पन्न डाली सकता है।

लेकिन, reinterpret_cast का उपयोग कितना निराश है, आप निष्कर्ष निकाल सकते हैं कि यह आपके उद्देश्यों के लिए "पर्याप्त सुरक्षित" है।

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