अंतिम अद्यतन:2026-10-12
C++ डिफ़ॉल्ट कंस्ट्रक्टर से बचना¶
C++ में कुछ कंटेनर जब resize() विधि से आकार बदलते हैं, तब डिफ़ॉल्ट कंस्ट्रक्टर कॉल हो जाता है — हो सकता है यह वह व्यवहार न हो जिसकी हम चाह रखते हों। यह लेख बताता है कि कस्टम एलोकेटर का उपयोग करके इस प्रक्रिया को नियंत्रित कैसे करें, ताकि डिफ़ॉल्ट कंस्ट्रक्शन का कॉल टल सके।
उदाहरण 1:
क्या आप जानते हैं कि इस समय क्या आउटपुट आएगा?
उत्तर है:
यहाँ कुछ बातें समझना आवश्यक हैं:
पहला: resize के बाद वापस आया डेटा 0 क्यों हो गया — वास्तव में क्या हुआ?
दूसरा: यदि मुझे मूल डेटा चाहिए, तो क्या करें? अर्थात् मैं चाहता हूँ कि आउटपुट हो:
तीसरा: यदि vector के एलिमेंट का डिफ़ॉल्ट कंस्ट्रक्टर न हो तो क्या होगा?
चौथा: resize की क्या-क्या सीमाएँ हैं?
तो इस अनुभाग में इसी शक्तिशाली सुविधा का अध्ययन करते हैं — resize से डिफ़ॉल्ट कंस्ट्रक्शन का कॉल कैसे टालें।
नोट: स्थान सीमित है; इस अनुभाग के सभी कोड का सरल (lazy) संस्करण प्रीमियम कम्युनिटी में अपडेट किया गया है।
1.resize का विश्लेषण¶
इस लेख को और सरल बनाने के लिए, मैंने नीचे का उदाहरण उपयोग किया है — Foo का निर्माण प्रतिबंधित करके, फिर उस पर vector में resize करते हैं।
इस समय संकलन त्रुटि होगी, और हमें ढेर सारी त्रुटि सूचनाएँ दिखेंगी।
पहली समस्या: देखा जा सकता है कि कॉल स्टैक __uninitialized_default_n तक पहुँचा — यहाँ वर्तमान टाइप की जाँच होती है कि वह trivial टाइप है या नहीं; यही resize की पहली सीमा है!
required from '_ForwardIterator std::__uninitialized_default_n(_ForwardIterator, _Size) [with _ForwardIterator = Foo*; _Size = long unsigned int]'
710 | return __uninitialized_default_n_1<__is_trivial(_ValueType)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
711 | && __can_fill>::
दूसरी समस्या: चूँकि हमने डिफ़ॉल्ट कंस्ट्रक्शन प्रतिबंधित कर दिया था, एलोकेटर मूल पते पर placement new से डिफ़ॉल्ट ऑब्जेक्ट बनाते समय विफल हो गया — तब त्रुटि आई; यही दूसरी सीमा है!
gcc/14.1.0_1/include/c++/14/bits/stl_construct.h:119:7: error: use of deleted function 'Foo::Foo()'
119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
| ^~~~~~~~~~~~~~~~~~~
अब यहाँ तक आ चुके हैं, मुख्य विषय में प्रवेश करते हैं — देखा जा सकता है कि resize के समय एलोकेटर से मेमोरी आवंटित होती है; जब पास किया गया आकार size से बड़ा हो (यहाँ मान लिया कि वह capacity के भीतर है, अर्थात् पता वैध है), तब एलोकेटर द्वारा placement new से डिफ़ॉल्ट कंस्ट्रक्टर वाला एक ऑब्जेक्ट बनाया जाता है।
gcc का सोर्स कोड कार्यान्वयन यहाँ देखिए — बहुत सरल है न? बस construct कॉल होता है।
__try
{
typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
for (; __n > 0; --__n, (void)++__cur)
__traits::construct(__alloc, std::__addressof(*__cur));
return __cur;
}
__catch(...)
{
std::_Destroy(__first, __cur, __alloc);
__throw_exception_again;
}
ठीक है, resize का सिद्धांत स्पष्ट हो गया; अब शुरुआत के प्रश्न पर आते हैं।
2.डिफ़ॉल्ट कंस्ट्रक्शन का कॉल टालना¶
डिफ़ॉल्ट कंस्ट्रक्शन का कॉल कैसे टालें?
वास्तव में यह बहुत सरल है — gcc के डिफ़ॉल्ट construct को निष्क्रिय कर देना; कैसे निष्क्रिय करें?
यहीं vector का दूसरा पैरामीटर काम आता है: एलोकेटर!
3.एलोकेटर¶
इस समस्या के समाधान के लिए हम कस्टम एलोकेटर उपयोग कर सकते हैं। कस्टम एलोकेटर ऑब्जेक्ट के निर्माण व्यवहार को नियंत्रित कर सकता है, जिससे हम चुन सकते हैं कि resize कॉल करते समय डिफ़ॉल्ट कंस्ट्रक्शन उपयोग हो या नहीं। नीचे एक सरल कस्टम एलोकेटर कार्यान्वयन है — केवल केंद्रीय कोड रखा गया है।
template <class T>
class no_init_alloc {
public:
using value_type = T;
// Override construct method to do nothing
template <class U>
void construct(U* p) {
// Do nothing, memory is not initialized
}
};
हमें बस construct में कुछ भी न करना है — तब इसका उपयोग किया जा सकता है, अर्थात्:
तब यह प्राप्त होता है:
न कि:
ठीक है — क्या आपने सीख लिया?
इतना ही इस अनुभाग में
संबंधित विषय¶
- C++ क्लास इनिशियलाइज़र लिस्ट इडियम | C++那些事
- C++ में फ़ंक्शन टेम्पलेट की जगह क्लास टेम्पलेट | C++那些事
- C++ copy-and-swap इडियम | C++那些事
- C++ डिपेंडेंसी इंजेक्शन | C++那些事
- C++ enum class स्ट्रॉन्ग टाइप्ड एनम | C++那些事
- C++ Header-Only तथा कार्यान्वयन का पृथक्करण | C++那些事
- C++ लेज़ी इवैल्यूएशन इडियम | C++那些事
- C++ लुकअप टेबल इडियम | C++那些事
- C++ मेंबर पॉइंटर तथा टाइप जानकारी | C++那些事
- C++ Mixin इडियम | C++那些事