Bagaimana cara menginisialisasi memori dengan operator baru di C ++?

176

Saya baru mulai masuk ke C ++ dan saya ingin mengambil beberapa kebiasaan baik. Jika saya baru saja mengalokasikan array tipe intdengannew operator, bagaimana saya bisa menginisialisasinya menjadi 0 tanpa melalui semuanya sendiri? Haruskah saya gunakan memset? Apakah ada cara "C ++" untuk melakukannya?

dreamlax
sumber
19
Jika Anda ingin mengambil kebiasaan C ++ yang baik, maka hindari menggunakan array secara langsung dan gunakan vektor. Vektor akan menginisialisasi semua item terlepas dari jenisnya, dan Anda tidak perlu mengingat untuk memanggil operator delete [].
brianegge
@ brianegge: Bagaimana jika saya harus melewatkan array ke fungsi C eksternal, bisakah saya memberikan vektornya?
dreamlax
12
Anda bisa lulus &vector[0].
jamesdlin
Tentu saja, ketika Anda meneruskan array ke fungsi C, Anda biasanya harus menentukan pointer ke elemen pertama, & vektor [0] seperti kata @jamesdlin, dan ukuran array, disediakan dalam kasus ini oleh vector.size ().
Trebor Rude
Terkait (meminta jenis non-array): stackoverflow.com/questions/7546620/…
Aconcagua

Jawaban:

392

Ini adalah fitur yang sangat sedikit diketahui dari C ++ (sebagaimana dibuktikan oleh fakta bahwa belum ada yang memberikan ini sebagai jawaban), tetapi sebenarnya memiliki sintaks khusus untuk menginisialisasi nilai array:

new int[10]();

Perhatikan bahwa Anda harus menggunakan tanda kurung kosong - Anda tidak dapat, misalnya, menggunakan(0) atau apa pun lainnya (itulah sebabnya ini hanya berguna untuk inisialisasi nilai).

Ini secara eksplisit diizinkan oleh ISO C ++ 03 5.3.4 [expr.new] / 15, yang mengatakan:

Ekspresi baru yang menciptakan objek tipe T menginisialisasi objek tersebut sebagai berikut:

...

  • Jika inisialisasi-baru dari bentuk (), item diinisialisasi-nilai (8,5);

dan tidak membatasi jenis yang diizinkan ini, sedangkan (expression-list)formulir secara eksplisit dibatasi oleh aturan lebih lanjut di bagian yang sama sehingga tidak mengizinkan jenis array.

Pavel Minaev
sumber
1
Meskipun saya setuju bahwa ini hanya sedikit diketahui, saya tidak bisa (sepenuhnya) setuju bahwa itu benar-benar sangat mengejutkan - itu ditambahkan dalam C ++ 03, yang kebanyakan orang tampaknya hampir mengabaikan (karena ini adalah salah satu dari beberapa hal baru itu memang menambah).
Jerry Coffin
2
@ Jerry: Saya harus mengakui bahwa saya belum tahu (mungkin karena ketika saya harus membaca standar, itu sudah C ++ 03). Yang mengatakan, itu luar biasa bahwa semua implementasi yang saya tahu mendukung ini (saya kira itu karena sangat sepele untuk diimplementasikan).
Pavel Minaev
2
Ya, itu sangat sepele untuk diterapkan. Sejauh yang baru, semua "inisialisasi nilai" adalah baru di C ++ 03.
Jerry Coffin
34
Dalam C ++ 11 Anda dapat menggunakan initializtion seragam juga: new int[10] {}. Anda juga dapat memberikan nilai untuk menginisialisasi dengan:new int[10] {1,2,3}
bames53
Tolong jangan bingung standar-diinisialisasi dengan nilai-diinisialisasi: Keduanya didefinisikan dengan jelas dalam standar dan inisialisasi yang berbeda.
Deduplicator
25

Dengan asumsi bahwa Anda benar-benar menginginkan array dan bukan std :: vector, "C ++ way" adalah ini

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

Tetapi perlu diketahui bahwa di bawah tenda ini sebenarnya masih hanya sebuah loop yang menetapkan setiap elemen ke 0 (benar-benar tidak ada cara lain untuk melakukannya, kecuali arsitektur khusus dengan dukungan tingkat perangkat keras).

Tyler McHenry
sumber
Saya tidak keberatan jika loop diimplementasikan di bawah fungsi, saya hanya ingin tahu apakah saya harus mengimplementasikan loop tersebut sendiri. Terima kasih atas tipnya.
dreamlax
4
Anda mungkin akan terkejut. Saya dulu. Pada STL saya (baik GCC dan Dinkumware), std :: copy benar-benar berubah menjadi memcpy jika mendeteksi itu dipanggil dengan tipe bawaan. Saya tidak akan terkejut jika std :: fill_n menggunakan memset.
Brian Neal
2
Nggak. Gunakan 'Nilai-Inisialisasi' untuk mengatur semua anggota menjadi 0.
Martin York
24

Ada sejumlah metode untuk mengalokasikan array tipe intrinsik dan semua metode ini benar, meskipun yang mana untuk dipilih, tergantung ...

Inisialisasi manual semua elemen dalam lingkaran

int* p = new int[10];
for (int i = 0; i < 10; i++)
{
    p[i] = 0;
}

Menggunakan std::memsetfungsi dari<cstring>

int* p = new int[10];
std::memset(p, 0, sizeof(int) * 10);

Menggunakan std::fill_nalgoritma dari<algorithm>

int* p = new int[10];
std::fill_n(p, 10, 0);

Menggunakan std::vectorwadah

std::vector<int> v(10); // elements zero'ed

Jika C ++ 0x tersedia, gunakan fitur daftar penginisialisasi

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime
mloskot
sumber
1
seharusnya menjadi vektor <int> Jika Anda menambahkan p = new int [10] () Anda memiliki daftar lengkap.
karsten
@mloskot, dalam kasus pertama di mana Anda telah menginisialisasi array menggunakan "baru", bagaimana pass by reference akan terjadi? Jika saya menggunakan int array[SIZE] ={1,2,3,4,5,6,7};notasi, saya bisa menggunakan void rotateArray(int (& input)[SIZE], unsigned int k);deklarasi fungsi saya, apa yang akan terjadi ketika menggunakan konvensi pertama? ada saran?
Anu
1
Saya khawatir contoh dengan std::memsetsalah - Anda lulus 10, sepertinya mengharapkan jumlah byte - lihat en.cppreference.com/w/cpp/string/byte/memset . (Saya pikir ini dengan baik menunjukkan mengapa kita harus menghindari konstruksi tingkat rendah seperti itu bila mungkin.)
Suma
@Suma Tangkapan hebat! Tetap. Ini tampaknya menjadi kandidat untuk bug berusia satu dekade :-) Ya, saya setuju dengan komentar Anda.
mloskot
7

Jika memori yang Anda alokasikan adalah kelas dengan konstruktor yang melakukan sesuatu yang bermanfaat, operator yang baru akan memanggil konstruktor itu dan membiarkan objek Anda diinisialisasi.

Tetapi jika Anda mengalokasikan POD atau sesuatu yang tidak memiliki konstruktor yang menginisialisasi keadaan objek, maka Anda tidak dapat mengalokasikan memori dan menginisialisasi memori itu dengan operator yang baru dalam satu operasi. Namun, Anda memiliki beberapa opsi:

1) Gunakan variabel tumpukan sebagai gantinya. Anda dapat mengalokasikan dan menginisialisasi default dalam satu langkah, seperti ini:

int vals[100] = {0};  // first element is a matter of style

2) gunakan memset(). Perhatikan bahwa jika objek yang Anda alokasikan bukan POD alokasikan , memsetting itu adalah ide yang buruk. Salah satu contoh spesifik adalah jika Anda memset kelas yang memiliki fungsi virtual, Anda akan menerbangkan vtable dan meninggalkan objek Anda dalam keadaan tidak dapat digunakan.

3) Banyak sistem operasi memiliki panggilan yang melakukan apa yang Anda inginkan - mengalokasikan pada tumpukan dan menginisialisasi data untuk sesuatu. Contoh Windows akan menjadiVirtualAlloc()

4) Ini biasanya merupakan pilihan terbaik. Hindari mengatur memori sendiri. Anda dapat menggunakan wadah STL untuk melakukan apa saja yang Anda lakukan dengan memori mentah, termasuk mengalokasikan dan menginisialisasi semua dalam satu gerakan:

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero
John Dibling
sumber
6

Ya ada:

std::vector<int> vec(SIZE, 0);

Gunakan vektor alih-alih array yang dialokasikan secara dinamis. Manfaatnya termasuk tidak perlu repot menghapus array secara eksplisit (itu dihapus ketika vektor keluar dari ruang lingkup) dan juga bahwa memori secara otomatis dihapus bahkan jika ada pengecualian yang dilemparkan.

Sunting: Untuk menghindari downvotes drive-by lebih lanjut dari orang-orang yang tidak repot-repot membaca komentar di bawah, saya harus membuatnya lebih jelas bahwa jawaban ini tidak mengatakan bahwa vektor selalu merupakan jawaban yang tepat. Tapi itu jelas merupakan cara yang lebih C ++ daripada "secara manual" memastikan untuk menghapus array.

Sekarang dengan C ++ 11, ada juga std :: array yang memodelkan array ukuran konstan (vs vektor yang dapat tumbuh). Ada juga std :: unique_ptr yang mengelola array yang dialokasikan secara dinamis (yang dapat dikombinasikan dengan inisialisasi seperti dijawab dalam jawaban lain untuk pertanyaan ini). Semua itu adalah cara yang lebih C ++ daripada secara manual menangani pointer ke array, IMHO.

villintehaspam
sumber
11
ini tidak benar-benar menjawab pertanyaan yang diajukan.
John Knoeller
1
Haruskah saya selalu menggunakan std::vectoralih-alih array yang dialokasikan secara dinamis? Apa manfaat menggunakan array di atas vektor, dan sebaliknya?
dreamlax
1
@ John Knoller: OP bertanya tentang cara C ++ untuk melakukannya, saya akan mengatakan bahwa vektor adalah cara c ++ untuk melakukan ini. Tentu saja, Anda benar bahwa mungkin ada situasi yang masih membutuhkan array sederhana dan tidak mengetahui situasi OP ini. Saya kira tidak, karena tampaknya masuk akal bahwa OP tidak tahu tentang vektor.
villintehaspam
1
@villintehaspam: Meskipun solusi ini tidak menjawab pertanyaan saya, ini adalah jalan yang akan saya ambil. Tyler McHenry menjawab pertanyaan saya lebih langsung, dan seharusnya membantu terutama bagi orang-orang yang tidak bisa — untuk alasan apa pun — menggunakan std::vector.
dreamlax
2
@villintehaspam: Tidak, ini bukan cara C ++ untuk melakukannya. Ini adalah cara Jawa untuk melakukannya. Menempel di vectormana-mana terlepas dari konteksnya disebut "Menulis kode Java dalam C ++".
AnT
2

std::filladalah satu arah. Mengambil dua iterator dan nilai untuk mengisi wilayah. Itu, atau untuk loop, akan (saya kira) menjadi lebih banyak cara C ++.

Untuk mengatur larik tipe integer primitif ke 0 secara khusus, memsettidak masalah, meskipun dapat menaikkan alis. Pertimbangkan juga calloc, meskipun agak tidak nyaman untuk digunakan dari C ++ karena para pemain.

Bagi saya, saya cukup sering menggunakan loop.

(Saya tidak suka menebak-nebak niat orang, tetapi memang benar bahwa std::vector, semua hal dianggap sama, lebih baik daripada menggunakan new[].)


sumber
1

Anda selalu dapat menggunakan memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
Gregor Brandt
sumber
Saya mengerti bahwa saya dapat menggunakan memset, tetapi saya tidak yakin apakah ini adalah cara C ++ untuk mendekati masalah.
dreamlax
1
Ini sebenarnya bukan 'cara C ++', tapi kemudian tidak ada array mentah.
Pete Kirkham
1
@ gbrandt: Artinya tidak berfungsi dengan baik di C atau C ++. Ini berfungsi untuk sebagian besar nilai dari tipe ini adalah char atau unsigned char. Ini berfungsi untuk sebagian besar jenis nilai yaitu 0 (setidaknya di sebagian besar implementasi). Kalau tidak, umumnya tidak berguna.
Jerry Coffin
1
10 * sizeof( *myArray )lebih terdokumentasi dan tahan terhadap perubahan 10 * sizeof( int ).
Kevin Reid
1
Bagaimanapun, OP memiliki array mentah dan memset adalah cara tercepat dan termudah untuk nol array itu.
Gregor Brandt
-1

Biasanya untuk daftar item yang dinamis, Anda menggunakan a std::vector.

Secara umum saya menggunakan memset atau loop untuk alokasi dinamis memori mentah, tergantung pada bagaimana variabel saya mengantisipasi area kode di masa depan.

Paul Nathan
sumber