Warum wird nicht "void" implizit in C ++ umgewandelt?

30

In C muss a nicht in einen void *anderen Zeigertyp umgewandelt werden, es wird immer sicher heraufgestuft. In C ++ ist dies jedoch nicht der Fall. Z.B,

int *a = malloc(sizeof(int));

funktioniert in C, aber nicht in C ++. (Hinweis: Ich weiß, dass Sie nicht mallocin C ++ oder für diese Angelegenheit verwenden newsollten und stattdessen intelligente Zeiger und / oder die STL bevorzugen sollten; dies wird nur aus Neugier gefragt.) Warum erlaubt der C ++ - Standard diese implizite Umwandlung nicht? während der C-Standard tut?

wolfPack88
quelle
3
long *a = malloc(sizeof(int));Hoppla, jemand hat vergessen, nur einen Typ zu ändern!
Doval
4
@Doval: Das ist immer noch leicht zu beheben, indem Sie sizeof(*a)stattdessen verwenden.
WolfPack88
3
Ich glaube, dass @ ratchetfreak der Grund dafür ist, dass C diese implizite Konvertierung durchführt, weil malloces keinen Zeiger auf den zugewiesenen Typ zurückgeben kann. newis C ++ gibt einen Zeiger auf den zugewiesenen Typ zurück, sodass ordnungsgemäß geschriebener C ++ - Code niemals void *s zum Umwandeln hat.
Gort the Robot
3
Abgesehen davon ist es kein anderer Zeigertyp. Es müssen nur Datenzeiger angewendet werden.
Deduplizierer
3
@ wolfPack88 du hast deine geschichte falsch geschrieben. C ++ hatte void, C nicht . Wenn das Schlüsselwort / Idee zu C hinzugefügt wurde, sie verändert es C Bedürfnissen gerecht zu werden . Das war kurz nach Zeigertypen gestartet geprüft werden überhaupt . Sehen Sie nach, ob Sie die K & R C-Beschreibungsbroschüre online oder eine Vintage-Kopie eines C-Programmiertexts wie C Primer von Waite Group finden . ANSI C war voll mit Features, die von C ++ zurückportiert oder inspiriert wurden, und K & R C war viel einfacher. Es ist also korrekter, dass C ++ C erweitert hat, wie es zu der Zeit existierte, und dass das C, das Sie kennen, von C ++ entfernt wurde.
JDługosz,

Antworten:

39

Weil implizite Typkonvertierungen normalerweise unsicher sind und C ++ eine sicherere Haltung beim Tippen einnimmt als C.

C lässt normalerweise implizite Konvertierungen zu, auch wenn die meisten Chancen bestehen, dass die Konvertierung ein Fehler ist. Dies liegt daran, dass C davon ausgeht, dass der Programmierer genau weiß, was er tut. Andernfalls liegt es am Problem des Programmierers und nicht am Problem des Compilers.

C ++ lässt normalerweise Dinge zu, die möglicherweise Fehler sind, und fordert Sie auf, Ihre Absicht explizit mit einer Typumwandlung zu erklären. Das liegt daran, dass C ++ versucht, programmiererfreundlich zu sein.

Sie könnten fragen, warum es freundlich ist, wenn Sie tatsächlich mehr eingeben müssen.

Wie Sie sehen, wird eine bestimmte Codezeile in jedem Programm, in jeder Programmiersprache im Allgemeinen viel öfter gelesen als geschrieben (*). Das Lesen ist also viel wichtiger als das Schreiben. Und beim Lesen hilft es, potenziell unsichere Konvertierungen durch explizite Typumwandlungen hervorzuheben, um zu verstehen, was vor sich geht, und um ein gewisses Maß an Gewissheit darüber zu haben, dass das, was geschieht, tatsächlich das ist, was geschehen soll.

Außerdem ist die Unannehmlichkeit, die explizite Besetzung eingeben zu müssen, im Vergleich zu der Unannehmlichkeit von Stunden zu Stunden der Fehlersuche, einen Fehler zu finden, der durch eine fehlerhafte Zuweisung verursacht wurde, vor der Sie gewarnt werden konnten, die Sie aber nie hatten.

(*) Idealerweise wird es nur einmal geschrieben, aber jedes Mal, wenn jemand es überprüfen muss, um festzustellen, ob es für die Wiederverwendung geeignet ist, und jedes Mal, wenn eine Fehlerbehebung durchgeführt wird und wenn jemand Code in der Nähe hinzufügen muss, wird es gelesen. und dann jedes Mal gibt es Fehlerbehebung von Code in der Nähe und so weiter. Dies gilt in allen Fällen, mit Ausnahme von Skripten, die nur einmal geschrieben, ausgeführt und dann weggeworfen werden. Kein Wunder, dass die meisten Skriptsprachen eine Syntax haben, die das Schreiben erleichtert, ohne die Lesefreundlichkeit zu beachten. Hast du jemals gedacht, dass Perl völlig unverständlich ist? Du bist nicht allein. Stellen Sie sich solche Sprachen als "Nur-Schreiben" -Sprachen vor.

Mike Nakis
quelle
C steht im Wesentlichen einen Schritt über dem Maschinencode. Ich kann C für solche Dinge entschuldigen.
Qix
8
Programme (egal in welcher Sprache) sollen gelesen werden. Explizite Operationen fallen auf.
Matthieu M.
10
Es ist erwähnenswert,, dass Abgüsse durch void*ist mehr unsicher in C ++, weil die Art und Weise bestimmte OOP Funktionen in C implementiert werden ++, Zeiger auf gleiches Objekt könnte anderen Wert hat je nach Zeigertyp.
Hyde
@MatthieuM. sehr richtig. Vielen Dank für das Hinzufügen, es lohnt sich, Teil der Antwort zu sein.
Mike Nakis
1
@MatthieuM .: Ah, aber du willst wirklich nicht alles explizit machen müssen. Die Lesbarkeit wird nicht dadurch verbessert, dass mehr gelesen werden muss. In diesem Punkt ist das Gleichgewicht eindeutig dafür, explizit zu sein.
Deduplizierer
28

Das sagt Stroustrup :

In C können Sie eine Leere * implizit in ein T * umwandeln. Das ist unsicher

Anschließend zeigt er ein Beispiel, wie leer * gefährlich sein kann und sagt:

... Folglich benötigen Sie in C ++ eine explizite Besetzung, um ein T * aus einer Lücke * zu erhalten. ...

Schließlich stellt er fest:

Eine der häufigsten Anwendungen dieser unsicheren Konvertierung in C besteht darin, das Ergebnis von malloc () einem geeigneten Zeiger zuzuweisen. Beispielsweise:

int * p = malloc (Größe von (int));

Verwenden Sie in C ++ den Operator typesafe new:

int * p = new int;

In " Das Design und die Entwicklung von C ++" geht er ausführlicher darauf ein .

Die Antwort lautet also: Der Sprachdesigner ist der Ansicht, dass es sich um ein unsicheres Muster handelt, und hat es daher illegal gemacht und alternative Methoden bereitgestellt, um das zu erreichen, wofür das Muster normalerweise verwendet wurde.

Gort den Roboter
quelle
1
In Bezug auf das mallocBeispiel ist anzumerken, dass malloc(offensichtlich) kein Konstruktor aufgerufen wird. Wenn es sich also um einen Klassentyp handelt, für den Sie Speicher zuweisen, wäre es irreführend, implizit in den Klassentyp umgewandelt zu werden.
chbaker0
Dies ist eine sehr gute Antwort, wohl besser als meine. Ich mag den Ansatz "Argument von Autorität" nur wenig.
Mike Nakis
"new int" - wow, als C ++ - Neuling hatte ich Probleme, dem Heap einen Basistyp hinzuzufügen, und ich wusste nicht einmal, dass Sie das können. -1 Verwendung für Malloc.
Katana314
3
@MikeNakisFWIW, meine Absicht war es, Ihre Antwort zu ergänzen. In diesem Fall lautete die Frage: "Warum haben sie das getan?"
Gort the Robot
11

In C muss keine Leerstelle * in einen anderen Zeigertyp umgewandelt werden. Sie wird immer sicher heraufgestuft.

Es wird immer beworben, aber kaum sicher .

C ++ deaktiviert dieses Verhalten genau deshalb, weil versucht wird, ein sichereres Typsystem als C zu verwenden, und dieses Verhalten ist nicht sicher.


Betrachten Sie im Allgemeinen diese drei Ansätze zur Typkonvertierung:

  1. erzwingen, dass der Benutzer alles aufschreibt, sodass alle Konvertierungen explizit sind
  2. Angenommen, der Benutzer weiß, was er tut, und konvertiert einen beliebigen Typ in einen anderen
  3. Implementieren Sie ein vollständiges Typsystem mit typsicheren Generika (Vorlagen, neue Ausdrücke), expliziten benutzerdefinierten Konvertierungsoperatoren und erzwingen Sie explizite Konvertierungen nur von Dingen, die der Compiler nicht sehen kann und die implizit sicher sind

Nun, ich bin hässlich und ein praktisches Hindernis für die Erledigung von Aufgaben, könnte aber auch wirklich dort eingesetzt werden, wo große Sorgfalt geboten ist. C entschied sich grob für 2, was einfacher zu implementieren ist, und C ++ für 3, was schwieriger zu implementieren ist, aber sicherer.

Nutzlos
quelle
Es war ein langer, schwerer Weg, bis zu 3 zu kommen, mit Ausnahmen, die später hinzugefügt wurden, und Vorlagen, die später noch hinzugefügt wurden.
JDługosz
Nun, Vorlagen sind nicht wirklich benötigt für eine solche vollständige sichere System: Hindley-Milner - basierten Sprachen auszukommen , ohne sie ganz gut, ohne implizite Konvertierungen überhaupt und keine Notwendigkeit , die Typen zu schreiben explictly entweder. (Natürlich basieren diese Sprachen auf Typ-Löschung / Garbage-Collection / höherwertigem Polymorphismus, Dinge, die C ++ eher vermeidet.)
leftaroundabout 10'15
1

Per Definition kann ein ungültiger Zeiger auf alles zeigen. Jeder Zeiger kann in einen ungültigen Zeiger konvertiert werden. Auf diese Weise können Sie den exakt gleichen Wert zurückkonvertieren. Zeiger auf andere Typen können jedoch Einschränkungen aufweisen, z. B. Ausrichtungsbeschränkungen. Stellen Sie sich beispielsweise eine Architektur vor, in der Zeichen eine beliebige Speicheradresse belegen können, Ganzzahlen jedoch an geraden Adressgrenzen beginnen müssen. In bestimmten Architekturen können Integer-Zeiger sogar 16, 32 oder 64 Bit gleichzeitig zählen, sodass ein char * tatsächlich ein Vielfaches des numerischen Werts von int * hat, während es auf dieselbe Stelle im Speicher verweist. In diesem Fall würde das Konvertieren von einer Lücke * tatsächlich Bits abrunden, die nicht wiederhergestellt werden können und daher nicht umkehrbar sind.

Einfach ausgedrückt kann der leere Zeiger auf alles zeigen, einschließlich auf Dinge, auf die andere Zeiger möglicherweise nicht zeigen können. Somit ist die Konvertierung in den leeren Zeiger sicher, aber nicht umgekehrt.

roserez
quelle