W C #, C ++ i Javie, kiedy tworzysz konstruktor pobierający parametry, domyślny bez parametrów znika. Zawsze akceptowałem ten fakt, ale teraz zacząłem się zastanawiać, dlaczego.
Jaki jest powód tego zachowania? Czy to tylko „środek bezpieczeństwa / domysł” mówiący „Jeśli stworzyłeś własnego konstruktora, prawdopodobnie nie chcesz, aby ten ukryty kręcił się w pobliżu”? A może ma to techniczny powód, który uniemożliwia kompilatorowi dodanie go po samodzielnym utworzeniu konstruktora?
c#
java
c++
default-constructor
olagjo
źródło
źródło
Foo() = default;
chcesz przywrócić domyślny.Odpowiedzi:
Nie ma powodu, dla którego kompilator nie mógł dodać konstruktora, jeśli dodałeś własny - kompilator mógł zrobić prawie wszystko, co chce! Musisz jednak przyjrzeć się temu, co ma największy sens:
Tak więc w każdym przypadku widać, że zachowanie obecnych kompilatorów ma największy sens, jeśli chodzi o zachowanie prawdopodobnej intencji kodu.
źródło
Z pewnością nie ma technicznego powodu, dla którego język miałby być zaprojektowany w ten sposób.
Widzę cztery nieco realistyczne opcje:
Wariant 1 jest dość atrakcyjny, że więcej kodu I rzadziej ja naprawdę chcę konstruktora bez parametrów. Niektóre dni mam policzyć, jak często ja rzeczywiście kończy się przy użyciu domyślnego konstruktora ...
Opcja 2 jest w porządku.
Opcja 3 jest sprzeczna z przepływem języka Java i C # w pozostałej części języka. Nigdy nie ma niczego, co wyraźnie „usuwasz”, chyba że policzysz jawnie czyniąc rzeczy bardziej prywatnymi, niż byłoby to domyślnie w Javie.
Opcja 4 jest okropna - absolutnie chcesz mieć możliwość wymuszenia konstrukcji o określonych parametrach. Co by to w
new FileStream()
ogóle oznaczało?Więc w zasadzie, jeśli akceptujesz założenie, że dostarczanie domyślnego konstruktora ma w ogóle sens, uważam, że bardzo sensowne jest wyłączenie go, gdy tylko dostarczysz własnego konstruktora.
źródło
struct
, na dobre lub na złe.Edytować. Właściwie, chociaż to, co powiem w mojej pierwszej odpowiedzi, jest ważne, to jest prawdziwy powód .:
Na początku było C. C nie jest zorientowane obiektowo (możesz przyjąć podejście obiektowe, ale to ci nie pomaga ani niczego nie wymusza).
Potem było C With Classes, które zostało później przemianowane na C ++. C ++ jest zorientowany obiektowo, dlatego zachęca do hermetyzacji i zapewnienia niezmienności obiektu - po skonstruowaniu oraz na początku i na końcu dowolnej metody obiekt jest w prawidłowym stanie.
Naturalną rzeczą jest wymuszenie, że klasa musi zawsze mieć konstruktora, aby upewnić się, że zaczyna się w poprawnym stanie - jeśli konstruktor nie musi nic robić, aby to zapewnić, pusty konstruktor udokumentuje ten fakt .
Ale celem z C ++ było być kompatybilnym z C do tego stopnia, że w miarę możliwości wszystkie prawidłowe programy w C były również prawidłowymi programami w C ++ (nie jest już tak aktywnym celem, a ewolucja C oddzielnie do C ++ oznacza, że już nie jest ).
Jednym z efektów tego było powielanie funkcjonalności między
struct
aclass
. Pierwszy robi rzeczy w C (domyślnie wszystko jest publiczne), a drugi robi wszystko w dobry sposób OO (domyślnie wszystko jest prywatne, deweloper aktywnie upublicznia to, czego chce).Innym jest to, że aby C
struct
, które nie mogło mieć konstruktora, ponieważ C nie ma konstruktorów, było poprawne w C ++, musi to mieć znaczenie w sposobie patrzenia na to w C ++. I tak, chociaż brak konstruktora byłby sprzeczny z praktyką OO polegającą na aktywnym zapewnianiu niezmiennika, C ++ przyjęło to jako oznaczenie, że istnieje domyślny konstruktor bez parametrów, który działał tak, jakby miał puste ciało.Wszystkie C
structs
były teraz poprawne w C ++structs
(co oznaczało, że były takie same jak C ++classes
ze wszystkim - składowymi i dziedziczeniem - publicznymi) traktowane z zewnątrz tak, jakby miały pojedynczy konstruktor bez parametrów.Jeśli jednak umieściłeś konstruktor w a
class
lubstruct
, to robiłeś rzeczy w sposób C ++ / OO, a nie w C, i nie było potrzeby stosowania domyślnego konstruktora.Ponieważ służył jako skrót, ludzie używali go nawet wtedy, gdy kompatybilność nie była możliwa w inny sposób (używał innych funkcji C ++ nie w C).
Dlatego kiedy pojawiła się Java (oparta na C ++ na wiele sposobów), a później C # (oparta na C ++ i Javie na różne sposoby), zachowali to podejście, ponieważ programiści mogli już być przyzwyczajeni.
Stroustrup pisze o tym w swoim The C ++ Programming Language, a nawet bardziej, skupiając się bardziej na tym, „dlaczego” języka w The Design and Evolution of C ++ .
=== Oryginalna odpowiedź ===
Powiedzmy, że tak się nie stało.
Powiedzmy, że nie chcę konstruktora bez parametrów, ponieważ bez niego nie mogę wprowadzić mojej klasy w znaczący stan. Rzeczywiście, jest to coś, co może się zdarzyć
struct
w C # (ale jeśli nie możesz sensownie używać samych zer i zerstruct
w C #, w najlepszym razie używasz niepublicznie widocznej optymalizacji, a poza tym masz wada projektowa w użyciustruct
).Aby moja klasa mogła chronić swoje niezmienniki, potrzebuję specjalnego
removeDefaultConstructor
słowa kluczowego. Musiałbym przynajmniej utworzyć prywatny konstruktor bez parametrów, aby upewnić się, że żaden kod wywołujący nie wywołuje wartości domyślnej.Co jeszcze bardziej komplikuje język. Lepiej tego nie robić.
Podsumowując, najlepiej nie myśleć o dodawaniu konstruktora jako usuwaniu wartości domyślnej, lepiej pomyśleć o braku konstruktora jako cukru składniowego do dodawania konstruktora bez parametrów, który nic nie robi.
źródło
Domyślny konstruktor bez parametrów jest dodawany, jeśli nie robisz niczego, aby przejąć kontrolę nad tworzeniem obiektów. Po utworzeniu pojedynczego konstruktora do przejęcia kontroli, kompilator „wycofuje się” i daje Ci pełną kontrolę.
Gdyby tak nie było, potrzebowałbyś jakiegoś jawnego sposobu wyłączenia domyślnego konstruktora, jeśli chcesz, aby obiekty były konstruowalne tylko za pomocą konstruktora z parametrami.
źródło
Jest to wygodna funkcja kompilatora. Jeśli zdefiniujesz Konstruktor z parametrami, ale nie zdefiniujesz konstruktora bez parametrów, prawdopodobieństwo, że nie chcesz zezwolić na konstruktor bez parametrów, jest znacznie większe.
Dzieje się tak w przypadku wielu obiektów, których inicjalizacja z pustym konstruktorem po prostu nie ma sensu.
W przeciwnym razie musiałbyś zadeklarować prywatny konstruktor bez parametrów dla każdej klasy, którą chcesz ograniczyć.
Moim zdaniem nie jest dobrym stylem zezwalanie na konstruktor bez parametrów dla klasy, która potrzebuje parametrów do działania.
źródło
Myślę, że pytanie powinno być odwrotne: dlaczego nie musisz zadeklarować domyślnego konstruktora, jeśli nie zdefiniowałeś żadnych innych konstruktorów?
Konstruktor jest obowiązkowy dla klas niestatycznych.
Więc myślę, że jeśli nie zdefiniowałeś żadnych konstruktorów, wygenerowany domyślny konstruktor jest po prostu wygodną funkcją kompilatora C #, również twoja klasa nie byłaby ważna bez konstruktora. Nie ma więc nic złego w niejawnym generowaniu konstruktora, który nic nie robi. Z pewnością wygląda to lepiej niż posiadanie pustych konstruktorów dookoła.
Jeśli masz już zdefiniowany konstruktor, twoja klasa jest prawidłowa, więc dlaczego kompilator miałby zakładać, że chcesz konstruktora domyślnego? A jeśli nie chcesz? Zaimplementować atrybut, aby poinformować kompilator, aby nie generował tego domyślnego konstruktora? Nie sądzę, żeby to był dobry pomysł.
źródło
Konstruktor domyślny można skonstruować tylko wtedy, gdy klasa nie ma konstruktora. Kompilatory są napisane w taki sposób, aby zapewnić to tylko jako mechanizm zapasowy.
Jeśli masz sparametryzowany konstruktor, możesz nie chcieć, aby obiekt był tworzony przy użyciu domyślnego konstruktora. Gdyby kompilator dostarczył domyślnego konstruktora, musiałbyś napisać konstruktor bezargumentowy i uczynić go prywatnym, aby zapobiec tworzeniu obiektów bez argumentów.
Ponadto istnieje większe prawdopodobieństwo, że zapomnisz o wyłączeniu lub „sprywatyzowaniu” domyślnego konstruktora, co spowoduje trudny do wykrycia potencjalny błąd funkcjonalny.
A teraz musisz jawnie zdefiniować konstruktor bezargumentowy, jeśli chcesz, aby obiekt został utworzony w domyślny sposób lub przez przekazanie parametrów. Jest to mocno sprawdzane, a kompilator narzeka inaczej, zapewniając w ten sposób brak luki w tym miejscu.
źródło
Przesłanka
To zachowanie można postrzegać jako naturalne rozszerzenie decyzji dla klas, aby miały domyślny publiczny konstruktor bez parametrów . Na podstawie zadanego pytania przyjmujemy tę decyzję jako przesłankę i zakładamy, że nie kwestionujemy jej w tym przypadku.
Sposoby usuwania domyślnego konstruktora
Wynika z tego, że musi istnieć sposób na usunięcie domyślnego publicznego konstruktora bez parametrów. To usunięcie można przeprowadzić na następujące sposoby:
Wybór najlepszego rozwiązania
Teraz zadajemy sobie pytanie: jeśli nie ma konstruktora bez parametrów, czym należy go zastąpić? iw jakich typach scenariuszy chcielibyśmy usunąć domyślny publiczny konstruktor bez parametrów?
Wszystko zaczyna się układać. Po pierwsze, należy go zastąpić konstruktorem z parametrami lub konstruktorem niepublicznym. Po drugie, scenariusze, w których nie chcesz konstruktora bez parametrów, to:
Wniosek
Mamy to - dokładnie na dwa sposoby, na które C #, C ++ i Java pozwalają na usunięcie domyślnego publicznego konstruktora bez parametrów.
źródło
= delete
w tym celu.Myślę, że jest to obsługiwane przez kompilator. Jeśli otworzysz
.net
zestaw wILDASM
, zobaczysz domyślny konstruktor, nawet jeśli nie ma go w kodzie. Jeśli zdefiniujesz sparametryzowany konstruktor, domyślny konstruktor nie będzie widoczny.W rzeczywistości, kiedy definiujesz klasę (niestatyczną), kompilator zapewnia tę funkcję, myśląc, że będziesz po prostu tworzyć instancję. A jeśli chcesz wykonać jakąś konkretną operację, na pewno będziesz mieć własnego konstruktora.
źródło
Dzieje się tak, ponieważ gdy nie definiujesz konstruktora, kompilator automatycznie generuje dla Ciebie konstruktor, który nie przyjmuje żadnych argumentów. Jeśli chcesz czegoś więcej od konstruktora, przeskakujesz to. To NIE jest przeciążenie funkcji. Zatem jedynym konstruktorem, który teraz widzi kompilator, jest konstruktor, który przyjmuje argument. Aby przeciwdziałać temu problemowi, możesz przekazać wartość domyślną, jeśli konstruktor zostanie przekazany bez wartości.
źródło
Klasa potrzebuje konstruktora. Jest to wymóg obowiązkowy.
Odpowiem na inne pytanie, dlaczego zawsze chcemy mieć domyślny konstruktor bez parametrów? są przypadki, w których nie jest to pożądane, więc programista ma kontrolę nad dodaniem lub usunięciem go w razie potrzeby.
źródło