2010-11-29 30 views
5

vi este ejemplo en un sitio web, y los sitios web menciones: - "Uno de sus usos (punteros void) puede haber para pasar parámetros genéricos a una función"punteros void en C++

// increaser 
#include <iostream> 
using namespace std; 

void increase (void* data, int psize) 
{ 
    if (psize == sizeof(char)) 
    { char* pchar; pchar=(char*)data; ++(*pchar); } 
    else if (psize == sizeof(int)) 
    { int* pint; pint=(int*)data; ++(*pint); } 
} 

int main() 
{ 
    char a = 'x'; 
    int b = 1602; 
    increase (&a,sizeof(a)); 
    increase (&b,sizeof(b)); 
    cout << a << ", " << b << endl; 
    return 0; 
} 

wouldn' ¿Sería más fácil escribir código como el siguiente?

void increaseChar (char* charData) 
{ 
    ++(*charData); 
} 

void increaseInt (int* intData) 
{ 
    ++(*intData); 
} 

int main() 
{ 
    char a = 'x'; 
    int b = 1602; 
    increaseChar (&a); 
    increaseInt (&b); 
    cout << a << ", " << b << endl; 
    string str; 
    cin >> str; 
    return 0; 
} 

Es menos código, y realmente sencillo. ¡Y en el primer código que tuve que enviar el tamaño del tipo de datos aquí no lo hago!

+0

Pero ahora tiene dos funciones en lugar de solo una, lo que significa que no puede (por ejemplo) registrar 'increase' como un detector de eventos en algún marco. Tiene razón en que el ejemplo dado no es realista, e incluso en un contexto más apropiado, es inusual pasar un puntero de datos * y * un tamaño a una devolución de llamada. La interfaz para 'pthread_create' es un ejemplo más plausible, aunque en C++ es posible que prefiera utilizar una API más C++ - ish como Boost.Thread. –

+1

La primera es una expresión en C común que * algunas * personas piensan que también es apropiada en C++. Por lo general, no lo es. Use herencia, plantillas, etc. siempre que sea posible para preservar la seguridad del tipo. –

+0

@larsman Estoy completamente de acuerdo. Este ejemplo solo sirve para C. No para C++. Principalmente había usado '(void *)' cuando interactuaba con el código C, como un puntero opaco. Y también con MS COM en C++. AFAIR :: QueryInterface() también usa 'void * &' como parámetro de salida. –

Respuesta

0

Su código es definitivamente preferible. void* pierde cualquier pretensión de tipo de seguridad, sus sobrecargas de tipo seguro son mucho mejores. Buenos instintos.

Para admitir más tipos genéricamente en C++ definiría una plantilla de clase o función, no use void*. Referencias serían preferibles a los punteros, pero su código podría ser cambiado a esto:

template <class T> T increase(T* value) 
{ 
    return ++(*value); 
} 

int main(int argc, char* argv[]) 
{ 
    int i(0); 
    increase<int>(&i); 

    char c(0); 
    increase<char>(&c); 

    return 0; 
} 

¿Cuál es la página web, como un asunto de interés?

+0

Bueno, lol, es el sitio web de cplusplus: http://www.cplusplus.com/doc/tutorial/pointers/ – w4j3d

+0

@ w4j3d - gracias por la nota, eso no es un gran consejo en http: //www.cplusplus. com/doc/tutorial/punteros/ –

0

Su solución funciona solo para int o char. Considere la situación que desea ir a través de una matriz de algún tipo personalizado (struct, por ejemplo). En su código, deberá agregar un método para hacerlo, sin embargo, en el ejemplo anterior, no se debe agregar una línea de para obtener el ejemplo de trabajo.

13

Lo mejor sería hacer que el tipo de función sea segura y genérica. También sería mejor tomar el argumento por referencia en lugar de por el puntero:

template <typename T> 
void increment(T& data) { 
    ++data; 
} 

void* debe evitarse siempre que sea posible en C++, ya que las plantillas y la herencia proporcionan alternativas con seguridad de tipos.

+2

Las plantillas son el camino a seguir. Tipo de seguridad aplicada en tiempo de compilación. Solo daría un paso más y pasaría datos como 'T &'. En la mayoría de los casos, ni siquiera tendrá que escribir 'incremento (i)', ya que el compilador generalmente deducirá el tipo 'T' automáticamente. – Mephane

+0

+1. Y tal vez usando referencias en lugar de punteros ... – paercebal

+0

@Mephane, la inferencia tipo debería funcionar aquí también, supongo? Usar un puntero aquí no es * tan * malo, como se ve explícitamente en la llamada de función, su parámetro va a ser modificado. – Kos

0

"Uno de sus usos (punteros void) puede haber para pasar parámetros genéricos a una función"

No

un buen uso de estilo, para estar seguro. Pasando parámetros genéricos de funciones se realiza mediante:

a) plantillas (y posiblemente especializaciones plantilla) que permite escribir código seguro,
b) enfoques programación orientada a objetos.

Me gusta su segunda solución increaseInt y increaseChar mejor. Puede volver a escribir como:

template<typename T> 
void increase(T* pData) 
{ 
    ++(*pData); 
} 

Se trabajará para cualquier tipo que apoya ++ y estar a salvo.


A veces usted todavía necesita un puntero nulo como un "parámetro genérico" - por ejemplo, cuando se utiliza el popular pthread biblioteca. Cuando genera un hilo, puede enviarle datos arbitrarios (de cualquier tipo que desee), y lo hace usando, bueno, un puntero void*.La biblioteca pthread está en C no en C++, lo que puede ser una de las razones, pero es posible que desee interactuar con ella desde C++ y esto requerirá que utilice el puntero void* como un "tipo genérico". No hay mucho daño hecho allí, sin embargo; solo recuerde que para la codificación diaria hay soluciones más elegantes.

0

En realidad, creo que el ejemplo es bastante malo. Uno de los ejemplos canónicos sería la biblioteca de C qsort, donde pasa void*y una función que interpreta los datos.

Sin embargo, en C++, hay mejores mecanismos disponibles que son menos propensos a un mal funcionamiento silencioso.

Por lo tanto, el uso de void* para pasar datos genéricos alrededor probablemente debería limitarse a

  1. casos donde el código hinchazón es importante, y
  2. código que interactúa con otro idioma, en particular C.
0

Cuando está programando C (no C++) void * es solo la forma de hacer algo genérico. Sin embargo, el ejemplo es terrible. Este esquema se usa a menudo para la mecánica de devolución de llamada, donde se pasa un puntero de función y un vacío *. Por ejemplo:

void handle_crash(void* data) 
{ 
    Log* log = (Log*)data; 
    log->print("We crashed."); 
} 

Log log; 
set_crash_handler(&handle_crash, &log); 

Verá esto a menudo en marcos C como libxml2 o spidermonkey. En el caso de C, esto es lo único que hay para lograrlo. No es una solución muy robusta.

En caso de que trabaje con C++ tiene más opciones. La programación genérica básica se puede hacer con plantillas o sobrecarga, como se menciona en otras respuestas. En caso de que necesite una sólida mecánica de devolución de llamada, es posible que desee examinar libsigC++ u otros marcos de "señales y ranuras".