शेयर्ड_एप्ट्र <Derived> को शेयर्ड_प्ट्र <बेस> के रूप में पास करना


93

shared_ptrएक व्युत्पन्न प्रकार को किसी फ़ंक्शन के shared_ptrआधार पर पास करने के लिए जाने के लिए सबसे अच्छा तरीका क्या है जो एक आधार प्रकार लेता है ?

मैं आमतौर पर shared_ptrएक अनावश्यक प्रतिलिपि से बचने के लिए संदर्भ से गुजरता हूं :

int foo(const shared_ptr<bar>& ptr);

लेकिन यह काम नहीं करता है अगर मैं ऐसा कुछ करने की कोशिश करता हूं

int foo(const shared_ptr<Base>& ptr);

...

shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);

मैं प्रयोग कर सकता हूँ

foo(dynamic_pointer_cast<Base, Derived>(bar));

लेकिन यह दो कारणों से उप-इष्टतम लगता है:

  • एक dynamic_castसाधारण व्युत्पन्न-आधार कलाकारों के लिए थोड़ा अधिक लगता है।
  • जैसा कि मैं इसे समझता हूं, dynamic_pointer_castफ़ंक्शन को पास करने के लिए पॉइंटर की एक प्रति (एक अस्थायी एक) बनाता है।

क्या कोई बेहतर समाधान है?

पोस्टरिटी के लिए अपडेट:

यह एक लापता हेडर फ़ाइल का एक मुद्दा निकला। इसके अलावा, जो मैं यहां करने की कोशिश कर रहा था, उसे एक एंटीपैटर्न माना जाता है। आम तौर पर,

  • ऐसे कार्य जो किसी वस्तु के जीवनकाल को प्रभावित नहीं करते हैं (जैसे कि फ़ंक्शन की अवधि के लिए ऑब्जेक्ट वैध रहता है) को एक सादा संदर्भ या सूचक लेना चाहिए, जैसे int foo(bar& b)

  • किसी वस्तु का उपभोग करने वाले कार्य (अर्थात किसी दिए गए ऑब्जेक्ट के अंतिम उपयोगकर्ता) को एक unique_ptrमान लेना चाहिए , जैसे int foo(unique_ptr<bar> b)std::moveफ़ंक्शन में कॉलर्स का मान होना चाहिए ।

  • ऐसे कार्य जो किसी वस्तु के जीवनकाल का विस्तार करते हैं shared_ptr, उदाहरण के लिए मान लेना चाहिए int foo(shared_ptr<bar> b)परिपत्र संदर्भों से बचने की सामान्य सलाह लागू होती है।

विवरण के लिए हर्ब सटर के बैक टू बेसिक्स पर बात करें।


8
आप एक पास क्यों करना चाहते हैं shared_ptr? बार का कोई संदर्भ क्यों नहीं?
आईपीसी

2
dynamicडाउनकास्टिंग के लिए किसी भी कास्ट की जरूरत होती है। इसके अलावा, व्युत्पन्न सूचक को पास करना ठीक काम करना चाहिए। यह shared_ptrउसी रीफ़ोकाउंट (और इसे बढ़ाएगा) और आधार के लिए एक पॉइंटर के साथ एक नया निर्माण करेगा, जो तब कॉन्स्टेंट संदर्भ को बांधता है। चूंकि आप पहले से ही एक संदर्भ ले रहे हैं, हालांकि, मैं यह नहीं देखता कि आप आखिर क्यों लेना चाहते हैं shared_ptr। लो Base const&और बुला लो foo(*bar)
Xeo

@ Xeo: व्युत्पन्न पॉइंटर पास करना (यानी foo(bar)) कम से कम MSVC 2010 में काम नहीं करता है।
मैट क्लाइन

1
"स्पष्ट रूप से काम नहीं करता है" से आपका क्या मतलब है? कोड सही ढंग से संकलन और व्यवहार करता है; क्या आप पूछ रहे हैं shared_ptrकि फ़ंक्शन को पास करने के लिए एक अस्थायी बनाने से कैसे बचें ? मुझे पूरा यकीन है कि इससे बचने का कोई रास्ता नहीं है।
माइक सेमोर

1
@ सेठ: मैं असहमत हूं। मुझे लगता है कि एक साझा पॉइंटर को मान से पास करने का कारण है, और संदर्भ द्वारा एक साझा पॉइंटर को पारित करने का बहुत कम कारण है (और यह सब अनावश्यक प्रतियों की वकालत के बिना)। यहाँ तर्क करना stackoverflow.com/questions/10826541/…
आर। मार्टिनहो फर्नांडीस

जवाबों:


47

हालांकि Baseऔर Derivedकर रहे हैं covariant और उन्हें कच्चे संकेत दिए गए उसके अनुसार कार्य, होगा shared_ptr<Base>और shared_ptr<Derived>कर रहे हैं नहीं covariant। dynamic_pointer_castइस समस्या को संभालने के लिए सही और आसान तरीका है।

( संपादित करें: static_pointer_cast यह अधिक उचित होगा क्योंकि आप व्युत्पन्न से आधार पर कास्टिंग कर रहे हैं, जो सुरक्षित है और रनटाइम चेक की आवश्यकता नहीं है। नीचे टिप्पणी देखें।)

हालांकि, यदि आपका foo()कार्य जीवनकाल को विस्तारित करने में भाग लेने की इच्छा नहीं रखता (या, बल्कि, वस्तु के साझा स्वामित्व में भाग लेता है), तो इसे स्वीकार करने const Base&और shared_ptrपास करने के लिए इसे स्वीकार करने के लिए अपना सर्वश्रेष्ठ foo()

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);

एक तरफ के रूप में, क्योंकि shared_ptrप्रकार सहसंयोजक नहीं हो सकते हैं, कोवरियेंट रिटर्न प्रकारों में निहित रूपांतरणों के नियम वापसी के प्रकारों पर लागू नहीं होते हैं shared_ptr<T>


39
वे सहसंयोजक नहीं हैं, लेकिन shared_ptr<Derived>स्पष्ट रूप से परिवर्तनीय हैं shared_ptr<Base>, इसलिए कोड को बिना किसी ढलाईकार के साथ काम करना चाहिए।
माइक सीमौर

9
उम, shared_ptr<Ty>एक कंस्ट्रक्टर है जो एक लेता है shared_ptr<Other>और उचित रूप से रूपांतरण करता है, अगर Ty*यह स्पष्ट रूप से परिवर्तनीय है Other*। और अगर एक कलाकार की आवश्यकता है, static_pointer_castतो यहां उपयुक्त है, नहीं dynamic_pointer_cast
पीट बेकर

सच है, लेकिन उसके संदर्भ पैरामीटर के साथ नहीं, जैसा कि प्रश्न में है। उसे परवाह किए बिना एक प्रति करने की आवश्यकता होगी। लेकिन, यदि वह shared_ptrसंदर्भ संख्या से बचने के लिए रेफ का उपयोग कर रहा है , तो वास्तव shared_ptrमें पहली जगह में उपयोग करने का कोई अच्छा कारण नहीं है। इसके const Base&बजाय इसका उपयोग करना सबसे अच्छा है ।
ब्रेट कुहंस

@PeteBecker रूपांतरण निर्माता के बारे में माइक पर मेरी टिप्पणी देखें। मैं ईमानदारी से static_pointer_castधन्यवाद के बारे में नहीं जानता था ।
ब्रेट कुह्न

1
@TanveerBadar यकीन नहीं होता। शायद यह 2012 में संकलन करने में विफल रहा? (विशेष रूप से विजुअल स्टूडियो 2010 या 2012 का उपयोग करके)। लेकिन आप बिल्कुल सही हैं, ओपी कोड पूरी तरह से संकलित करना चाहिए यदि कंपाइलर को / publicly व्युत्पन्न / वर्ग की पूरी परिभाषा दिखाई दे रही है।
ब्रेट कुहंस

32

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

class Derived : Base
{
};

classटेम्पलेट मापदंडों के लिए है; structवर्गों को परिभाषित करने के लिए है। (यह अधिकतम 45% एक मजाक है।)
डेविस हेरिंग

इसे निश्चित रूप से समाधान माना जाना चाहिए, इसमें कलाकारों की कोई आवश्यकता नहीं है क्योंकि यह केवल सार्वजनिक रूप से गायब है।
एलेक्सिस पेक्स

12

लगता है कि आप बहुत कठिन प्रयास कर रहे हैं। shared_ptrकॉपी करने के लिए सस्ता है; यह इसका एक लक्ष्य है। संदर्भ के द्वारा उन्हें पास करना वास्तव में बहुत पूरा नहीं करता है। यदि आप साझा नहीं करना चाहते हैं, तो कच्चे सूचक को पास करें।

उस ने कहा, ऐसा करने के दो तरीके हैं जो मैं अपने सिर के ऊपर से सोच सकता हूं:

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));

9
नहीं, वे नकल करने के लिए सस्ते नहीं हैं, उन्हें जब भी संभव हो संदर्भ द्वारा पारित किया जाना चाहिए।
सेठ कार्नेगी

6
@SethCarnegie - क्या हर्ब ने यह देखने के लिए आपका कोड प्रोफाइल किया कि क्या मूल्य से गुजरना अड़चन था?
पीट बेकर

25
@SethCarnegie - मेरे द्वारा पूछे गए सवाल का जवाब नहीं है। और, इसके लायक क्या है, मैंने shared_ptrकार्यान्वयन को लिखा कि Microsoft जहाज।
पीट बेकर

6
@SethCarnegie - आपको हेयुरिस्टिक बैकवर्ड मिला है। हाथ का अनुकूलन आमतौर पर तब तक नहीं किया जाना चाहिए जब तक कि आप यह न दिखा सकें कि उनकी जरूरत है।
पीट बेकर

21
यदि आपको इस पर काम करने की आवश्यकता है तो यह केवल "समय से पहले" अनुकूलन है। मुझे अक्षम लोगों पर कुशल मुहावरों को अपनाने में कोई समस्या नहीं दिखाई देती है, चाहे वह किसी विशेष संदर्भ में फर्क करता हो या नहीं।
मार्क रैनसम

11

यह भी जांचें कि #includeहेडर फ़ाइल की व्युत्पन्न वर्ग की पूर्ण घोषणा के साथ आपके स्रोत फ़ाइल में है।

मुझे यह समस्या थी। के std::shared<derived>लिए नहीं डाली जाएगी std::shared<base>। मैंने दोनों वर्गों को घोषित कर दिया था ताकि मैं उनके लिए संकेत रख सकूं, लेकिन क्योंकि मेरे पास #includeकंपाइलर नहीं था कि वे देख नहीं सकते थे कि एक कक्षा दूसरे से ली गई थी।


1
वाह, मुझे इसकी उम्मीद नहीं थी लेकिन इसने मेरे लिए इसे ठीक कर दिया। मैं अतिरिक्त सावधानी बरत रहा था और केवल हेडर फ़ाइलों को शामिल कर रहा था जहाँ मुझे उनकी आवश्यकता थी इसलिए उनमें से कुछ केवल स्रोत फ़ाइलों में थे और आगे उन्हें हेडर में घोषित किया गया था जैसा आपने कहा था।
जिग्लीपफ

बेवकूफ संकलक बेवकूफ है। यह मेरा मुद्दा था। धन्यवाद!
तनवीर बदर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.