¿Es una buena idea tipear punteros?

Miré a través de un código y noté que la convención era girar tipos de puntero como

SomeStruct* 

dentro

 typedef SomeStruct* pSomeStruct; 

¿Hay algún mérito para esto?

Esto puede ser apropiado cuando el puntero mismo se puede considerar como una “caja negra”, es decir, una pieza de datos cuya representación interna debería ser irrelevante para el código.

Esencialmente, si su código nunca desreferenciará el puntero, y usted simplemente lo pasará por las funciones API (a veces por referencia), no solo el typedef reducirá el número de * s en su código, sino que también sugerirá al progtwigdor que el puntero realmente no debería ser entrometido.

Esto también hace que sea más fácil cambiar la API en el futuro si surge la necesidad, por ejemplo, usando una ID en lugar de un puntero (o viceversa). Como el puntero nunca se debió desreferenciar en primer lugar, el código existente no se romperá.

No en mi experiencia. Ocultar el ‘ * ‘ hace que el código sea difícil de leer.

La única vez que uso un puntero dentro de typedef es cuando trato con punteros a funciones:

 typedef void (*SigCatcher(int, void (*)(int)))(int); 

 typedef void (*SigCatcher)(int); SigCatcher old = signal(SIGINT, SIG_IGN); 

De lo contrario, los encuentro más confusos que útiles.


La statement tachada es el tipo correcto para un puntero a la función de signal() , no del receptor de señal. Se podría aclarar (utilizando el tipo de SigCatcher corregido anterior) escribiendo:

  typedef SigCatcher (*SignalFunction)(int, SigCatcher); 

O bien, para declarar la función de signal() :

  extern SigCatcher signal(int, SigCatcher); 

Es decir, una SignalFunction es un puntero a una función que toma dos argumentos (un int y un SigCatcher ) y devuelve un SigCatcher . Y signal() sí es una función que toma dos argumentos (un int y un SigCatcher ) y devuelve un SigCatcher .

Esto puede ayudarte a evitar algunos errores. Por ejemplo en el siguiente código:

 int* pointer1, pointer2; 

pointer2 no es un int * , es simple int . Pero con typedefs esto no va a suceder:

 typedef int* pInt; pInt pointer1, pointer2; 

Ambos son int * ahora.

Es (como tantas respuestas) depende.

En C esto es muy común ya que estás tratando de ocultar que un objeto es un puntero. Está tratando de dar a entender que este es el objeto que manipulan todas sus funciones (sabemos que es un puntero debajo pero representa el objeto que está manipulando).

 MYDB db = MYDBcreateDB("Plop://djdjdjjdjd"); MYDBDoSomthingWithDB(db,5,6,7); CallLocalFuc(db); // if db is not a pointer things could be complicated. MYDBdestroyDB(db); 

Debajo de MYDB es probablemente un puntero a algún objeto.

En C ++ esto ya no es necesario.
Principalmente porque podemos pasar cosas por referencia y los métodos se incorporan a la statement de clase.

 MyDB db("Plop://djdjdjjdjd"); db.DoSomthingWithDB(5,6,7); CallLocalFuc(db); // This time we can call be reference. db.destroyDB(); // Or let the destructor handle it. 

Esta es una cuestión de estilo. Verá este tipo de código con mucha frecuencia en los archivos de encabezado de Windows. Aunque tienden a preferir la versión en mayúscula en vez de prefijar con una minúscula p.

Personalmente evito este uso de typedef. Es mucho más claro que el usuario diga explícitamente que quiere un Foo * que PFoo. Los Typedef son los más adecuados en estos días para hacer que AWL sea legible 🙂

 typedef stl::map> NameToFooMap; 

Typedef se usa para hacer que el código sea más legible, pero convertir el puntero en typedef boostá la confusión. Es mejor evitar punteros typedef.

Si hace esto, no podrá crear contenedores STL de const pSomeStruct ya que el comstackdor lee:

 list structs; 

como

 list structs; 

que no es un contenedor STL legal ya que los elementos no son asignables.

Ver esta pregunta

Mi respuesta es un claro “No”.

¿Por qué?

Bueno, antes que nada, simplemente intercambias un solo carácter * por otro personaje individual p . Eso es ganancia cero . Esto solo debería evitar que hagas esto, ya que siempre es malo hacer cosas extra que no tienen sentido.

Segundo, y esa es la razón importante, el * tiene un significado que no es bueno esconder . Si paso algo a una función como esta

 void foo(SomeType bar); void baz() { SomeType myBar = getSomeType(); foo(myBar); } 

No espero que el significado de myBar pasándolo a foo() . Después de todo, estoy pasando por valor, entonces foo() solo ve una copia de myBar ¿verdad? ¡No cuando SomeType tiene un alias para significar algún tipo de puntero!

Esto se aplica tanto a los punteros C como a los punteros inteligentes C ++: si oculta el hecho de que son punteros para sus usuarios, creará confusión que es totalmente innecesaria. Entonces, por favor, no alias tus punteros.

(Creo que el hábito de los tipos de puntero deflectores es simplemente un bash equivocado de ocultar cuántas estrellas se tiene como progtwigdor http://wiki.c2.com/?ThreeStarProgrammer ).

Win32 API hace esto con casi todas las estructuras (si no todas)

 POINT => *LPPOINT WNDCLASSEX => *LPWNDCLASSEX RECT => *LPRECT PRINT_INFO_2 => *LPPRINT_INFO_2 

Es lindo cómo es consistente, pero en mi opinión no agrega elegancia.

Hace algún tiempo, hubiera respondido “no” a esta pregunta. Ahora, con el aumento de punteros inteligentes, los punteros no siempre se definen con una estrella ‘*’. Entonces, no hay nada obvio en que un tipo sea un puntero o no.

Así que ahora diría: está bien escribir punteros de definición, siempre que quede muy claro que es un “tipo de puntero”. Eso significa que tiene que usar un prefijo / sufijo específicamente para eso. No, “p” no es un prefijo suficiente, por ejemplo. Probablemente vaya con “ptr”.

Discusión lanzada asumiendo que el lenguaje de interés es C. No se han considerado las ramificaciones para C ++.

Uso de un puntero typedef para una estructura sin etiquetar

La pregunta El tamaño de una estructura que se define como un puntero genera una luz lateral interesante al usar typedef para punteros (de estructura).

Considere la definición de tipo de estructura de hormigón sin tags (no opaca):

 typedef struct { int field1; double field2; } *Information; 

Los detalles de los miembros son completamente tangenciales a esta discusión; lo único que importa es que este no es un tipo opaco como typedef struct tag *tag; (y no puede definir tales tipos opacos a través de un typedef sin una etiqueta).

La pregunta que se plantea es ‘¿cómo se puede encontrar el tamaño de esa estructura’?

La respuesta corta es ‘solo a través de una variable del tipo’. No hay etiqueta para usar con sizeof(struct tag) . Por ejemplo, no se puede escribir sizeof(*Information) , y sizeof(Information *) es el tamaño de un puntero al tipo de puntero, no el tamaño del tipo de estructura.

De hecho, si desea asignar una estructura de este tipo, no puede crear una, excepto a través de la asignación dinámica (o técnicas indirectas que imitan la asignación dinámica). No hay forma de crear una variable local del tipo de estructura cuyos punteros se llaman Information , ni existe una forma de crear una variable de ámbito de archivo (global o static ) del tipo de estructura, ni existe una forma de incrustar dicha estructura. (a diferencia de un puntero a dicha estructura) en otra estructura o tipo de unión.

Usted puede – debe – escribir:

 Information info = malloc(sizeof(*info)); 

Además del hecho de que el puntero está oculto en typedef , esta es una buena práctica: si el tipo de info cambia, la asignación de tamaño seguirá siendo precisa. Pero en este caso, también es la única forma de obtener el tamaño de la estructura y asignar la estructura. Y no hay otra manera de crear una instancia de la estructura.

¿Esto es dañino?

Depende de tus objectives

Este no es un tipo opaco: los detalles de la estructura deben definirse cuando el tipo de puntero es typedef ‘d.

Es un tipo que solo se puede usar con asignación de memoria dinámica.

Es un tipo que no tiene nombre. El puntero al tipo de estructura tiene un nombre, pero el tipo de estructura no lo hace.

Si desea imponer la asignación dinámica, esta parece ser una forma de hacerlo.

En general, sin embargo, es más probable que cause confusión y angustia que la iluminación.

Resumen

En general, es una mala idea usar typedef para definir un puntero a un tipo de estructura sin tags.

El propósito de typedef es ocultar los detalles de la implementación, pero la definición de tipo de letra del puntero oculta demasiado y hace que el código sea más difícil de leer / comprender. Entonces, por favor no hagas eso.


Si desea ocultar los detalles de la implementación (que a menudo es una buena cosa que hacer), no oculte la parte del puntero. Tomemos como ejemplo el prototipo de la interfaz FILE estándar:

 FILE *fopen(const char *filename, const char *mode); char *fgets(char *s, int size, FILE *stream); 

aquí fopen devuelve un puntero a una estructura FILE (para la que no conoce los detalles de implementación). Quizás FILE no es un buen ejemplo porque en este caso podría haber funcionado con algún tipo de archivo que ocultara el hecho de que es un puntero.

 pFILE fopen(const char *filename, const char *mode); char *fgets(char *s, int size, pFILE stream); 

Sin embargo, eso solo funcionaría porque nunca te metas con el contenido al que se apunta directamente. En el momento en que escribe de algún puntero que algunos lugares modifican el código se vuelve muy difícil de leer en mi experiencia.