التكرار على std::vector:متغير الفهرس غير الموقع مقابل الموقع

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

  •  03-07-2019
  •  | 
  •  

سؤال

ما هي الطريقة الصحيحة للتكرار على ناقل في C++؟

خذ بعين الاعتبار جزأين التعليمات البرمجية، هذا يعمل بشكل جيد:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

و هذه:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

الذي يولد warning: comparison between signed and unsigned integer expressions.

أنا جديد في عالم C++، لذا فإن unsigned المتغير يبدو مخيفًا بعض الشيء بالنسبة لي وأنا أعلم ذلك unsigned يمكن أن تكون المتغيرات خطيرة إذا لم يتم استخدامها بشكل صحيح، فهل هذا صحيح؟

هل كانت مفيدة؟

المحلول

التكرار إلى الوراء

يرى هذه الإجابة.

التكرار إلى الأمام

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

باستخدام الأمراض المنقولة جنسيا::vector

باستخدام التكرارات

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

المهم هو أن تستخدم دائمًا نموذج زيادة البادئة للمكررات التي لا تعرف تعريفاتها.سيضمن ذلك تشغيل التعليمات البرمجية الخاصة بك بشكل عام قدر الإمكان.

باستخدام النطاق C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

باستخدام المؤشرات

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

باستخدام المصفوفات

باستخدام التكرارات

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

باستخدام النطاق C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

باستخدام المؤشرات

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

اقرأ في الإجابة المتكررة للخلف ما هي المشكلة sizeof النهج يمكن أن يستسلم ل، على أية حال.

نصائح أخرى

وبعد أربع سنوات مرت، <م> جوجل أعطاني هذه الإجابة. مع معيار <م> C ++ 11 (ويعرف أيضا باسم C ++ 0X ) هو في الواقع هناك وسيلة ممتعة جديدة للقيام بذلك (بسعر كسر التوافق): وauto الكلمة الجديدة. فإنه يوفر لك الألم من الحاجة إلى تحديد بشكل واضح نوع مكرر لاستخدام (تكرار نوع ناقلات مرة أخرى)، عندما يكون واضحا (للمترجم)، أي نوع الاستخدام. مع v يجري vector الخاص بك، يمكنك أن تفعل شيئا من هذا القبيل:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11 يذهب إلى أبعد من ذلك ويعطيك جملة خاص لبالتكرار عبر مجموعات مثل ناقلات. فهو يزيل ضرورة كتابة الأشياء التي هي دائما نفسها:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

لنرى ذلك في برنامج العمل، وبناء auto.cpp الملف:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

وأما كتابة هذا، عند ترجمة هذا مع ز ++ ، تحتاج عادة لتحديد ذلك للعمل مع المعيار الجديد بإعطاء العلم اضافية:

g++ -std=c++0x -o auto auto.cpp

والآن يمكنك تشغيل المثال:

$ ./auto
17
12
23
42

الرجاء ملاحظة أن التعليمات على تجميع وتشغيل خاصة ب <م> جنو سي ++ مترجم على لينكس ، ينبغي أن يكون برنامجه (ومترجم ) مستقلة.

في حالة محددة في المثال الخاص بك، فما استقاموا لكم فاستقيموا استخدام خوارزميات STL لتحقيق ذلك.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

لقضية أكثر عمومية، ولكن لا تزال بسيطة إلى حد ما، كنت اذهب مع:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

وفيما يتعلق الإجابة يوهانس شواب في:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

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

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

والحل هو استخدام الكلمة 'typename' كما قال:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

ودعوة إلى vector<T>::size() بإرجاع قيمة من نوع std::vector<T>::size_type، وليس كثافة العمليات، غير موقعة صحيح أو غير ذلك.

وأيضا يتم التكرار عموما على الحاويات في C ++ باستخدام <م> المكررات ، من هذا القبيل.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

وأين T هو نوع البيانات التي تخزن في ناقلات.

وأو باستخدام خوارزميات التكرار مختلفة (std::transform، std::copy، std::fill، std::for_each وهلم جرا).

استخدم size_t:

for (size_t i=0; i < polygon.size(); i++)

ويكيبيديا :

<اقتباس فقرة>   

ووstdlib.h والملفات رأس stddef.h تحديد نوع البيانات يسمى size_t الذي يستخدم لتمثيل حجم كائن. تتوقع مكتبة الوظائف التي تأخذ أحجام لها أن تكون من نوع size_t، والمشغل sizeof يقيم لsize_t.

     

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

وعادة ما تستخدم BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

ويعمل على حاويات STL، المصفوفات، سلاسل على الطراز C، وما إلى ذلك.

وهناك القليل من التاريخ:

لتمثيل ما إذا كان الرقم الكمبيوتر سلبي أو عدم استخدام 'علامة' قليلا. int هو نوع البيانات الموقعة وهذا يعني أنه يمكن أن تعقد القيم الإيجابية والسلبية (حوالي -2billion إلى 2billion). Unsigned يمكن تخزين فقط أرقام موجبة (ونظرا لأنه لا يضيع قليلا على التعريف فإنه يمكن تخزين أكثر: 0 إلى حوالي 4billion).

وstd::vector::size() بإرجاع unsigned، عن كيف يمكن لناقلات يكون طول سلبي؟

والتحذير أقول لك أن المعامل الأيمن من بيان عدم المساواة الخاصة بك يمكن أن تعقد المزيد من البيانات ثم اليسار.

وأساسا إذا كان لديك واسطة مع أكثر من 2 مليار مداخل واستخدام عدد صحيح إلى مؤشر إلى أنك سوف تصل مشاكل تجاوز (فإن كثافة التفاف حول العودة إلى مليار سلبية 2).

لكي يكتمل، يتيح بناء جملة C++ 11 إصدارًا واحدًا فقط للمكررات (المرجع):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

وهو أيضًا مريح للتكرار العكسي

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

في سي++ 11

سأستخدم خوارزميات عامة مثل for_each لتجنب البحث عن النوع الصحيح من المكرر وتعبير لامدا لتجنب الوظائف/الكائنات المسماة الإضافية.

المثال القصير "الجميل" لحالتك الخاصة (بافتراض أن المضلع هو متجه للأعداد الصحيحة):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

اختبارها على: http://ideone.com/i6Ethd

لا تنسى أن يشمل: الخوارزمية وبالطبع المتجه :)

لدى Microsoft أيضًا مثال جيد على هذا:
مصدر: http://msdn.microsoft.com/en-us/library/dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

والأول هو النوع الصحيح، والصحيح في بعض بالمعنى الدقيق للكلمة. (إذا كنت تفكر في غير والحجم لا يمكن أبدا أن يكون أقل من الصفر.) وهذا تحذير يضرب لي واحدا من المرشحين الجيدين ليجري تجاهلها، وإن كان.

تحليل النظر في ما إذا كنت بحاجة إلى تكرار في كل

ويوفر رأس <algorithm> مستوى لنا مع مرافق لهذا:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

وظائف أخرى في المكتبة خوارزمية أداء المهام المشتركة - تأكد من أنك تعرف ما هو متاح إذا كنت تريد أن تنقذ نفسك جهد

.

والتفاصيل غامضة لكن المهم: إذا قلت "ل(السيارات عليه)" على النحو التالي، وتحصل على نسخة من الكائن، وليس العنصر الفعلي:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

لتعديل عناصر مكافحة ناقلات، تحتاج إلى تعريف مكرر كمرجع:

for(auto &it : v)

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

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

والمطبوعات: 1 2 3. ملاحظة لا يمكنك استخدام هذه التقنية لتغيير عناصر مكافحة ناقلات.

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

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

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