एसटीएल वेक्टर अनियंत्रित भंडारण के साथ?

मैं एक आंतरिक लूप लिख रहा हूं जिसे संगत भंडारण में struct s रखने की आवश्यकता है। मुझे नहीं पता कि इनमें से कितने struct s समय से पहले होंगे। मेरी समस्या यह है कि एसटीएल का <�कोड> वेक्टर अपने मानों को 0 तक प्रारंभ करता है, इसलिए कोई फर्क नहीं पड़ता कि मैं क्या करता हूं, मुझे प्रारंभिक लागत और struct के सदस्यों को सेट करने की लागत लगती है उनके मूल्यों के लिए।

क्या प्रारंभिकरण को रोकने के लिए कोई तरीका है, या क्या वहां एक एसटीएल-जैसे कंटेनर है जो आकार बदलने योग्य संगत भंडारण और अनियमित तत्वों के साथ है?

(मुझे यकीन है कि कोड के इस हिस्से को अनुकूलित करने की आवश्यकता है, और मुझे यकीन है कि प्रारंभिकरण एक महत्वपूर्ण लागत है।)

इसके अलावा, प्रारंभिकरण कब होता है इसके बारे में स्पष्टीकरण के लिए नीचे दी गई मेरी टिप्पणियां देखें।

कुछ कोड:

void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size()
    memberVector.resize(mvSize + count);//causes 0-initialization

    for (int i = 0; i < count; ++i) {
        memberVector[mvSize + i].d1 = data1[i];
        memberVector[mvSize + i].d2 = data2[i];
    }
}
0
एक अन्य स्पष्टीकरण: यह नहीं है कि कन्स्ट्रक्टर मूल्यों को 0 तक शुरू करता है। यह कॉल का आकार बदलता है, जो करता है।
जोड़ा लेखक Jim Hunziker, स्रोत
नोट - आरक्षित() का उपयोग एक समाधान नहीं है, क्योंकि आप कानूनी रूप से उन स्थानों तक पहुंच नहीं सकते हैं जो स्थानों() और ऊपर के स्थानों पर हैं।
जोड़ा लेखक Jim Hunziker, स्रोत
नोट: आप उस डेटा तक नहीं पहुंच सकते हैं जो वैसे भी अनियमित है। वेक्टर <�टी> पिछले .end() और टी [] के प्रारंभिक सदस्यों के लिए एक ही समस्या। लेकिन एक वेक्टर के साथ, संभावना है कि डिबगिंग कोड आपको बताता है कि अब। सरणी कोड ग्राहकों पीसी पर चुपचाप विफल हो जाएगा।
जोड़ा लेखक MSalters, स्रोत
एक आवंटक दृष्टिकोण के लिए stackoverflow.com/q/7218574/1969455 भी देखें। (पीओडी प्रकार के लिए अच्छा)
जोड़ा लेखक Matthäus Brandl, स्रोत
क्या आप हमें संरचना घोषणा भी दे सकते हैं? धन्यवाद... :-)
जोड़ा लेखक paercebal, स्रोत
यह अच्छा प्रश्न है। कुछ अनुप्रयोगों के लिए, यह जानना महत्वपूर्ण है कि std :: वेक्टर हमेशा अपने तत्वों को प्रारंभ करता है, भले ही वे सादे-पुराने डेटा (पीओडी) हों।
जोड़ा लेखक nobar, स्रोत

14 उत्तर

तो यहां समस्या है, आकार बदलना सम्मिलित है, जो नए जोड़े गए तत्वों के लिए एक डिफ़ॉल्ट निर्मित तत्व से एक प्रति निर्माण कर रहा है। इसे 0 लागत तक पहुंचने के लिए आपको अपने स्वयं के डिफ़ॉल्ट कन्स्ट्रक्टर और अपनी खुद की प्रतिलिपि बनाने वाले को खाली कार्यों के रूप में लिखना होगा। इसे अपनी कॉपी कन्स्ट्रक्टर में करना एक बहुत बुरा विचार है क्योंकि यह std :: वेक्टर के आंतरिक पुनर्विक्रय एल्गोरिदम को तोड़ देगा।

सारांश: आप std :: वेक्टर के साथ ऐसा करने में सक्षम नहीं होंगे।

0
जोड़ा
यह असली मुद्दा है। std :: vector को यह एहसास होना चाहिए कि टी को कोई छोटा डिफॉल्ट कन्स्ट्रक्टर है तो इसे प्रारंभ करने की आवश्यकता नहीं है। यह इंगित करने के लिए धन्यवाद कि कॉपी कन्स्ट्रक्टर यहां अनावश्यक काम कर रहा है।
जोड़ा लेखक Eric Hein, स्रोत

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

vect.push_back(MyStruct(fieldValue1, fieldValue2))

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

0
जोड़ा
यह पता चला है कि स्तर ओ 3 पर जीसीसी के लिए अनुकूलक प्रतिलिपि से बचने के लिए पर्याप्त स्मार्ट नहीं है।
जोड़ा लेखक Jim Hunziker, स्रोत

सी ++ 0x एक नया सदस्य फ़ंक्शन टेम्पलेट emplace_back को vector (जो विविध टेम्पलेट्स और पूर्ण अग्रेषण पर निर्भर करता है) जो किसी भी अस्थायी से पूरी तरह से छुटकारा पाता है:

memberVector.emplace_back(data1[i], data2[i]);
0
जोड़ा

सी ++ 11 (और बूस्ट) में आप एक अनियमित सरणी आवंटित करने के लिए unique_ptr के सरणी संस्करण का उपयोग कर सकते हैं। यह काफी stl कंटेनर नहीं है, लेकिन अभी भी स्मृति प्रबंधित है और C ++ - ish जो कई अनुप्रयोगों के लिए पर्याप्त होगा।

auto my_uninit_array = std::unique_ptr(new mystruct[count]);
0
जोड़ा

Std :: vector :: reserve() विधि का प्रयोग करें। यह वेक्टर का आकार बदल नहीं पाएगा, लेकिन यह अंतरिक्ष आवंटित करेगा।

0
जोड़ा

अं ...

विधि आज़माएं:

std::vector::reserve(x)

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

दूसरा बिंदु यह है कि वेक्टर शून्य को मानों को प्रारंभ नहीं करेगा। क्या आप डीबग में अपने कोड का परीक्षण कर रहे हैं?

जी ++ पर सत्यापन के बाद, निम्नलिखित कोड:

#include 
#include 

struct MyStruct
{
   int m_iValue00 ;
   int m_iValue01 ;
} ;

int main()
{
   MyStruct aaa, bbb, ccc ;

   std::vector aMyStruct ;

   aMyStruct.push_back(aaa) ;
   aMyStruct.push_back(bbb) ;
   aMyStruct.push_back(ccc) ;

   aMyStruct.resize(6) ;//[EDIT] double the size

   for(std::vector::size_type i = 0, iMax = aMyStruct.size(); i < iMax; ++i)
   {
      std::cout << "[" << i << "] : " << aMyStruct[i].m_iValue00 << ", " << aMyStruct[0].m_iValue01 << "\n" ;
   }

   return 0 ;
}

निम्नलिखित परिणाम देता है:

[0] : 134515780, -16121856
[1] : 134554052, -16121856
[2] : 134544501, -16121856
[3] : 0, -16121856
[4] : 0, -16121856
[5] : 0, -16121856

आपके द्वारा देखा गया प्रारंभिक रूप से एक आर्टिफैक्ट था।

[संपादित करें] आकार बदलने पर टिप्पणी के बाद, मैंने आकार बदलने के लिए कोड को संशोधित किया। आकार बदलना प्रभावी रूप से वेक्टर के अंदर ऑब्जेक्ट का डिफ़ॉल्ट कन्स्ट्रक्टर कहता है, लेकिन यदि डिफ़ॉल्ट कन्स्ट्रक्टर कुछ भी नहीं करता है, तो कुछ भी शुरू नहीं किया गया है ... मुझे अभी भी विश्वास है कि यह एक आर्टिफैक्ट था (मैंने पहली बार वेक्टर के साथ पूरे वेक्टर को झुकाया निम्नलिखित कोड:

aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;

इसलिए... : - /

[संपादित करें 2] पहले से ही Arkadiy द्वारा की पेशकश की तरह, समाधान वांछित पैरामीटर लेने के लिए एक इनलाइन कन्स्ट्रक्टर का उपयोग करना है। कुछ इस तरह

struct MyStruct
{
   MyStruct(int p_d1, int p_d2) : d1(p_d1), d2(p_d2) {}
   int d1, d2 ;
} ;

यह शायद आपके कोड में रेखांकित होगा।

लेकिन आपको यह सुनिश्चित करने के लिए एक कोडर के साथ अपने कोड का अध्ययन करना चाहिए कि कोड का यह टुकड़ा आपके आवेदन की बाधा है।

0
जोड़ा
मैंने ऊपर एक नोट लिखा था। यह वेक्टर का निर्माता नहीं है जो 0 से शुरू होता है। यह आकार बदलता है() जो करता है।
जोड़ा लेखक Jim Hunziker, स्रोत
मुझे लगता है कि आप सही रास्ते पर हैं। मेरे पास संरचना में परिभाषित कोई कन्स्ट्रक्टर नहीं है, इसलिए इसका डिफ़ॉल्ट कन्स्ट्रक्टर (मेरा मानना ​​है) शून्य-प्रारंभ होता है। मैं जांच करूंगा कि एक डिफ़ॉल्ट कन्स्ट्रक्टर जोड़ना जो कुछ भी हल नहीं करता है।
जोड़ा लेखक Jim Hunziker, स्रोत
ग्रेग रोजर्स सही है। मेरा अनुमान है कि स्मृति को "शून्य" था क्योंकि कुछ प्रक्रिया प्रारंभिक कोड मैंने लिखा था। सी ++ में, आप उस चीज़ के लिए भुगतान नहीं करते जिसका आप उपयोग नहीं करते हैं। तो यदि आप सी-जैसे कोड लिख रहे हैं, तो आपको ओवरहेड नहीं होना चाहिए। और वैक्टर उस पर काफी अच्छे हैं।
जोड़ा लेखक paercebal, स्रोत
@ नोबार: यह MyStruct कन्स्ट्रक्टर पर निर्भर करता है। यदि यह खाली है, और रेखांकित है, और MyStruct सदस्यों के पास शून्य लागत निर्माणकर्ता हैं, तो C ++ संकलक इसे कुछ भी अनुकूलित नहीं करेगा। फिर, हम इसके लिए भुगतान नहीं करेंगे। केवल आकार बदलने के लिए।
जोड़ा लेखक paercebal, स्रोत
ऐसा प्रतीत होता है कि वेक्टर ने हमें इस मामले में गिरा दिया है। हम प्रारंभ करने के लिए भुगतान करते हैं, भले ही हमें इसकी आवश्यकता न हो या चाहें। यह सम्मिलित() के अर्थशास्त्र द्वारा गारंटीकृत है जिसे आकार() द्वारा बुलाया जाता है। प्रारंभिकरण के लिए उपयोग किया जाने वाला मान आकार बदलने के लिए पास किए गए MyStruct में जो भी होता है, उस पर आधारित होता है। चूंकि आपने आकार बदलने के लिए कुछ भी निर्दिष्ट नहीं किया है (), डिफ़ॉल्ट कन्स्ट्रक्टर का उपयोग किया गया था। चूंकि डिफ़ॉल्ट कन्स्ट्रक्टर इस मामले में कुछ भी नहीं करता है, इसलिए आपको शून्य मिल सकती है या आपको कुछ और मिल सकता है। किसी भी तरह से, आप आ
जोड़ा लेखक nobar, स्रोत
यदि आपके पास कोई कन्स्ट्रक्टर परिभाषित नहीं है और सभी तत्व पीओडी प्रकार हैं, तो कन्स्ट्रक्टर कुछ भी नहीं करता है। यदि तत्व पीओडी नहीं हैं तो यह केवल उनके डिफ़ॉल्ट कन्स्ट्रक्टर को कॉल करेगा।
जोड़ा लेखक Greg Rogers, स्रोत
इस मामले में माईस्ट्रक्चर का एक छोटा सा कन्स्ट्रक्टर है इसलिए कुछ भी शुरू नहीं हुआ है। यह ओपी की स्थिति से अलग हो सकता है।
जोड़ा लेखक Greg Rogers, स्रोत

क्या structs खुद को संगत स्मृति में होना चाहिए, या आप संरचना के वेक्टर होने से दूर हो सकते हैं *?

वेक्टर जो कुछ भी आप जोड़ते हैं उसकी एक प्रति बनाते हैं, इसलिए ऑब्जेक्ट्स के बजाए पॉइंटर्स के वैक्टर का उपयोग करना प्रदर्शन को बेहतर बनाने का एक तरीका है।

0
जोड़ा
उन्हें संगत होना है। वे एक बफर में हैं जो नेटवर्क पर एक विशाल हिस्से के रूप में भेजा जा रहा है।
जोड़ा लेखक Jim Hunziker, स्रोत

मुझे नहीं लगता कि एसटीएल आपका जवाब है। आपको realloc() का उपयोग करके अपने स्वयं के समाधान को रोल करने की आवश्यकता होगी। आपको एक पॉइंटर और या तो आकार, या तत्वों की संख्या को स्टोर करना होगा, और इसका उपयोग यह पता लगाने के लिए करना होगा कि realloc() के बाद तत्व जोड़ने के लिए कहां से प्रारंभ करना है।

int *memberArray;
int arrayCount;
void GetsCalledALot(int* data1, int* data2, int count) {
    memberArray = realloc(memberArray, sizeof(int) * (arrayCount + count);
    for (int i = 0; i < count; ++i) {
        memberArray[arrayCount + i].d1 = data1[i];
        memberArray[arrayCount + i].d2 = data2[i];
    }
    arrayCount += count;
}
0
जोड़ा

आपकी टिप्पणियों से अन्य पोस्टर्स तक, ऐसा लगता है कि आप malloc() और दोस्तों के साथ छोड़े गए हैं। वेक्टर आपको असंगठित तत्व नहीं होने देगा।

0
जोड़ा

आपके कोड से, ऐसा लगता है कि आपके पास structs का वेक्टर है जिसमें से प्रत्येक में 2 इन्स शामिल हैं। क्या आप इसके बजाय इनट्स के 2 वैक्टर का उपयोग कर सकते हैं? फिर

copy(data1, data1 + count, back_inserter(v1));
copy(data2, data2 + count, back_inserter(v2));

अब आप प्रत्येक बार एक संरचना की प्रतिलिपि बनाने के लिए भुगतान नहीं करते हैं।

0
जोड़ा
दिलचस्प। यह सिर्फ काम कर सकता है - ऐसा लगता है कि यह एक मध्यवर्ती वस्तु के निर्माण से बच जाएगा।
जोड़ा लेखक nobar, स्रोत

मैं कुछ ऐसा करूंगा:

void GetsCalledALot(int* data1, int* data2, int count)
{
  const size_t mvSize = memberVector.size();
  memberVector.reserve(mvSize + count);

  for (int i = 0; i < count; ++i) {
    memberVector.push_back(MyType(data1[i], data2[i]));
  }
}

आपको सदस्य वेक्टर में संग्रहीत प्रकार के लिए एक ctor परिभाषित करने की आवश्यकता है, लेकिन यह एक छोटी सी लागत है क्योंकि यह आपको दोनों दुनिया के सर्वश्रेष्ठ प्रदान करेगी; कोई अनावश्यक प्रारंभ नहीं किया गया है और लूप के दौरान कोई पुनर्वितरण नहीं होगा।

0
जोड़ा
यह समस्या को हल करने के लिए प्रतीत नहीं होता है क्योंकि यह अस्थायी MyType() का उपयोग करता है और वेक्टर में प्रतिलिपि बनाता है। अभी भी एक डबल प्रारंभिक है।
जोड़ा लेखक nobar, स्रोत

std::vector must initialize the values in the array somehow, which means some constructor (or copy-constructor) must be called. The behavior of vector (or any container class) is undefined if you were to access the uninitialized section of the array as if it were initialized.

reserve() और push_back() का उपयोग करने का सबसे अच्छा तरीका है, ताकि कॉपी-कन्स्ट्रक्टर का उपयोग किया जा सके, डिफ़ॉल्ट-निर्माण से परहेज किया जा सके।

अपने उदाहरण कोड का उपयोग करना:

struct YourData {
    int d1;
    int d2;
    YourData(int v1, int v2) : d1(v1), d2(v2) {}
};

std::vector memberVector;

void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size();

   //Does not initialize the extra elements
    memberVector.reserve(mvSize + count);

   //Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
       //Copy construct using a temporary.
        memberVector.push_back(YourData(data1[i], data2[i]));
    }
}

reserve() (या resize() ) को कॉल करने में एकमात्र समस्या यह है कि आप प्रतिलिपि बनाने के लिए अक्सर कॉपी-कन्स्ट्रक्टर का आविष्कार कर सकते हैं। यदि आप सरणी के अंतिम आकार के रूप में अच्छी भविष्यवाणी कर सकते हैं, तो शुरुआत में एक बार reserve() स्थान बेहतर होगा। यदि आप अंतिम आकार को नहीं जानते हैं, तो कम से कम प्रतियों की संख्या औसतन न्यूनतम होगी।

In the current version of C++, the inner loop is a bit inefficient as a temporary value is constructed on the stack, copy-constructed to the vectors memory, and finally the temporary is destroyed. However the next version of C++ has a feature called R-Value references (T&&) which will help.

std :: vector द्वारा प्रदान किया गया इंटरफ़ेस किसी अन्य विकल्प की अनुमति नहीं देता है, जो डिफ़ॉल्ट रूप से अन्य मानों को बनाने के लिए कुछ फैक्ट्री-जैसी कक्षा का उपयोग करना है। सी ++ में यह पैटर्न कैसा दिखता है इसका एक मोटा उदाहरण यहां दिया गया है:

template 
class my_vector_replacement {

   //...

    template 
    my_vector::push_back_using_factory(F factory) {
       //... check size of array, and resize if needed.

       //Copy construct using placement new,
        new(arrayData+end) T(factory())
        end += sizeof(T);
    }

    char* arrayData;
    size_t end;//Of initialized data in arrayData
};

// One of many possible implementations
struct MyFactory {
    MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
    YourData operator()() const {
        return YourData(*d1,*d2);
    }
    int* d1;
    int* d2;
};

void GetsCalledALot(int* data1, int* data2, int count) {
   //... Still will need the same call to a reserve() type function.

   //Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
       //Copy construct using a factory
        memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
    }
}

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

0
जोड़ा

यदि आप वास्तव में तत्वों को अनियमित करते हैं और कुछ तरीकों जैसे फ्रंट (), बैक (), push_back() को बलिदान देते हैं, तो संख्यात्मक से बूस्ट वेक्टर का उपयोग करें। यह आपको आकार बदलने के दौरान मौजूदा तत्वों को संरक्षित नहीं करने की अनुमति देता है() ...

0
जोड़ा

आप एक डिफ़ॉल्ट कन्स्ट्रक्टर के साथ अपने तत्व प्रकार के चारों ओर एक रैपर प्रकार का उपयोग कर सकते हैं जो कुछ भी नहीं करता है। उदा .:

template 
struct no_init
{
    T value;

    no_init() { static_assert(std::is_standard_layout>::value && sizeof(T) == sizeof(no_init), "T does not have standard layout"); }

    no_init(T& v) { value = v; }
    T& operator=(T& v) { value = v; return value; }

    no_init(no_init& n) { value = n.value; }
    no_init(no_init&& n) { value = std::move(n.value); }
    T& operator=(no_init& n) { value = n.value; return this; }
    T& operator=(no_init&& n) { value = std::move(n.value); return this; }

    T* operator&() { return &value; }//So you can use &(vec[0]) etc.
};

काम में लाना:

std::vector> vec;
vec.resize(2ul * 1024ul * 1024ul * 1024ul);
0
जोड़ा