¿Existe un uso legítimo de void*
en C ++? ¿O se introdujo porque C lo tenía?
Solo para recapitular mis pensamientos:
Entrada : Si queremos permitir múltiples tipos de entrada, podemos sobrecargar funciones y métodos, alternativamente podemos definir una clase base común o plantilla (gracias por mencionar esto en las respuestas). En ambos casos, el código se vuelve más descriptivo y menos propenso a errores (siempre que la clase base se implemente de una manera sensata).
Resultado : No puedo pensar en ninguna situación en la que prefiera recibir void*
en lugar de algo derivado de una clase base conocida.
Solo para dejar en claro lo que quiero decir: no estoy preguntando específicamente si hay un caso de uso para void*
, sino si hay un caso en el que void*
sea la mejor o la única opción disponible. Lo cual ha sido perfectamente respondido por varias personas a continuación.
variant
,any
, etiquetada unión. Cualquier cosa que pueda decirle el tipo real de contenido y que sea más seguro de usar.Respuestas:
void*
es al menos necesario como resultado de::operator new
(también cadaoperator new
...) y demalloc
y como argumento delnew
operador de colocación .void*
se puede pensar como el supertipo común de todo tipo de puntero. Por lo tanto, no significa exactamente punterovoid
, sino puntero a cualquier cosa.Por cierto, si desea conservar algunos datos para varias variables globales no relacionadas, puede usar algunos
std::map<void*,int> score;
luego, después de haber declarado globalint x;
anddouble y;
andstd::string s;
doscore[&x]=1;
andscore[&y]=2;
andscore[&z]=3;
memset
quiere unavoid*
dirección (las más genéricas)Además, los sistemas POSIX tienen dlsym y su tipo de retorno evidentemente debería ser
void*
fuente
char *
. Están muy cerca en este aspecto, aunque el significado es un poco diferente.C++
está escrito enC++
esto es motivo suficiente para tenerlovoid*
. Me encanta este sitio. Haz una pregunta y aprende mucho.void*
tipo. Todas las funciones de biblioteca estándar utilizadaschar*
Existen múltiples razones para usarlo
void*
, siendo las 3 más comunes:void*
en su interfazEn orden inverso, denotar memoria sin tipear con
void*
(3) en lugar dechar*
(o variantes) ayuda a prevenir la aritmética de puntero accidental; hay muy pocas operaciones disponibles en,void*
por lo que generalmente requiere fundición antes de ser útil. Y, por supuesto, al igualchar*
que con el aliasing, no hay problema.Type-erasure (2) todavía se usa en C ++, junto con plantillas o no:
std::function
Y obviamente, cuando la interfaz con la que trabaja usa
void*
(1), tiene pocas opciones.fuente
Oh si. Incluso en C ++ a veces vamos con
void *
más quetemplate<class T*>
porque a veces el código extra de la expansión de la plantilla pesa demasiado.Por lo general, lo usaría como la implementación real del tipo, y el tipo de plantilla lo heredaría y envolvería los moldes.
Además, los asignadores de losas personalizados (nuevas implementaciones del operador) deben usar
void *
. Esta es una de las razones por las que g ++ agregó una extensión para permitir la aritmática del punterovoid *
como si fuera de tamaño 1.fuente
struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;
es un simulador de vacío mínimo - * - que utiliza plantillas.Cierto.
Esto es parcialmente cierto: ¿qué pasa si no puede definir una clase base común, una interfaz o similar? Para definirlos, debe tener acceso al código fuente, lo que a menudo no es posible.
No mencionaste las plantillas. Sin embargo, las plantillas no pueden ayudarlo con el polimorfismo: funcionan con tipos estáticos, es decir, conocidos en tiempo de compilación.
void*
puede considerarse como el mínimo común denominador. En C ++, normalmente no lo necesita porque (i) no puede hacer mucho con él por naturaleza y (ii) casi siempre hay mejores soluciones.Aún más, normalmente terminará convirtiéndolo en otros tipos de concreto. Por eso
char *
suele ser mejor, aunque puede indicar que está esperando una cadena de estilo C, en lugar de un bloque puro de datos. Por esovoid*
es mejor quechar*
eso, porque permite la conversión implícita de otros tipos de punteros.Se supone que debe recibir algunos datos, trabajar con ellos y producir una salida; para lograrlo, necesita conocer los datos con los que está trabajando; de lo contrario, tiene un problema diferente que no es el que estaba resolviendo originalmente. Muchos idiomas no tienen
void*
y no tienen ningún problema con eso, por ejemplo.Otro uso legítimo
Al imprimir direcciones de puntero con funciones como
printf
el puntero, debe tenervoid*
tipo y, por lo tanto, es posible que necesite una conversión avoid
*fuente
Sí, es tan útil como cualquier otra cosa en el idioma.
Como ejemplo, puede usarlo para borrar el tipo de una clase que puede convertir estáticamente al tipo correcto cuando sea necesario, para tener una interfaz mínima y flexible.
En esa respuesta hay un ejemplo de uso que debería darte una idea.
Lo copio y pego a continuación en aras de la claridad:
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; };
Obviamente, este es solo uno de los muchos usos de
void*
.fuente
Interfaz con una función de biblioteca externa que devuelve un puntero. Aquí hay uno para una aplicación Ada.
extern "C" { void* ada_function();} void* m_status_ptr = ada_function();
Esto devuelve un puntero a lo que sea que Ada quería contarte. No tienes que hacer nada elegante con él, puedes devolvérselo a Ada para que haga lo siguiente. De hecho, desenredar un puntero Ada en C ++ no es trivial.
fuente
auto
en su lugar en este caso? Suponiendo que el tipo se conoce en tiempo de compilación.En resumen, C ++ como lenguaje estricto (sin tener en cuenta las reliquias de C como malloc () ) requiere void * ya que no tiene un padre común de todos los tipos posibles. A diferencia de ObjC, por ejemplo, que tiene objeto .
fuente
malloc
ynew
ambos regresanvoid *
, por lo que necesitaría incluso si hubiera una clase de objeto en C ++operator new()
regresavoid *
, pero lanew
expresión noint
2. si es EBC no virtual, ¿en qué se diferencia devoid*
?Lo primero que se me ocurre (sospecho que es un caso concreto de algunas de las respuestas anteriores) es la capacidad de pasar una instancia de objeto a un threadproc en Windows.
Tengo un par de clases de C ++ que necesitan hacer esto, tienen implementaciones de subprocesos de trabajo y el parámetro LPVOID en la API CreateThread () obtiene una dirección de una implementación de método estático en la clase para que el subproceso de trabajo pueda hacer el trabajo con una instancia específica de la clase. La conversión estática simple en el threadproc produce la instancia con la que trabajar, lo que permite que cada objeto instanciado tenga un subproceso de trabajo a partir de una implementación de método estático único.
fuente
En caso de herencia múltiple, si necesita obtener un puntero al primer byte de un fragmento de memoria ocupado por un objeto, puede
dynamic_cast
hacerlovoid*
.fuente