क्या सरणी के साथ unique_ptr के लिए कोई उपयोग है?


238

std::unique_ptr उदाहरण के लिए, सरणियों का समर्थन है:

std::unique_ptr<int[]> p(new int[10]);

लेकिन क्या इसकी जरूरत है? शायद यह उपयोग करने के लिए अधिक सुविधाजनक है std::vectorया std::array

क्या आपको उस निर्माण के लिए कोई उपयोग मिलता है?


6
पूर्णता के लिए, मुझे इंगित करना चाहिए कि कोई नहीं है std::shared_ptr<T[]>, लेकिन होना चाहिए, और शायद सी ++ 14 में होगा यदि किसी को प्रस्ताव लिखने के लिए परेशान किया जा सकता है। मतलब समय में, हमेशा होता है boost::shared_array
छद्म नाम

13
std::shared_ptr<T []> अब c ++ 17 में है।
陳 陳

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

जवाबों:


256

कुछ लोगों के पास std::vectorआवंटन के साथ भी उपयोग करने की लक्जरी नहीं है । कुछ लोगों को गतिशील रूप से आकार की सरणी की आवश्यकता होती है, इसलिए std::arrayयह बाहर है। और कुछ लोगों को अन्य कोड से उनके एरे मिलते हैं जो एक सरणी वापस करने के लिए जाना जाता है; और उस कोड को वापस करने के लिए फिर से लिखा नहीं जा रहा हैvector या कुछ है।

अनुमति देकर unique_ptr<T[]>, आप उन जरूरतों को पूरा करते हैं।

संक्षेप में, आप का उपयोग करें unique_ptr<T[]>जब आप की जरूरत है। जब विकल्प केवल आपके लिए काम करने वाले नहीं हैं। यह अंतिम उपाय का एक उपकरण है।


27
@NoSenseEtAl: मुझे यकीन नहीं है कि "कुछ लोगों को किस काम की अनुमति नहीं है" जो आपको हटा दें। कुछ परियोजनाओं में बहुत विशिष्ट आवश्यकताएं होती हैं, और उनमें से "आपको उपयोग करने के लिए नहीं मिलता है" हो सकता है vector। आप तर्क दे सकते हैं कि वे उचित आवश्यकताएं हैं या नहीं, लेकिन आप इससे इनकार नहीं कर सकते कि वे मौजूद हैं
निकोल बोल

21
दुनिया में कोई कारण नहीं है कि कोई उपयोग करने में सक्षम नहीं होगा std::vectorयदि वे उपयोग कर सकते हैं std::unique_ptr
माइल राउत

66
यहाँ वेक्टर का उपयोग नहीं करने का एक कारण है: sizeof (std :: वेक्टर <char>) == 24; sizeof (std :: unique_ptr <char []>) == 8
अरविद

13
@DanNissenbaum ये परियोजनाएँ मौजूद हैं। कुछ उद्योग जो बहुत कठिन जांच के तहत होते हैं, उदाहरण के लिए विमानन या रक्षा, मानक पुस्तकालय ऑफ-लिमिट है क्योंकि यह सत्यापित करना और साबित करना मुश्किल है कि जो भी निकाय नियमों को निर्धारित करता है वह सही है। आप यह तर्क दे सकते हैं कि मानक पुस्तकालय का अच्छी तरह से परीक्षण किया गया है और मैं आपसे सहमत होगा लेकिन आप और मैं नियम नहीं बनाते हैं।
एमिली एल।

16
@DanNissenbaum इसके अलावा कुछ कठिन रीयल-टाइम सिस्टम को डायनेमिक मेमोरी आवंटन का उपयोग करने की अनुमति नहीं है क्योंकि सिस्टम कॉल कारणों में देरी सैद्धांतिक रूप से बाध्य नहीं हो सकती है और आप प्रोग्राम के वास्तविक समय के व्यवहार को साबित नहीं कर सकते हैं। या बाउंड बहुत बड़ा हो सकता है जो आपकी WCET सीमा को तोड़ता है। हालांकि यहां लागू नहीं है, क्योंकि वे unique_ptrया तो उपयोग नहीं करेंगे, लेकिन उन प्रकार की परियोजनाएं वास्तव में मौजूद हैं।
एमिली एल।

124

ट्रेडऑफ़ हैं, और आप उस समाधान को चुनते हैं जो आप चाहते हैं। मेरे सर के ऊपर से चला गया:

प्रारम्भिक आकार

  • vectorऔर unique_ptr<T[]>आकार को रन-टाइम पर निर्दिष्ट करने की अनुमति दें
  • array केवल संकलन समय पर आकार निर्दिष्ट करने की अनुमति देता है

पुन: आकार देने

  • arrayऔर unique_ptr<T[]>आकार बदलने की अनुमति न दें
  • vector कर देता है

भंडारण

  • vectorऔर unique_ptr<T[]>वस्तु के बाहर डेटा स्टोर करें (आमतौर पर ढेर पर)
  • array डेटा को सीधे ऑब्जेक्ट में स्टोर करता है

प्रतिलिपि बनाई जा रही

  • arrayऔर vectorनकल की अनुमति दें
  • unique_ptr<T[]> नकल की अनुमति नहीं देता है

स्वैप / स्थानांतरित

  • vectorऔर unique_ptr<T[]>O (1) समय swapऔर चालन कार्य हैं
  • arrayO (n) समय swapऔर चालन चालन है, जहाँ n सरणी में तत्वों की संख्या है

सूचक / संदर्भ / पुनरावृत्ति अमान्य

  • array संकेत, संदर्भ और पुनरावृत्तियों को कभी भी अमान्य नहीं किया जाएगा, जबकि वस्तु जीवित है, तब भी swap()
  • unique_ptr<T[]>कोई पुनरावृत्तियों नहीं है; पॉइंटर्स और रेफरेंस केवल swap()ऑब्जेक्ट द्वारा लाइव होने पर अमान्य हैं। (स्वैप करने के बाद, पॉइंटर्स उस सरणी में इंगित करते हैं जिसे आपने स्वैप किया था, इसलिए वे अभी भी उस अर्थ में "मान्य" हैं)
  • vector किसी भी पुनर्स्थापन पर बिंदुओं, संदर्भों और पुनरावृत्तियों को अमान्य कर सकता है (और कुछ गारंटी देता है कि वास्तविककरण केवल कुछ कार्यों पर ही हो सकता है)।

अवधारणाओं और एल्गोरिदम के साथ संगतता

  • arrayऔर vectorदोनों कंटेनर हैं
  • unique_ptr<T[]> एक कंटेनर नहीं है

मुझे स्वीकार करना होगा, यह नीति-आधारित डिजाइन के साथ कुछ रिफैक्टिंग के लिए एक अवसर की तरह दिखता है।


1
मुझे यकीन नहीं है कि मैं समझता हूं कि आपको सूचक अमान्यता के संदर्भ में क्या मतलब है । क्या यह ऑब्जेक्ट्स के लिए पॉइंटर्स के बारे में है, या तत्वों को इंगित करता है? या कुछ और? आपको एक वेक्टर से किस प्रकार की गारंटी नहीं मिलती है?
जोगोजापान

3
मान लीजिए कि आपके पास इट्रेटर, पॉइंटर, या ए के तत्व का संदर्भ है vector। फिर आप आकार या क्षमता को इस vectorतरह बढ़ाते हैं कि यह एक वास्तविक स्थिति को मजबूर करता है। फिर वह पुनरावृत्ति, सूचक या संदर्भ अब उस तत्व को इंगित नहीं करता है vector। यह हम "अमान्य" से मतलब है। यह समस्या नहीं होती है array, क्योंकि कोई "वास्तविककरण" नहीं है। वास्तव में, मैंने बस उसी के साथ एक विवरण देखा, और मैंने इसे सूट करने के लिए संपादित किया है।
छद्म नाम

1
ठीक है, किसी व्यूह में वास्तविककरण के परिणामस्वरूप अमान्य नहीं किया जा सकता है या unique_ptr<T[]>क्योंकि कोई वास्तविक स्थान नहीं है। लेकिन निश्चित रूप से, जब सरणी दायरे से बाहर हो जाती है, तो विशिष्ट तत्वों के संकेत अभी भी अमान्य होंगे।
जोगोजपन

हां, यदि वस्तु अब नहीं रहती है तो सभी दांव बंद हो जाते हैं।
छद्म नाम

1
@rubenvb निश्चित रूप से आप कर सकते हैं, लेकिन आप (लोप) सीधे लूप के लिए रेंज-आधारित का उपयोग नहीं कर सकते हैं। संयोग से, एक सामान्य के विपरीत T[], आकार (या समतुल्य जानकारी) operator delete[]सरणी के तत्वों को सही ढंग से नष्ट करने के लिए कहीं न कहीं चारों ओर लटका होना चाहिए । यह अच्छा होगा यदि प्रोग्रामर की पहुंच उस तक हो।
छद्म नाम

73

एक कारण यह है कि आप उपयोग कर सकते हैं unique_ptrयदि आप मूल्य को शुरू करने के क्रम का भुगतान नहीं करना चाहते हैं तो सरणी को शुरू करना।

std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars

std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars

std::vectorनिर्माता और std::vector::resize()मूल्य-प्रारंभ हो जाएगा T- लेकिन newयह है कि अगर काम नहीं चलेगा Tएक पॉड है।

C ++ 11 और std :: वेक्टर कंस्ट्रक्टर में मूल्य-प्रारंभिक वस्तुएँ देखें

ध्यान दें कि vector::reserveयहां कोई विकल्प नहीं है: क्या std :: वेक्टर :: रिजर्व सुरक्षित होने के बाद कच्चे सूचक को एक्सेस करना है?

यह इसी कारण एक सी प्रोग्रामर चुन सकते है mallocअधिक calloc



@Ruslan लिंक किए गए समाधान में गतिशील सरणी के तत्व अभी भी मूल्य-आरंभिक हैं, लेकिन मूल्य आरंभीकरण कुछ भी नहीं करता है। मैं इस बात से सहमत होता हूं कि एक आशावादी व्यक्ति यह महसूस करने में विफल रहता है कि 1000000 बार कुछ भी नहीं करने से कोई कोड लागू नहीं किया जा सकता है, लेकिन यह एक अनुकूलन के लिए निर्भर नहीं है।
मार्क वैन लीउवेन

अभी तक एक और संभावना std::vectorएक कस्टम आवंटनकर्ता को प्रदान करने की है जो उन प्रकारों के निर्माण से बचती है जो हैं std::is_trivially_default_constructibleऔर वस्तुओं का विनाश जो हैं std::is_trivially_destructible, हालांकि कड़ाई से यह सी ++ मानक का उल्लंघन करता है (क्योंकि इस प्रकार के डिफ़ॉल्ट आरंभिक नहीं हैं)।
वाल्टर

इसके अलावा std::unique_ptrबहुत से std::vectorकार्यान्वयनों के विपरीत कोई बाध्य जाँच प्रदान नहीं करता है ।
डायपर

@apapir यह कार्यान्वयन के बारे में नहीं है: std::vectorमानक द्वारा सीमा की जाँच करने के लिए आवश्यक है .at()। मुझे लगता है कि आप का मतलब है कि कुछ कार्यान्वयन में डिबग मोड हैं जो .operator[]बहुत अधिक जांच करेंगे , लेकिन मेरा मानना ​​है कि अच्छा, पोर्टेबल कोड लिखने के लिए बेकार है।
अंडरस्कोर_ड

30

एक std::vector, चारों ओर कॉपी किया जा सकता है, जबकि unique_ptr<int[]>सरणी के अद्वितीय स्वामित्व व्यक्त अनुमति देता है। std::arrayदूसरी ओर, संकलन-समय पर आकार निर्धारित करने की आवश्यकता होती है, जो कुछ स्थितियों में असंभव हो सकता है।


2
सिर्फ इसलिए कि कुछ के आसपास नकल किया जा सकता है इसका मतलब यह नहीं है कि यह होना चाहिए।
निकोल बोलस

4
@ निकोलबोलस: मुझे समझ नहीं आता। एक को रोकने के लिए एक ही कारण है कि एक के unique_ptrबजाय का उपयोग क्यों करना चाहते हो सकता है shared_ptr। क्या मैं कुछ भूल रहा हूँ?
एंडी प्रोल

4
unique_ptrकेवल आकस्मिक दुरुपयोग को रोकने से अधिक है। यह भी तुलना में छोटा और कम ओवरहेड है shared_ptr। बिंदु यह है कि एक वर्ग में शब्दार्थ होने के बावजूद "दुरुपयोग" को रोकना अच्छा है, केवल एक विशेष प्रकार का उपयोग करने का एकमात्र कारण नहीं है। और vectorसरणी भंडारण की तुलना में कहीं अधिक उपयोगी है unique_ptr<T[]>, अगर इस तथ्य के अलावा कोई कारण नहीं है कि इसका आकार है
निकोल बोलस

3
मुझे लगा कि मैंने बात को स्पष्ट कर दिया है: एक विशेष प्रकार का उपयोग करने के अन्य कारण हैं । बस वहाँ कारणों पसंद कर रहे हैं की तरह vectorखत्म हो गया unique_ptr<T[]>जहां संभव हो, के बजाय सिर्फ कहने का, "आप इसे कॉपी नहीं कर सकते" और इसलिए लेने unique_ptr<T[]>के लिए जब आप प्रतियां नहीं चाहते हैं। किसी को गलत काम करने से रोकना जरूरी नहीं कि कक्षा लेने का सबसे महत्वपूर्ण कारण है।
निकोल बोलस

8
std::vectorएक से अधिक ओवरहेड है std::unique_ptr- यह ~ 1 के बजाय ~ 3 पॉइंटर्स का उपयोग करता है। std::unique_ptrनिर्माण की प्रतिलिपि बनाता है, लेकिन निर्माण को सक्षम बनाता है, जो कि यदि आप जिस डेटा के साथ काम कर रहे हैं, उसे केवल स्थानांतरित किया जा सकता है, लेकिन कॉपी नहीं किया गया है, classतो डेटा को संक्रमित करता है । डेटा पर एक ऑपरेशन होने जो वास्तव में वैध नहीं है , आपके कंटेनर वर्ग को बदतर बनाता है, और "बस इसका उपयोग न करें" सभी पापों को धोता नहीं है। अपने हर उदाहरण std::vectorको एक ऐसी कक्षा में रखना जहाँ आप स्वयं को अक्षम करते हैं move, सिरदर्द है। std::unique_ptr<std::array>ए है size
यक्क - एडम नेवरामोंट

22

स्कॉट मेयर्स के पास प्रभावी आधुनिक C ++ में कहने के लिए यह है

के अस्तित्व std::unique_ptrके लिए सरणियों, आप के लिए एक ही बौद्धिक रुचि का हो जाना चाहिए, क्योंकि std::array, std::vector, std::stringकच्चे सरणियों की तुलना में बेहतर डेटा संरचना विकल्प हैं लगभग हमेशा। केवल उसी स्थिति के बारे में जब मैं गर्भधारण कर सकता हूं जब कोई std::unique_ptr<T[]>मतलब होगा कि आप सी-जैसे एपीआई का उपयोग कर रहे हैं जो एक कच्चे सूचक को ढेर सरणी में लौटाता है जिसे आप स्वामित्व मानते हैं।

मुझे लगता है कि चार्ल्स साल्विया का जवाब हालांकि प्रासंगिक है: यह std::unique_ptr<T[]>एक खाली सरणी को इनिशियलाइज़ करने का एकमात्र तरीका है जिसका आकार संकलन समय पर ज्ञात नहीं है। उपयोग करने के लिए इस प्रेरणा के बारे में स्कॉट मेयर्स का क्या कहना होगा std::unique_ptr<T[]>?


4
ऐसा लगता है कि उन्होंने बस कुछ उपयोग के मामलों की कल्पना नहीं की है, अर्थात् एक बफर जिसका आकार निर्धारित है, लेकिन संकलन समय पर अज्ञात है, और / या एक बफर जिसके लिए हम प्रतियों की अनुमति नहीं देते हैं। वहाँ भी इसे करने के लिए पसंद करते हैं करने के लिए एक संभावित कारण के रूप में दक्षता है vector stackoverflow.com/a/24852984/2436175
एंटोनियो

17

के विपरीत , std::vectorऔर std::array, std::unique_ptrएक पूर्ण सूचक का मालिक हो सकता है।
सी एपीआई के साथ काम करते समय यह काम आता है जो किसी सरणी या NULL की अपेक्षा करता है:

void legacy_func(const int *array_or_null);

void some_func() {    
    std::unique_ptr<int[]> ptr;
    if (some_condition) {
        ptr.reset(new int[10]);
    }

    legacy_func(ptr.get());
}

10

मैंने unique_ptr<char[]>एक गेम इंजन में उपयोग किए जाने वाले एक प्रचारित स्मृति पूल को लागू करने के लिए उपयोग किया है। यह विचार प्रत्येक फ्रेम में आवंटित / मुक्त स्मृति को आवंटित किए बिना टकराव अनुरोध परिणामों और कण भौतिकी जैसे अन्य सामान को वापस करने के लिए गतिशील आवंटन के बजाय इस्तेमाल किए गए उपदेशित मेमोरी पूल प्रदान करना है। इस तरह के परिदृश्यों के लिए यह बहुत सुविधाजनक है, जहां आपको सीमित जीवन समय (आमतौर पर एक, 2 या 3 फ़्रेम) के साथ ऑब्जेक्ट्स को आवंटित करने के लिए मेमोरी पूल की आवश्यकता होती है, जिसमें विनाश तर्क (केवल मेमोरी डीलक्लेशन) की आवश्यकता नहीं होती है।


9

कुछ विंडोज Win32 API कॉल्स में एक सामान्य पैटर्न पाया जा सकता है , जिसमें उपयोग std::unique_ptr<T[]>में काम आ सकता है, उदाहरण के लिए, जब आप वास्तव में नहीं जानते कि कुछ Win32 API को कॉल करते समय आउटपुट बफर कितना बड़ा होना चाहिए (जो कुछ डेटा को अंदर लिख देगा। वह बफ़र):

// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;

// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;

LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
    // Allocate buffer of specified length
    buffer.reset( BYTE[bufferLength] );
    //        
    // Or, in C++14, could use make_unique() instead, e.g.
    //
    // buffer = std::make_unique<BYTE[]>(bufferLength);
    //

    //
    // Call some Win32 API.
    //
    // If the size of the buffer (stored in 'bufferLength') is not big enough,
    // the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
    // in the [in, out] parameter 'bufferLength'.
    // In that case, there will be another try in the next loop iteration
    // (with the allocation of a bigger buffer).
    //
    // Else, we'll exit the while loop body, and there will be either a failure
    // different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
    // and the required information will be available in the buffer.
    //
    returnCode = ::SomeApiCall(inParam1, inParam2, inParam3, 
                               &bufferLength, // size of output buffer
                               buffer.get(),  // output buffer pointer
                               &outParam1, &outParam2);
}

if (Failed(returnCode))
{
    // Handle failure, or throw exception, etc.
    ...
}

// All right!
// Do some processing with the returned information...
...

आप std::vector<char>इन मामलों में उपयोग कर सकते हैं।
आर्थर टक्का

@ArthurTacca - ... यदि आपको अपने बफर में प्रत्येक वर्ण को 0 एक-एक करके संकलित करने में कोई आपत्ति नहीं है।
TED

9

मुझे एक ऐसे मामले का सामना करना पड़ा जहां मुझे इस्तेमाल करना था std::unique_ptr<bool[]> , जो एचडीएफ 5 लाइब्रेरी में था (कुशल बाइनरी डेटा स्टोरेज के लिए एक पुस्तकालय, विज्ञान में बहुत उपयोग किया गया)। कुछ संकलक (मेरे मामले में विजुअल स्टूडियो 2015) (प्रत्येक बाइट में 8 बूल का उपयोग करके ) का संपीड़न प्रदान करते हैंstd::vector<bool> , जो कि एचडीएफ 5 जैसी किसी चीज के लिए एक तबाही है, जो उस संपीड़न की परवाह नहीं करता है। साथ std::vector<bool>, HDF5 अंततः कि संपीड़न की वजह से कचरा पढ़ रहा था।

लगता है कि बचाव के लिए कौन था, ऐसे मामले में जहां std::vectorकाम नहीं किया गया था, और मुझे सफाई से एक गतिशील सरणी आवंटित करने की आवश्यकता थी? :-)


9

संक्षेप में: यह अब तक का सबसे अधिक स्मृति-कुशल है।

A std::stringएक पॉइंटर, एक लंबाई और एक "शॉर्ट-स्ट्रिंग-ऑप्टिमाइज़ेशन" बफर के साथ आता है। लेकिन मेरी स्थिति यह है कि मुझे एक स्ट्रिंग को स्टोर करने की आवश्यकता है जो लगभग हमेशा खाली है, एक संरचना में जिसमें मेरे पास सैकड़ों हजारों हैं। सी में, मैं बस उपयोग करूंगा char *, और यह ज्यादातर समय अशक्त रहेगा। जो C ++ के लिए भी काम करता है, सिवाय इसके कि char *कोई डिस्ट्रक्टर नहीं है, और खुद को हटाना नहीं जानता। इसके विपरीत, std::unique_ptr<char[]>जब यह कार्यक्षेत्र से बाहर चला जाता है तो एक विल खुद को हटा देगा। एक खाली std::string32 बाइट्स लेता है, लेकिन एक खाली std::unique_ptr<char[]>8 बाइट्स लेता है, यानी इसके पॉइंटर का आकार।

सबसे बड़ा नकारात्मक पक्ष यह है कि हर बार जब मैं स्ट्रिंग की लंबाई जानना चाहता हूं, मुझे उस पर कॉल strlenकरना होगा।


3

आपको सोचने वाले लोगों को जवाब देने के लिए " मुझे vectorइसके बजाय उपयोग करना होगा" unique_ptrमेरे पास GPU पर CUDA प्रोग्रामिंग में एक मामला है जब आप डिवाइस में मेमोरी आवंटित करते हैं तो आपको एक पॉइंटर सरणी (के साथ cudaMalloc) के लिए जाना चाहिए । फिर, होस्ट में इस डेटा को पुनर्प्राप्त करते समय, आपको एक पॉइंटर के लिए फिर से जाना चाहिए और unique_ptrपॉइंटर को आसानी से संभालना ठीक है। को परिवर्तित double*करने की अतिरिक्त लागत vector<double>अनावश्यक है और पूर्ण की हानि की ओर जाता है।


3

अनुमति देने और उपयोग करने का एक अतिरिक्त कारण std::unique_ptr<T[]> , जो अब तक की प्रतिक्रियाओं में नहीं बताया गया है: यह आपको सरणी तत्व प्रकार को अग्रेषित करने की अनुमति देता है।

यह तब उपयोगी होता है जब आप जंजीरों को कम से कम करना चाहते हैं #include शीर्ष लेखों में हैं (बिल्ड प्रदर्शन का अनुकूलन करने के लिए।)

उदाहरण के लिए -

myclass.h:

class ALargeAndComplicatedClassWithLotsOfDependencies;

class MyClass {
   ...
private:
   std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};

myclass.cpp:

#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"

// MyClass implementation goes here

उपरोक्त कोड संरचना के साथ, किसी को भी आंतरिक कार्यान्वयन पर निर्भरता को शामिल करने के बिना #include "myclass.h"उपयोग और उपयोग कर सकते MyClassहैं MyClass::m_InternalArray

यदि m_InternalArrayइसके बजाय क्रमशः std::array<ALargeAndComplicatedClassWithLotsOfDependencies>, या ए के रूप में घोषित किया गया था std::vector<...>- परिणाम को अपूर्ण प्रकार का उपयोग करने का प्रयास किया जाएगा, जो एक संकलन-समय की त्रुटि है।


इस विशेष उपयोग के मामले के लिए, मैं निर्भरता को तोड़ने के लिए पिंपल पैटर्न का विकल्प चुनूंगा - यदि यह केवल निजी तौर पर उपयोग किया जाता है, तो परिभाषा तब तक स्थगित की जा सकती है जब तक कि क्लास के तरीकों को लागू नहीं किया जाता है; यदि यह सार्वजनिक रूप से उपयोग किया जाता है, तो कक्षा के उपयोगकर्ताओं को पहले से ही इसके बारे में ठोस ज्ञान होना चाहिए था class ALargeAndComplicatedClassWithLotsOfDependencies। तो तार्किक रूप से आपको ऐसे परिदृश्यों में नहीं चलना चाहिए।

3

मैं स्वीकार किए गए उत्तर की भावना के साथ दृढ़ता से पर्याप्त असहमत नहीं हो सकता। "अंतिम उपाय का एक उपकरण"? इससे दूर!

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

इसलिए जब किसी को एक सरणी की आवश्यकता होती है, तो निम्नलिखित प्रश्नों के उत्तर उसके व्यवहार को निर्दिष्ट करते हैं: 1. इसका आकार a) रनटाइम पर गतिशील है, या b) स्टेटिक है, लेकिन केवल रनटाइम में जाना जाता है, या c) स्थिर और संकलन समय पर जाना जाता है? 2. सरणी को स्टैक पर आवंटित किया जा सकता है या नहीं?

और उत्तरों के आधार पर, यह वह है जो मैं इस तरह के एक सरणी के लिए सबसे अच्छी डेटा संरचना के रूप में देखता हूं:

       Dynamic     |   Runtime static   |         Static
Stack std::vector      unique_ptr<T[]>          std::array
Heap  std::vector      unique_ptr<T[]>     unique_ptr<std::array>

हां, मुझे लगता है unique_ptr<std::array> इस पर भी विचार किया जाना चाहिए, और न ही अंतिम उपाय का एक उपकरण है। जरा सोचें कि आपके एल्गोरिथ्म में सबसे अच्छा क्या है।

ये सभी कच्चे पॉइंटर के माध्यम से डेटा सरणी ( vector.data()/ array.data()/ uniquePtr.get()) के लिए सादे सी एपीआई के साथ संगत हैं ।

PS उपरोक्त विचारों के अलावा, स्वामित्व में से एक भी है: std::arrayऔर std::vectorइसमें मूल्य शब्दार्थ है (मूल्य के आधार पर प्रतिलिपि बनाने और पास करने के लिए देशी समर्थन है), जबकि unique_ptr<T[]>केवल स्थानांतरित किया जा सकता है (एकल स्वामित्व लागू करता है)। या तो विभिन्न परिदृश्यों में उपयोगी हो सकता है। इसके विपरीत, सादे स्थिर सरणियाँ ( int[N]) और सादे गतिशील सरणियाँ ( new int[10]) न तो प्रदान करती हैं और इस प्रकार यदि संभव हो तो बचा जाना चाहिए - जो कि अधिकांश मामलों में संभव होना चाहिए। यदि यह पर्याप्त नहीं था, तो प्लेन डायनेमिक सरणियाँ भी उनके आकार को क्वेरी करने का कोई तरीका नहीं देती हैं - स्मृति भ्रष्टाचार और सुरक्षा छेद के लिए अतिरिक्त अवसर।


2

जब आप केवल मौजूदा एपीआई (थिंक विंडो मैसेज या थ्रेडिंग से संबंधित कॉलबैक पैरामीटर) के माध्यम से एक पॉइंटर को प्राप्त करने के लिए संभव हो तो वे सबसे सही उत्तर हो सकते हैं, जो हैच के दूसरी तरफ "पकड़े जाने" के बाद जीवनकाल के कुछ मापक होते हैं, लेकिन जो कॉलिंग कोड से संबंधित नहीं है:

unique_ptr<byte[]> data = get_some_data();

threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
                      data.release());

हम सभी चाहते हैं कि चीजें हमारे लिए अच्छी हों। C ++ दूसरी बार के लिए है।


2

unique_ptr<char[]>का उपयोग किया जा सकता है जहाँ आप C का प्रदर्शन और C ++ की सुविधा चाहते हैं। विचार करें कि आपको स्ट्रिंग्स के लाखों (ठीक, अरबों पर भरोसा नहीं करना चाहिए) उनमें से प्रत्येक को एक अलग stringया vector<char>ऑब्जेक्ट में संग्रहीत करना स्मृति (हीप) प्रबंधन दिनचर्या के लिए एक आपदा होगी। खासकर यदि आपको कई बार अलग-अलग तारों को आवंटित करने और हटाने की आवश्यकता होती है।

हालाँकि, आप एक स्ट्रिंग को आवंटित कर सकते हैं कि कई तार। आप char* buffer = (char*)malloc(total_size);स्पष्ट कारणों के लिए पसंद नहीं करेंगे (यदि स्पष्ट नहीं है, तो "स्मार्ट ptrs का उपयोग क्यों करें")। आप बल्कि चाहेंगेunique_ptr<char[]> buffer(new char[total_size]);

सादृश्य द्वारा, एक ही प्रदर्शन और सुविधा के विचार गैर- charडेटा (लाखों वैक्टर / मेट्रिसेस / ऑब्जेक्ट्स पर विचार) पर लागू होते हैं ।


एक उन सभी को एक बड़े में नहीं डाला vector<char>? जवाब, मुझे लगता है, क्योंकि जब आप बफर बनाते हैं, तो वे शून्य-प्रारंभिक होंगे, जबकि वे उपयोग नहीं करेंगे unique_ptr<char[]>। लेकिन यह कुंजी डली आपके उत्तर से गायब है।
आर्थर टक्का

2
  • द्विआधारी-संगतता कारणों के लिए आपको केवल एक संकेतक रखने के लिए आपकी संरचना की आवश्यकता है।
  • आपको एक एपीआई के साथ इंटरफेस करने की आवश्यकता है जो आवंटित की गई मेमोरी लौटाता है new[]
  • आपकी फर्म या परियोजना का उपयोग करने के खिलाफ एक सामान्य नियम है std::vector , उदाहरण के लिए, लापरवाह प्रोग्रामर को गलती से प्रतियों को पेश करने से रोकने के लिए
  • आप लापरवाह प्रोग्रामर को गलती से इस उदाहरण में प्रतियां शुरू करने से रोकना चाहते हैं।

एक सामान्य नियम है कि सी ++ कंटेनरों को पॉइंटर्स के साथ रोलिंग-योर-ओनलाइन पसंद किया जाना है। यह एक सामान्य नियम है; इसके अपवाद हैं। अभी और है; ये सिर्फ उदाहरण हैं।


0

यदि आपको वस्तुओं की एक गतिशील सरणी की आवश्यकता है जो प्रतिलिपि-निर्माण योग्य नहीं हैं, तो सरणी के लिए एक स्मार्ट पॉइंटर जाने का रास्ता है। उदाहरण के लिए, अगर आपको परमाणु की एक सरणी की आवश्यकता है तो क्या होगा।

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