Entonces, ¿cómo es el tipo inseguro?
void SortElements(void* MyArray, // what type is pointed here?
unsigned int N, // Are there really N elements?
size_t size, // Is the size correct?
int(*cmp)(void*,void*)); // Is this the correct function?
El código que se presente es de tipo inseguro, no por el puntero de función, sino más bien debido a la utilización de void*
tanto en el SortElements
firma y la firma del puntero de función.
La razón para que esto no es seguro es porque la persona que llama tiene toda la responsabilidad de pasar los argumentos correctos, y el compilador no puede garantizar que el puntero puntos a una región de memoria contigua que sostiene iNumofElems
cada uno de los cuales tiene la size
ofrecida en La interfaz.Si el programador comete un error, el compilador no podrá ayudarlo, si un mantenedor modifica el tipo almacenado en la matriz (cambios de tamaño) o la cantidad de elementos, el compilador no podrá detectarlo y le dirá que necesita actualizar la llamada al SortElements
. Finalmente, dado que el puntero de función que se pasa también usa void*
, la firma de un comparador que compara manzanas y peras es exactamente la misma, y el compilador no puede ayudar si pasa el puntero de función incorrecto.
struct Apple {
int weight;
};
struct Pear {
double weight;
};
int compare_pears(void * pear1, void * pear2) {
return static_cast<Pear*>(pear1)->weight - static_cast<Pear*>(pear2)->weight;
}
int main() {
Apple apples[10];
SortElements(apples, 20, sizeof(Pear), compare_pears);
}
Mientras que el compilador es capaz de verificar que la firma del puntero de función coincide con la firma que necesita la función, la función de puntero en sí no es seguro, y permite pasar un comparador para básicamente cualquier cosa.
Compare eso con esta otra alternativa:
template <typename T, std::size_t N>
void SortElements(T (&array)[N], int (*cmp)(T const &, T const &));
Aquí el compilador inferirá el tipo de los elementos T
y el tamaño de la matriz N
de la llamada. No es necesario pasar el tamaño de T
, ya que el compilador lo sabe. La función de comparador que se pasa a esta versión de SortElements
está fuertemente tipada: toma dos referencias constantes al tipo del elemento almacenado en la matriz y devuelve un int
. Si lo intentamos en el programa anterior:
int compare_pears(Pear const & lhs, Pear const & rhs);
int compare_apples(Apple const & l, Apple const & r);
Apple array[10];
//SortElements(array, compare_pears); // Error!!!!
SortElements(array, compare_apples); // Good!
No se puede confundir el tamaño de la matriz o el tamaño de los elementos, si alguien cambia el tipo Apple
, el compilador recogerlo, si el tamaño de la cambios de matriz, el compilador lo recogerá. No se puede confundir el comparador que se pasa a la función ya que el compilador también la recogerá. Ahora el programa es seguro, incluso si utiliza punteros a funciones (que pueden tener un impacto en el rendimiento ya que inhiben la alineación, por lo que std::sort
suele ser más rápido que qsort
)
¿Dónde lo vio descrito como inseguro? Ver la fuente ayudará a entender la intención del escritor. – selalerer
"Typesafe" significa que la corrección de un fragmento de código se debe únicamente a su tipificación estática, y no depende de * valores * particulares de variables. Por ejemplo, 'printf' no es seguro, ya que su corrección depende del * valor * de la cadena de formato, así como de sus tipos de argumentos. –
¿Por qué se dice entonces que los delegados de .NET son punteros de función de tipo seguro en comparación con los pionters de función normal? –