W C nie ma potrzeby rzucania void *
żadnego innego wskaźnika, zawsze jest bezpiecznie promowany. Jednak w C ++ tak nie jest. Na przykład,
int *a = malloc(sizeof(int));
działa w C, ale nie w C ++. (Uwaga: Wiem, że nie powinieneś używać malloc
w C ++ ani w tym przypadkunew
, a zamiast tego powinieneś preferować inteligentne wskaźniki i / lub STL; jest to pytane wyłącznie z ciekawości) Dlaczego standard C ++ nie pozwala na taką niejawną obsadę, podczas gdy standard C robi?
c++
language-features
wolfPack88
źródło
źródło
long *a = malloc(sizeof(int));
Ups, ktoś zapomniał zmienić tylko jeden typ!sizeof(*a)
zamiast tego.malloc
nie może zwrócić wskaźnika do przydzielonego typu.new
to C ++ zwraca wskaźnik do przydzielonego typu, więc poprawnie napisany kod C ++ nigdy nie będzie miał żadnychvoid *
rzutów.void
, C nie . Kiedy to słowo / pomysł został dodany do C, to zmienił go do potrzeb użytkownika c. Było to krótko po tym, jak typy wskaźników zaczęły być w ogóle sprawdzane . Sprawdź, czy możesz znaleźć broszurę z opisem K&R C online, czy też starą kopię tekstu programowego C, takiego jak C Primer firmy Waite Group . ANSI C było pełne, funkcji wspieranych lub inspirowanych przez C ++, a K&R C był znacznie prostszy. Bardziej poprawne jest więc to, że C ++ rozszerzył C tak, jak istniał w tym czasie, a C, o którym wiesz, że zostało usunięte z C ++.Odpowiedzi:
Ponieważ konwersje typu niejawnego są zwykle niebezpieczne, a C ++ przyjmuje bezpieczniejsze podejście do pisania niż C.
C zwykle zezwala na konwersje niejawne, nawet jeśli istnieje duże prawdopodobieństwo, że konwersja jest błędem. To dlatego, że C zakłada, że programista wie dokładnie, co robią, a jeśli nie, to jest to problem programisty, a nie problem kompilatora.
C ++ zwykle nie zezwala na rzeczy, które mogą potencjalnie być błędami, i wymaga jawnego określenia swojej intencji za pomocą rzutowania typu. To dlatego, że C ++ stara się być przyjazny dla programistów.
Możesz zapytać, dlaczego jest przyjazny, gdy w rzeczywistości wymaga więcej pisania.
Widzisz, każdy wiersz kodu, w dowolnym programie, w dowolnym języku programowania, będzie na ogół czytany wiele razy, niż zostanie napisany (*). Zatem łatwość czytania jest o wiele ważniejsza niż łatwość pisania. A podczas czytania wyróżnianie się potencjalnie niebezpiecznymi konwersjami za pomocą rzutowania typu jawnego pomaga zrozumieć, co się dzieje i mieć pewien poziom pewności, że to, co się dzieje, faktycznie jest tym, co miało się wydarzyć.
Poza tym niedogodność związana z wpisywaniem jawnej obsady jest trywialna w porównaniu z niedogodnościami godzinnego rozwiązywania problemów w celu znalezienia błędu, który został spowodowany przez błędne zadanie, o którym można było Cię uprzedzić, ale którego nigdy nie było.
(*) Najlepiej, jeśli zostanie napisany tylko raz, ale będzie czytany za każdym razem, gdy ktoś będzie musiał go przejrzeć, aby określić, czy nadaje się do ponownego użycia, i za każdym razem, gdy pojawia się problem, i za każdym razem, gdy ktoś musi dodać kod w pobliżu a następnie za każdym razem, gdy pojawia się problem z pobliskim kodem i tak dalej. Dzieje się tak we wszystkich przypadkach, z wyjątkiem skryptów „napisz raz, uruchom, a potem wyrzuć”, dlatego nic dziwnego, że większość języków skryptowych ma składnię, która ułatwia pisanie z całkowitym lekceważeniem łatwości czytania. Czy kiedykolwiek myślałeś, że perl jest całkowicie niezrozumiały? Nie jesteś sam. Pomyśl o takich językach jak o językach „tylko do zapisu”.
źródło
void*
jest bardziej niebezpieczne w C ++, ponieważ sposób, w jaki niektóre funkcje OOP są implementowane w C ++, wskaźnik do tego samego obiektu może mieć różną wartość w zależności od typu wskaźnika.Oto, co mówi Stroustrup :
Następnie pokazuje przykład, jak pustka * może być niebezpieczna i mówi:
Wreszcie zauważa:
Zagłębia się bardziej szczegółowo w The Design and Evolution of C ++ .
Odpowiedź sprowadza się więc do: Projektant języka uważa, że jest to niebezpieczny wzorzec, dlatego uczynił go nielegalnym i zapewnił alternatywne sposoby osiągnięcia tego, do czego normalnie był używany.
źródło
malloc
przykładu, warto zauważyć, żemalloc
(oczywiście) nie wywoła konstruktora, więc jeśli jest to typ klasy, dla której alokujesz pamięć, możliwość niejawnego rzutowania na typ klasy byłaby myląca.To jest zawsze promowane, tak, ale prawie nie bezpiecznie .
C ++ wyłącza to zachowanie właśnie dlatego, że próbuje mieć bezpieczniejszy system typów niż C, a to zachowanie nie jest bezpieczne.
Rozważ ogólnie następujące 3 podejścia do konwersji typów:
Cóż, 1 jest brzydka i stanowi praktyczną przeszkodę w zrobieniu czegokolwiek, ale może być naprawdę używana tam, gdzie potrzebna jest wielka ostrożność. C z grubsza wybrał 2, które jest łatwiejsze do wdrożenia, i C ++ dla 3, które jest trudniejsze do wdrożenia, ale bezpieczniejsze.
źródło
Z definicji wskaźnik pustki może wskazywać na wszystko. Dowolny wskaźnik można przekonwertować na wskaźnik pustki, dzięki czemu będzie można przekonwertować wracając do tej samej wartości. Jednak wskaźniki do innych typów mogą mieć ograniczenia, takie jak ograniczenia wyrównania. Na przykład wyobraź sobie architekturę, w której znaki mogą zajmować dowolny adres pamięci, ale liczby całkowite muszą zaczynać się od równych granic adresów. W niektórych architekturach wskaźniki całkowite mogą nawet liczyć 16, 32 lub 64 bity jednocześnie, tak że char * może faktycznie mieć wielokrotność wartości liczbowej int *, wskazując jednocześnie to samo miejsce w pamięci. W takim przypadku konwersja z pustki * faktycznie zaokrągliłaby bity, których nie można odzyskać, a zatem nie jest odwracalna.
Mówiąc prościej, wskaźnik pustki może wskazywać na wszystko, w tym na rzeczy, na które inne wskaźniki mogą nie być w stanie wskazać. Zatem konwersja do pustego wskaźnika jest bezpieczna, ale nie na odwrót.
źródło