32.C和C++的类型安全
32.C和C++的类型安全
什么是类型安全?
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。“类型安全”常被用来形容编程语言,其根据在于该门编程语言是否提供保障类型安全的机制;有的时候也用“类型安全”形容某个程序,判别的标准在于该程序是否隐含类型错误。
类型安全的编程语言与类型安全的程序之间,没有必然联系。好的程序员可以使用类型不那么安全的语言写出类型相当安全的程序,相反的,差一点儿的程序员可能使用类型相当安全的语言写出类型不太安全的程序。绝对类型安全的编程语言暂时还没有。
(1)C的类型安全
C只在局部上下文中表现出类型安全,比如试图从一种结构体的指针转换成另一种结构体的指针时,编译器将会报告错误,除非使用显式类型转换。然而,C中相当多的操作是不安全的。以下是两个十分常见的例子:
●printf格式输出
#include <stdio.h>
int main()
{
printf("整型输出:%d\n", 10);
printf("浮点型输出:%f\n", 10);
return 0;
}
输出:
整型输出:10
整型输出:0.000000
上述代码中,使用%d控制整型数字的输出,没有问题,但是改成%f时,明显输出错误,再改成%s时,运行直接报segmentation fault错误
●malloc函数的返回值
malloc是C中进行内存分配的函数,它的返回类型是void*即空类型指针,常常有这样的用法char* pStr=(char*)malloc(100*sizeof(char)),这里明显做了显式的类型转换。
类型匹配尚且没有问题,但是一旦出现int* pInt=(int*)malloc(100*sizeof(char))就很可能带来一些问题,而这样的转换C并不会提示错误。
(2)C++的类型安全
如果C++使用得当,它将远比C更有类型安全性。相比于C语言,C++提供了一些新的机制保障类型安全:
●操作符new返回的指针类型严格与对象匹配,而不是void*
●C中很多以void*为参数的函数可以改写为C++模板函数,而模板是支持类型检查的;
●引入const关键字代替#define constants,它是有类型、有作用域的,而#define constants只是简单的文本替换
●一些#define宏可被改写为inline函数,结合函数的重载,可在类型安全的前提下支持多种类型,当然改写为模板也能保证类型安全
●C++提供了dynamic_cast关键字,使得转换过程更加安全,因为dynamic_cast比static_cast涉及更多具体的类型检查。
例1:使用void进行类型转换*
在C++中,使用void*
指针可以进行类型转换。void*
指针是一个通用指针,它可以指向任何类型的数据,包括结构体、类和数组等。
以下是一些使用void*
进行类型转换的示例:
1.将一个结构体指针转换为void*
指针:
struct MyStruct
{
int x;
float y;
};
MyStruct* myStruct = new MyStruct();
void* voidPtr = myStruct; // 将结构体指针转换为void*指针
2.将一个类指针转换为void*
指针:
class MyClass
{
public:
int x;
float y;
};
MyClass* myClass = new MyClass();
void* voidPtr = static_cast<void*>(myClass); //将类指针转换为void*指针
例2:不同类型指针之间转换
#include<iostream>
using namespace std;
class Parent{};
class Child1 : public Parent
{
public:
int i;
Child1(int e):i(e){}
};
class Child2 : public Parent
{
public:
double d;
Child2(double e):d(e){}
};
int main()
{
Child1 c1(5);
Child2 c2(4.1);
Parent* pp;
Child1* pc1;
pp=&c1;
pc1=(Child1*)pp; // 类型向下转换 强制转换,由于类型仍然为Child1*,不造成错误
cout<<pc1->i<<endl; //输出:5
pp=&c2;
pc1=(Child1*)pp; //强制转换,且类型发生变化,将造成错误
cout<<pc1->i<<endl;// 输出:1717986918
return 0;
}
上面两个例子之所以引起类型不安全的问题,是因为程序员使用不得当。第一个例子用到了空类型指针void*,第二个例子则是在两个类型指针之间进行强制转换。父类转子类不安全,子类转父类安全。发生多态安全。因此,想保证程序的类型安全性,应尽量避免使用空类型指针void*,尽量不对两种类型指针做强制转换
参考资料来源:
阿秀