Y a-t-il une utilisation légitime de void*
en C ++? Ou cela a-t-il été introduit parce que C l'avait?
Juste pour récapituler mes pensées:
Entrée : Si nous voulons autoriser plusieurs types d'entrée, nous pouvons surcharger les fonctions et les méthodes, sinon nous pouvons définir une classe de base commune, ou un modèle (merci de l'avoir mentionné dans les réponses). Dans les deux cas, le code est plus descriptif et moins sujet aux erreurs (à condition que la classe de base soit implémentée de manière sensée).
Sortie : Je ne peux penser à aucune situation où je préférerais recevoir void*
plutôt que quelque chose dérivé d'une classe de base connue.
Juste pour clarifier ce que je veux dire: je ne demande pas spécifiquement s'il y a un cas d'utilisation pour void*
, mais s'il y a un cas où void*
est le meilleur ou le seul choix disponible. Ce qui a été parfaitement répondu par plusieurs personnes ci-dessous.
variant
,any
, union étiquetée. Tout ce qui peut vous indiquer le type de contenu réel et plus sûr à utiliser.Réponses:
void*
est au moins nécessaire en tant que résultat de::operator new
(également tous lesoperator new
...) et demalloc
et comme argument de l'new
opérateur de placement .void*
peut être considéré comme le supertype commun de chaque type de pointeur. Il ne s'agit donc pas exactement d'un pointeur versvoid
, mais d'un pointeur vers n'importe quoi.BTW, si vous souhaitez conserver des données pour plusieurs variables globales non liées, vous pouvez en utiliser
std::map<void*,int> score;
ensuite, après avoir déclaré globalint x;
anddouble y;
etstd::string s;
fairescore[&x]=1;
etscore[&y]=2;
etscore[&z]=3;
memset
veut unevoid*
adresse (les plus génériques)De plus, les systèmes POSIX ont dlsym et son type de retour devrait évidemment être
void*
la source
char *
aussi. Ils sont très proches sur cet aspect, même si le sens est un peu différent.C++
est écrit dansC++
c'est une raison suffisante pour avoirvoid*
. Je dois adorer ce site. Posez une question et apprenez beaucoup.void*
genre. Toutes les fonctions de bibliothèque standard utiliséeschar*
Il y a plusieurs raisons d'utiliser
void*
, les 3 plus courantes étant:void*
dans son interfaceDans l'ordre inverse, désigner la mémoire non typée avec
void*
(3) au lieu dechar*
(ou des variantes) permet d'éviter l'arithmétique accidentelle des pointeurs; il y a très peu d'opérations disponiblesvoid*
, il faut donc généralement lancer un casting avant d'être utile. Et bien sûr, tout comme avecchar*
il n'y a aucun problème avec l'aliasing.L'effacement de type (2) est toujours utilisé en C ++, en conjonction avec des templates ou non:
std::function
Et évidemment, lorsque l'interface que vous traitez utilise
void*
(1), vous n'avez guère le choix.la source
Oh oui. Même en C ++, nous choisissons parfois
void *
plutôt quetemplate<class T*>
parce que parfois le code supplémentaire de l'expansion du modèle pèse trop.Généralement, je l'utiliserais comme implémentation réelle du type, et le type de modèle en hériterait et encapsulerait les moulages.
En outre, les allocateurs de dalles personnalisés (nouvelles implémentations d'opérateur) doivent utiliser
void *
. C'est l'une des raisons pour lesquelles g ++ a ajouté une extension permettant l'arithmatique du pointeurvoid *
comme s'il était de taille 1.la source
struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;
est un simulateur de vide minimal - * - utilisant des modèles.Vrai.
C'est partiellement vrai: que se passe-t-il si vous ne pouvez pas définir une classe de base commune, une interface ou similaire? Pour les définir, vous devez avoir accès au code source, ce qui n'est souvent pas possible.
Vous n'avez pas mentionné de modèles. Cependant, les templates ne peuvent pas vous aider avec le polymorphisme: ils fonctionnent avec des types statiques c'est à dire connus au moment de la compilation.
void*
peut être considéré comme le plus petit dénominateur commun. En C ++, vous n'en avez généralement pas besoin car (i) vous ne pouvez pas en faire grand chose en soi et (ii) il y a presque toujours de meilleures solutions.Encore plus loin, vous finirez généralement par le convertir en d'autres types de béton. C'est pourquoi
char *
c'est généralement mieux, bien que cela puisse indiquer que vous attendez une chaîne de style C, plutôt qu'un bloc de données pur. C'est pourquoivoid*
c'est mieux quechar*
pour cela, car cela permet une conversion implicite à partir d'autres types de pointeurs.Vous êtes censé recevoir des données, travailler avec elles et produire une sortie; pour y parvenir, vous devez connaître les données avec lesquelles vous travaillez, sinon vous avez un problème différent qui n'est pas celui que vous résolviez à l'origine. De nombreuses langues n'ont pas
void*
et n'ont aucun problème avec cela, par exemple.Une autre utilisation légitime
Lors de l'impression des adresses de pointeur avec des fonctions telles que
printf
le pointeur doit avoir levoid*
type et, par conséquent, vous devrez peut-être un cast envoid
*la source
Oui, c'est aussi utile que n'importe quelle autre chose dans la langue.
Par exemple, vous pouvez l'utiliser pour effacer le type d'une classe que vous êtes capable de convertir statiquement dans le bon type en cas de besoin, afin d'avoir une interface minimale et flexible.
Dans cette réponse, il y a un exemple d'utilisation qui devrait vous donner une idée.
Je le copie et le colle ci-dessous par souci de clarté:
class Dispatcher { Dispatcher() { } template<class C, void(C::*M)() = C::receive> static void invoke(void *instance) { (static_cast<C*>(instance)->*M)(); } public: template<class C, void(C::*M)() = &C::receive> static Dispatcher create(C *instance) { Dispatcher d; d.fn = &invoke<C, M>; d.instance = instance; return d; } void operator()() { (fn)(instance); } private: using Fn = void(*)(void *); Fn fn; void *instance; };
De toute évidence, ce n'est que l'une des utilisations de
void*
.la source
Interfaçage avec une fonction de bibliothèque externe qui renvoie un pointeur. En voici un pour une application Ada.
extern "C" { void* ada_function();} void* m_status_ptr = ada_function();
Cela renvoie un pointeur sur tout ce dont Ada voulait vous parler. Vous n'avez rien à faire de fantaisie avec, vous pouvez le rendre à Ada pour faire la prochaine chose. En fait, démêler un pointeur Ada en C ++ n'est pas trivial.
la source
auto
place dans ce cas? En supposant que le type est connu au moment de la compilation.En bref, C ++ en tant que langage strict (ne prenant pas en compte les reliques C comme malloc () ) nécessite void * car il n'a pas de parent commun de tous les types possibles. Contrairement à ObjC, par exemple, qui a un objet .
la source
malloc
et lesnew
deux reviennentvoid *
, vous auriez donc besoin si même s'il y avait une classe d'objet en C ++operator new()
retournevoid *
, mais l'new
expression ne le fait pasint
2. s'il s'agit d'EBC non virtuel, en quoi est-il différent devoid*
?La première chose qui me vient à l'esprit (ce que je soupçonne est un cas concret de quelques-unes des réponses ci-dessus) est la capacité de passer une instance d'objet à un threadproc dans Windows.
J'ai quelques classes C ++ qui doivent faire cela, elles ont des implémentations de thread de travail et le paramètre LPVOID dans l'API CreateThread () obtient une adresse d'implémentation de méthode statique dans la classe afin que le thread de travail puisse faire le travail avec une instance spécifique de la classe. Un simple cast statique dans le threadproc produit l'instance avec laquelle travailler, permettant à chaque objet instancié d'avoir un thread de travail à partir d'une seule implémentation de méthode statique.
la source
En cas d'héritage multiple, si vous avez besoin d'obtenir un pointeur vers le premier octet d'un bloc de mémoire occupé par un objet, vous pouvez le
dynamic_cast
fairevoid*
.la source