2011-02-07 20 views
5

pimpl मुहावरा के साथ स्मार्ट संकेत का उपयोग करते समय के रूप में,Pimpl: अजीब अधूरा प्रकार मुद्दा

struct Foo 
{ 
private: 
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl; 
}; 

में स्पष्ट समस्या यह है कि Foo::Impl बिंदु पर अधूरा है वह जगह है जहाँ Foo का विनाशक उत्पन्न होता है।

संकलनकर्ता आमतौर पर एक चेतावनी वहाँ फेंकना, और boost::checked_delete, जो बूस्ट स्मार्ट संकेत द्वारा आंतरिक रूप से प्रयोग किया जाता है, स्थिर दावा वर्ग Foo::Impl पूरा हो गया है कि और एक त्रुटि से चलाता है अगर यह मामला नहीं है।

ऊपर के उदाहरण को संकलित करने के लिए, एक इसलिए

struct Foo 
{ 
    ~Foo(); 

private: 
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl; 
}; 

लिखने और कार्यान्वयन फ़ाइल, जहां Foo::Impl पूरा हो गया है में एक खाली Foo::~Foo को लागू करना चाहिए। यह नंगे पॉइंटर्स पर स्मार्ट पॉइंटर्स का एक लाभ है, क्योंकि हम विनाशक को लागू करने में विफल नहीं हो सकते हैं।

अभी तक, बहुत अच्छा है। लेकिन मैं एक अजीब व्यवहार जब मैं एक ऐसी ही Bar कक्षा में एक टेम्पलेट निर्माता को पेश करने की कोशिश में आए (पूर्ण कोड, यह अपने आप कोशिश करें):

// File Bar.h 
#ifndef BAR_H 
#define BAR_H 1 

#include <vector> 
#include <boost/scoped_ptr.hpp> 

struct Bar 
{ 
    template <typename I> 
    Bar(I begin, I end); 

    ~Bar(); 

private: 
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl; 

    void buildImpl(std::vector<double>&); 
}; 


template <typename I> 
Bar::Bar(I begin, I end) 
{ 
    std::vector<double> tmp(begin, end); 
    this->buildImpl(tmp); 
} 

#endif // BAR_H 

// File Bar.cpp 
#include "Bar.h" 

struct Bar::Impl 
{ 
    std::vector<double> v; 
}; 

void Bar::buildImpl(std::vector<double>& v) 
{ 
    pImpl.reset(new Impl); 
    pImpl->v.swap(v); 
} 

Bar::~Bar() {} 

// File Foo.h 
#ifndef FOO_H 
#define FOO_H 1 

#include <boost/scoped_ptr.hpp> 


struct Foo 
{ 
    Foo(); 
    ~Foo(); 

private: 
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl; 
}; 

#endif // FOO_H 

// File Foo.cpp 
#include "Foo.h" 

struct Foo::Impl 
{}; 


Foo::Foo() : pImpl(new Impl) 
{} 


Foo::~Foo() {} 


// File Main.cpp 
#include "Foo.h" 
#include "Bar.h" 

int main() 
{ 
    std::vector<double> v(42); 
    Foo f; 
    Bar b(v.begin(), v.end()); 
} 

जब दृश्य स्टूडियो 2005 SP1 के साथ इस उदाहरण संकलन, मैं एक मिल Foo साथ Bar नहीं बल्कि साथ त्रुटि:

1>Compiling... 
1>main.cpp 
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2027: use of undefined type 'Bar::Impl' 
1>  c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl' 
1>  c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(80) : see reference to function template instantiation 'void boost::checked_delete<T>(T *)' being compiled 
1>  with 
1>  [ 
1>   T=Bar::Impl 
1>  ] 
1>  c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(76) : while compiling class template member function 'boost::scoped_ptr<T>::~scoped_ptr(void)' 
1>  with 
1>  [ 
1>   T=Bar::Impl 
1>  ] 
1>  c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(16) : see reference to class template instantiation 'boost::scoped_ptr<T>' being compiled 
1>  with 
1>  [ 
1>   T=Bar::Impl 
1>  ] 
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2118: negative subscript 
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(34) : warning C4150: deletion of pointer to incomplete type 'Bar::Impl'; no destructor called 
1>  c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl' 

मैं जितनी जल्दी घर पहुंचने पर के रूप में हाल ही में एक जीसीसी के साथ इस कोशिश करेंगे।

मुझे समझ नहीं आता कि क्या हो रहा है: (। यानी Bar.cpp में) बिंदु जहां नाशक परिभाषित किया गया है पर, Bar::Impl की एक परिभाषा उपलब्ध है, तो कोई समस्या नहीं होनी चाहिए। यह Foo के साथ क्यों काम करता है और Bar के साथ नहीं?

मुझे यहां क्या याद आ रही है?

उत्तर

5

यह boost::shared_ptr<> का विनाशक है जिसके लिए ऑब्जेक्ट को boost::checked_deleter<> डिलीटर का उपयोग करते समय पूर्ण होने की आवश्यकता होती है। चूंकि आपने हेडर फ़ाइल में रेंज कन्स्ट्रक्टर Bar::Bar(I begin, I end) डाला है, इसलिए कंपाइलर को कोड उत्पन्न करना होगा जो आपके कन्स्ट्रक्टर फेंकने पर पहले से ही बनाए गए सदस्यों को नष्ट कर दे, इसलिए यह इस टेम्पलेट कन्स्ट्रक्टर को तत्काल करने पर boost::scoped_ptr<T>::~scoped_ptr(void) को तुरंत चालू करने का प्रयास कर रहा है।

यह पिंपल के साथ स्मार्ट पॉइंटर्स का उपयोग करने के लिए उपयोगी से कम है। चूंकि आपको सामान्य रूप से विनाशक को प्रदान करने की आवश्यकता होती है, इसलिए आप उस विनाशक में delete pimpl डाल सकते हैं और इसके साथ किया जा सकता है।

+0

ठीक है, मैं देखता हूं कि मैं क्या खो रहा था। प्रश्न आज एक वास्तविक दुनिया के मुद्दे का एक खिलौना सरलीकरण है जो मेरे साथ हुआ था। इस्तेमाल किया जाने वाला वास्तविक पॉइंटर क्लास 'boost :: shared_ptr' है, इस प्रकार मेरे पास यहां एक नंगे पॉइंटर नहीं हो सकता है। मैं कस्टम डिलीटर का उपयोग करके जांच करूंगा। धन्यवाद। –

+3

मैं उपयोगी से कम से असहमत हूं। ** क्योंकि 'scoped_ptr' के उपयोग के कारण ** संकलक द्वारा समस्या को हाइलाइट किया गया है यदि आप विनाशक को लिखना भूल जाते हैं, तो विनाशक शरीर भी बेहद सरल (खाली ...) है। दूसरी ओर, कच्चे सूचक का उपयोग करके आपको अधिसूचित नहीं किया जाएगा और कॉल करना होगा। साथ ही, 'shared_ptr' का उपयोग करके, एम्बेडेड डिलीटर के लिए धन्यवाद, वास्तव में आपको एक विनाशक लिखने के बोझ से राहत देता है। –

+0

@ मैथियू: यह स्वाद का विषय है, मैं सरल और शक्तिशाली कोड पसंद करता हूं, इसमें कोई अनावश्यक शामिल नहीं है। –

1

बूस्ट Boost documentation से:

ध्यान दें कि scoped_ptr की आवश्यकता है कि टी विनाश समय में एक पूरा प्रकार हो, लेकिन shared_ptr नहीं करता है।

shared_ptr पर स्विच करें और सभी को अच्छी तरह से होना चाहिए - या तो विनाशक (खाली या अन्यथा) रखने की आवश्यकता नहीं है। यदि आप कक्षा को अपरिवर्तनीय बनाना चाहते हैं तो इसमें सेमेन्टिक्स है जो आपको scoped_ptr से प्राप्त होगा, boost :: noncopyable से (निजी तौर पर) प्राप्त करें।

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