首先,类型安全意味着如果做错了,编译器可以立即捕获的任何内容。
现在,我听说函数指针不是类型安全的,但每当我试图错误地使用它们时,编译器都会为我报告错误。那么,它是如何键入不安全的?
E.g这是一个函数原型,它接受一个函数指针
void SortElements(void* MyArray, unsigned int iNumofElems,size_t size, int(*compare_funct)(void* First,void* SecondElem))
我已经定义了几个函数来传递给它:
int MySortAsc(void* First, void* Second);
void MyFunct2();
void MyFunct3(void* First);
代码仅编译为:
SortElements(MyArray, 10, sizeof(DataType), &MySortAsc); //Compiles
SortElements(MyArray, 10, sizeof(DataType), &MyFunct2); //Fails
任何想法如何在这里误用函数指针?
是不是因为这个:
void (*functionPointer)();
...
int integer = 0xFFFFFFFF;
functionPointer = (void(*)())integer;
functionPointer();
答案: 我要看到的是C ++中的函数指针是类型安全的。当然,它们可以通过非常不安全的方式使用它们,但这并不能使它们成为被称为类型不安全的理由。 .NET代理也是强类型的,对我而言,它们看起来都是类型安全的。
答案 0 :(得分:10)
那么,它是如何键入不安全的?
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?
您提供的代码是类型不安全的,不是因为函数指针,而是因为void*
签名和函数指针的签名都使用SortElements
。
这是不安全的原因是因为调用者完全负责传递正确的参数,并且编译器无法确保指针MyArray
指向每个包含iNumofElems
的连续内存区域其中有size
在界面中提供。如果程序员犯了错误,编译器将无法在那里提供帮助,如果维护者修改存储在数组中的类型(大小更改)或元素数量,编译器将无法检测到它并告诉您您需要将呼叫更新为SortElements
。最后,因为传递的函数指针也使用void*
,比较苹果和梨的比较器的签名完全相同,如果传递不正确的函数指针,编译器也无法帮助。
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 );
}
虽然编译器能够验证函数指针的签名是否与函数所需的签名匹配,但函数指针本身是不安全的,并且允许您为基本上任何东西传递比较器。
将其与其他替代方案进行比较:
template <typename T, std::size_t N>
void SortElements( T (&array)[N], int (*cmp)( T const &, T const & ) );
这里编译器将从调用中推断出元素T
的类型和数组N
的大小。没有必要传递T
的大小,因为编译器知道它。传递给此版本SortElements
的比较器函数是强类型的:它对数组中存储的元素类型进行两次常量引用,并返回int
。如果我们在之前的程序中尝试过这个:
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!
你不能误认为数组的大小或元素的大小,如果有人改变了类型Apple
,编译器就会把它拿起来,如果数组的大小发生变化,编译器就会把它拿起来。你不能误认为传递给函数的比较器,因为编译器也会把它拿起来。现在程序是类型安全的,即使它使用函数指针(这可能会影响性能,因为它们会抑制内联,这就是std::sort
通常比qsort
更快的原因)
答案 1 :(得分:6)
函数指针是类型安全的。然而,许多环境迫使程序员重新制作它们。不正确的铸造可能会导致严重问题。
答案 2 :(得分:5)
事实上,函数指针已经过类型检查并且类型安全。
答案 3 :(得分:1)
强烈建议不要在nesC(TinyOs中使用的C方言)中使用函数指针,因为它们会妨碍优化。静态代码分析(或者说缺乏适用性)比类型安全更受关注,但我不确定这些问题是否会被混淆。
另一个问题可能是使用函数指针作为事件处理程序。使用通用事件调度程序时,您可能希望从正确的类型中抽象出来,这意味着您可以将函数指针存储为void*
只是为了模块化。这将是函数指针的类型不安全使用的一个突出示例,而不是类型安全的动态绑定用法。