Niedawno uczyłem się D i zaczynam się trochę zaznajomić z językiem. Wiem, co oferuje, nie wiem jeszcze, jak korzystać ze wszystkiego, i niewiele wiem o idiomach D i tak dalej, ale uczę się.
Lubię D. To fajny język, będący w pewnym sensie ogromną aktualizacją do C i ładnie wykonany. Żadna z funkcji nie wydaje się być „przykręcona”, ale w rzeczywistości całkiem przemyślana i dobrze zaprojektowana.
Często słyszysz, że D był tym, czym powinien być C ++ (pozostawiam pytanie, czy jest to prawdą dla każdego i dla wszystkich, aby sami decydowali, aby uniknąć niepotrzebnych wojen o płomienie). Słyszałem też od kilku programistów C ++, że podoba im się D znacznie bardziej niż C ++.
Ja, choć znam C, nie mogę powiedzieć, że znam C ++. Chciałbym usłyszeć od kogoś znającego zarówno C ++, jak i D, jeśli uważają, że jest coś, co C ++ robi lepiej niż D jako język (co oznacza, że nie jest to zwykłe „ma więcej bibliotek stron trzecich” lub „jest więcej zasobów” lub „ istnieje więcej zadań wymagających C ++ niż D ”).
D został zaprojektowany przez niektórych bardzo wykwalifikowanych programistów C ++ ( Walter Bright i Andrei Alexandrescu , z pomocą społeczności D), aby naprawić wiele problemów, które miał C ++, ale czy było coś, co tak naprawdę nie poprawiło się? Coś, za czym tęsknił? Coś, co Twoim zdaniem nie było lepszym rozwiązaniem?
źródło
Odpowiedzi:
Większość rzeczy, które C ++ „robi” lepiej niż D, to meta rzeczy: C ++ ma lepsze kompilatory, lepsze narzędzia, bardziej dojrzałe biblioteki, więcej powiązań, więcej ekspertów, więcej samouczków itp. Zasadniczo ma coraz więcej wszystkich rzeczy zewnętrznych, które możesz oczekiwałbym od bardziej dojrzałego języka. To jest bezdyskusyjne.
Jeśli chodzi o sam język, moim zdaniem C ++ radzi sobie lepiej niż D. Prawdopodobnie jest ich więcej, ale oto kilka, które mogę wymienić na czubku głowy:
C ++ ma lepiej przemyślany system typów
W tej chwili istnieje sporo problemów z systemem typów w D, które wydają się być niedopatrzeniem w projekcie. Na przykład obecnie niemożliwe jest skopiowanie struktury const do struktury non-const, jeśli struktura zawiera odwołania do obiektów klasy lub wskaźniki ze względu na przechodniość const i sposób działania konstruktorów postblit na typach wartości. Andrei mówi, że wie, jak to rozwiązać, ale nie podał żadnych szczegółów. Problem można z pewnością naprawić (wprowadzenie konstruktorów kopiujących w stylu C ++ byłoby jedną naprawą), ale obecnie jest to poważny problem w języku.
Innym problemem, który mnie zepsuł, jest brak logicznej stałej (tj. Nie
mutable
jak w C ++). Jest to świetne do pisania kodu zabezpieczającego wątki, ale utrudnia (niemożliwe?) Robienie leniwej intralizacji w obiektach const (pomyśl o funkcji const „get”, która konstruuje i buforuje zwracaną wartość przy pierwszym wywołaniu).Wreszcie, biorąc pod uwagę te istniejące problemy, martwię się o tym, jak reszta systemu typu (
pure
,shared
etc.) będą wchodzić w interakcje ze wszystkim w języku, gdy zostaną oddane do użytku. Standardowa biblioteka (Phobos) obecnie w bardzo niewielkim stopniu korzysta z zaawansowanego systemu typu D, więc myślę, że uzasadnione jest pytanie, czy wytrzyma stres. Jestem sceptyczny, ale optymistyczny.Zauważ, że C ++ ma brodawki w systemie typów (np. Stała nieprzechodnia, wymagająca
iterator
iconst_iterator
), które sprawiają, że jest dość brzydka, ale chociaż system typów w C ++ jest trochę niewłaściwy w części, nie powstrzymuje cię przed wykonaniem pracy jak D czasami robi.Edycja: Aby wyjaśnić, uważam, że C ++ ma lepiej przemyślany system typów - niekoniecznie lepszy - jeśli ma to sens. Zasadniczo w DI uważam, że istnieje ryzyko związane ze stosowaniem wszystkich aspektów jego systemu typów, które nie są obecne w C ++.
D jest czasem trochę zbyt wygodne
Jedną z krytycznych uwag, które często słyszysz o C ++, jest to, że ukrywa przed Tobą pewne problemy niskiego poziomu, np. Proste zadania, takie jak
a = b;
wykonywanie wielu operacji, takich jak wywoływanie operatorów konwersji, wywoływanie operatorów przypisania przeciążenia itp., Które mogą być trudno zobaczyć z kodu. Niektórzy to lubią, inni nie. Tak czy inaczej, w D jest gorzej (lepsza?) Ze względu na takie rzeczy jakopDispatch
,@property
,opApply
,lazy
które mają potencjał, aby zmienić kod niewinnie patrząc na rzeczy, że nie należy się spodziewać.Nie sądzę, aby był to duży problem osobiście, ale niektórzy mogą uznać to za odrażające.
D wymaga odśmiecania.
Może to być postrzegane jako kontrowersyjne, ponieważ możliwe jest uruchomienie D bez GC. Jednak tylko dlatego, że jest to możliwe, nie oznacza, że jest to praktyczne. Bez GC tracisz wiele funkcji D, a korzystanie ze standardowej biblioteki byłoby jak chodzenie po polu minowym (kto wie, które funkcje przydzielają pamięć?). Osobiście uważam, że używanie D bez GC jest całkowicie niepraktyczne, a jeśli nie jesteś fanem GC (tak jak ja), może to być dość odrażające.
Naiwne definicje tablic w D alokują pamięć
To jest mój wkurzający zwierzak:
Najwyraźniej, aby uniknąć przydziału w D, musisz zrobić:
Te małe przydziały „za twoimi plecami” są dobrym przykładem moich dwóch poprzednich punktów.
Edycja: Zauważ, że jest to znany problem, nad którym pracujemy.Edycja: Zostało to naprawione. Nie następuje przydział.
Wniosek
Skoncentrowałem się na negatywach D w porównaniu do C ++, ponieważ o to właśnie zadałem pytanie, ale proszę nie postrzegać tego postu jako stwierdzenia, że C ++ jest lepszy niż D. Mogłabym z łatwością zrobić większy post z miejsc, w których D jest lepszy niż C ++. Od Ciebie zależy, który z nich wybrać.
źródło
Kiedy dołączyłem do rozwoju D, byłem w szczególnej sytuacji, ponieważ byłem jedną z osób, które wiedzą najwięcej o C ++. Teraz jestem w jeszcze bardziej szczególnej sytuacji, aby być także jedną z osób, które wiedzą najwięcej na temat D. Nie mówię tego o odpowiednich prawach do przechwalania się i chwaleniu, a jedynie o tym, że jestem ciekawy korzystna pozycja do udzielenia odpowiedzi na to pytanie. To samo dotyczy Waltera.
Ogólnie rzecz biorąc, pytanie, co C ++ (a przez to mam na myśli C ++ 2011) lepiej niż D, jest tak samo wewnętrznie sprzeczne jak pytanie: „Jeśli zapłacisz profesjonaliście za posprzątanie domu, w jakich miejscach on opuści brudniejszy niż wcześniej? ” Niezależnie od tego, jaką wartością było to, że C ++ mogło to zrobić, czego D nie potrafił, zawsze było dla mnie i Waltera jak obolały kciuk, więc prawie z definicji C ++ nigdy nie może zrobić niczego, co nie jest w zasięgu D.
Jedną rzeczą, która rzadko jest rozumiana w projektowaniu języka (ponieważ niewiele osób ma szczęście, aby to zrobić) jest to, że jest o wiele mniej niewymuszonych błędów, niż może się wydawać. Wielu z nas użytkowników języka patrzy na jakąś konstrukcję lub inną i mówi: „Ew! To takie złe! Co oni sobie myśleli?” Faktem jest, że najbardziej niezręczne przypadki w języku są następstwem kilku fundamentalnych decyzji, które wszystkie są rozsądne i pożądane, ale zasadniczo konkurują ze sobą lub są ze sobą sprzeczne (np. Modułowość i wydajność, zwięzłość i kontrola itp.).
Mając to na uwadze, wymienię kilka rzeczy, o których mogę pomyśleć, i dla każdego wyjaśnię, w jaki sposób wybór D wynika z chęci spełnienia innej, wyższej karty, czarteru.
D zakłada, że wszystkie obiekty są ruchome przez kopiowanie bitowe. Pozostawia to mniejszość projektów w C ++, szczególnie tych, które wykorzystują wewnętrzne wskaźniki, tj. Klasę zawierającą wskaźniki wewnątrz siebie. (Każdy taki projekt można przetłumaczyć na D bez żadnych lub nieznacznych kosztów wydajności, ale wiąże się to z koniecznością tłumaczenia). Podjęliśmy decyzję, aby znacznie uprościć język, zwiększyć efektywność kopiowania obiektów bez interwencji użytkownika lub przy minimalnej interwencji użytkownika i uniknąć cały tekst budowy kopii i odniesienia do wartości są w całości przedstawione.
D nie zezwala na typy o niejednoznacznej płci (które nie mogą zdecydować, czy są to typy wartościowe czy referencyjne). Takie projekty są jednogłośnie odrzucane w C ++ i prawie zawsze są błędne, ale niektóre z nich są technicznie poprawne. Dokonaliśmy tego wyboru, ponieważ uniemożliwia to w większości niepoprawny kod i tylko niewielki ułamek poprawnego kodu, który można przeprojektować. Uważamy, że to dobry kompromis.
D nie zezwala na hierarchie wielu korzeni. Poprzedni plakat był bardzo podekscytowany tym konkretnym tematem, ale jest to dobrze zdeptany grunt i nie ma wyraźnej przewagi hierarchicznych hierarchii nad hierarchiami, które mają wspólny katalog główny.
W D nie możesz rzucić np. Int. Musisz rzucić obiekt dziedziczący Throwable. Żaden spór nie ma lepszego stanu rzeczy w D, ale cóż, C ++ może to zrobić, czego D nie potrafi.
W C ++ jednostką enkapsulacji jest klasa. W D jest to moduł (tzn. Plik). Walter podjął tę decyzję z dwóch powodów: w naturalny sposób odwzorować enkapsulację na semantykę ochrony systemu plików i uniknąć potrzeby posiadania „przyjaciela”. Ten wybór bardzo dobrze integruje się z ogólną konstrukcją modułową D. Można by zmienić rzeczy tak, by były bardziej podobne do C ++, ale to by wymusiło; Wybrane zakresy enkapsulacji C ++ są dobre tylko dla fizycznego projektu C ++.
Może być jedna lub dwie mniejsze rzeczy, ale ogólnie to powinno być to.
źródło
Object*
tak powszechnie używane jakoint*
?), A D wydaje się całkowicie ignorować kara za wydajność lub twierdzenie, że nie istnieje. To oczywiście nieprawda - w wielu przypadkach brak pamięci podręcznej jest dość zauważalny, więc C ++ zawsze będzie miał tę przewagę elastyczności nad D.Myślę, że będzie ci bardzo trudno znaleźć wiele w D, co jest obiektywniegorzej niż C ++. Większość problemów z D, w których można obiektywnie powiedzieć, że jest gorzej, to albo problemy z jakością implementacji (które są na ogół spowodowane młodym językiem i implementacją i zostały naprawione w zawrotnym tempie), albo są to problemy z brakiem bibliotek stron trzecich (które przyjdą z czasem). Sam język jest na ogół lepszy niż C ++, a przypadki, w których C ++, jako język, jest lepszy, zwykle albo będą tam, gdzie jest kompromis, w którym C ++ poszedł w jedną stronę, a D poszedł w inną, lub gdzie ktoś ma subiektywne powody, dla których myślę, że jedno jest lepsze od drugiego. Ale liczba obiektywnych powodów, dla których C ++, jako język, jest lepszy, prawdopodobnie będzie niewielka.
W rzeczywistości muszę naprawdę rozwalić mózg, aby wymyślić powody, dla których C ++, jako język, jest lepszy od D. To, co ogólnie przychodzi na myśl, to kwestia kompromisów.
Ponieważ D's const jest przechodni, a ponieważ język jest niezmienny , to ma o wiele silniejsze niż C ++ gwarancje 's
const
, co oznacza, że D nie ma i nie może miećmutable
. Nie może mieć logicznej stałej . Tak więc zyskujesz ogromną przewagę dzięki systemowi const D, ale w niektórych sytuacjach po prostu nie możesz używaćconst
tak jak w C ++.D ma tylko jeden operator rzutowania, podczas gdy C ++ ma 4 (5, jeśli policzysz operator rzutowania C). To sprawia, że radzenie sobie z rzutami w D jest łatwiejsze w ogólnym przypadku, ale jest problematyczne, gdy naprawdę chcesz dodatkowych komplikacji / korzyści, które zapewniają
const_cast
i jego bracia. Ale D jest w rzeczywistości na tyle potężny, że można użyć szablonów do implementacji rzutowań C ++, więc jeśli naprawdę ich potrzebujesz, możesz je mieć (a nawet mogą kiedyś znaleźć się w standardowej bibliotece D).D ma znacznie mniej niejawnych rzutowań niż C ++ i jest o wiele bardziej prawdopodobne, że deklarują, że dwie funkcje są ze sobą w konflikcie (zmuszając cię do bardziej szczegółowego określenia, które funkcje masz na myśli - albo z rzutowaniami, albo podając pełną ścieżkę modułu ). Czasami może to być denerwujące, ale zapobiega wszelkim problemom z przejmowaniem funkcji . Wiesz, że naprawdę wywołujesz funkcję, którą masz na myśli.
System modułów D jest znacznie czystszy niż #include C ++ (nie wspominając, znacznie szybszy w kompilacji), ale brakuje mu jakiejkolwiek przestrzeni nazw poza samymi modułami. Tak więc, jeśli chcesz mieć przestrzeń nazw w module, musisz przejść trasę Java i użyć funkcji statycznych dla klasy lub struktury. Działa, ale jeśli naprawdę chcesz przestrzeni nazw, to oczywiście nie jest tak czyste jak prawdziwa przestrzeń nazw. Jednak w większości sytuacji przestrzeń nazw, którą zapewniają same moduły, jest duża (i dość wyrafinowana, jeśli chodzi o rzeczy takie jak konflikty).
Podobnie jak Java i C #, D ma pojedyncze dziedziczenie zamiast wielokrotnego dziedziczenia, ale w przeciwieństwie do Java i C #, daje to fantastyczne sposoby uzyskania tego samego efektu bez wszystkich problemów, jakie ma wielokrotne dziedziczenie C ++ (a wielokrotne dziedziczenie C ++ może być bardzo nieuporządkowane czasami). D ma nie tylko interfejsy , ale ma także mieszanki łańcuchów , szablony i alias tego . Tak więc ostateczny wynik jest prawdopodobnie silniejszy i nie ma wszystkich problemów, które powoduje wielokrotne dziedziczenie C ++.
Podobnie jak w C #, D oddziela struktury i klasy . Klasy są typami referencyjnymi, które mają dziedziczenie i pochodzą od nich
Object
, podczas gdy struktury są typami wartości bez dziedziczenia. Ten rozdział może być zarówno dobry, jak i zły. Pozbywa się klasycznego problemu krojenia w C ++ i pomaga oddzielić typy, które są tak naprawdę typami wartości, od tych, które powinny być polimorficzne, ale na początku przynajmniej rozróżnienie może być denerwujące dla programisty C ++. Ostatecznie ma to wiele zalet, ale zmusza cię do nieco odmiennego traktowania swoich typów.Użytkownika funkcje z klas są polimorficzne domyślnie. Nie można zadeklarować, że nie są wirtualne . Kompilator decyduje, czy mogą być (co tak naprawdę ma miejsce tylko wtedy, gdy są ostateczne i nie zastępują funkcji z klasy podstawowej). W niektórych przypadkach może to stanowić problem z wydajnością. Jeśli jednak naprawdę nie potrzebujesz polimorfizmu, wystarczy użyć struktur i nie stanowi to problemu.
D ma wbudowany pojemnik na śmieci . Wielu z C ++ uznałoby to za poważną wadę i prawdę mówiąc, w chwili obecnej jego wdrożenie może wymagać poważnej pracy. Poprawia się, ale zdecydowanie nie jest jeszcze na równi z urządzeniem do usuwania śmieci Java. Łagodzi to jednak dwa czynniki. Po pierwsze, jeśli używasz struktur i innych typów danych na stosie, nie jest to duży problem. Jeśli twój program nie stale przydziela i zwalnia rzeczy na stosie, wszystko będzie dobrze. Po drugie, możesz pominąć śmietnik, jeśli chcesz i po prostu użyć C
malloc
ifree
. Istnieje kilka funkcji językowych (takich jak dzielenie tablic), których należy unikać lub zachować ostrożność, a niektóre ze standardowych bibliotek nie są tak naprawdę przydatne bez przynajmniej użycia GC (szczególnie przetwarzania ciągów), ale można pisać w D bez użycia modułu wyrzucania elementów bezużytecznych, jeśli naprawdę chcesz. Rozsądną rzeczą jest prawdopodobnie ogólne użycie go, a następnie unikanie go tam, gdzie profilowanie pokazuje, że powoduje problemy z kodem krytycznym dla wydajności, ale można tego całkowicie uniknąć, jeśli chcesz. A jakość wdrożenia GC poprawi się z czasem, eliminując wiele obaw, które może powodować używanie GC . Ostatecznie GC nie będzie tak dużym problemem, a w przeciwieństwie do Javy możesz tego uniknąć, jeśli chcesz.Prawdopodobnie są też inni, ale w tej chwili mogę to wymyślić. A jeśli zauważysz, wszystkie są kompromisami. D zdecydował się robić pewne rzeczy inaczej niż C ++, które mają wyraźną przewagę nad tym, jak robi to C ++, ale mają też pewne wady. To, co jest lepsze, zależy od tego, co robisz, aw wielu przypadkach prawdopodobnie na początku będzie się wydawało gorsze, a potem nie będziesz mieć z tym problemu, kiedy się przyzwyczaisz. Jeśli cokolwiek, problemy w D będą na ogół nowe spowodowane przez nowe rzeczy, których inne języki nie zrobiły wcześniej lub nie zrobiły w sposób podobny do D. Ogólnie rzecz biorąc, D bardzo dobrze nauczył się na błędach C ++.
A D, jako język, poprawia się w stosunku do C ++ na tyle sposobów, że uważam, że ogólnie rzecz biorąc, D jest obiektywnie lepszy.
D ma kompilację warunkową . Jest to jedna z funkcji, których często brakuje mi podczas programowania w C ++. Jeśli C ++ to doda, to C ++ poprawi się skokowo, jeśli chodzi o rzeczy takie jak szablony.
D ma odbicie w czasie kompilacji .
Zmienne są domyślnie lokalne dla wątków, ale mogą być,
shared
jeśli chcesz, aby były. To sprawia, że radzenie sobie z wątkami jest znacznie czystsze niż w C ++. Masz pełną kontrolę. Możesz używaćimmutable
i przekazywać wiadomości do komunikacji między wątkami, lub możesz tworzyć zmienneshared
i robić to w sposób C ++ z muteksami i zmiennymi warunkowymi. Nawet to zostało ulepszone w stosunku do C ++ dzięki wprowadzeniu synchronizacji (podobnej do C # i Java). Tak więc sytuacja wątków w D jest znacznie lepsza niż w C ++.Szablony D są znacznie potężniejsze niż szablony C ++, co pozwala robić znacznie więcej, znacznie łatwiej. Dzięki dodaniu ograniczeń szablonów komunikaty o błędach są znacznie lepsze niż w C ++. D sprawia, że szablony są bardzo wydajne i użyteczne. To nie przypadek, że autor Modern C ++ Design jest jednym z głównych współpracowników D. Uważam, że szablonów C ++ poważnie brakuje w porównaniu do szablonów D i może to być bardzo frustrujące w momentach programowania w C ++.
D ma wbudowane programowanie kontraktów .
D ma wbudowaną platformę testów jednostkowych .
D ma wbudowaną obsługę Unicode z
string
(UTF-8),wstring
(UTF-16) idstring
(UTF-32). Ułatwia radzenie sobie z Unicode. A jeśli chcesz po prostu używaćstring
i ogólnie nie martwisz się o Unicode, możesz - chociaż pewne zrozumienie podstaw Unicode pomaga w niektórych standardowych funkcjach biblioteki.Przeciążenie operatora D jest znacznie ładniejsze niż w C ++, pozwalając na użycie jednej funkcji do przeciążenia wielu operatorów jednocześnie. Najlepszym tego przykładem jest przeciążenie podstawowych operatorów arytmetycznych, a ich implementacje są identyczne, z wyjątkiem operatora. Mixiny łańcuchowe sprawiają, że jest to bardzo proste, dzięki czemu możesz mieć dla nich jedną prostą definicję funkcji.
Tablice D są znacznie lepsze niż tablice C ++. Są nie tylko odpowiednim typem o długości, ale można je dołączać i zmieniać ich rozmiar. Łączenie ich jest łatwe. A co najważniejsze, mają krojenie . To ogromny dar dla wydajnego przetwarzania macierzy. Łańcuchy to tablice znaków w D i nie stanowi to problemu (w rzeczywistości jest świetny!), Ponieważ tablice D są tak potężne.
Mógłbym kontynuować. Wiele ulepszeń, które zapewnia D, to małe rzeczy (np. Używanie
this
nazw konstruktorów lub niedozwolone, jeśli instrukcje lub ciała pętli, w których średnikiem jest całe ich ciało), ale niektóre z nich są dość duże, a po dodaniu wszystkiego razem zapewnia znacznie lepsze wrażenia z programowania. C ++ 0x dodaje niektóre funkcje D, których brakowało w C ++ (np.auto
I lambdas), ale nawet pomimo wszystkich jego ulepszeń, nadal nie będzie wiele, co byłoby obiektywnie lepsze w C ++ jako języku niż D.Nie ma wątpliwości, że istnieje wiele subiektywnych powodów, aby lubić się jeden na drugim, a względna niedojrzałość implementacji D może czasami stanowić problem (chociaż ostatnio bardzo szybko się poprawia - zwłaszcza, że repozytoria zostały przeniesione do github ) , a brak bibliotek stron trzecich może zdecydowanie stanowić problem (chociaż fakt, że D może łatwo wywoływać funkcje C - i, w mniejszym stopniu, funkcje C ++ - zdecydowanie łagodzi problem). Ale są to raczej problemy z jakością implementacji niż problemy z samym językiem. A gdy problemy z jakością wykonania zostaną naprawione, korzystanie z D. stanie się o wiele przyjemniejsze
Tak więc przypuszczam, że krótka odpowiedź na to pytanie jest „bardzo mała”. D, jako język, jest ogólnie lepszy od C ++.
źródło
Wykorzystanie pamięci RAII i stosu
D 2.0 nie pozwala
scope
na wystąpienie RAII na stosie, ponieważ usunął wartość słowa kluczowego przy przydzielaniu instancji klas na stosie.Nie można wykonywać dziedziczenia typu wartości w D, tak skutecznie, że zmusza do dokonania alokacji sterty dla dowolnej formy RAII.
To znaczy, chyba że używasz
emplace
, ale jest to bardzo bolesne w użyciu, ponieważ musisz ręcznie przydzielić pamięć. (Nie znalazłem jeszcze praktycznego zastosowaniaemplace
w D.)źródło
C ++ jest znacznie lepszy w zmuszaniu cię do gadatliwości. Może to być lepsze lub gorsze w twoich oczach, w zależności od tego, czy lubisz wnioskowanie, czy gadatliwość.
Porównaj zapamiętywanie w czasie wykonywania w C ++ :
z tą samą rzeczą w D:
Zauważ, na przykład, dodatkową gadatliwość
template <typename ReturnType, typename... Args>
względem versus(F)
,Args...
versusArgs
,args...
versusargs
itd.Dla lepszego lub gorszego C ++ jest bardziej gadatliwy.
Oczywiście możesz to zrobić również w D:
i wyglądałyby prawie identycznie, ale wtedy wymagałoby to
delegate
, podczas gdy oryginał zaakceptował dowolny obiekt możliwy do wywołania. (Wersja C ++ 0x wymagastd::function
obiektu, więc w obu przypadkach jest bardziej gadatliwy i restrykcyjny w wejściach ... co może być dobre, jeśli lubisz gadatliwość, a złe, jeśli nie.)źródło
Nie wiem dużo o D, ale wielu, wielu programistów C ++ bardzo go nie lubię i osobiście muszę się zgodzić - nie podoba mi się wygląd D i nie będę się zbliżał.
Aby zrozumieć, dlaczego D nie zyskuje większej przyczepności, musisz zacząć od zrozumienia, co przyciąga ludzi do C ++. Jednym słowem powodem numer jeden jest kontrola. Kiedy programujesz w C ++, masz pełną kontrolę nad swoim programem. Chcesz zastąpić bibliotekę Standard? Możesz. Chcesz robić niebezpieczne rzuty wskaźnikowe? Możesz. Chcesz naruszyć stałą poprawność? Możesz. Chcesz wymienić alokator pamięci? Możesz. Chcesz kopiować surową pamięć bez względu na jej typ? Jeśli naprawdę chcesz. Chcesz dziedziczyć po wielu implementacjach? To twój pogrzeb. Do diabła, możesz nawet uzyskać biblioteki śmieci, takie jak kolektor Boehm. Potem masz problemy, takie jak wydajność, która ściśle towarzyszy kontroli - im więcej kontroli ma programista, tym bardziej zoptymalizowany może stworzyć swój program.
Oto kilka rzeczy, które widziałem, przeprowadzając małe badania i rozmawiając z kilkoma osobami, które próbowały:
Ujednolicona hierarchia typów. Użytkownicy C ++ bardzo rzadko korzystają z dziedziczenia, większość programistów C ++ woli kompozycję, a typy powinny być łączone poprzez dziedziczenie tylko wtedy, gdy istnieje ku temu dobry powód. Pojęcie obiektu silnie narusza tę zasadę, łącząc każdy typ. Ponadto narusza jedną z podstawowych zasad C ++ - używasz tylko tego, co chcesz. Brak możliwości wyboru dziedziczenia po Object i związane z tym koszty są bardzo sprzeczne z tym, co C ++ oznacza jako język, jeśli chodzi o zapewnienie programiście kontroli nad jego programem.
Słyszałem o problemach z funkcjami i delegatami. Najwyraźniej D ma zarówno funkcje, jak i delegatów jako typy funkcji wywoływalnych w czasie wykonywania, i nie są one takie same, ale są one wymienne lub ... coś? Mój przyjaciel miał z nimi sporo problemów. Jest to zdecydowanie obniżenie wersji C ++, która właśnie ma
std::function
i gotowe.Masz kompatybilność. D nie jest szczególnie kompatybilny z C ++. Mam na myśli, że żaden język nie jest kompatybilny z C ++, spójrzmy prawdzie w oczy, z wyjątkiem C ++ / CLI, który jest rodzajem oszustwa, ale jako bariera wejścia, należy o tym wspomnieć.
Są jeszcze inne rzeczy. Na przykład po prostu przeczytaj wpis w Wikipedii.
printf
jest jedną z najbardziej niebezpiecznych funkcji, jakie kiedykolwiek wymyślono, w tej samej rodzinie, co duże problemy, jakgets
ze starej biblioteki C Standard. Jeśli szukasz tego w Stack Overflow, znajdziesz wiele, wiele pytań związanych z jego niewłaściwym użyciem. Zasadniczoprintf
jest to naruszenie SUSZENIA- podajesz typ w ciągu formatu, a następnie podajesz go ponownie, gdy podasz argument. Naruszenie DRY, gdy źle się pomylisz, zdarzają się bardzo złe rzeczy - powiedzmy, jeśli zmieniłeś typedef z 16-bitowej liczby całkowitej na 32-bitową. Nie można go w ogóle rozszerzyć - wyobraź sobie, co by się stało, gdyby wszyscy wymyślili własne specyfikatory formatu. Iostreamy w C ++ mogą być powolne, a ich wybór operatora może nie być najlepszy, a ich interfejs może korzystać z pracy, ale są zasadniczo zagwarantowane, że są bezpieczne, a DRY nie jest naruszone i można je łatwo rozszerzyć. Tego nie można powiedziećprintf
.Bez wielokrotnego dziedziczenia. To nie jest sposób w C ++. Programiści C ++ oczekują, że będą mieć pełną kontrolę nad swoim programem, a język wymuszający to, czego nie można odziedziczyć, stanowi naruszenie tej zasady. Ponadto powoduje, że dziedziczenie (jeszcze bardziej) jest kruche, ponieważ jeśli zmienisz typ interfejsu na klasę, ponieważ chcesz zapewnić domyślną implementację lub coś takiego, nagle cały kod użytkownika zostanie uszkodzony. To nie jest dobra rzecz.
Innym przykładem jest
string
iwstring
. W C ++ jest już dość bolesna konieczność konwersji między nimi, i czy ta biblioteka obsługuje Unicode, a ta stara biblioteka C używa tylkoconst char*
i trzeba pisać różne wersje tej samej funkcji w zależności od żądanego typu argumentu ciągu. Na przykład nagłówki Windows mają na przykład bardzo irytujące makra, aby poradzić sobie z problemem, który często może zakłócać twój własny kod. Dodaniedstring
do miksu tylko pogorszy sytuację, ponieważ teraz zamiast dwóch typów strun musisz zarządzać trzema. Posiadanie więcej niż jednego typu łańcucha zwiększy bóle konserwacyjne i wprowadzi powtarzalny kod dotyczący łańcuchów.Scott Meyers pisze:
Wymuszona przez język izolacja wątków nie jest plusem. Programiści C ++ oczekują pełnej kontroli nad swoimi programami, a język wymuszający coś zdecydowanie nie jest tym, co zalecił lekarz.
Wspomnę również o manipulacji ciągami w czasie kompilacji. D ma zdolność interpretowania kodu D w czasie kompilacji. To nie jest plus. Rozważ ogromne bóle głowy spowodowane stosunkowo ograniczonym preprocesorem C, dobrze znanym przez wszystkich doświadczonych programistów C ++, a następnie wyobraź sobie, jak bardzo ta funkcja będzie nadużywana. Możliwość tworzenia kodu D w czasie kompilacji jest świetna, ale powinna być semantyczna , a nie składniowa.
Ponadto możesz spodziewać się pewnego odruchu. D ma wyrzucanie elementów bezużytecznych, które programiści C ++ będą kojarzyli z takimi językami, jak Java i C #, które są w filozofii wprost przeciwstawne, a podobieństwa syntaktyczne przywołają ich również na myśl. Niekoniecznie jest to obiektywnie uzasadnione, ale z pewnością należy to odnotować.
Zasadniczo nie oferuje tak wiele, że programiści C ++ nie mogą tego zrobić. Może łatwiej napisać silni metaprogram D, ale może już pisać czynnikowymi metaprograms w C ++. Może D ty może napisać kompilacji ray-tracer, ale nikt tak naprawdę nie chce zrobić, że tak. W porównaniu z podstawowymi naruszeniami filozofii C ++, to, co możesz zrobić w D, nie jest szczególnie godne uwagi.
Nawet jeśli te rzeczy są tylko problemami na powierzchni, to jestem prawie pewien, że fakt, że na powierzchni D wcale nie wygląda jak C ++, jest prawdopodobnie dobrym powodem, dla którego wielu programistów C ++ nie migruje do D. Być może D sam musi wykonać lepszą pracę, reklamując się.
źródło
std::function
i gotowe. Dlaczego? Ponieważ masz na przykład wskaźniki funkcji. Jest dokładnie tak samo w „funkcjach” D: D są wskaźniki funkcji, a „delegaci” D są tacy sami jak C ++std::function
(z wyjątkiem tego, że są wbudowane). Nigdzie nie ma żadnej „obniżki” - a między nimi jest zgodność 1: 1, więc nie powinno to być mylące, jeśli znasz C ++.Jedną z rzeczy, które doceniam w C ++, jest możliwość udokumentowania argumentu funkcji lub zwrócenia wartości jako odwołania do C ++ zamiast wskaźnika, co oznacza implikację przyjęcia
null
wartości innej niż wartość.Wersja D:
Wersja C ++:
Aby być uczciwym, możesz bardzo zbliżyć się do C ++, przekształcając
A
go w Dstruct
i oznaczającfoo()
-argument jako aref
(klasy to typy referencyjne, a struktury to typy wartości w D, podobne do C #).Uważam, że
NonNullable
zamiast tego istnieje plan stworzenia szablonu dla klas jako standardowej biblioteki D. Mimo to lubię zwięzłość wType&
porównaniu zNonNullable(Type)
, i wolałbym, aby domyślna była wartość nullable (renderowanie czegoś takiego jakType
iNullable(Type)
). Ale jest już za późno, żeby to zmienić dla D, a teraz odpływam od tematu.źródło
ref
aby dać taki sam efekt jak w C ++&
. Jedną z głównych różnic jest to, żeref
nie potrwa to tymczasowo, nawet jeśli tak jestconst
.Najważniejszą rzeczą, jaką C ++ „robi lepiej” niż D, jest połączenie ze starszymi bibliotekami . Różne silniki 3D, OpenCL i podobne. Ponieważ D jest nowy, ma znacznie mniejszą liczbę różnych bibliotek do wyboru.
Inną ważną różnicą między C ++ i D jest to, że C ++ ma wielu niezależnych finansowo dostawców, ale od 2014 roku D ma praktycznie tylko jednego , dwuosobowego zespołu, który go stworzył. Interesujące jest to, że „zasada drugiego źródła”, która mówi, że projekty nigdy nie powinny zależeć od technologii, komponentów, które mają tylko jednego, jednego dostawcę, wydaje się obowiązywać nawet dla oprogramowania.
Dla porównania, pierwsza wersja interpretera Ruby została napisana przez wynalazcę Ruby, Yukihiro Matsumoto, ale główny nurt z 2014 roku, tłumacz z Ruby, został napisany praktycznie od zera przez innych ludzi. Dlatego Ruby można postrzegać jako język, który ma więcej niż jednego niezależnego finansowo dostawcę. Z drugiej strony D może być niesamowitą technologią, ale zależy to od kilku programistów, którzy ją rozwijają.
Historia Java pokazuje, że nawet jeśli technologia, w tym przypadku Java, ma świetny, ale pojedynczy, finansujący, istnieje duże ryzyko, że technologia zostanie zasadniczo zrzucona, niezależnie od ogromnej bazy użytkowników korporacyjnych. Cytat z Apache Software Foundation , gdzie EC oznacza „Komitet Wykonawczy”:
Oracle dostarczyło do EC zapytanie o licencję Java SE 7 i licencję, które są wewnętrznie sprzeczne, poważnie ograniczają dystrybucję niezależnych implementacji specyfikacji, a co najważniejsze, zabraniają dystrybucji niezależnych implementacji specyfikacji Open Source.
Historycznie można powiedzieć, że aplety Java miały przyspieszone sprzętowo płótno 3D na wiele lat przed opracowaniem HTML5 WebGL. Problem z szybkością uruchamiania apletów Java mógłby zostać rozwiązany, gdyby Java była projektem społecznościowym, ale menedżerowie jedynego podmiotu finansującego Javę, Sun Microsystems, nie uważali za wystarczająco ważne, aby naprawić implementację Java. Rezultat końcowy: płótno HTML5 wielu dostawców jako „replika biedaka” frameworku Java GUI (Swing itp.). Co ciekawe, po stronie serwera język programowania Python ma te same zalety, które obiecał Java: pisz raz, uruchamiaj na każdym serwerze, niezależnie od sprzętu, pod warunkiem, że aplikacja Python nie jest skompilowana do kodu maszynowego. Python jest mniej więcej tak stary / młody jak Java, ale w przeciwieństwie do Javy jest to „
Podsumowanie:
Podczas oceny technologii do użytku produkcyjnego najważniejszymi właściwościami technologii są ludzie, którzy ją rozwijają, proces społeczny, w którym odbywa się rozwój, oraz liczba niezależnych finansowo zespołów programistycznych.
To, czy korzystniejsze jest zastosowanie technologii T1 w porównaniu z technologią T2, zależy od dostawców technologii oraz od tego, czy technologia T1 pozwala na rozwiązanie problemów związanych z projektem taniej niż T2. Na przykład, jeśli problem pojedynczego dostawcy zostałby zignorowany, wówczas dla systemów informatycznych Java byłaby „lepszą” technologią niż C ++, ponieważ pliki binarne Java nie wymagają ponownej kompilacji przy wdrażaniu na nowym sprzęcie i pracach programistycznych związanych z zarządzaniem pamięcią jest mniejszy dla Javy niż dla C ++. Projekty tworzone od zera, np. Projekty, które nie zależą od innych bibliotek, mogą być tańsze w D niż C ++, ale z drugiej strony C ++ ma więcej niż jednego dostawcę i dlatego jest mniej ryzykowne w dłuższej perspektywie . (Przykład Java, w którym Sun Microsystems był prawie OK,
Możliwe obejście niektórych ograniczeń C ++
Jednym z możliwych obejść niektórych ograniczeń C ++ jest użycie wzorca projektowego, w którym początkowe zadanie jest dzielone na części i elementy są „sortowane” (klasyfikowane, grupowane, dzielone, inne-miłe-słowa-za- to samo) do 2 klas: kontroluj zadania związane z logiką , w których wzorce dostępu do pamięci nie pozwalają na lokalizację; zadania podobne do przetwarzania sygnałów , w których lokalizacja jest łatwo osiągalna. Zadania „podobne” przetwarzania sygnałów mogą być realizowane jako zestaw stosunkowo uproszczonych programów konsolowych. Zadania związane z logiką sterowania są umieszczone w jednym skrypcie napisanym w Ruby, Pythonie lub w innym miejscu, w którym wygoda tworzenia oprogramowania ma wyższy priorytet niż szybkość.
Aby uniknąć kosztownej inicjalizacji procesu systemu operacyjnego i kopiowania danych między procesami systemu operacyjnego, zestaw małych aplikacji konsoli C ++ można zaimplementować jako pojedynczy program C ++ uruchamiany jako „serwlet” przez Ruby / Python / itp. scenariusz. Ruby / Python / etc. skrypt zamyka serwlet przed wyjściem. Komunikacja między „serwletem” a Ruby / Python / etc. skrypt działa na potoku o nazwie Linux lub innym podobnym mechanizmie.
Jeśli początkowe zadanie nie daje się łatwo podzielić na części, które można zaklasyfikować do dwóch wyżej wymienionych klas, wówczas można spróbować ponownie sformułować problem, aby początkowe zadanie uległo zmianie.
źródło