Birkaç yol vardır, ancak önce nesne temizlemenin neden önemli olduğunu anlamanız gerekir ve bu nedenle neden std::exit
C ++ programcıları arasında marjinalleştirilir.
RAII ve Yığın Açma
C ++, basit bir şekilde nesnelerin yapıcıda başlatma ve yıkıcıda temizleme yapması gerektiği anlamına gelen RAII adı verilen bir deyim kullanır . Örneğin, std::ofstream
sınıf kurucu sırasında dosyayı açabilir, daha sonra kullanıcı üzerinde çıktı işlemleri gerçekleştirir ve son olarak yaşam döngüsünün sonunda, genellikle kapsamı ile belirlenir, esasen dosyayı kapatan ve yıkayan yıkıcı denir herhangi bir yazılı içerik diske.
Dosyayı yıkamak ve kapatmak için yıkıcıya ulaşmazsanız ne olur? Kim bilir! Ama muhtemelen dosyaya yazması gereken tüm verileri yazmaz.
Örneğin bu kodu düşünün
#include <fstream>
#include <exception>
#include <memory>
void inner_mad()
{
throw std::exception();
}
void mad()
{
auto ptr = std::make_unique<int>();
inner_mad();
}
int main()
{
std::ofstream os("file.txt");
os << "Content!!!";
int possibility = /* either 1, 2, 3 or 4 */;
if(possibility == 1)
return 0;
else if(possibility == 2)
throw std::exception();
else if(possibility == 3)
mad();
else if(possibility == 4)
exit(0);
}
Her olasılıkta ne olur:
- Olasılık 1: Geri Dönüş esas olarak mevcut fonksiyon kapsamını terk eder, böylece
os
yıkıcıyı çağırmanın ve dosyayı diske kapatarak ve temizleyerek düzgün temizleme yapmanın ömrünün sona ermesini bilir .
- Olasılık 2: İstisna atmak, mevcut kapsamdaki nesnelerin yaşam döngüsünü de halleder, böylece uygun temizlik yapar ...
- Olasılık 3: Burada yığın çözme işlemi devreye giriyor! İstisna atıldığında bile
inner_mad
, çözücü yığını mad
ve main
düzgün temizleme gerçekleştirmek için gidecek, ptr
ve dahil olmak üzere tüm nesneler doğru şekilde yok edilecek os
.
- Olasılık 4: Burada mı?
exit
bir C fonksiyonudur ve C ++ deyimlerinin farkında veya uyumlu değildir. O değil de dahil olmak üzere nesneler üzerinde temizleme gerçekleştirmek os
aynı kapsamda. Bu nedenle dosyanız düzgün bir şekilde kapatılmayacak ve bu nedenle içerik asla üzerine yazılamayabilir!
- Diğer Olasılıklar: Örtük bir performans göstererek
return 0
ve böylece olasılık 1 ile aynı etkiye, yani uygun temizliğe sahip olarak ana kapsamı terk edecektir.
Ama size az önce söylediğim şeyden o kadar emin olma (özellikle 2 ve 3 olasılıklar); okumaya devam ederseniz, kural dışı durum tabanlı uygun bir temizleme işlemini nasıl yapacağımızı öğreneceğiz.
Sonlandırmanın Olası Yolları
Anadan dön!
Bunu mümkün olduğunda yapmalısınız; her zaman ana programdan uygun bir çıkış durumu döndürerek programınızdan dönmeyi tercih edin.
Programınızın arayanı ve muhtemelen işletim sistemi, programınızın ne yapması gerektiğini başarılı bir şekilde yapıp yapmadığını bilmek isteyebilir. Aynı nedenden ötürü sıfıra dönmelisiniz ya da EXIT_SUCCESS
programın başarıyla sonlandırıldığını ve programın başarısız bir şekilde sonlandırıldığını belirtmek için EXIT_FAILURE
başka herhangi bir dönüş değeri biçimi uygulama tanımlıdır ( §18.5 / 8 ).
Ancak, çağrı yığınında çok derin olabilirsiniz ve hepsini geri döndürmek acı verici olabilir ...
Bir istisna atmayın
Bir istisna atmak, daha önce herhangi bir kapsamdaki her nesnenin yıkıcısını çağırarak yığın çözmeyi kullanarak uygun nesne temizliğini gerçekleştirir.
Ama işte yakalama ! Bir yığın istisnası işlenmediğinde (catch (...) yantümcesi ile) veya noexcept
çağrı yığının ortasında bir fonksiyonunuz olsa bile yığın çözme işleminin gerçekleştirilip gerçekleştirilmediği uygulama tarafından tanımlanır . Bu, §15.5.1'de [sona ermek hariç] belirtilmiştir :
Bazı durumlarda, daha az ince hata işleme teknikleri için kural dışı durum işleme terk edilmelidir. [Not: Bu durumlar:
[...]
- istisna işleme mekanizması atılan bir istisna için bir işleyici bulamadığında (15.3) veya bir işleyici (15.3) aranmasınoexcept
, istisnaya izin vermeyen bir belirtime (15.4) sahip bir fonksiyonun en dıştaki bloğuyla karşılaştığında veya [...]
[...]
Bu gibi durumlarda, std :: terminate () denir (18.8.3). Hiçbir eşleşen işleyici bulunmadığı durumda, std :: terminate () çağrılmadan önce yığının açılmış olup olmadığı uygulama tanımlıdır [...]
Yani onu yakalamak zorundayız!
Bir istisna atmak ve ana yakalamak!
Yakalanmayan istisnalar yığın çözme gerçekleştiremeyebileceğinden (ve dolayısıyla düzgün temizleme gerçekleştirmeyeceğinden) , istisnayı ana olarak yakalamalı ve ardından bir çıkış durumu ( EXIT_SUCCESS
veya EXIT_FAILURE
) döndürmeliyiz .
Yani muhtemelen iyi bir kurulum:
int main()
{
/* ... */
try
{
// Insert code that will return by throwing a exception.
}
catch(const std::exception&) // Consider using a custom exception type for intentional
{ // throws. A good idea might be a `return_exception`.
return EXIT_FAILURE;
}
/* ... */
}
[Yapma] std :: çıkış
Bu, herhangi bir yığın çözme gerçekleştirmez ve yığın üzerindeki hiçbir canlı nesne, temizleme işlemi için ilgili yıkıcısını çağırmaz.
Bu, §3.6.1 / 4 [basic.start.init] içinde uygulanır :
Geçerli bloktan ayrılmadan programın sonlandırılması (örneğin, std :: exit (int) (18.5) işlevini çağırarak) otomatik saklama süresine (12.4) sahip herhangi bir nesneyi yok etmez . Stad veya iş parçacığı depolama süresi olan bir nesnenin imhası sırasında bir programı sonlandırmak için std :: exit çağrılırsa, programın tanımlanmamış davranışı vardır.
Şimdi düşünün, neden böyle bir şey yapasınız ki? Acı kaç nesneye zarar verdiniz?
Diğer [kötü olarak] alternatifler
Bir programı sonlandırmanın (çökme dışında) başka yolları da vardır, ancak önerilmez. Sadece açıklama uğruna burada sunulacaklar. Uyarı nasıl Normal program sonlanması değil ortalama yığın gevşemek ama tamam işletim sistemi için devlet.
std::_Exit
normal bir program sonlandırmasına neden olur ve hepsi bu kadar.
std::quick_exit
program sonlandırılmasına neden olur ve std::at_quick_exit
işleyicileri çağırır , başka bir temizleme gerçekleştirilmez.
std::exit
program sonlandırılmasına neden olur ve std::atexit
işleyicileri çağırır . Statik nesne yıkıcılarını çağırmak gibi diğer temizleme işlemleri de gerçekleştirilir.
std::abort
programın sonlandırılmasına neden oluyor, temizleme yapılmıyor. Program gerçekten, gerçekten beklenmedik bir şekilde sonlandırıldıysa bu çağrılmalıdır. İşletim sistemine anormal sonlandırma hakkında sinyal vermekten başka bir şey yapmaz. Bazı sistemler bu durumda bir çekirdek dökümü gerçekleştirir.
std::terminate
çağrıları std::terminate_handler
çağrıları hangi std::abort
varsayılan olarak.
main()
kullanımı karşılığında, işlevlerin düzgün dönüş değeri kullanmak veya uygun bir İstisna atmak. Do not kullanmakexit()
!