2010-03-12 13 views
15

मैं एक सी ++ शौकिया हूँ। मैं कुछ Win32 एपीआई कोड लिख रहा हूं और हैंडल और अजीब रूप से आवंटित ऑब्जेक्ट ऑब्जेक्ट्स हैं। तो मैं सोच रहा था - क्या कुछ रैपर वर्ग है जो संसाधन प्रबंधन को आसान बनाती है?सी ++ में किस रैपर क्लास को स्वचालित संसाधन प्रबंधन के लिए उपयोग करना चाहिए?

उदाहरण के लिए, जब मैं कुछ डेटा लोड करना चाहता हूं तो मैं CreateFile() के साथ एक फ़ाइल खोलता हूं और HANDLE प्राप्त करता हूं। जब मैं इसके साथ काम करता हूं, तो मुझे CloseHandle() पर कॉल करना चाहिए। लेकिन किसी भी उचित जटिल लोडिंग फ़ंक्शन के लिए अपवादों का उल्लेख न करने के लिए दर्जनों संभावित निकास बिंदु होंगे।

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

अवधारणा सरल है - लेकिन क्या मानक लाइब्रेरी में ऐसा कुछ है? मैं विजुअल स्टूडियो 2008 का उपयोग कर रहा हूं, और मैं बूस्ट या कुछ जैसे तृतीय पक्ष ढांचे को संलग्न नहीं करना चाहता हूं।

उत्तर

11

अपना खुद का लिखें। यह कोड की केवल कुछ पंक्तियां हैं। यह एक साधारण काम है कि यह के लायक नहीं है जो सामान्य पुन: प्रयोज्य संस्करण प्रदान करता है।

struct FileWrapper { 
    FileWrapper(...) : h(CreateFile(...)) {} 
    ~FileWrapper() { CloseHandle(h); } 

private: 
    HANDLE h; 
}; 

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

बेशक, सी ++ 0x लैम्बडा अभिव्यक्तियों के अतिरिक्त कुछ हद तक संतुलन को टिप सकता है। दो लैम्ब्डा अभिव्यक्तियों को आसानी से जेनेरिक रैपर वर्ग में पारित किया जा सकता है, इसलिए एक बार सी ++ 0 एक्स समर्थन के आसपास आता है, हम बूस्ट या कुछ में जोड़े गए ऐसे सामान्य RAII क्लास को देख सकते हैं।

लेकिन फिलहाल, जब भी आपको इसकी आवश्यकता हो, तो बस अपना खुद का रोल करना आसान है।

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

तुम क्या हैं संदर्भ गिनती की जरूरत है, बस boost::shared_ptr<FileWrapper> की तरह कुछ कार्य करें: एक shared_ptr में अपने कस्टम तदर्थ आरए II कक्षाएं लपेट दें।

+0

सबसे अच्छा विचार, आईएमएचओ। इन वर्गों को हैंडल गार्ड कहा जाता है ... – SadSido

+2

कोड की प्रतिलिपि बनाई जा सकती है क्योंकि कोड कॉपी किया जा सकता है। Http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization – Kerido

+2

@ केरिडो, शायद, शायद नहीं। यह आपके द्वारा लपेट रहे संसाधन के अर्थशास्त्र पर निर्भर करता है। मुझे लगता है संदेह का आधा लाभ देना उचित है और पोस्ट कोड मानना ​​एक साधारण उदाहरण उदाहरण है। –

0

एमएफसी के पास कुछ उपयुक्त प्राइमेटिव हैं (उदाहरण के लिए CFile देखें), लेकिन मानक लाइब्रेरी नहीं।

+0

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

+0

उदाहरण के लिए इसे देखें: http://bbdsoft.com/win32.html "CreateFile CloseHandle wrapper" क्वेरी के लिए पहला मिलान। – sharptooth

+0

कच्चे Win32 के साथ सभी कोड लिखने की तुलना में भी सीएफआईएल और चीजों को सरल बना देगा। – sharptooth

2

अनिवार्य रूप से, fstream फ़ाइल हैंडल के लिए एक अच्छा सी ++ रैपर है। यह मानक का हिस्सा है जिसका अर्थ यह पोर्टेबल, अच्छी तरह से परीक्षण किया गया है, और ऑब्जेक्ट उन्मुख तरीके से एक्स्टेंसिबल है। फ़ाइल संसाधनों के लिए, यह एक महान अवधारणा है।

हालांकि, fstream केवल, फ़ाइलों के लिए काम सामान्य हैंडल, यानी धागे, प्रक्रियाओं, तुल्यकालन वस्तुओं, स्मृति-मैप की गई फ़ाइलों, आदि

+0

मैंने केवल एक सामान्य आसान समझने वाले उदाहरण के रूप में फ़ाइल हैंडल का उपयोग किया। अभ्यास में चीजें हैं ... weirder। –

+0

तब आप किस हैंडल का मतलब रखते थे? – Kerido

+0

एसएसपीआई क्रेडहैंडल, सीटीक्स्टहैंडल और सेकबफर डीस्क जैसे हैंडल करता है। आखिरी वाला एक अजीब संरचना है जिसमें संरचनाओं की गतिशील रूप से आवंटित सरणी होती है जहां प्रत्येक संरचना में गतिशील आवंटित बफर के लिए सूचक होता है। संक्षेप में, यह परिवर्तनीय आकार के बफर का एक चर आकार का संग्रह है। रिलीजिंग फ़ंक्शन केवल "हटाएं" के रूप में छोटा नहीं है। :( –

0

दृश्य C++ 2008 फ़ीचर पैक के माध्यम से TR1 का समर्थन करता है, और TR1 shared_ptr शामिल नहीं । मैं इसका उपयोग करूंगा - यह एक बहुत ही शक्तिशाली स्मार्ट पॉइंटर वर्ग है और संसाधन प्रबंधन के प्रकार को करने के लिए सामान्यीकृत किया जा सकता है जिसे आप पूछ रहे हैं।

TR1 प्रभावी रूप से मानक के लिए एक विस्तार है। मेरा मानना ​​है कि यह अभी भी आधिकारिक तौर पर "पूर्व-मानक" है, लेकिन प्रभावी रूप से आप इसे लॉक कर सकते हैं।

+0

ध्यान दें कि 'shared_ptr' का उपयोग करना इसके लिए आपको कुछ मामलों में एक कस्टम डिलीटर फ़ंक्शन लिखना आवश्यक है। (साधारण मामलों में आप केवल 'क्लोजहैंडल' फ़ंक्शन को हटाए जाने के रूप में पास कर सकते हैं।) – celticminstrel

0

मुझे नहीं लगता कि मानक पुस्तकालय में कुछ भी नहीं है, और मैं भी संदेह नहीं है कि (साझा बढ़ावा में के रूप में संकेत) का उपयोग किया जा सकता है (क्योंकि वे संकेतक को हैंडल की उम्मीद करेंगे, हैंडल नहीं)।

scope guard idiom (और यदि आप चुनते हैं तो टेम्पलेट्स/फ़ंक्शन पॉइंटर्स इत्यादि का उपयोग करना) के बाद खुद को लिखना मुश्किल नहीं होना चाहिए।

0
template <typename Traits> 
class unique_handle 
{ 
    using pointer = typename Traits::pointer; 

    pointer m_value; 

    auto close() throw() -> void 
    { 
     if (*this) 
     { 
      Traits::close(m_value); 
     } 
    } 

public: 

    unique_handle(unique_handle const &) = delete; 
    auto operator=(unique_handle const &)->unique_handle & = delete; 

    explicit unique_handle(pointer value = Traits::invalid()) throw() : 
     m_value{ value } 
    { 
    } 

    unique_handle(unique_handle && other) throw() : 
     m_value{ other.release() } 
    { 
    } 

    auto operator=(unique_handle && other) throw() -> unique_handle & 
    { 
     if (this != &other) 
     { 
      reset(other.release()); 
     } 

     return *this; 
    } 

    ~unique_handle() throw() 
    { 
     close(); 
    } 

    explicit operator bool() const throw() 
    { 
     return m_value != Traits::invalid(); 
    } 

    auto get() const throw() -> pointer 
    { 
     return m_value; 
    } 

    auto get_address_of() throw() -> pointer * 
    { 
     ASSERT(!*this); 
     return &m_value; 
    } 

    auto release() throw() -> pointer 
    { 
     auto value = m_value; 
     m_value = Traits::invalid(); 
     return value; 
    } 

    auto reset(pointer value = Traits::invalid()) throw() -> bool 
    { 
     if (m_value != value) 
     { 
      close(); 
      m_value = value; 
     } 

     return static_cast<bool>(*this); 
    } 

    auto swap(unique_handle<Traits> & other) throw() -> void 
    { 
     std::swap(m_value, other.m_value); 
    } 
}; 

template <typename Traits> 
auto swap(unique_handle<Traits> & left, 
    unique_handle<Traits> & right) throw() -> void 
{ 
    left.swap(right); 
} 

template <typename Traits> 
auto operator==(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() == right.get(); 
} 

template <typename Traits> 
auto operator!=(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() != right.get(); 
} 

template <typename Traits> 
auto operator<(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() < right.get(); 
} 

template <typename Traits> 
auto operator>=(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() >= right.get(); 
} 

template <typename Traits> 
auto operator>(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() > right.get(); 
} 

template <typename Traits> 
auto operator<=(unique_handle<Traits> const & left, 
    unique_handle<Traits> const & right) throw() -> bool 
{ 
    return left.get() <= right.get(); 
} 

struct null_handle_traits 
{ 
    using pointer = HANDLE; 

    static auto invalid() throw() -> pointer 
    { 
     return nullptr; 
    } 

    static auto close(pointer value) throw() -> void 
    { 
     VERIFY(CloseHandle(value)); 
    } 
}; 

struct invalid_handle_traits 
{ 
    using pointer = HANDLE; 

    static auto invalid() throw() -> pointer 
    { 
     return INVALID_HANDLE_VALUE; 
    } 

    static auto close(pointer value) throw() -> void 
    { 
     VERIFY(CloseHandle(value)); 
    } 
}; 

using null_handle = unique_handle<null_handle_traits>; 
using invalid_handle = unique_handle<invalid_handle_traits>; 
+3

अपने उत्तर में कुछ विवरण जोड़ना सबसे अच्छा है। – GMchris

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