Parametreli bir tane oluşturduğunuzda, varsayılan parametresiz yapıcı neden kayboluyor


161

C #, C ++ ve Java'da, parametreleri alan bir kurucu oluşturduğunuzda, varsayılan parametresiz olan yok olur. Bu gerçeği her zaman kabul ettim, ama şimdi nedenini merak etmeye başladım.

Bu davranışın nedeni nedir? O "Kendine ait bir yapıcısı oluşturduysanız, ne diyordun sadece bir "güvenlik önlemi / tahmin" mi muhtemelen bu örtülü bir çevrede asılı istemiyoruz"? Yoksa bir kurucu kendiniz oluşturduktan sonra derleyicinin bir tane eklemesini imkansız kılan teknik bir nedeni var mı?


7
Bu davranışa sahip diller listesine C ++ ekleyebilirsiniz.
Henk Holterman

18
Ve C ++ ile artık Foo() = default;varsayılan olanı geri alabilirsiniz.
MSalters

1
Parametrelere sahip kurucunuz tüm parametreleri için varsayılan bağımsız değişkenlere sahipse, yerleşik parametresiz kurucu ile çakışır, bu nedenle kendi parametrenizi oluşturduğunuzda silmeniz gerekir.
Morwenn

3
Varsayılan derleyici gereksinimini ve ilham alacağı hararetli tartışmayı yapmak için ilk derleyicinin kurucuları arasındaki bu tartışmaya hazır olduğunuzu düşünün.
kingdango

7
@HenkHolterman C ++ sadece bu davranışla başka bir şey değil, aynı zamanda yaratıcısıdır ve C ++ Tasarım ve Evrimi'nde Stroustrup tarafından tartışıldığı ve güncellenmiş cevabımda özetlendiği gibi C uyumluluğuna izin vermek.
Jon Hanna

Yanıtlar:


219

Kendinizi eklediyseniz, derleyicinin yapıcıyı ekleyememesinin bir nedeni yoktur - derleyici istediği her şeyi yapabilir! Bununla birlikte, en mantıklı olana bakmak zorundasınız:

  • Statik olmayan bir sınıf için herhangi bir kurucu tanımlamamış olsaydım, büyük olasılıkla o sınıfı başlatabilmek isterim. Buna izin vermek için, derleyicinin örneklemeye izin vermekten başka bir etkisi olmayacak parametresiz bir kurucu eklemesi gerekir . Bu, sadece çalışması için koduma boş bir kurucu eklemek zorunda olmadığım anlamına gelir.
  • Kendi parametreleriyle, özellikle de parametreleri olan bir kurucu tanımladıysam, büyük olasılıkla sınıfı oluştururken yürütülmesi gereken kendi mantığım var. Derleyici bu durumda boş, parametresiz bir kurucu oluştursaydı , birisinin yazdığım mantığı atlamasına izin verirdi , bu da kodumun çeşitli şekillerde kırılmasına neden olabilir. Bu durumda varsayılan bir boş kurucu istiyorsanız, bunu açıkça söylemeliyim.

Böylece, her durumda, geçerli derleyicilerin davranışının , kodun olası amacını korumak açısından en anlamlı olduğunu görebilirsiniz .


2
Sanırım cevabınızın geri kalanı ilk cümlenizin yanlış olduğunu kanıtlıyor.
Konrad Rudolph

76
@KonradRudolph, ilk cümle derleyici diyor olabilir , bu senaryoda bir kurucu ekleyin - Ya etmezse neden cevabın kalan açıklıyor (Söz belirtilen diller için)
JohnL

1
Peki hayır. Sıfırdan bir OO dili tasarlamış olsaydık, hiçbir kurucu olmamanın en belirgin anlamı, "sınıfın değişmezini" sağlayan bir kurucu eklemeyi ihmal ettiniz ve bu bir derleme hatası yaratacaktır.
Jon Hanna

70

Dil neden hiçbir teknik sebebi kesinlikle yoktur vardır bu şekilde tasarlanmış olması.

Görebildiğim biraz gerçekçi iki seçenek var:

  1. Hiç varsayılan kurucu yok
  2. Mevcut senaryo
  3. Her zaman varsayılan olarak varsayılan bir kurucu sağlar, ancak açıkça bastırılmasına izin verir
  4. Bastırılmasına izin vermeden her zaman varsayılan bir kurucu sağlar

Seçenek 1 biraz çekici, daha fazla kod daha az sıklıkta gerçekten parametresiz bir kurucu istiyorum ki. Bir gün , varsayılan bir kurucu kullanarak ne kadar sıklıkta olduğumu saymalıyım ...

Seçenek 2 İyiyim.

Seçenek 3, dilin geri kalanı için hem Java hem de C # akışına aykırıdır. Açıkça Java'da varsayılan olarak olduğundan daha özel bir şey yapmadığınız sürece, açıkça "kaldırdığınız" hiçbir şey yoktur.

Seçenek 4 korkunçtur - kesinlikle belirli parametrelerle inşaatı zorlamak istersiniz. Ne new FileStream()anlama geliyor?

Yani temelde, eğer varsayılan bir kurucu sağlayarak hiç mantıklı olduğunu önermesini kabul, ben en kısa sürede kendi kurucu sağlamaktayız olarak bastırmak için anlamlı bir sürü yapar inanıyoruz.


1
Seçenek 3'ü severim, çünkü bir şey yazarken, her iki kurucu türüne daha sonra sadece parametre ile yapıcıya sahip olmak için daha sık ihtiyacım var. Bu yüzden günde bir kez bazı parametresiz yapıcıları özel hale getirip günde 10 kez parametresiz yapıcılar yazarım. Ama bu muhtemelen sadece ben, birçok seriaziable sınıf yazıyorum ...
Petr Mensik

8
@PetrMensik: Parametresiz kurucunuzun gerçekten hiçbir şey yapması gerekmiyorsa, kesinlikle "açık kaldırma" deyiminden çok daha fazla kod almaz.
Jon Skeet

2
@PetrMensik: Üzgünüm, hatam evet. Bu kesinlikle deneyimlerimin tam tersi - ve otomatik olarak bir şey eklemenin de daha tehlikeli bir seçenek olduğunu iddia ediyorum ... eğer yanlışlıkla dışarıda bırakmazsanız, değişmezlerinizi vb. Berbat edebilirsiniz
Jon Skeet

2
# 4 structdaha iyi veya daha kötü için C # s ile durumdur .
Jay Bazuzi

4
1. seçeneği seviyorum çünkü anlaşılması daha kolay. Kodda göremediğiniz "sihirli" yapıcı yok. Seçenek 1'de, statik olmayan bir sınıfın örnek oluşturucuları olmadığında derleyici bir uyarı yayınlamalıdır (hatta belki de izin vermeyebilir mi?). Nasıl olur: "<TYPE> sınıfı için örnek oluşturucu bulunamadı. Sınıfı statik olarak mı bildirmek istediniz?"
Jeppe Stig Nielsen

19

Düzenle. Aslında, ilk cevabımda söylediğim şey geçerli olsa da, gerçek neden budur .:

Başlangıçta C vardı. Nesne yönelimli değil (bir OO yaklaşımı alabilirsin, ama sana yardım etmiyor ya da hiçbir şeyi zorlamıyor).

Sonra C With Classes vardı, daha sonra C ++ olarak yeniden adlandırıldı. C ++, nesne yönelimlidir ve bu nedenle kapsüllenmeyi teşvik eder ve bir nesnenin değişmez olmasını sağlar - inşaatta ve herhangi bir yöntemin başında ve sonunda, nesne geçerli bir durumdadır.

Bununla yapılacak doğal şey, bir sınıfın geçerli bir durumda başlamasını sağlamak için her zaman bir kurucuya sahip olması gerektiğini zorlamaktır - eğer kurucu bunu sağlamak için herhangi bir şey yapmak zorunda değilse, boş kurucu bu gerçeği belgeleyecektir .

Ancak C ++ ile bir hedef, mümkün olan tüm C programlarının da geçerli C ++ programları olduğu noktaya kadar C ile uyumlu olmaktı (artık aktif bir hedef olarak değil ve C ++ 'dan ayrı olarak C'nin evrimi, artık ).

Bunun bir etkisi arasındaki işlevsellik çoğaltma oldu structve class. Birincisi C yolunu (varsayılan olarak herkese açıktır) ve ikincisini iyi bir OO yolunda (varsayılan olarak özel olan her şey geliştirici herkese açık olarak kamusal olarak istediklerini yapar) yapar.

Bir diğeri, bir yapıcıya structsahip olamayan bir C'nin , C yapıcıları olmadığından, C ++ 'da geçerli olması için, C ++' a bakmanın bunun için bir anlamı olması gerektiğidir. Ve böylece, bir yapıcıya sahip olmamak, bir değişmezi aktif olarak sağlamak için OO uygulamasına karşı çıkarken, C ++ bunu, boş bir gövdeye sahip gibi davranan varsayılan parametresiz bir yapıcı olduğu anlamına geldi.

Tüm C structsartık geçerli C ++ idi structs(yani classesher şey - üyeler ve miras - genel olan C ++ ile aynı oldukları anlamına geliyordu ) dışarıdan tek bir parametresiz yapıcıya sahipmiş gibi muamele gördü.

Bununla birlikte, bir yapıcıyı bir classveya içine koyduysanız struct, şeyleri C yolundan ziyade C ++ / OO yolunda yapıyordunuz ve varsayılan bir kurucuya gerek yoktu.

Bir stenografi görevi gördüğü için, uyumluluk mümkün olmadığında bile insanlar kullanmaya devam etti (C'de olmayan diğer C ++ özelliklerini kullandı).

Java (birçok yönden C ++ 'a dayanarak) ve daha sonra C # (farklı şekillerde C ++ ve Java'ya dayanarak) geldiğinde, bu yaklaşımı kodlayıcıların zaten alışkın olabileceği şekilde tuttular.

Stroustrup, C ++ Programlama Dili ve daha da fazlası , C ++ Tasarım ve Evrimi'ndeki dilin "nedenlerine" daha fazla odaklanarak bu konuda yazıyor .

=== Orijinal Cevap ===

Diyelim ki bu olmadı.

Diyelim ki parametresiz bir kurucu istemiyorum, çünkü sınıfımı bir tane olmadan anlamlı bir duruma sokamıyorum. Gerçekten de, bu structC # ' da gerçekleşebilecek bir şeydir (ancak C #' daki tüm sıfırlar ve sıfırlardan anlamlı bir şekilde yararlanamıyorsanız, structen iyi şekilde herkese görünür olmayan bir optimizasyon kullanırsınız ve aksi takdirde bir tasarım kusurunu kullanarak struct).

Sınıfımın değişmezlerini koruyabilmesi için özel bir removeDefaultConstructoranahtar kelimeye ihtiyacım var . En azından, hiçbir arama kodunun varsayılanı çağırmadığından emin olmak için özel bir parametresiz yapıcı oluşturmanız gerekir.

Bu da dili biraz daha zorlaştırıyor. Yapmamak daha iyi.

Sonuçta, bir kurucu eklemeyi varsayılanı kaldırmak olarak düşünmemek en iyisidir, hiçbir şey yapmayan parametresiz bir kurucu eklemek için hiçbir kurucu sözdizimsel şeker olarak düşünmemek daha iyidir.


1
Ve yine, birisinin bunun oy vermek için yeterince kötü bir cevap olduğunu hissettiğini görüyorum, ama beni veya başkalarını aydınlatan rahatsız edilemedi. Hangisi faydalı olabilir.
Jon Hanna

Cevabımda da aynı. Burada yanlış bir şey görmüyorum yani benden +1.
Botz3000

@ Botz3000 Skor umurumda değil, ama eğer bir eleştiri varsa, onu okumayı tercih ederim. Yine de, yukarıdakilere eklenecek bir şey düşünmemi sağladı.
Jon Hanna

1
Ve yine herhangi bir açıklama yapmadan aşağı oylama ile. Lütfen, açıklamaya gerek duymayacak kadar açık bir şey kaçırırsam, sadece aptal olduğumu varsayın ve bana yine de açıklamaktan yana olun.
Jon Hanna

1
@jogojapan Ben de uzman değilim, ama karar veren adam olarak - bu konuda yazdı, bu yüzden olmak zorunda değilim. Bu arada, çok ilginç bir kitap; düşük seviyeli teknoloji ve tasarım kararlarında çok az, bir kişinin konuşması gibi eğlenceli bitler, konuşmasından hemen önce yeni bir özellik (çok fazla düşünürsünüz ve sadece iki kez yaparsınız) önermeden önce bir böbrek bağışlamanız gerektiğini söyler hem şablonları hem de istisnaları tanıtmak.
Jon Hanna

13

Nesne oluşturma üzerinde denetim sahibi olmak için hiçbir şey yapmazsanız, varsayılan parametresiz yapıcı eklenir. Kontrol almak için tek bir kurucu oluşturduktan sonra, derleyici "geri çekilir" ve tam kontrole sahip olmanızı sağlar.

Bu şekilde olmazsa, yalnızca nesnelerin parametreleri olan bir kurucu aracılığıyla oluşturulabilmesini istiyorsanız, varsayılan kurucuyu devre dışı bırakmanın açık bir yoluna ihtiyacınız olacaktır.


Aslında bu seçeneğe sahipsiniz. Parametresiz yapıcıyı özel yapmak.
mw_21

5
Bu aynı değil. Statik yöntemler de dahil olmak üzere sınıfın herhangi bir yöntemi bunu çağırabilir. Tamamen varolmamayı tercih ediyorum.
Anders Abel

3

Derleyicinin kolaylık işlevi. Parametreleri olan bir Oluşturucu tanımlarsanız, ancak parametresiz bir kurucu tanımlamazsanız, parametresiz bir kurucuya izin vermek istememeniz olasılığı çok daha yüksektir.

Bu, boş bir kurucu ile başlatılması mantıklı olmayan birçok nesne için geçerlidir.

Aksi takdirde, kısıtlamak istediğiniz her sınıf için parametresiz özel bir kurucu bildirmeniz gerekir.

Bence parametrelerin çalışması gereken bir sınıf için parametresiz bir kurucuya izin vermek iyi bir stil değil.


3

Sorunun başka bir şekilde olması gerektiğini düşünüyorum: Başka bir kurucu tanımlamadıysanız neden varsayılan bir kurucu bildirmeniz gerekmez?

Statik olmayan sınıflar için bir yapıcı zorunludur.
Bu yüzden herhangi bir kurucu tanımlamadıysanız, oluşturulan varsayılan kurucu C # derleyicisinin sadece kullanışlı bir özelliğidir, ayrıca sınıfınız bir kurucu olmadan geçerli olmaz. Öyleyse hiçbir şey yapmayan bir yapıcı üretmenin yanlış bir yanı yok. Her yerde boş inşaatçılara sahip olmaktan kesinlikle daha temiz görünüyor.

Zaten bir kurucu tanımladıysanız, sınıfınız geçerlidir, bu yüzden derleyici neden varsayılan bir kurucu istediğinizi varsayalım? Ya istemiyorsan? Derleyiciye bu varsayılan kurucuyu oluşturmamasını söyleyen bir öznitelik uygulansın mı? Bunun iyi bir fikir olacağını sanmıyorum.


1

Varsayılan yapıcı yalnızca sınıfta bir kurucu olmadığında oluşturulabilir. Derleyiciler bunu sadece bir yedekleme mekanizması olarak sağlayacak şekilde yazılır.

Parametreli bir kurucunuz varsa, varsayılan kurucuyu kullanarak bir nesnenin oluşturulmasını istemeyebilirsiniz. Derleyici varsayılan bir kurucu sağlamış olsaydı, argüman kullanmadan nesnelerin oluşturulmasını önlemek için arg-yok bir kurucu yazmanız ve onu özel yapmanız gerekirdi.

Ayrıca, varsayılan kurucuyu devre dışı bırakma veya 'özelleştirme' ve böylece yakalanması zor bir potansiyel işlevsel hataya neden olma şansınız daha yüksek olacaktır.

Ve şimdi, bir nesnenin varsayılan yolla veya parametreleri ileterek oluşturulmasını istiyorsanız, açıkça bir bağımsız değişken yapıcısı tanımlamalısınız. Bu güçlü bir şekilde kontrol edilir ve derleyici aksi halde şikayet eder, böylece burada boşluk kalmaz.


1

Öncül

Bu davranış, sınıfların varsayılan bir genel parametresiz kurucuya sahip olma kararının doğal bir uzantısı olarak görülebilir . Sorulan soruya dayanarak, bu kararı bir öncül olarak alıyoruz ve bu örnekte onu sorgulamadığımızı varsayıyoruz.

Varsayılan Yapıcıyı Kaldırma Yolları

Varsayılan ortak parametresiz kurucuyu kaldırmak için bir yol olması gerekir. Bu kaldırma aşağıdaki şekillerde gerçekleştirilebilir:

  1. Herkese açık olmayan parametresiz bir yapıcı bildirin
  2. Parametreleri olan bir kurucu bildirildiğinde parametresiz kurucuyu otomatik olarak kaldırın
  3. Parametresiz yapıcıyı kaldırmayı derleyiciye bildiren bazı anahtar sözcükler / öznitelikler (dışlanması kolay olacak kadar garip)

En İyi Çözümü Seçme

Şimdi kendimize soruyoruz: Parametresiz bir kurucu yoksa, ne ile değiştirilmelidir? ve Hangi genel senaryolarda varsayılan genel parametresiz yapıcıyı kaldırmak isteriz?

İşler yerinde düşmeye başlar. İlk olarak, parametreleri olan bir kurucu ile veya ortak olmayan bir kurucu ile değiştirilmelidir. İkincisi, altında parametresiz bir kurucu istemediğiniz senaryolar şunlardır:

  1. Sınıfın hiç örneklenmesini istemiyoruz ya da kurucunun görünürlüğünü kontrol etmek istiyoruz: kamuya açık olmayan bir kurucu beyan etmek
  2. Parametrelerin inşaatta sağlanmasını zorunlu kılmak istiyoruz: parametreleri olan bir kurucu beyan ederiz

Sonuç

Orada var - tam olarak C #, C ++ ve Java'nın varsayılan genel parametresiz kurucunun kaldırılmasına izin vermesinin iki yolu.


Açıkça yapılandırılmış ve takip edilmesi kolay +1. Ama yukarıdaki (3.) ile ilgili olarak: Ben yapıcı kaldırma için özel bir anahtar kelime gibi garip bir fikir olduğunu düşünmüyorum ve aslında C ++ 11 = deletebu amaç için tanıttı .
jogojapan

1

Bunun derleyici tarafından ele alındığını düşünüyorum. .netMontajı açarsanız ILDASM, kodda olmasa bile varsayılan kurucuyu görürsünüz. Parametreli bir kurucu tanımlarsanız, varsayılan kurucu arı görülmez.

Aslında sınıfı (statik olmayan) tanımladığınızda, derleyici bu özelliği yalnızca bir örnek oluşturacağınızı düşünerek sağlar. Ve herhangi bir özel işlem gerçekleştirmek istiyorsanız mutlaka kendi yapıcıya sahip olacaksınız.


0

Çünkü bir kurucu tanımlamazsanız derleyici sizin için otomatik olarak herhangi bir argüman almayan bir kurucu oluşturur. Bir kurucudan daha fazla bir şey istediğinizde, aşırıya kaçarsınız. Bu fonksiyon aşırı yüklenmesi DEĞİLDİR. Derleyicinin şimdi gördüğü tek kurucu, tartışmayı yapan kurucunuzdur. Bu soruna karşı koymak için yapıcı değer olmadan geçirilirse varsayılan bir değer iletebilirsiniz.


0

Bir sınıfın bir kurucuya ihtiyacı vardır. Zorunlu bir gerekliliktir.

  • Bir tane oluşturmazsanız, parametresiz kurucu size otomatik olarak verilecektir.
  • Parametresiz bir kurucu istemiyorsanız, kendi kurucunuzu oluşturmanız gerekir.
  • Hem parametresiz yapıcıya hem de parametre tabanlı yapıcıya ihtiyacınız varsa bunları manuel olarak ekleyebilirsiniz.

Size başka bir soru ile cevap vereceğim, neden her zaman varsayılan parametresiz bir kurucu istiyoruz? bunun istenmediği bir durum vardır, bu nedenle geliştirici gerektiğinde ekleme veya kaldırma denetimine sahiptir.

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.