المحكمة الخاصة بلبنان ناقلات مع غير مهيأ التخزين ؟

StackOverflow https://stackoverflow.com/questions/96579

  •  01-07-2019
  •  | 
  •  

سؤال

انا اكتب الحلقة الداخلية التي تحتاج إلى مكان structs في متجاورة التخزين.أنا لا أعرف كيف العديد من هذه structs سيكون هناك في وقت مبكر.مشكلتي هي أن المحكمة vector تهيئة قيمها إلى 0 ، لذلك لا يهم ما أفعله تتحمل تكلفة التهيئة بالإضافة إلى تكلفة إعداد struct's أعضاء قيم.

هل هناك أي طريقة لمنع التهيئة ، STL-مثل حاوية هناك مع تغيير الحجم متجاورة تخزين غير مهيأ العناصر ؟

(أنا متأكد أن هذا الجزء من القانون يحتاج إلى أن يكون الأمثل ، وأنا على يقين أن التهيئة تكلفة كبيرة.)

أيضا انظر التعليقات أدناه توضيحا حول عندما يحدث تهيئة.

بعض التعليمات البرمجية:

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];
    }
}
هل كانت مفيدة؟

المحلول

std::vector يجب تهيئة القيم في المصفوفة بطريقة أو بأخرى ، وهو ما يعني بعض المنشئ (أو نسخ-منشئ) يجب أن يسمى.سلوك vector (أو أي حاوية فئة) غير معرف إذا كنت تريد الوصول إلى غير مهيأ القسم من مجموعة كما لو كانت تهيئة.

أفضل طريقة هي استخدام reserve() و push_back(), حتى أن نسخ-منشئ استخدام تجنب الافتراضي البناء.

باستخدام رمز المثال:

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

std::vector<YourData> 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() الفضاء مرة واحدة في البداية.إذا كنت لا تعرف الحجم النهائي رغم أن, على الأقل عدد النسخ سوف يكون الحد الأدنى في المتوسط.

في النسخة الحالية من C++, الحلقة الداخلية غير فعالة قليلا مؤقتة القيمة هي التي شيدت على المكدس ، نسخ شيدت على ناقلات الذاكرة ، وأخيرا مؤقتة يتم تدميرها.إلا أن الإصدار القادم من C++ لديه ميزة تسمى R-قيمة المراجع (T&&) والتي سوف تساعد.

واجهة قدمتها std::vector لا يسمح خيار آخر ، وهو أن استخدام بعض المصانع مثل فئة لبناء القيم غير الافتراضي.هنا هو الخام على سبيل المثال ما هذا النمط من شأنه أن تبدو وكأنها نفذت في C++:

template <typename T>
class my_vector_replacement {

    // ...

    template <typename F>
    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));
    }
}

يفعل هذا لا يعني أن عليك أن إنشاء الخاصة بك ناقلات الدرجة.في هذه الحالة كان يعقد أيضا ما ينبغي أن يكون مثال بسيط.ولكن قد يكون هناك أوقات حيث استخدام مصنع وظيفة مثل هذا هو أفضل ، فعلى سبيل المثال إذا كان إدراج مشروط قيمة أخرى ، أن خلاف ذلك دون قيد أو شرط بناء بعض مكلفة مؤقتة حتى إذا لم يكن هناك حاجة فعلا.

نصائح أخرى

C++0x يضيف عضو جديد وظيفة القالب emplace_back إلى vector (الذي يعتمد على variadic قوالب مثالية الشحن) أن يتخلص من أي المؤقتات تماما:

memberVector.emplace_back(data1[i], data2[i]);

توضيح بشأن الاحتياطي() الردود:تحتاج إلى استخدام الاحتياطي() بالتزامن مع push_back().بهذه الطريقة, منشئ افتراضي لا يسمى لكل عنصر ، بل منشئ نسخة.كنت لا تزال تحمل عقوبة من إعداد البنية على المكدس ، ثم نسخها إلى ناقلات.من ناحية أخرى, فمن الممكن أنه إذا كنت تستخدم

vect.push_back(MyStruct(fieldValue1, fieldValue2))

المترجم إنشاء مثيل جديد مباشرة في الذاكرة thatbelongs إلى ناقلات.ذلك يعتمد على كيفية الذكية محسن.تحتاج إلى التحقق من التعليمات البرمجية التي تم إنشاؤها إلى معرفة.

في C++11 (وزيادة) يمكنك استخدام مجموعة نسخة من unique_ptr تخصيص غير مهيأ مجموعة.هذا ليس تماما stl حاوية, ولكن لا تزال الذاكرة مدارة C++العش والتي سوف تكون جيدة بما يكفي بالنسبة للعديد من التطبيقات.

auto my_uninit_array = std::unique_ptr<mystruct[]>(new mystruct[count]);

حتى هنا هي المشكلة ، وحجمها هو الدعوة إدراج ، والتي تقوم بعمل نسخ البناء من الافتراضي شيدت عنصر لكل حديثا العناصر المضافة.للحصول على هذا 0 التكلفة تحتاج إلى كتابة الخاصة بك منشئ افتراضي و نسخة خاصة بك منشئ فارغة وظائف.تفعل هذا نسختك منشئ هو فكرة سيئة جدا لأنه كسر std::ناقلات الداخلية تخصيص الخوارزميات.

موجز:أنت لن تكون قادرة على القيام بذلك مع std::المتجهات.

يخطئ...

جرب الطريقة:

std::vector<T>::reserve(x)

وسوف تمكنك من حجز ما يكفي من الذاكرة x البنود دون تهيئة أي (ناقلات الخاص بك لا تزال فارغة).وبالتالي لن يكون هناك تخصيص حتى أن أكثر من x.

النقطة الثانية هي أن ناقلات لن تهيئة القيم إلى الصفر.هل اختبار التعليمات البرمجية الخاصة بك في التصحيح ؟

بعد التحقق على g++ التعليمات البرمجية التالي:

#include <iostream>
#include <vector>

struct MyStruct
{
   int m_iValue00 ;
   int m_iValue01 ;
} ;

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

   std::vector<MyStruct> aMyStruct ;

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

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

   for(std::vector<MyStruct>::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

التهيئة رأيت ربما كانت قطعة أثرية.

[تحرير] بعد التعليق على تغيير الحجم, I تعديل التعليمات البرمجية لإضافة تغيير حجم الخط.تغيير الحجم بشكل فعال المكالمات منشئ افتراضي الكائن داخل ناقلات, ولكن إذا كان منشئ افتراضي لا يفعل شيئا, ثم ليس هناك ما هو تهيئة...ما زلت أعتقد أنه كان قطعة أثرية (تمكنت أول الوقت كله متجه zerooed مع التعليمات البرمجية التالية:

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 ;
} ;

هذا وسوف تحصل على الأرجح المضمنة في التعليمات البرمجية الخاصة بك.

ولكن يجب على كل حال دراسة التعليمات البرمجية الخاصة بك مع منشئ ملفات التعريف للتأكد من هذا قطعة من التعليمات البرمجية هو عنق الزجاجة من التطبيق الخاص بك.

استخدام std::vector::الاحتياطي الأسلوب ().لن حجم ناقلات, ولكن سوف تخصص مساحة.

من التعليقات على ملصقات أخرى ، يبدو أن كنت تركت مع malloc() والأصدقاء.ناقلات لن تسمح لك unconstructed العناصر.

من التعليمات البرمجية الخاصة بك ، يبدو أنك متجه من البنيات تتكون كل منها من 2 رجات.يمكنك بدلا من ذلك استخدام 2 ناقلات رجات?ثم

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

الآن أنت لا تدفع ثمن نسخ البنية في كل مرة.

إذا كنت حقا يصرون على وجود عناصر غير مهيأ والتضحية بعض الأساليب مثل الجبهة () ، (), push_back () ، زيادة استخدام ناقلات من الرقمية .فإنه يسمح لك حتى لا للمحافظة على العناصر الموجودة عند استدعاء تغيير حجم()...

يمكنك استخدام المجمع نوع حول نوع العنصر مع منشئ افتراضي الذي لا يفعل شيئا.E. g.:

template <typename T>
struct no_init
{
    T value;

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

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

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

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

استخدام:

std::vector<no_init<char>> vec;
vec.resize(2ul * 1024ul * 1024ul * 1024ul);

هل البنيات أنفسهم بحاجة إلى أن تكون في الذاكرة القريبة ، أو يمكن أن تحصل بعيدا مع وجود ناقلات البنية*?

ناقلات جعل نسخة من كل ما يمكنك إضافة لهم ، وذلك باستخدام ناقلات من المؤشرات بدلا من الكائنات هي طريقة واحدة لتحسين الأداء.

لا أعتقد أن المحكمة الخاصة بلبنان هي الجواب.أنت تسير في حاجة إلى لفة نوع الخاصة بك من الحل باستخدام 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;
}

وأود أن تفعل شيئا مثل:

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]));
  }
}

تحتاج إلى تحديد المنشئ نوع التي يتم تخزينها في memberVector ، ولكن هذا قليل التكلفة كما أنها سوف تعطيك أفضل من كلا العالمين ؛ لا لزوم لها التهيئة يتم أي توزيع سوف تحدث خلال الحلقة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top