'Yeni' ve 'sil' C ++ 'da kullanımdan kaldırılıyor mu?


68

Farklı boyutlarda dizi bildirimi içeren bir sınav tökezledi. Aklıma gelen ilk şey şu newkomutla dinamik ayırma kullanmanız gerekecekti , şöyle:

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

Ancak, çözümlerden birinin aşağıdaki duruma izin verdiğini gördüm:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

Biraz araştırma yaptıktan sonra g ++ 'nın buna izin verdiğini okudum, ancak beni düşünmeye devam etti, hangi durumlarda dinamik ayırma kullanmak gerekli? Yoksa derleyici bunu dinamik ayırma olarak mı çeviriyor?

Silme işlevi dahildir. Bununla birlikte, buradaki sorunun bellek sızıntıları ile ilgili olmadığını unutmayın.


54
İkinci örnek, hiçbir zaman C ++ 'ın bir parçası olmayan değişken uzunlukta bir dizi kullanır . Bu durumda std::vectorbunun yerine ( std::vector<int> array(N);) kullanın .
Bazı programcı ahbap

7
Sorunuzun doğrudan yanıtı şu olmalıdır: hayır, kullanımdan kaldırılmıyor. Modern C ++ sürümleri, bellek sahipliği yönetimini (akıllı işaretçiler) basitleştiren birçok özellik sunsa da, nesneleri new OBJdoğrudan çağırarak nesneleri ayırmak hala yaygın bir uygulamadır .
pptaszni

8
İnsanların neden bellek sızıntıları hakkında konuştukları konusunda kafası karışan diğer insanlar için, soruya önemli olmayan bir hatayı düzeltmek için soru düzenlendi
Mike Caron

4
@ Mannoj, yığın ve yığın için Dinamik ve Otomatik terimlerini kullanmayı tercih eder. Nadirdir, ancak yığınları ve yığınlar olmadan C ++ uygulamak mümkündür.
user4581301

1
C ++ 'da hiçbir şey onaylanmamıştır ve hiçbir şey olmayacaktır. Bu C ++ 'nın anlamının bir parçasıdır.
JoelFan

Yanıtlar:


114

Gösterdiğiniz snippet'in hiçbiri deyimsel, modern C ++ kodu değildir.

newve delete(ve new[]ve delete[]) C ++ 'da kullanımdan kaldırılmamıştır ve asla kullanılmayacaktır. Onlar hala dinamik olarak ayrılmış nesne örneğini oluşturmak için bir yol. Ancak, sahip olduğu zaman bir maç için bir ile (ve bir bir ile , onlar sizin için Bunu sağlamak (kitaplık) sınıfları içinde muhafaza en iyisidir). Bkz. C ++ programcıları 'yeni' kullanımını neden en aza indirmeli? .newdeletenew[]delete[]

İlk snippet'iniz bir "çıplak" kullanır new[]ve daha sonra delete[]oluşturulan diziyi asla içermez. Bu bir sorun. std::vectorBurada ihtiyacınız olan her şey gayet iyi. newSahnelerin bir çeşit biçimini kullanacak (uygulama ayrıntılarına dalmayacağım), ancak bakmanız gereken her şey için dinamik bir dizi ama daha iyi ve daha güvenli.

İkinci snippet'iniz, bazı derleyicilerin bir uzantı olarak C ++ 'da izin verdiği bir C özelliği olan "değişken uzunluklu diziler" (VLA) kullanır . Bunun aksine new, VLA'lar esas olarak yığına tahsis edilir (çok sınırlı bir kaynak). Ancak daha da önemlisi, standart bir C ++ özelliği değildir ve taşınabilir olmadıklarından kaçınılmalıdır. Kesinlikle dinamik (yani yığın) tahsisinin yerini almazlar.


3
VLA'lar resmi olarak standartta olmasa da, tüm büyük derleyiciler tarafından desteklendiklerini ve bu nedenle bunlardan kaçınıp kaçınmayacakları kararının, taşınabilirlik için gerçekçi bir endişeden daha fazla bir stil / tercih meselesi olduğunu eklemek isterim.
Stack Tracer

4
Ayrıca, bir diziyi geri döndüremeyeceğinizi veya başka bir yerde saklayamayacağınızı unutmayın, bu nedenle VLA, işlevin yürütme süresinden daha uzun süre dayanmaz
Ruslan

16
@StackTracer Bildiğim kadarıyla MSVC VLA'ları desteklemiyor. Ve MSVC kesinlikle bir "büyük derleyici" dir.
Max Langhof

2
"her zaman yeni bir silme ile eşleşmek zorundasınız" - Qttemel sınıflarının hepsinde çöp toplayıcılar olduğu için newdeğil, çoğu zaman sadece kullanın ve unutun. GUI öğeleri için, üst widget kapalı olduğunda, çocuklar kapsam dışına çıkar ve otomatik olarak çöp toplanır.
vsz

6
@vsz Qt'de bile, her birinin newhala bir eşleşmesi vardır delete; sadece deletes , s ile aynı kod bloğundan ziyade üst widget tarafından yapılır new.
jjramsey

22

Yeni başlayanlar için, new/ deletekullanımdan kaldırılmıyor.

Sizin durumunuzda, tek çözüm onlar değil. Seçtiğiniz şey, "dizi ile bir şeyler yapın" yorumunuz altında nelerin gizlendiğine bağlıdır.

2. örneğiniz, diziyi yığına sığdırmaya çalışan standart olmayan bir VLA uzantısı kullanır. Bunun belirli sınırlamaları vardır - sınırlı boyut ve dizi kapsam dışına çıktıktan sonra bu belleği kullanamama. Dışarı taşıyamazsınız, yığın çözüldükten sonra "kaybolacaktır".

Tek amacınız yerel bir hesaplama yapmak ve ardından verileri atmaksa, aslında işe yarayabilir. Bununla birlikte, hafızayı dinamik olarak, tercihen ile ayırmak daha sağlam bir yaklaşım olacaktır std::vector. Bu şekilde, bir çalışma zamanı değerine dayanarak ihtiyacınız olduğu kadar çok öğe için alan yaratma yeteneğine sahip olursunuz (bu, her şey için gidiyoruz), ancak aynı zamanda kendini güzelce temizler ve taşıyabilirsiniz belleği daha sonra kullanmak için bu kapsamda kullanın.

Başlangıca geri dönmek vector , muhtemelen newbirkaç kat daha derin kullanacaktır , ancak bununla ilgilenmemelisiniz, çünkü sunduğu arayüz çok daha üstündür. Bu anlamda kullanmak newve deletecesaretini kırmak düşünülebilir.


1
"... birkaç kat daha derine" dikkat edin. Kendi kaplarınızı uygulayacak olsanız bile , newve kullanmaktan kaçınmalı delete, aksine akıllı işaretçiler kullanmalısınız std::unique_pointer.
Max

1
aslında denilenstd::unique_ptr
user253751

2
@Max: std::unique_ptr'in varsayılan yıkıcı çağrıları deleteveya delete[]bu, sahip olunan nesnenin C ++ 14'ten beri gizlenmiş olduğu newveya new[]yine de tahsis edilmesi gerektiği anlamına gelir std::make_unique.
Laurent LA RIZZA

15

İkinci örnekleriniz , aslında bir C99 ( C ++! Değil ) özelliği olan, ancak yine de g ++ tarafından desteklenen değişken uzunluklu diziler (VLA'lar) kullanır .

Ayrıca bu cevaba bakınız .

Değişken uzunlukta diziler farklı olduğunu Not new/ deleteve onlara hiçbir şekilde "Kullanımdan kaldırma" yok.

VLA'ların ISO C ++ olmadığını da unutmayın .


13

Modern C ++, dinamik ayırmalarla çalışmanın daha kolay yollarını sunar. Akıllı işaretçiler, atıfta bulunulan veri yapıları kapsam dışına çıkar çıkmaz istisnalar (izin verilirse herhangi bir yerde olabilir) ve erken dönüşlerden sonra temizlemeye dikkat edebilir, bu nedenle bunları kullanmak mantıklı olabilir:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

C ++ 14'ten ayrıca yazabilirsiniz

auto buffer_new = std::make_unique<int[]>(size);

bu daha hoş görünür ve ayırma başarısız olursa bellek sızıntısını önler. C ++ 20'den yapabildiğiniz kadarıyla

auto a = std::make_shared<int[]>(size);

bu benim için hala gcc 7.4.0 ile yazarken derlemiyor. Bu iki örnekte autosoldaki tür bildirimi yerine de kullanıyoruz . Her durumda, diziyi her zamanki gibi kullanın:

buffer_old[0] = buffer_new[0] = 17;

Bellek newiki katından sızıyor ve çöküyor deleteC ++, yıllardır başka dillere geçmenin argümantasyonunun "merkezi noktası" olan bir şeydir. Belki kaçınmak daha iyi.


unique/shared_ptrYapıcılar lehine kaçınmalısınız, make_unique/sharedsadece inşa edilen türü iki kez yazmak autozorunda değilsiniz (kullanarak ), ancak inşaat yarıda başarısız olursa (başarısız olabilecek bir tip kullanıyorsanız ) bellek veya kaynak sızıntısı riskiyle karşılaşmazsınız.
Simon Buchan

2
make_unique, C ++ 14 dizileriyle ve make_shared yalnızca C ++ 20 dizileriyle kullanılabilir. Bu hala nadiren varsayılan bir ayardır, bu nedenle std :: make_shared <int []> (size) önermek beni biraz önceden aradı.
Audrius Meskauskas

Yeterince adil! Çok make_shared<int[]>fazla kullanmıyorum , neredeyse her zaman istediğin zaman vector<int>, ama bilmek güzel.
Simon Buchan

Aşırı bilgiçlik, ancak unique_ptrkurucu IIRC değil , bu nedenle bunun için Tyapıcı olmayan kurucular var, bu nedenle sızıntı riski yoktur unique_ptr(new int[size])ve shared_ptraşağıdakilere sahiptir: "Bir istisna atılırsa, T bir dizi tipi olmadığında p silinir, silin [ ] p aksi. ", böylece aynı etkiye sahipsiniz - risk içindir unique/shared_ptr(new MyPossiblyAllocatingType[size]).
Simon Buchan

3

new ve delete artık kullanılmıyor.

Yeni operatör tarafından oluşturulan nesneler referans ile aktarılabilir. Nesneler sil kullanılarak silinebilir.

yeni ve sil dilin temel özellikleridir. Bir nesnenin kalıcılığı, new ve delete kullanılarak yönetilebilir. Bunlar kesinlikle reddedilmeyecek.

- int dizisi [N] ifadesi, bir diziyi tanımlamanın bir yoludur. Dizi, ekteki kod bloğu kapsamında kullanılabilir. Bir nesnenin başka bir işleve nasıl aktarıldığı gibi geçirilemez.


2

İlk örneğin delete[]sonunda bir a gerekir , yoksa bellek sızıntısı olur.

İkinci örnek, C ++ tarafından desteklenmeyen değişken dizi uzunluğunu kullanır; bu sadece bir dizi uzunluğu sabit ekspresyonuna izin verir .

Bu durumda std::vector<>çözelti olarak kullanılması yararlı olur ; bir dizi üzerinde gerçekleştirebileceğiniz tüm eylemleri bir şablon sınıfına kaydırır.


3
"C ++ 11'e kadar" ile ne demek istiyorsun? Eminim, VLA'lar asla standardın bir parçası olmadı.
churill

c ++ 14 [c ++ 14 standardı] standardına bakın ( isocpp.org/files/papers/N3690.pdf ) sayfa 184 paragraf 8.3.4
Zig Razor

4
Thats değil . Standart, ama sadece bir taslak ve anlarım kadarıyla standart haline yapmadığını “bağlı çalışma zamanının diziler" hakkında parçası cppreference . VLAS orada söz etmez
churill

1
@zigrazor cppreference.com sahip bir liste standartlarının her yayınlanmasından önce / sonra en yakın taslaklara bağlantıların. Yayınlanan standartlar serbestçe mevcut değildir, ancak bu taslaklar çok yakın olmalıdır. Belge numaralarından da görebileceğiniz gibi, bağlantılı taslağınız C ++ 14 için daha eski bir çalışma taslağıdır.
ceviz

2
@learning_dude O edilir değil standartlarına göre destekledi. Cevap (şimdi) doğru (kısa da olsa). Yalnızca sizin için çalışır, çünkü GCC standart olmayan bir uzantı olarak izin verir .
ceviz

-4

Sözdizimi C ++ gibi görünüyor, ancak deyim eski Algol60'a benziyor. Bunun gibi kod blokları olması yaygındı:

read n;
begin
    integer array x[1:n];
    ... 
end;

Örnek şu şekilde yazılabilir:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

Bunu şu anki dillerde kaçırı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.