C ++ 'ta yeni operatör ile bellek nasıl başlatılır?


176

C ++ 'a girmeye başladım ve bazı iyi alışkanlıklar kazanmak istiyorum. Ben sadece operatör intile tür bir dizi newayırdıysanız, hepsini kendim döngü olmadan hepsini 0 olarak nasıl başlatabilirim? Sadece kullanmalı mıyım memset? Bunu yapmanın bir “C ++” yolu var mı?


19
İyi bir C ++ alışkanlığı almak istiyorsanız, doğrudan dizileri kullanmaktan kaçının ve bunun yerine vektörü kullanın. Vector, türden bağımsız olarak tüm öğeleri başlatır ve ardından delete [] operatörünü çağırmayı hatırlamanız gerekmez.
brianegge

@brianegge: Bir diziyi harici bir C işlevine geçirmem gerekirse, ona vektörü verebilir miyim?
dreamlax

12
Geçebilirsin &vector[0].
jamesdlin

Elbette, dizileri C işlevlerine geçirdiğinizde, tipik olarak ilk öğeye işaretçiyi belirtmeniz ve @jamesdlin'in söylediği gibi [0] ve bu durumda vector.size () tarafından sağlanan dizinin boyutunu belirtmeniz gerekir.
Trebor Rude

İlgili (dizi dışı türleri sorar): stackoverflow.com/questions/7546620/…
Aconcagua

Yanıtlar:


392

C ++'ın şaşırtıcı derecede az bilinen bir özelliği (hiç kimsenin bunu henüz bir cevap olarak vermediği kanıtlandığı gibi), ancak aslında bir diziyi değerlendirmek için özel bir sözdizimine sahiptir:

new int[10]();

Boş parantezleri kullanmanız gerektiğini unutmayın; örneğin,(0) başka bir şey (bu nedenle bu yalnızca değer başlatma için yararlıdır).

Buna ISO C ++ 03 5.3.4 [expr.new] / 15 tarafından açıkça izin verilmektedir:

Tür nesnesi oluşturan yeni ifade T bu nesneyi aşağıdaki gibi başlatır:

...

  • Yeni başlatıcı formdaysa, ()öğe değerle başlatılır (8.5);

ve buna izin verilen türleri kısıtlamaz, (expression-list)form aynı bölümdeki diğer kurallar tarafından dizi türlerine izin vermeyecek şekilde açıkça kısıtlanır.


1
Bunun çok az bilinen olduğunu kabul etsem de, (tamamen) gerçekten çok şaşırtıcı olduğunu kabul edemiyorum - çoğu insanın neredeyse görmezden geldiği görünen C ++ 03'e eklendi (bu birkaç yeni şeyden biri olduğu için) ekledi).
Jerry Coffin

2
@ Jerry: Henüz bilmediğimi itiraf etmeliyim (muhtemelen standardı okuduğumda zaten C ++ 03 idi). Bununla birlikte, bildiğim tüm uygulamaların bunu desteklemesi dikkat çekicidir (sanırım bunu uygulamak çok önemsizdir).
Pavel Minaev

2
Evet, uygulanması oldukça önemsiz. Yeni olduğu sürece, tüm "değer başlatma" C ++ 03'te yeniydi.
Jerry Coffin

34
C ++ 11 yılında siz de üniforma initializtion kullanabilirsiniz: new int[10] {}. Ayrıca şu değerlerle başlatmak için değerler de sağlayabilirsiniz:new int[10] {1,2,3}
bames53

Lütfen varsayılan başlatmayı değer başlatılmış ile karıştırmayın: Her ikisi de standartta açıkça tanımlanmıştır ve farklı başlatmalardır.
Tekilleştirici

25

Gerçekten std :: vector değil bir dizi istediğinizi varsayarsak, "C ++ yolu"

#include <algorithm> 

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

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

Ancak, kaputun altında bunun aslında her öğeyi 0'a atayan bir döngü olduğunu unutmayın (bunu yapmanın gerçekten başka bir yolu yoktur, donanım düzeyinde desteğe sahip özel bir mimariyi engeller).


Döngünün bir fonksiyonun altına uygulanıp uygulanmadığını umursamıyorum, sadece böyle bir döngüyü kendim uygulamak zorunda olup olmadığımı bilmek istedim. Bahşiş için teşekkürler.
dreamlax

4
Şaşırmış olabilirsiniz. Ben ... idim. STL'de (hem GCC hem de Dinkumware), std :: copy, yerleşik türlerle çağrıldığını algılarsa aslında bir memcpy'ye dönüşür. Eğer std :: fill_n memset kullansaydı şaşırmazdım.
Brian Neal

2
Hayır! Tüm üyeleri 0 olarak ayarlamak için 'Değer Başlatma'yı kullanın.
Martin York

24

İçsel tip bir dizi tahsis etmek için birkaç yöntem vardır ve bu yöntemlerin hepsi doğru olsa da, hangisini seçmek için bağlıdır ...

Döngüdeki tüm elemanların manuel olarak başlatılması

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

Kullanımı std::memsetgelen işlevi<cstring>

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

std::fill_nAlgoritmasını kullanma<algorithm>

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

std::vectorKonteyner kullanma

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

Eğer C ++ 0x mevcut kullanarak başlatıcı listesi özellikleri

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

1
<int> vektörü olmalıdır. p = new int [10] () eklediyseniz, tam bir listeniz vardı.
karsten

@loskot, "new" kullanarak bir dizi başlattığınız ilk durumda, referansla nasıl geçiş yapılır? Eğer int array[SIZE] ={1,2,3,4,5,6,7};gösterim kullansaydım void rotateArray(int (& input)[SIZE], unsigned int k);, işlev beyannamem olarak kullanabilirdim , ilk sözleşmeyi kullanırken ne olurdu? herhangi bir öneri?
Anu

1
Korkarım ki örnek std::memsetyanlış - 10'u geçiyorsunuz , bayt sayısı bekleniyor - bkz. En.cppreference.com/w/cpp/string/byte/memset . (Sanırım bu neden mümkün olduğu kadar düşük seviyeli bir yapıdan kaçınmalı?)
Suma

@Suma Harika yakala! Sabit. Bu on yıllık bir hata için aday gibi görünüyor :-) Evet, yorumunuzu kabul ediyorum.
mloskot

7

Ayırdığınız bellek, yararlı bir şey yapan bir kurucuya sahip bir sınıfsa, yeni işleç bu kurucuyu çağırır ve nesnenizi başlatılmış olarak bırakır.

Ancak , nesnenin durumunu başlatan bir yapıcıya sahip olmayan bir POD veya bir şey ayırıyorsanız , bellek ayıramaz ve bu belleği bir işlemde yeni operatörle başlatamazsınız. Ancak, birkaç seçeneğiniz vardır:

1) Bunun yerine bir yığın değişkeni kullanın. Tek bir adımda aşağıdaki gibi tahsis edebilir ve varsayılan olarak başlatabilirsiniz :

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

2) kullanın memset(). Tahsis ettiğiniz nesne bir POD değilse, , taklit etmenin kötü bir fikir olduğunu unutmayın. Bunun özel bir örneği, sanal işlevleri olan bir sınıfı takarsanız, vtable'ı uçuracak ve nesnenizi kullanılamaz bir durumda bırakacaksınız.

3) Birçok işletim sistemi, istediğinizi yapan çağrılara sahiptir - bir yığına ayırın ve verileri bir şeye başlatın. Windows örneğiVirtualAlloc()

4) Bu genellikle en iyi seçenektir. Belleği kendiniz yönetmek zorunda kalmayın. Ham bellekle yapacağınız hemen hemen her şeyi yapmak için STL kaplarını kullanabilirsiniz, hepsi bir arada bırakma ve başlatma dahil olmak üzere:

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

6

Evet var:

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

Dinamik olarak ayrılmış bir dizi yerine bir vektör kullanın. Faydaları arasında, diziyi açıkça silmeyle uğraşmak zorunda kalmamak (vektör kapsam dışına çıktığında silinir) ve ayrıca bir istisna olsa bile belleğin otomatik olarak silinmesi yer alır.

Düzenleme: Aşağıdaki yorumları okumak için uğraşmayan kişilerden daha fazla sürmek downvotes önlemek için, bu cevabın vektör her zaman doğru cevap olduğunu söylemediğini daha açık hale getirmek gerekir . Ancak bir diziyi sildiğinizden emin olmak için "manuel" olandan daha fazla C ++ yoludur.

Şimdi C ++ 11 ile, sabit boyutlu bir diziyi modelleyen std :: dizisi de var (büyüyebilen vektöre karşı). Dinamik olarak ayrılmış bir diziyi yöneten std :: unique_ptr de vardır (bu sorunun diğer yanıtlarında yanıtlandığı gibi başlatma ile birleştirilebilir). Bunlardan herhangi biri, işaretçiyi IMHO dizisine elle işlemekten daha C ++ yoludur.


11
bu sorulan soruya gerçekten cevap vermiyor.
John Knoeller

1
std::vectorDinamik olarak ayrılmış diziler yerine her zaman kullanmalı mıyım ? Bir diziyi bir vektör üzerinde kullanmanın faydaları nelerdir?
dreamlax

1
@John Knoller: OP bunu yapmak için bir C ++ yolunu sordu, vektörün bunu yapmanın c ++ yolu olduğunu söyleyebilirim. Tabii ki, hala düz bir dizi gerektirecek durumlar olabilir ve OP'nin durumunu bilmeden bunun bir olabilir. Ama sanırım hayır, çünkü OP'nin vektörler hakkında bir şey bilmediği akla yatkın görünüyor.
villintehaspam

1
@villintehaspam: Bu çözüm sorumu yanıtlamasa da alacağım yol bu. Tyler McHenry sorumu daha doğrudan cevaplıyor ve özellikle -her ne sebeple olursa olsun- kullanamayanlara yardım etmeli std::vector.
dreamlax

2
@villintehaspam: Hayır, bunu yapmanın C ++ yolu değil. Bunu yapmanın Java yoludur. vectorBağlamdan bağımsız olarak her yere yapışmaya "Java kodu C ++ ile yazma" denir.
AnT

2

std::fillbir yoludur. İki yineleyici ve bölgeyi doldurmak için bir değer alır. Bu ya da for döngüsü, (sanırım) daha fazla C ++ yolu olurdu.

Bir dizi ilkel tamsayı türünü özel olarak 0 olarak ayarlamak için memsetiyidir, ancak kaşları artırabilir. Ayrıca düşününcalloc , çünkü döküm nedeniyle C ++ 'dan kullanmak biraz rahatsız edici.

Kendi adıma, hemen hemen her zaman bir döngü kullanıyorum.

(İnsanların niyetlerini ikinci olarak tahmin etmekten hoşlanmıyorum, ancak std::vectorher şeyin eşit olduğu, kullanılmaya tercih edildiği doğrudur new[].)


1

her zaman memset kullanabilirsiniz:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));

Kullanabileceğimi anlıyorum memset, ancak bu soruna yaklaşmak için C ++ yolu olup olmadığından emin değildim.
dreamlax

1
Gerçekten 'C ++ yolu' değil, ama ikisi de ham diziler değil.
Pete Kirkham

1
@gbrandt: Yani C veya C ++ 'da çok iyi çalışmaz. Türün çoğu değeri char veya unsigned char için çalışır. Değer türlerinin çoğu için 0'dır (en azından çoğu uygulamada). Aksi takdirde, genellikle işe yaramaz.
Jerry Coffin

1
10 * sizeof( *myArray )daha fazla dokümante edilmiş ve değişikliğe dayanıklıdır 10 * sizeof( int ).
Kevin Reid

1
Her durumda, OP bir ham diziye sahiptir ve memset bu diziyi sıfırlamanın en hızlı ve en kolay yoludur.
Gregor Brandt

-1

Genellikle dinamik öğe listeleri için a kullanırsınız std::vector.

Genel olarak, değişkenin gelecekte kod alanının ne kadar olacağını tahmin ettiğime bağlı olarak ham bellek dinamik ayırma için memset veya bir döngü kullanıyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.