He visto código C ++ como el siguiente con muchos typedef
s.
¿Cuáles son los beneficios de usar muchos typedef
s como este en lugar de usar primitivas de C ++? ¿Existe otro enfoque que también pueda lograr esos beneficios?
Al final, todos los datos se almacenan en la memoria o se transmiten a través del cable como bits y bytes, ¿realmente importa?
types.h:
typedef int16_t Version;
typedef int32_t PacketLength;
typedef int32_t Identity;
typedef int32_t CabinetNumber;
typedef int64_t Time64;
typedef int64_t RFID;
typedef int64_t NetworkAddress;
typedef int64_t PathfinderAddress;
typedef int16_t PathfinderPan;
typedef int16_t PathfinderChannel;
typedef int64_t HandsetSerialNumber;
typedef int16_t PinNumber;
typedef int16_t LoggingInterval;
typedef int16_t DelayMinutes;
typedef int16_t ReminderDelayMinutes;
typedef int16_t EscalationDelayMinutes;
typedef float CalibrationOffset;
typedef float AnalogValue;
typedef int8_t PathfinderEtrx;
typedef int8_t DampingFactor;
typedef int8_t RankNumber;
typedef int8_t SlavePort;
typedef int8_t EventLevel;
typedef int8_t Percent;
typedef int8_t SensorNumber;
typedef int8_t RoleCode;
typedef int8_t Hour;
typedef int8_t Minute;
typedef int8_t Second;
typedef int8_t Day;
typedef int8_t Month;
typedef int16_t Year;
typedef int8_t EscalationLevel;
Parece lógico tratar de asegurarme de que siempre se use el mismo tipo para algo en particular para evitar desbordamientos, pero a menudo veo código donde "int" se ha usado prácticamente en todas partes. Sin typedef
embargo, a menudo el código conduce a un código que se parece un poco a esto:
DoSomething(EscalationLevel escalationLevel) {
...
}
¿Qué me hace preguntarme qué token describe realmente el parámetro: el tipo de parámetro o el nombre del parámetro?
fuente
Minute
a una función que tiene un argumento declarado como tipoSecond
.Respuestas:
El nombre de un parámetro debe describir lo que significa, en su caso, el nivel de escalamiento. El tipo es cómo se representa el valor: agregar typedefs como en su ejemplo ofusca esta parte de la firma de la función, por lo que no lo recomendaría.
Los typedefs son útiles para las plantillas, o si desea cambiar el tipo utilizado para ciertos parámetros, por ejemplo, al migrar de una plataforma de 32 bits a una de 64 bits.
fuente
int32_t
es que debes asegurarte de que sean correctos al compilar en diferentes plataformas. Si espera que el rangoIdentity
cambie en algún momento, creo que preferiría hacer los cambios directamente en todo el código afectado. Pero no estoy seguro, porque necesitaría saber más sobre su diseño específico. Es posible que desee hacer eso una pregunta por separado.Al principio pensé "¿Por qué no?", Pero luego se me ocurrió que si ibas a hacer todo lo posible para separar los tipos como ese, entonces harías un mejor uso del lenguaje. En lugar de usar alias, defina los tipos:
No hay diferencia de rendimiento entre:
y:
y también tiene las ventajas de agregar validación de parámetros y seguridad de tipos. Por ejemplo, considere el código que trata con dinero usando tipos primitivos:
Además de los problemas de redondeo, también permite cualquier tipo que se pueda convertir en flotante:
En este caso no es un gran problema, pero las conversiones implícitas pueden ser una fuente de errores difíciles de precisar. El uso de a
typedef
no ayuda aquí, ya que son simplemente un alias de tipo.Usar un nuevo tipo completamente significa que no hay conversiones implícitas a menos que codifique un operador de conversión, lo cual es una mala idea específicamente porque permite conversiones implícitas. También puede encapsular datos adicionales:
Nada más encajará en esa función a menos que escribamos código para que suceda. Las conversiones accidentales son imposibles. También podemos escribir tipos más complejos según sea necesario sin mucha molestia.
fuente
BOOST_STRONG_TYPEDEF
realidad;)El uso de typedefs para tipos primitivos como ese se parece más al código de estilo C.
En C ++ obtendrá errores interesantes tan pronto como intente sobrecargar las funciones para, digamos,
EventLevel
yHour
. Eso hace que los nombres de tipo adicionales sean bastante inútiles.fuente
Nosotros (en nuestra empresa) lo hacemos mucho en C ++. Ayuda a comprender y mantener el código. Lo cual es bueno cuando mueves personas entre equipos o realizas refactorizaciones. Ejemplo:
Creemos que es una buena práctica crear typedef para el nombre de dimensión desde el tipo de representación. Este nuevo nombre representa un rol general en un software. El nombre de un parámetro es un rol local . Al igual que en
User sender, User receiver
. En algunos lugares puede ser redundante,void register(User user)
pero no considero que sea un problema.Más adelante, uno puede tener la idea de que
float
no es lo mejor para representar precios debido a las reglas especiales de redondeo de la reserva, por lo que uno descarga o implementa un tipoBCDFloat
(decimal codificado en binario) y cambia la definición de tipo. No hay una búsqueda y reemplazo de trabajofloat
aBCDFloat
que se endurezca por el hecho de que hay posiblemente muchos más flotadores en su código.No es una bala de plata y tiene sus propias advertencias, pero creemos que es mucho mejor usarlo que no.
fuente
BOOST_STRONG_TYPEDEF(float, Price)
, pero no llegaría tan lejos en un proyecto promedio. O tal vez lo haría. Tengo que consultarlo con la almohada. :-)typedef
básicamente le permite dar un alias para untype
.Le brinda la flexibilidad de evitar escribir el teclado
type names
una y otra vez y hacer que sutype
lectura sea más fácil en donde el nombre del alias indica la intención o el propósito deltype
.Es más una cuestión de elección si desea tener nombres más legibles
typedef
en su proyecto.Por lo general, evito usar
typedef
en tipos primitivos, a menos que sean demasiado largos para escribirlos. Mantengo mis nombres de parámetros más indicativos.fuente
Nunca haría algo como esto. Asegurarse de que todos tengan el mismo tamaño es una cosa, pero solo debe referirse a ellos como tipos integrales, entonces.
fuente
Usar typedefs como este está bien siempre y cuando quien termine de usarlos no necesite saber nada sobre su representación subyacente . Por ejemplo, si desea pasar un
PacketLength
objeto a unoprintf
o a otroscanf
, deberá conocer su tipo real para poder elegir el especificador de conversión correcto. En casos como ese, typedef solo agrega un nivel de ofuscación sin comprar nada a cambio; bien podría haber definido el objeto comoint32_t
.Si necesita aplicar una semántica específica para cada tipo (como rangos o valores permitidos), es mejor que cree un tipo de datos abstractos y funciones para operar con ese tipo, en lugar de simplemente crear una definición de tipo.
fuente