Soru Bir kurucu bir NULL değeri verebilir mi?


Kurucuların hiçbir şeyi "geri döndürmediğini" biliyorum ama örneğin ararsam CMyClass *object = new CMyClass() Kurucu başarısız olursa, nesne NULL olmak için herhangi bir yolu var mı? Benim durumumda, yüklenmesi gereken bazı resimlerim var ve eğer dosya okuması başarısız olursa, null dönmek istiyorum. Bunu yapmanın bir yolu var mı?
Şimdiden teşekkürler.


25
2018-05-18 16:16


Menşei


Sorun şu ki, istisna oluşturmak istemiyorum. - Sanctus2099
Tıpkı olduğu gibi, nesneyi doğrulayan bir işlev sağlayabilirsiniz. fstream nerede ara is_open() Devam etmeden önce işlerin doğru gittiğini kontrol etmek için. - AraK
Neden özel durumlardan kaçınmak istiyorsunuz? C ++ 'nın oldukça basit bir parçası. - David Thornley
@DavidThornley Yazmayı seçtiğiniz koda belirli bir "görsel maliyet" var. try/catch bloklar "istisna" değildir. Kod C gibi bir çok kodla etkileşime giriyorsa, try/catch blok yerinden görünebilir. Var O*o=O::createO() ; if( !o ) { /*handle*/ }, çok C gibi görünüyor, vs O*o;try{o=new O();}catch(...){/*handle*/}. Benim amacım tamamen farklı görünüyor ve bence seçim değil istisnaları kullanmak gerçekten stilistik olabilir. Ama biliyorsun, bir düzine kadar bir şey, diğerinin 6'sı. - bobobobo


Cevaplar:


İstisnalar kullanmanız gerektiğine herkesle katılıyorum, ancak bir nedenden dolayı NULL kullanmanız gerekiyorsa, kurucuyu özel hale getirin ve bir fabrika yöntemi kullanın:

static CMyClass* CMyClass::create();

Bu, normal olarak örnekleri oluşturamazsınız demektir ve artık onları yığına ayıramazsınız, ki bu oldukça büyük bir dezavantajdır.


25
2018-05-18 16:25



@Sanctus Yapıcınız, yığın örnekleri ile "null" döndürmek nasıl? - fredoverflow
@Fred O ihtiyacı olmadığını kastediyor, varsayalım - Michael Mrozek
Yanlış soruya doğru cevap budur: Bunun için denememesi gerekir. - Steven Sudit
@Steven Muhtemelen, ama insanlar bir soruyu cevaplamayı reddettiklerinde nefret ediyorum çünkü posterin yanlış bir şey yaptığını düşünüyorlar; O (ya da bu sorunun cevabını arayan gelecekteki bir okuyucu) bize açıklamak için çok uzun zaman alacak meşru bir ihtiyacı vardır tamamen mümkündür - Michael Mrozek
@Michael: Ve soruyu cevaplamaya çalıştığın için teşekkür ederim, ama bence bir sorumuz var. Ayrıca İnsanlara soruları kırıldığında söyle. Bu yüzden cevabımın kabul edilmesi gerektiğini iddia etmiyorum. - Steven Sudit


İnşaatçılar değer vermezler. Bir nesneyi başlatır ve hataları bildirmenin tek yolu bir istisnadır.

Kurucunun herhangi bir tür bellek yönetimi yapmadığını unutmayın. Bellek harici olarak tahsis edilir ve daha sonra kurucuyu başlatmak için çağrılır. Ve bu bellek dinamik olarak tahsis edilebilir (type *x = new type;) ama yığında da olabilir (type x;) veya daha karmaşık bir türden bir alt nesne. Her şeyden önce, ilk durumda, null hiç mantıklı değil.


21
2018-05-18 16:19



Her zaman mükemmel bir fikir olmasa da, başarısız bir kurgunun örneği daha sonra test edilebilecek iyi tanımlanmış bir geçersiz durumda bırakması da mümkündür. Cevabım bunun bir örneğini içerir. Bunu söyledikten sonra, sadece kurucuya atmayı tercih ederim. - Steven Sudit


"Doğru" ** yolu bir istisna atmaktır.

** Üye gibi bir işlev sağlayabilirsiniz is_valid Bir nesneyi oluşturduktan sonra kontrol edebileceğiniz, ancak C ++ 'da sadece deyimsel değil.


9
2018-05-18 16:19





Bunu yapmanın yolu, kurucunuzda çalışmayan bir şey bulursanız, bir istisna atmanız gerekir. C ++ nesneniz için bellek ayıramazsa bu gerçekleşir - std :: bad_alloc atar. Std :: exception veya bir alt sınıf kullanmalısınız.


6
2018-05-18 16:19



Yeni iki aroma var, normal atılacak, ama eğer bellek tahsis edemezse, boş bir gösterici döndürecek olan nothrow versiyonunu kullanabilirsiniz. Yine, cevap doğrudur çünkü boş gösterici döndürme seçeneği yalnızca bellek ayırma hataları için kullanılabilir önce kurucu çağırıyor. - David Rodríguez - dribeas


Bunun yerine statik bir fabrika metodu kullanabilir mi? Türler arasında dönüştürme yaparken, orijinal statik CMyClass dönüştürme (özgün) yapabilir ve özgün boşsa null döndürürüm. Yine de yine de geçersiz veriler için istisnalar atmak isteyebilirsiniz.


3
2018-05-18 16:25





Kötü tadı.

Aslında bunu yapmak istiyorsanız, yeni bir aşırı yükleme yapın, yeni bir çağrısı başlatmayan bir özel yapıcıya sahip olun, yeni sürümde başlatma yapın ve başlatma başarısız olursa yeni dönüşe sahip olun.


1
2018-05-18 16:32



Bu oldukça sinsi bir yaklaşım ama çok yararlı olabilir. Sanırım biraz test edeceğim. - Sanctus2099
@Sanctus: Bunu yapma. Onun korkunç bir fikir. Kimse beklemez new hiç dönmek 0 açıkça talep etmedikçe nothrow sürümü. p = new Blah; if(p) ... ne yaptığını bilmeyen birinin işaretidir. - Dennis Zickefoose
Özür dilerim ama mesajın gerçekten iyi argümanlar vermiyor. Muhtemelen fabrikaları kullanacağım ama eğlenceli bir test. - Sanctus2099
@Sanctus: Sanırım beni mi kastediyorsun? Eğer öyleyse, en az sürpriz prensibi her zaman iyi bir argüman. Bir kez aşırı yükleme egzersiz olarak kodlayın operator new Eğer tecrübeyi istiyorsan, ama sonra kodu at ve tekrar bir daha yapma. - Dennis Zickefoose


Sıfır dönmek için bir kurucuya nasıl ulaşacağınızı ya da nasıl sahtekarlık yapacağınızı söylemek yerine, bir alternatif önereyim: gecikmeli başlatma veya atıcı olmayan bir kurucu gibi bir istisna atmanın önünü açmayı teklif edin. Bunu yaptıktan sonra, geçerliliği kontrol etmek ve herhangi bir girişimde bulunmak için bir yolun olması gerekir. kullanım geçersiz bir örnek bir istisna atar. Diğer bir deyişle, istisnayı geciktiriyorsunuz, tamamen kaçınmıyorsunuz.

İşte nasıl: Zaten bir dosya yolunu alan ve yükleyen, başarısızlığa atlayan bir kurucunuz var. Cesareti, dosya yolunu alan ve başarıyı göstermek için bir boole döndüren bir Load yöntemine taşıyın. Daha sonra kurucuyu değiştirin, böylece sadece Yükler ve yanlış üzerine atar. Yükte, örnek düzgün başlatılırsa hemen false döndürdüğünüzden emin olun. Sonra bir varsayılan yıkıcı ve bir IsValid yöntemi ekleyin.

Dennis'e göre: Şimdi, bir istisnanın atılıp atılmadığını kontrol etmek için bir boolean çeken ikinci bir kurucu ekleyin ve Yükü özel olarak yeniden seçmeyi düşünün, bu durumda varsayılan kurucuyu da aynı şekilde kaldırabilirsiniz.

Bu, elde edilemez kodlar yapmadan, isteyebileceğiniz her şeyi verir. Bunun gibi bir şeye benzemeli:

// Per Dennis, should go away if Load becomes private.
Image()
{
    _valid = false;
}

Image(const string& filepath)
{
    if (!Load(filepath))
        throw new exception("Cannot open image.");
}

// Per Dennis.
Image(const string& filepath, bool doThrow)
{
    if (!Load(filepath) && doThrow)
        throw new exception("Cannot open image.");
}

// Per Dennis, this should probably be made private now.
bool Load(const string& filepath)
{
    if (_valid)
        return false;

    // Try to load...
    _valid = WhetherItLoadedExpression;
    return _valid;
}

bool IsValid()
{
    return _valid;
}

void Draw()
{
    if (!IsValid())
        throw new exception("Invalid object.");

    // Draw...
}

Düzenle

Dennis'in yorumuna yanıt olarak yapılan değişiklikler için aşağıya bakın.


1
2018-05-18 19:47



RAII hakkında bu kadar çok şey yaptığınızı düşünürsek, bunun kullanılmasını gerektirmeyen bir cevap vermek oldukça tuhaf görünüyor. - Dennis Zickefoose
Alınan nokta ama dogmatik değilim. Yukarıdaki sınıf verir RAII için ve hatta teşvik eder, ama aynı zamanda size bir alternatif sunar. Başlatma geciktiğinde bile, hala kaynakları temizleyen bir yıkıcıya sahiptir, bu yüzden RAII'nin temel amacını gerçekleştirir: kaçaklardan kaçınma. Yine de, yorumunuza cevaben, istisna bastırma sağlayan başka bir kurucu ekleyeceğim. - Steven Sudit
@Dennis: Açıklığa kavuşturmak için, atıcı olmayan kurucu RAII için izin verir ancak isteğe bağlı olarak bir başarısızlık işaretinin göstergesi olarak istisnaları bir geçerlilik bayrağıyla değiştirir. Yük yedeklemesinin açık bir sorun olup olmadığı. Yine ben dogmatik değilim; Yine de kodun sızması umurumda değil. - Steven Sudit


Bu yeni operatörü geçersiz kılarak biraz hackish yapılabilir

Bu örneğe bakın:

http://coliru.stacked-crooked.com/a/62e097827724f91e

Teknik olarak artık bir kurucu değil ama istediğin gibi davranıyor.


1
2018-04-08 12:09





Std :: nothrow'u kullanarak yeni "return" 0'a neden olabilirsiniz, ancak bu yalnızca bellek ayırma başarısız olursa 0'a dönmesine neden olur. Yapıcınıza ulaştıktan sonra, istediğini elde etmenin bir yolu yok.

Endişelerinizi sınıfta ayırmalısınız. Bir kurucu hemen hemen hiç bir zaman ('asla' dönemini söylemek için cazip gelmem gerekir ama sanmıyorum nadir istisna için odadan ayrılırım) dosya işleme kendi sorumluluğu (fstream gibi) olmadığı sürece dosya işleme.


0
2018-05-18 16:33



Bir kurucu, önemli bir işlem gerektirse bile, nesneyi oluşturmak için gerekli olan her şeyi yapmalıdır. Dennis'in bir dosyayı okuyan ve sıkıştırılmış bir görüntünün kodunu çözen bir Resim sınıfının örneğini düşünün. - Steven Sudit
Ergo, Görüntü örneğini oluşturmanın doğru yolu, kurucunun dosya sistemindeki bir dosyayı aramak, açmak, işlemek ve bunlardan herhangi biri başarısız olursa patlayabilmek için kullanabileceği karakter dizisini iletmektir. Ben düzeltilmeyi bekliyorum .... Sanırım. Evet, alaycı oluyorum. Bu sadece aptalca değil mi? Oluşturucuların, genel olarak, boş bedenleri olması gerekir. - Crazy Eddie
Bu sizin iddianızdır, ancak tamamen desteklenmez ve kolayca reddedilir. - Steven Sudit


Kullanabilirsin malloc yerine new dan beri malloc istisnalar atmaz. Sonucunu test etmek zorunda kalacaksın malloc işaretçiyi kullanmadan önce. Ayrıca eğer malloc başarılı olur, nesneyi başlatmanız gerekir.

Uyarı:

malloc nesnenin yapıcısını çağırmaz.


0
2018-05-18 18:07



Bu kötü bir fikir çünkü, yeni tahsis edilen nesneyi yeni ayırdığınız bellekte taklit etseniz bile, nesneyi temizlemeniz bir karmaşaya dönüşür: açık imhaya ve ardından ücretsiz bir aramaya ihtiyacınız olacaktır. Ve yine de bir şey çözmez çünkü kurucunun kendisi fırlatabilir. - Steven Sudit
Artı sadece önlemek istiyorsanız new atmaktan bölüm, kullanabilirsiniz new (nothrow)... - David Rodríguez - dribeas
Bakın, standardın belleğini almak için malloc çağırmamasının yeni bir yol olduğunu biliyorum, ama standart kütüphane (aşırı yüklenmemiş) sürümü için malloc'u baypas etmek çok parlak bir fikir değildi. - Joshua
Evet, bunun kötü bir fikir olduğunu biliyorum, ancak OP'nin NULL'u bir nesne oluşturmaya döndürme gereksinimini karşılıyor. - Thomas Matthews


eğer ararsam CMyClass* object = new CMyClass() Kurucu başarısız olursa, nesne NULL olmak için herhangi bir yolu var mı?

Ne demek istediğini anladım! Dinamik olarak ayrılmış belleği yoğun bir şekilde kullanan ve bu fikri uygulayan C ++ kütüphaneleri var. QtGstreamer), bu yüzden kodunuzu şöyle yazmanız kesinlikle mümkün:

CMyClass* object = new CMyClass()
if (!object)
{
    // FAILED!
}

Ancak, döndüren nesnenin yapıcısı değil NULL. Aşırı yüklü bir versiyonu operator new.


0
2017-12-08 17:13