2012-09-13 14 views
17

के पते को पार करके एक std :: unique_ptr प्रारंभ करना मैं एक क्लास बना रहा हूं जो कुछ विंडोज एपीआई कोड के साथ इंटरप्ट करता है, अब मुझे शुरू करने वाले पॉइंटर्स में से एक मूल कार्य को कॉल करके किया जाता है जो इसे प्रारंभ करता है।पॉइंटर

मेरे संकेत दिए गए, जो WinAPI Deleter समारोह प्रदान की कॉल एक कस्टम Deleter, साथ प्रकार std::unique_ptr के हैं लेकिन मैं & init-कार्य करने के लिए ऑपरेटर का पता-के साथ unique_ptr पारित नहीं कर सकते हैं। क्यूं कर?

मैं एक नमूना है कि मेरी समस्या को दर्शाता है बनाया है:

#include <memory> 

struct foo 
{ 
    int x; 
}; 

struct custom_deleter {}; 

void init_foo(foo** init) 
{ 
    *init = new foo(); 
} 

int main() 
{ 
    std::unique_ptr<foo, custom_deleter> foo_ptr; 

    init_foo(&foo_ptr); 
} 

संकलक छाल और कहते हैं:

source.cpp: In function 'int main()': 
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)' 

उत्तर

18

कहीं कवर के तहत, unique_ptr<foo> प्रकार foo* के एक डेटा सदस्य है।

हालांकि, कक्षा के उपयोगकर्ता के लिए यह डेटा सदस्य सीधे संशोधित करने के लिए वैध नहीं है। ऐसा करने से unique_ptr के वर्ग आविष्कारों को अनिवार्य रूप से संरक्षित नहीं किया जाएगा, विशेष रूप से यह पुराने सूचक मूल्य (यदि कोई हो) को मुक्त नहीं करेगा। आपके विशेष मामले में आपको ऐसा होने की आवश्यकता नहीं है, क्योंकि पिछला मान 0 है, लेकिन आम तौर पर ऐसा होना चाहिए।

इसी कारण से unique_ptr डेटा सदस्य तक पहुंच प्रदान नहीं करता है, केवल इसके मूल्य की एक प्रति (get() और operator-> के माध्यम से)। आपको unique_ptr में से foo** नहीं मिल सकता है।

इसके बजाय आप लिख सकते हैं:

foo *tmp; 
init_foo(&tmp); 
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp); 

यह अपवाद-सुरक्षित एक ही कारण है कि std::unique_ptr<foo, custom_deleter> foo_ptr(new foo()); अपवाद-सुरक्षित है के लिए है: unique_ptr गारंटी देता है कि जो कुछ भी आप अपने निर्माता करने के लिए पारित अंततः Deleter का उपयोग कर हटाया नहीं जाएगा।

बीटीडब्ल्यू, custom_deleteroperator()(foo*) की आवश्यकता नहीं है? या मुझसे कोई चीज चूक रही है?

+0

तो समाधान 'shared_ptr' या कच्चे संकेत के लिए वापस जाना है? –

+0

और कैसे प्राप्त करें? – ForEveR

+0

@ForEverR: इसके बारे में क्या? यह उस डेटा सदस्य का * मान * देता है, न कि उस डेटा सदस्य के सूचक। –

5

स्टीव ने पहले से ही समझाया है कि तकनीकी समस्या क्या है, हालांकि, अंतर्निहित समस्या बहुत गहरी हो जाती है: जब आप नग्न पॉइंटर्स से निपटते हैं तो कोड एक मुहावरे को उपयोगी बनाता है। यह कोड दो-चरणीय प्रारंभिकरण क्यों करता है (पहले ऑब्जेक्ट बनाएं, फिर इसे प्रारंभ करें) पहली जगह में? create_foo() को init_foo() का नाम बदलने और इसे वापस एक std::unique_ptr<foo> सीधे बेहतर होगा होने,

foo* init_foo() 
{ 
    return new foo(); 
} 

int main() 
{ 
    std::unique_ptr<foo, custom_deleter> foo_ptr(init_foo()); 

} 
बेशक

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

+0

देखकर कि मेरा 'init_foo' एक विंडोज एपीआई कॉल है, मैं इसे बदल नहीं सकता। तो क्या आप सुझाव देते हैं कि मैं उस समारोह के चारों ओर एक रैपर लिखूं? ऐसा क्यों? अपवाद सुरक्षा @SteveJessop प्रति समस्या नहीं है। –

+2

@ टोनी: एसबीआई का कोड सिर्फ "क्लीनर" है, जो 'मुख्य' में एक पंक्ति है जो यह कहता है। जिस चीज को आप वास्तव में चाहते हैं उसे बनाने के लिए मेरे कोड को एक अस्थायी चर की आवश्यकता है। यह बेहतर नहीं है, मेरा कोड सिर्फ 'init_foo' को कॉल करने के लिए करना है जैसा कि यह था। –

+0

@ स्टेवेजसेप: "... और इसे एक 'std :: unique_ptr ' सीधे वापस करना होगा। – sbi

0

आप निम्नलिखित चाल का उपयोग कर सकते हैं:

template<class T> 
class ptr_setter 
{ 
public: 
    ptr_setter(T& Ptr): m_Ptr{Ptr} {} 
    ~ptr_setter() { m_Ptr.reset(m_RawPtr); } 

    ptr_setter(const ptr_setter&) = delete; 
    ptr_setter& operator=(const ptr_setter&) = delete; 

    auto operator&() { return &m_RawPtr; } 

private: 
    T& m_Ptr; 
    typename T::pointer m_RawPtr{}; 
}; 


// Macro will not be needed with C++17 class template deduction. 
// If you dislike macros (as all normal people should) 
// it's possible to replace it with a helper function, 
// although this would make the code a little more complex. 

#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr) 

और उसके बाद:

std::unique_ptr<foo, custom_deleter> foo_ptr; 
init_foo(&ptr_setter(foo_ptr)); 
संबंधित मुद्दे