วนซ้ำผ่านเวกเตอร์ C ++ โดยใช้ลูป 'for'


156

ฉันยังใหม่กับภาษา C ++ ฉันเริ่มใช้เวกเตอร์และสังเกตเห็นว่าในโค้ดทั้งหมดที่ฉันเห็นว่าจะวนซ้ำแม้ว่าเวกเตอร์ผ่านดัชนีพารามิเตอร์แรกของforลูปมักจะขึ้นอยู่กับเวกเตอร์เสมอ ใน Java ฉันอาจทำสิ่งนี้ด้วย ArrayList:

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

มีเหตุผลที่ฉันไม่เห็นสิ่งนี้ใน C ++ หรือไม่? ปฏิบัติไม่ดีหรือไม่?


1
for loop ไม่ใช่ฟังก์ชันดังนั้นจึงไม่มีพารามิเตอร์ (หรืออาร์กิวเมนต์ซึ่งเป็นสิ่งที่คุณส่งผ่าน) คุณหมายถึงอะไรstd::vector<int>::size_type i = 0;หรือบางทีstd::vector<int>::iterator it = vector.begin();?
chris

แน่นอนว่าตัวอย่างทั้งหมดที่ฉันเห็นเขียนไว้เช่นนั้น
Flynn

4
ใน Java ฉันต้องการสำหรับแต่ละลูปหรือใช้ตัววนซ้ำ ค่อนข้างเหมือนกับ C ++ แม้ว่าไวยากรณ์จะแตกต่างกันเล็กน้อย
Jesse Good


10
ส่วนใหญ่คำตอบที่นี่ไม่ถูกต้องถือว่า Q จะเป็น: อะไรคือสิ่งที่ดีที่สุด / ทางที่สั้นที่สุดที่จะย้ำกว่าstd::vector? คำถามจริงที่ถูกถามคือ: มีเหตุผลใดบ้างที่ฉันไม่เห็นสิ่งนี้ใน C ++? ปฏิบัติไม่ดีหรือไม่? หรือที่รู้จักว่าทำไมฉันมักจะเห็นรหัสใน C ++ ซึ่งใช้ iterators ในขณะที่การทำซ้ำมากกว่าstd::vector?
Alok บันทึก

คำตอบ:


106

มีเหตุผลใดที่ฉันไม่เห็นสิ่งนี้ใน C ++? ปฏิบัติไม่ดีหรือไม่?

เลขที่มันไม่ได้มีการปฏิบัติที่ไม่ดี แต่วิธีการดังต่อไปนี้แสดงผลรหัสบางอย่างของคุณมีความยืดหยุ่น

โดยปกติแล้วโค้ดก่อน C ++ 11 สำหรับการวนซ้ำองค์ประกอบคอนเทนเนอร์จะใช้ตัววนซ้ำเช่น:

std::vector<int>::iterator it = vector.begin();

นี่เป็นเพราะมันทำให้โค้ดมีความยืดหยุ่นมากขึ้น

คอนเทนเนอร์ไลบรารีมาตรฐานทั้งหมดรองรับและจัดเตรียมตัวทำซ้ำ หากในช่วงหลังของการพัฒนาคุณจำเป็นต้องเปลี่ยนไปใช้คอนเทนเนอร์อื่นก็ไม่จำเป็นต้องเปลี่ยนรหัสนี้

หมายเหตุ:การเขียนโค้ดซึ่งใช้ได้กับทุกไลบรารีคอนเทนเนอร์มาตรฐานที่เป็นไปได้นั้นไม่ง่ายอย่างที่คิด


25
ใครช่วยอธิบายหน่อยได้ไหมว่าทำไมในกรณีนี้ / ข้อมูลโค้ดที่คุณแนะนำให้ทำซ้ำเกี่ยวกับการสร้างดัชนี "ความยืดหยุ่น" ที่คุณกำลังพูดถึงนี้คืออะไร? โดยส่วนตัวแล้วฉันไม่ชอบตัวทำซ้ำพวกมันขยายโค้ด - เพียงแค่พิมพ์อักขระให้มากขึ้นเพื่อให้ได้เอฟเฟกต์เดียวกัน โดยเฉพาะอย่างยิ่งถ้าคุณไม่สามารถใช้auto.
ยีราฟสีม่วง

8
@VioletGiraffe: ในขณะที่ใช้ตัวทำซ้ำมันยากที่จะผิดพลาดในบางกรณีเช่นช่วงว่างและรหัสนั้นละเอียดกว่าแน่นอนว่ามันเป็นเรื่องหรือการรับรู้และทางเลือกดังนั้นจึงสามารถถกเถียงกันได้ไม่รู้จบ
Alok บันทึก

10
ทำไมคุณถึงแสดงเฉพาะวิธีการประกาศตัววนซ้ำ แต่ไม่แสดงวิธีใช้เพื่อทำลูป ... ?
underscore_d

125

สาเหตุที่คุณไม่เห็นการปฏิบัติดังกล่าวค่อนข้างเป็นอัตวิสัยและไม่สามารถมีคำตอบที่แน่นอนได้เนื่องจากฉันได้เห็นโค้ดจำนวนมากที่ใช้วิธีที่คุณกล่าวถึงแทนที่จะเป็นiteratorโค้ดสไตล์

ต่อไปนี้อาจเป็นสาเหตุของคนที่ไม่พิจารณาvector.size()วิธีการวนซ้ำ:

  1. หวาดระแวงกับการโทรsize()ทุกครั้งที่อยู่ในสภาพลูป อย่างไรก็ตามอาจไม่ใช่ปัญหาหรือสามารถแก้ไขได้เล็กน้อย
  2. ชอบstd::for_each()มากกว่าการforวนซ้ำ
  3. ต่อมาเปลี่ยนภาชนะจากstd::vectorไปอีกคนหนึ่ง (เช่น map, list) นอกจากนี้ยังจะเรียกร้องการเปลี่ยนแปลงของกลไกการวนลูปเพราะไม่ทุกการสนับสนุนภาชนะsize()รูปแบบของการวนลูป

C ++ 11 ให้สิ่งอำนวยความสะดวกที่ดีในการเคลื่อนย้ายผ่านตู้คอนเทนเนอร์ ซึ่งเรียกว่า "range based for loop" (หรือ "Enhanced for loop" ใน Java)

ด้วยรหัสเล็ก ๆ น้อย ๆ คุณสามารถสำรวจผ่านแบบเต็ม (บังคับ!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;

13
เพียงเพื่อสังเกตข้อเสียเล็กน้อยของช่วงที่อิงตามลูป : คุณไม่สามารถใช้กับ#pragma omp parallel for.
liborm

2
ฉันชอบรุ่นกะทัดรัดเพราะมีโค้ดให้อ่านน้อยกว่า เมื่อคุณปรับสภาพจิตใจแล้วคุณจะเข้าใจได้ง่ายขึ้นและจุดบกพร่องก็โดดเด่นมากขึ้น นอกจากนี้ยังทำให้ชัดเจนมากขึ้นเมื่อมีการทำซ้ำที่ไม่ได้มาตรฐานเกิดขึ้นเนื่องจากมีโค้ดที่ใหญ่กว่ามาก
Code Abominator

94

วิธีที่สะอาดที่สุดในการวนซ้ำผ่านเวกเตอร์คือการทำซ้ำ:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

หรือ (เทียบเท่ากับข้างต้น)

for (auto & element : vector) {
    element.doSomething ();
}

ก่อนหน้า C ++ 0x คุณต้องแทนที่อัตโนมัติด้วยประเภทตัววนซ้ำและใช้ฟังก์ชันสมาชิกแทนฟังก์ชันส่วนกลางเริ่มต้นและสิ้นสุด

นี่อาจเป็นสิ่งที่คุณได้เห็น เมื่อเทียบกับแนวทางที่คุณพูดถึงข้อดีคือคุณไม่ได้ขึ้นอยู่กับประเภทของvector. หากคุณเปลี่ยนvectorเป็นคลาส "ประเภทคอลเลกชัน" อื่นรหัสของคุณอาจยังใช้งานได้ อย่างไรก็ตามคุณสามารถทำสิ่งที่คล้ายกันใน Java ได้เช่นกัน ไม่มีความแตกต่างทางแนวคิดมากนัก อย่างไรก็ตาม C ++ ใช้เทมเพลตเพื่อใช้งานสิ่งนี้ (เมื่อเทียบกับ generics ใน Java) ดังนั้นวิธีการนี้จะใช้ได้กับทุกประเภทที่มีการกำหนดฟังก์ชันbeginและendแม้กระทั่งสำหรับประเภทที่ไม่ใช่คลาสเช่นอาร์เรย์แบบคงที่ ดูที่นี่: range-based สำหรับทำงานสำหรับอาร์เรย์ธรรมดาอย่างไร


5
อัตโนมัติเริ่มต้น / สิ้นสุดฟรียังเป็น C ++ 11 และคุณควรใช้ ++ แทน ++ ในหลาย ๆ กรณี
ForEveR

ใช่คุณพูดถูก. การใช้งานbeginและendอย่างไรก็ตามเป็นซับเดียว
JohnB

@JohnB มันเป็นซับมากกว่าหนึ่งเพราะมันใช้งานได้กับอาร์เรย์ขนาดคงที่ด้วย autoในทางกลับกันจะค่อนข้างยุ่งยาก
juanchopanza

หากคุณต้องการมันสำหรับเวกเตอร์เพียงแค่ซับเดียว
JohnB

อย่างไรก็ตามตัวอย่างแรกยังทำให้เข้าใจผิดเนื่องจากไม่สามารถทำงานใน C ++ 03 ได้ในขณะที่วลีของคุณแนะนำว่าเป็นเช่นนั้น
juanchopanza

35

วิธีที่ถูกต้องคือ:

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

โดยที่ T คือประเภทของคลาสภายในเวกเตอร์ ตัวอย่างเช่นถ้าคลาสคือ CActivity ให้เขียน CActivity แทน T.

วิธีการประเภทนี้จะใช้ได้กับทุก STL (ไม่ใช่เฉพาะเวกเตอร์เท่านั้นที่ดีกว่าเล็กน้อย)

หากคุณยังต้องการใช้ดัชนีวิธีคือ:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}

ไม่std::vector<T>::size_typeเสมอไปsize_t? นั่นเป็นประเภทที่ฉันใช้เสมอ
Violet Giraffe

1
@VioletGiraffe ฉันค่อนข้างมั่นใจว่าคุณพูดถูก (ยังไม่ได้ตรวจสอบจริงๆ) แต่ควรใช้ std :: vector <T> :: size_type ดีกว่า
DiGMi

8

มีเหตุผลที่ชัดเจนสองประการในการใช้ตัวทำซ้ำซึ่งบางส่วนมีการกล่าวถึงที่นี่:

การเปลี่ยนคอนเทนเนอร์ในภายหลังจะไม่ทำให้โค้ดของคุณเป็นโมฆะ

กล่าวคือถ้าคุณเปลี่ยนจาก std :: vector เป็น std :: list หรือ std :: set คุณจะไม่สามารถใช้ดัชนีตัวเลขเพื่อรับค่าที่มีอยู่ได้ การใช้ตัววนซ้ำยังใช้ได้

รันไทม์ตรวจจับการวนซ้ำที่ไม่ถูกต้อง

หากคุณแก้ไขคอนเทนเนอร์ของคุณกลางวงในครั้งต่อไปที่คุณใช้ตัววนซ้ำมันจะทำให้เกิดข้อยกเว้นตัววนซ้ำที่ไม่ถูกต้อง


1
คุณช่วยชี้ไปที่บทความ / โพสต์ของเราที่อธิบายประเด็นข้างต้นพร้อมโค้ดตัวอย่างได้ไหม จะดี! หรือถ้าจะเพิ่มก็ได้ :)
อนุ

5

ฉันไม่แปลกใจเลยที่ไม่มีใครพูดถึงว่าการวนซ้ำผ่านอาร์เรย์ด้วยดัชนีจำนวนเต็มทำให้คุณเขียนโค้ดผิดพลาดได้ง่ายโดยการสมัครอาร์เรย์ด้วยดัชนีที่ไม่ถูกต้อง ตัวอย่างเช่นหากคุณมีลูปซ้อนกันโดยใช้iและjเป็นดัชนีคุณอาจไม่ถูกต้องในการสมัครสมาชิกอาร์เรย์ด้วยjแทนที่จะiแนะนำข้อบกพร่องในโปรแกรม

ในทางตรงกันข้ามรูปแบบอื่น ๆ ที่แสดงในที่นี้ ได้แก่ การforวนซ้ำตามช่วงและตัวทำซ้ำมีโอกาสเกิดข้อผิดพลาดน้อยกว่ามาก ความหมายของภาษาและกลไกการตรวจสอบประเภทของคอมไพเลอร์จะป้องกันไม่ให้คุณเข้าถึงอาร์เรย์โดยไม่ได้ตั้งใจโดยใช้ดัชนีที่ไม่ถูกต้อง


4

ด้วย STL โปรแกรมเมอร์ใช้iteratorsสำหรับการสำรวจผ่านคอนเทนเนอร์เนื่องจากตัววนซ้ำเป็นแนวคิดนามธรรมที่นำไปใช้ในคอนเทนเนอร์มาตรฐานทั้งหมด ตัวอย่างเช่นstd::listไม่มีoperator []เลย


4

การใช้ตัวดำเนินการอัตโนมัติทำให้ง่ายต่อการใช้งานเนื่องจากไม่ต้องกังวลเกี่ยวกับประเภทข้อมูลและขนาดของเวกเตอร์หรือโครงสร้างข้อมูลอื่น ๆ

การทำซ้ำเวกเตอร์โดยใช้อัตโนมัติและสำหรับการวนซ้ำ

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

เอาท์พุต:

1 2 3 4 5

คุณยังสามารถใช้วิธีนี้เพื่อวนซ้ำชุดและรายการ การใช้อัตโนมัติจะตรวจจับประเภทข้อมูลที่ใช้ในเทมเพลตและให้คุณใช้งานได้ ดังนั้นแม้ว่าเรามีvectorของstringหรือcharไวยากรณ์เดียวกันจะทำงานได้ดี


3

วิธีที่ถูกต้องในการวนซ้ำและพิมพ์ค่ามีดังนี้:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}


0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;

-1

ถ้าคุณใช้

std::vector<std::reference_wrapper<std::string>> names{ };

อย่าลืมเมื่อคุณใช้ auto ใน for loop เพื่อใช้ get เช่นนี้:

for (auto element in : names)
{
    element.get()//do something
}

นั่นไม่ใช่ไวยากรณ์ที่ถูกต้อง
John Doe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.