Di C ++ 11 Anda dapat menggunakan berbasis jangkauan for
, yang bertindak sebagai foreach
bahasa lain. Ia bekerja bahkan dengan array C biasa:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
Bagaimana cara mengetahui kapan harus berhenti? Apakah ini hanya bekerja dengan array statis yang telah dideklarasikan dalam lingkup yang sama dengan for
yang digunakan? Bagaimana Anda akan menggunakan ini for
dengan array dinamis?
for
. Tapi saat array itu meluruh menjadi pointer, informasi ukuran hilang.numbers
issizeof(numbers)/sizeof(int)
, misalnya.Jawaban:
Ini berfungsi untuk ekspresi apa pun yang tipenya adalah array. Sebagai contoh:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Untuk penjelasan yang lebih mendetail, jika jenis ekspresi yang diteruskan di sebelah kanan
:
adalah jenis array, maka loop akan beriterasi dariptr
keptr + size
(ptr
menunjuk ke elemen pertama dari array,size
menjadi jumlah elemen array).Ini berbeda dengan tipe yang ditentukan pengguna, yang bekerja dengan mencari
begin
danend
sebagai anggota jika Anda meneruskan objek kelas atau (jika tidak ada anggota yang dipanggil dengan cara itu) fungsi non-anggota. Fungsi-fungsi tersebut akan menghasilkan iterator awal dan akhir (masing-masing menunjuk langsung setelah elemen terakhir dan awal urutan).Pertanyaan ini menjelaskan mengapa perbedaan itu ada.
sumber
begin
`end. It just happens that
std :: begin`std::end
gunakan fungsi anggota, dan akan digunakan jika kecocokan yang lebih baik tidak tersedia.Saya pikir bagian terpenting dari pertanyaan ini adalah, bagaimana C ++ tahu apa ukuran array (setidaknya saya ingin mengetahuinya ketika saya menemukan pertanyaan ini).
C ++ mengetahui ukuran sebuah larik, karena itu adalah bagian dari definisi larik - ini adalah tipe dari variabelnya. Seorang compiler harus mengetahui tipenya.
Karena C ++ 11
std::extent
dapat digunakan untuk mendapatkan ukuran array:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Tentu saja, ini tidak masuk akal, karena Anda harus secara eksplisit memberikan ukuran di baris pertama, yang kemudian Anda dapatkan di baris kedua. Tapi Anda juga bisa menggunakan
decltype
dan kemudian menjadi lebih menarik:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
sumber
Menurut Draf Kerja C ++ terbaru (n3376), pernyataan ranged for setara dengan berikut ini:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Jadi ia tahu bagaimana menghentikan dengan cara yang sama seperti
for
loop biasa yang menggunakan iterator.Saya pikir Anda mungkin mencari sesuatu seperti berikut ini untuk menyediakan cara menggunakan sintaks di atas dengan array yang hanya terdiri dari pointer dan ukuran (array dinamis):
template <typename T> class Range { public: Range(T* collection, size_t size) : mCollection(collection), mSize(size) { } T* begin() { return &mCollection[0]; } T* end () { return &mCollection[mSize]; } private: T* mCollection; size_t mSize; };
Template kelas ini kemudian dapat digunakan untuk membuat rentang, di mana Anda dapat mengulang menggunakan sintaks ranged untuk yang baru . Saya menggunakan ini untuk menjalankan semua objek animasi dalam sebuah adegan yang diimpor menggunakan pustaka yang hanya mengembalikan pointer ke array dan ukuran sebagai nilai terpisah.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
Sintaks ini, menurut pendapat saya, jauh lebih jelas daripada apa yang akan Anda gunakan
std::for_each
ataufor
loop biasa .sumber
Ia tahu kapan harus berhenti karena ia tahu batas-batas array statis.
Saya tidak yakin apa yang Anda maksud dengan "array dinamis", dalam hal apa pun, jika tidak melakukan iterasi pada array statis, secara informal, kompilator mencari nama
begin
danend
dalam lingkup kelas objek yang Anda iterasi, atau lihat up untukbegin(range)
danend(range)
menggunakan pencarian yang bergantung pada argumen dan menggunakannya sebagai iterator.Untuk informasi lebih lanjut, dalam standar C ++ 11 (atau draf publiknya), "6.5.4
for
Pernyataan berbasis rentang ", hal.145sumber
new[]
. Dalam hal ini, Anda hanya memiliki pointer tanpa indikasi ukurannya, jadi tidak ada cara untuk range-basedfor
untuk bekerja dengannya.Apakah itu dibaca sebagai, " Katakan padaku apa yang dilakukan ranged-untuk (dengan array)? "
Saya akan menjawab dengan asumsi bahwa - Ambil contoh berikut menggunakan array bersarang:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Versi teks:
ia
adalah larik larik ("larik bersarang"), berisi[3]
larik, dengan masing-masing berisi[4]
nilai. Contoh di atas mengulang melaluiia
'range' ([3]
) primernya , dan oleh karena itu mengulang[3]
kali. Setiap loop menghasilkan salah satuia
's[3]
nilai utama mulai dari yang pertama dan berakhir dengan yang terakhir - Sebuah array yang mengandung[4]
nilai-nilai.pl
sama dengan{1,2,3,4}
- Arraypl
sama dengan{5,6,7,8}
- Arraypl
sama dengan{9,10,11,12}
- Sebuah arraySebelum kami menjelaskan prosesnya, berikut adalah beberapa pengingat ramah tentang array:
pl
harus menjadi referensi karena kita tidak dapat menyalin arrayn
bilangan yang dimaksud, makaia[n]
sama dengan*(ia+n)
(Kami mendereferensi alamat yang merupakann
entri maju), dania+n
sama dengan&ia[n]
(Kami mendapatkan alamat dari entri itu dalam larik).Inilah yang terjadi:
pl
diatur sebagai referensi untukia[n]
, dengann
menyamai jumlah loop arus mulai dari 0. Jadi,pl
adalahia[0]
pada putaran pertama, pada kedua ituia[1]
, dan sebagainya. Ini mengambil nilai melalui iterasi.ia+n
kurang dariend(ia)
.... Dan itu saja.
Ini benar-benar hanya cara yang disederhanakan untuk menulis ini :
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int n = 0; n != 3; ++n) auto &pl = ia[n];
Jika array Anda tidak bersarang, maka proses ini menjadi sedikit lebih sederhana karena referensi tidak diperlukan, karena nilai yang diiterasi bukanlah array, melainkan nilai 'normal':
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Beberapa informasi tambahan
Bagaimana jika kami tidak ingin menggunakan
auto
kata kunci saat membuatpl
? Seperti apa kelihatannya?Dalam contoh berikut,
pl
mengacu pada filearray of four integers
. Pada setiap looppl
diberi nilaiia[n]
:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
Dan ... Begitulah cara kerjanya, dengan informasi tambahan untuk menghilangkan kebingungan. Ini hanya
for
loop 'singkat' yang secara otomatis dihitung untuk Anda, tetapi tidak memiliki cara untuk mengambil loop saat ini tanpa melakukannya secara manual.sumber
Beberapa kode contoh untuk menunjukkan perbedaan antara array di Stack vs array di Heap
/** * Question: Can we use range based for built-in arrays * Answer: Maybe * 1) Yes, when array is on the Stack * 2) No, when array is the Heap * 3) Yes, When the array is on the Stack, * but the array elements are on the HEAP */ void testStackHeapArrays() { int Size = 5; Square StackSquares[Size]; // 5 Square's on Stack int StackInts[Size]; // 5 int's on Stack // auto is Square, passed as constant reference for (const auto &Sq : StackSquares) cout << "StackSquare has length " << Sq.getLength() << endl; // auto is int, passed as constant reference // the int values are whatever is in memory!!! for (const auto &I : StackInts) cout << "StackInts value is " << I << endl; // Better version would be: auto HeapSquares = new Square[Size]; Square *HeapSquares = new Square[Size]; // 5 Square's on Heap int *HeapInts = new int[Size]; // 5 int's on Heap // does not compile, // *HeapSquares is a pointer to the start of a memory location, // compiler cannot know how many Square's it has // for (auto &Sq : HeapSquares) // cout << "HeapSquare has length " << Sq.getLength() << endl; // does not compile, same reason as above // for (const auto &I : HeapInts) // cout << "HeapInts value is " << I << endl; // Create 3 Square objects on the Heap // Create an array of size-3 on the Stack with Square pointers // size of array is known to compiler Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)}; // auto is Square*, passed as constant reference for (const auto &Sq : HeapSquares2) cout << "HeapSquare2 has length " << Sq->getLength() << endl; // Create 3 int objects on the Heap // Create an array of size-3 on the Stack with int pointers // size of array is known to compiler int *HeapInts2[]{new int(23), new int(57), new int(99)}; // auto is int*, passed as constant reference for (const auto &I : HeapInts2) cout << "HeapInts2 has value " << *I << endl; delete[] HeapSquares; delete[] HeapInts; for (const auto &Sq : HeapSquares2) delete Sq; for (const auto &I : HeapInts2) delete I; // cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack }
sumber