2010-06-22 11 views
7

बनाम मैं हाल ही में सी ++ जावा और रूबी से वापस आए, और मेरे आश्चर्य करने के लिए बहुत मुझे लगता है कि सार्वजनिक इंटरफ़ेस का उपयोग फ़ाइलों पुन: संयोजित करने के लिए जब मैं एक की विधि हस्ताक्षर बदलने के लिए है निजी विधि, क्योंकि निजी भाग भी .h फ़ाइल में हैं।रखने गुप्तांगों: शुद्ध आभासी आधार वर्ग pimpl

मैं जल्दी से एक समाधान है कि है, मुझे लगता है, एक जावा प्रोग्रामर के लिए विशिष्ट के साथ आया था: इंटरफेस (= शुद्ध आभासी आधार वर्ग)।

BananaTree.h:

class Banana; 

class BananaTree 
{ 
public: 
    virtual Banana* getBanana(std::string const& name) = 0; 

    static BananaTree* create(std::string const& name); 
}; 

BananaTree.cpp: उदाहरण के लिए

class BananaTreeImpl : public BananaTree 
{ 
private: 
    string name; 

    Banana* findBanana(string const& name) 
    { 
    return //obtain banana, somehow; 
    } 

public: 
    BananaTreeImpl(string name) 
    : name(name) 
    {} 

    virtual Banana* getBanana(string const& name) 
    { 
    return findBanana(name); 
    } 
}; 

BananaTree* BananaTree::create(string const& name) 
{ 
    return new BananaTreeImpl(name); 
} 

केवल परेशानी यहाँ, कि मैं new उपयोग नहीं कर सकते, और बदले BananaTree::create() कॉल करना होगा है। मुझे नहीं लगता कि यह वास्तव में एक समस्या है, खासकर जब से मैं कारखानों का उपयोग करने की उम्मीद करता हूं वैसे भी।

अब, सी ++ प्रसिद्धि के बुद्धिमान पुरुष, तथापि, एक और समाधान के साथ, pImpl idiom आया था।

BananaTree.h: कि के साथ, अगर मैं इसे सही ढंग से समझ, मेरे कोड कैसा दिखेगा

class BananaTree 
{ 
public: 
    Banana* addStep(std::string const& name); 

private: 
    struct Impl; 
    shared_ptr<Impl> pimpl_; 
}; 

BananaTree.cpp:

struct BananaTree::Impl 
{ 
    string name; 

    Banana* findBanana(string const& name) 
    { 
    return //obtain banana, somehow; 
    } 

    Banana* getBanana(string const& name) 
    { 
    return findBanana(name); 
    } 

    Impl(string const& name) : name(name) {} 
} 

BananaTree::BananaTree(string const& name) 
    : pimpl_(shared_ptr<Impl>(new Impl(name))) 
{} 

Banana* BananaTree::getBanana(string const& name) 
{ 
    return pimpl_->getBanana(name); 
} 

इसका मतलब यह होगा कि मैं एक डेकोरेटर को लागू करना इस मामले में getBanana, BananaTree की प्रत्येक सार्वजनिक विधि के लिए स्टाइल अग्रेषण विधि। यह जटिलता और रखरखाव प्रयास के एक अतिरिक्त स्तर की तरह लगता है जिसे मैं आवश्यकता नहीं करना पसंद करता हूं।

तो, अब प्रश्न के लिए: क्या शुद्ध आभासी कक्षा दृष्टिकोण के साथ गलत क्या है? पीआईएमपीएल दृष्टिकोण इतना बेहतर दस्तावेज क्यों है? क्या मैं कुछ भूल गया?

उत्तर

12

मैं कुछ अंतर के बारे में सोच सकते हैं:

आभासी आधार वर्ग आप अर्थ विज्ञान लोग अच्छी तरह से व्यवहार सी ++ वर्गों से उम्मीद से कुछ को तोड़ने के साथ

:

मैं उम्मीद होती है (या की आवश्यकता होती है, यहां तक ​​कि) वर्ग स्टैक पर instantiated हो, इस तरह:

BananaTree myTree("somename"); 

अन्यथा, मैं आरए II खो देते हैं, और मैं मैन्युअल ट्रैकिंग आवंटन शुरू करने के लिए है, जो सिर दर्द और मेमोरी लीक की एक बहुत कुछ करने के लिए सुराग की है।

मैं भी उस वर्ग की नकल करने की उम्मीद है, मैं तो बस इस

BananaTree tree2 = mytree; 

निश्चित रूप से जब तक कर सकते हैं, नकल प्रतिलिपि निर्माता निजी, जिस स्थिति में है कि रेखा भी संकलन नहीं होगा अंकन द्वारा अस्वीकृत है।

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

क्या आपके कोड का एक पाठक समझता है कि BananaTree मूल रूप से काम नहीं करता है, उसे इसके बजाय BananaTree* या BananaTree& का उपयोग करना होगा?

मूल रूप से, अपने इंटरफेस सिर्फ आधुनिक सी ++, जहां हम

  • से बचने के संकेत के रूप में ज्यादा संभव के रूप में पसंद करते हैं के साथ कि अच्छी तरह से नहीं चलता है, और
  • सभी वस्तुओं प्रपत्र स्वत: जीवन भर लाभ के लिए ढेर-आवंटन प्रबंधन।

वैसे, आपका वर्चुअल बेस क्लास आभासी विनाशक भूल गया। यह एक स्पष्ट बग है।

आखिरकार, बॉयलरप्लेट कोड की मात्रा को कम करने के लिए कभी-कभी पिंपल का एक सरल संस्करण आंतरिक ऑब्जेक्ट के डेटा सदस्यों को "बाहरी" ऑब्जेक्ट एक्सेस देना है, इसलिए आप इंटरफ़ेस को डुप्लिकेट करने से बचें। बाहरी ऑब्जेक्ट पर कोई भी फ़ंक्शन सीधे उस आंतरिक डेटा से आवश्यक डेटा तक पहुंचता है, या यह आंतरिक ऑब्जेक्ट पर एक सहायक फ़ंक्शन को कॉल करता है, जो बाहरी ऑब्जेक्ट पर बराबर नहीं है।

अपने उदाहरण में, आप समारोह और Impl::getBanana को दूर कर सकता है, और बदले BananaTree::getBanana इस तरह लागू:

तो आप केवल एक है getBanana समारोह (BananaTree कक्षा में) लागू करने के लिए, और एक findBanana समारोह (Impl कक्षा में)।

1

वास्तव में, यह सुनिश्चित करने के लिए सिर्फ एक डिजाइन निर्णय है। और यहां तक ​​कि यदि आप "गलत" निर्णय लेते हैं, तो स्विच करना मुश्किल नहीं है।

pimpl भी ढेर पर ligthweight वस्तुओं प्रदान करने के लिए या "प्रतियां" पेश करने के लिए एक ही कार्यान्वयन वस्तु को संदर्भित किया जाता है।
प्रतिनिधिमंडल-कार्यों एक परेशानी हो सकती है, लेकिन यह एक छोटी सी समस्या (सरल है, तो कोई वास्तविक जटिलता) है, विशेष रूप से सीमित वर्गों के साथ। सी में

इंटरफेस ++ रणनीति की तरह तरीके जहां कार्यान्वयन चयन करने में सक्षम होने की उम्मीद में आम तौर पर अधिक इस्तेमाल कर रहे हैं, हालांकि उस आवश्यकता नहीं है।

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