Gibt es eine legitime Verwendung void*
in C ++? Oder wurde dies eingeführt, weil C es hatte?
Nur um meine Gedanken zusammenzufassen:
Eingabe : Wenn wir mehrere Eingabetypen zulassen möchten, können wir Funktionen und Methoden überladen. Alternativ können wir eine gemeinsame Basisklasse oder Vorlage definieren (danke, dass Sie dies in den Antworten erwähnt haben). In beiden Fällen wird der Code aussagekräftiger und weniger fehleranfällig (vorausgesetzt, die Basisklasse wird auf vernünftige Weise implementiert).
Ausgabe : Ich kann mir keine Situation vorstellen, in der ich lieber empfangen würde void*
als etwas, das von einer bekannten Basisklasse abgeleitet ist.
Nur um klar zu machen, was ich meine: Ich frage nicht speziell, ob es einen Anwendungsfall void*
gibt, sondern ob es einen Fall void*
gibt, in dem die beste oder einzige verfügbare Wahl ist. Was von mehreren Personen unten perfekt beantwortet wurde.
variant
,any
, getaggt Vereinigung. Alles, was Ihnen die tatsächliche Art des Inhalts und die Sicherheit der Verwendung mitteilen kann.Antworten:
void*
ist zumindest als Ergebnis von::operator new
(auch jedemoperator new
...) und vonmalloc
und als Argument des Platzierungsoperators notwendignew
.void*
kann als der gemeinsame Supertyp jedes Zeigertyps angesehen werden. Es bedeutet also nicht genau Zeiger aufvoid
, sondern Zeiger auf irgendetwas.Übrigens, wenn Sie einige Daten für mehrere nicht verwandte globale Variablen behalten möchten, können Sie einige verwenden
std::map<void*,int> score;
, nachdem Sie globalint x;
unddouble y;
undstd::string s;
doscore[&x]=1;
undscore[&y]=2;
und deklariert habenscore[&z]=3;
memset
will einevoid*
Adresse (die allgemeinsten)Außerdem haben POSIX-Systeme dlsym und sein Rückgabetyp sollte offensichtlich sein
void*
quelle
char *
werden. Sie sind sich in diesem Aspekt sehr nahe, obwohl die Bedeutung etwas anders ist.C++
inC++
diesem geschrieben steht, ist Grund genug zu habenvoid*
. Ich muss diese Seite lieben. Stellen Sie eine Frage, lernen Sie viel.void*
Art. Alle verwendeten Standardbibliotheksfunktionenchar*
Es gibt mehrere Gründe für die Verwendung
void*
, die drei häufigsten sind:void*
ihrer SchnittstelleIn umgekehrter Reihenfolge hilft das Bezeichnen von nicht typisiertem Speicher mit
void*
(3) anstelle vonchar*
(oder Varianten) dabei, eine versehentliche Zeigerarithmetik zu verhindern. Es sind nur sehr wenige Operationen verfügbar,void*
daher ist normalerweise ein Gießen erforderlich, bevor es nützlich ist. Und natürlich gibt es ähnlich wiechar*
bei Aliasing kein Problem.Type-Erasure (2) wird in C ++ weiterhin in Verbindung mit Vorlagen verwendet oder nicht:
std::function
Und wenn die Schnittstelle, mit der Sie sich befassen, verwendet wird
void*
(1), haben Sie offensichtlich keine andere Wahl.quelle
Oh ja. Sogar in C ++ gehen wir manchmal
void *
eher mit alstemplate<class T*>
weil manchmal der zusätzliche Code aus der Vorlagenerweiterung zu viel wiegt.Normalerweise würde ich es als eigentliche Implementierung des Typs verwenden, und der Vorlagentyp würde davon erben und die Casts umbrechen.
Außerdem müssen benutzerdefinierte Plattenzuordnungen (neue Implementierungen des Bedieners) verwendet werden
void *
. Dies ist einer der Gründe, warum g ++ eine Erweiterung für das Zulassen von Zeigerarithmatik hinzugefügt hat,void *
als wäre sie von Größe 1.quelle
struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;
ist ein minimaler void - * - Simulator, der Vorlagen verwendet.Wahr.
Dies ist teilweise richtig: Was ist, wenn Sie keine gemeinsame Basisklasse, Schnittstelle oder ähnliches definieren können? Um diese zu definieren, müssen Sie Zugriff auf den Quellcode haben, was häufig nicht möglich ist.
Sie haben keine Vorlagen erwähnt. Vorlagen können Ihnen jedoch beim Polymorphismus nicht helfen: Sie arbeiten mit statischen Typen, die zur Kompilierungszeit bekannt sind.
void*
kann als kleinster gemeinsamer Nenner betrachtet werden. In C ++ benötigen Sie es normalerweise nicht , weil (i) Sie von Natur aus nicht viel damit anfangen können und (ii) es fast immer bessere Lösungen gibt.Darüber hinaus werden Sie es normalerweise in andere konkrete Typen umwandeln. Aus diesem Grund
char *
ist dies normalerweise besser, obwohl dies möglicherweise darauf hinweist, dass Sie eher eine Zeichenfolge im C-Stil als einen reinen Datenblock erwarten. Deshalbvoid*
ist es besser alschar*
das, weil es implizite Umwandlung von anderen Zeigertypen erlaubt.Sie sollten einige Daten empfangen, damit arbeiten und eine Ausgabe erstellen. Um dies zu erreichen, müssen Sie die Daten kennen, mit denen Sie arbeiten. Andernfalls haben Sie ein anderes Problem, das nicht das ist, das Sie ursprünglich gelöst haben. Viele Sprachen haben
void*
und haben zum Beispiel kein Problem damit.Eine andere legitime Verwendung
Wenn Sie Zeigeradressen mit Funktionen wie
printf
dem Zeiger drucken , muss dervoid*
Typ angezeigt werden. Daher benötigen Sie möglicherweise eine Umwandlung invoid
*quelle
Ja, es ist genauso nützlich wie alles andere in der Sprache.
Sie können es beispielsweise verwenden, um den Typ einer Klasse zu löschen, die Sie bei Bedarf statisch in den richtigen Typ umwandeln können, um eine minimale und flexible Schnittstelle zu erhalten.
In dieser Antwort gibt es ein Anwendungsbeispiel, das Ihnen eine Idee geben soll.
Ich kopiere es und füge es der Klarheit halber unten ein:
Offensichtlich ist dies nur eine der vielen Verwendungsmöglichkeiten von
void*
.quelle
Schnittstelle mit einer externen Bibliotheksfunktion, die einen Zeiger zurückgibt. Hier ist eine für eine Ada-Anwendung.
Dies gibt einen Zeiger auf das zurück, worüber Ada Ihnen erzählen wollte. Sie müssen nichts Besonderes damit anfangen, Sie können es Ada zurückgeben, um das nächste zu tun. Tatsächlich ist das Entwirren eines Ada-Zeigers in C ++ nicht trivial.
quelle
auto
in diesem Fall nicht stattdessen verwenden? Angenommen, der Typ ist zur Kompilierungszeit bekannt.Kurz gesagt, C ++ als strenge Sprache (ohne Berücksichtigung von C-Relikten wie malloc () ) erfordert void *, da es kein gemeinsames übergeordnetes Element aller möglichen Typen hat. Im Gegensatz zu ObjC zum Beispiel, das Objekt hat .
quelle
malloc
undnew
beide kehren zurückvoid *
, also würden Sie es brauchen, selbst wenn es eine Objektklasse in C ++operator new()
kehrt zurückvoid *
, aber dernew
Ausdruck nichtint
2. Wenn es sich um ein nicht virtuelles EBC handelt, wie unterscheidet es sich dann vonvoid*
?Das erste, was mir in den Sinn kommt (was meiner Meinung nach ein konkreter Fall einiger der oben genannten Antworten ist), ist die Fähigkeit, eine Objektinstanz an einen Thread-Prozess in Windows zu übergeben.
Ich habe ein paar C ++ - Klassen, die dies tun müssen, sie haben Worker-Thread-Implementierungen und der LPVOID-Parameter in der CreateThread () -API erhält eine Adresse einer statischen Methodenimplementierung in der Klasse, damit der Worker-Thread die Arbeit erledigen kann eine bestimmte Instanz der Klasse. Durch einfaches statisches Zurücksetzen im Threadproc wird die Instanz bereitgestellt, mit der gearbeitet werden kann, sodass jedes instanziierte Objekt einen Arbeitsthread aus einer einzelnen statischen Methodenimplementierung haben kann.
quelle
Im Falle von Mehrfachvererbung, wenn Sie einen Zeiger auf das erste Byte eines Speicher Brocken bekommen müssen durch ein Objekt besetzt, Sie können
dynamic_cast
zuvoid*
.quelle