浅谈新式转型操作符:
const_cast
#include "stdafx.h"
#include <IOSTREAM>
#include "Animal.h"
#include "Cat.h"
using namespace std;
int main(int argc, char* argv[])
{
const CCat mycat;
Animal* cat=&mycat;
cat->eat();
return 0;
}
上面代码Animal是基类,并且有一个eat的虚方法,CCat派生于Animal并实现了eat方法。
在运行如上代码时,毋庸置疑会发生错误。cannot convert from 'const class CCat *' to 'class Animal *'
所以利用旧式转型操作,可以很简单的避免这个错误,作如下类型转换:
#include "stdafx.h"
#include <IOSTREAM>
#include "Animal.h"
#include "Cat.h"
using namespace std;
int main(int argc, char* argv[])
{
const CCat mycat;
Animal*cat=(Animal*)&mycat;
cat->eat();
return 0;
}
仔细考虑,上面代码其实做了两个转型,一个是去除了mycat的const,另一个将*CCat转换成了*Animal.
这看上去很正确,但考虑下面代码:
我们新增一个Person类,人不是动物所以无须继承Animal类。
int main(int argc, char* argv[])
{
const CCat mycat;
Person*p=(Person*)&mycat;
return 0;
}
但上面的代码依然是编译通过的。这不就是cat is a person.然后利用const_cast进行转换时
int main(int argc, char* argv[])
{
const CCat mycat;
Person*p=const_cast<Person*>(&mycat);
return 0;
}
//或
int main(int argc, char* argv[])
{
const CCat mycat;
Person*p=const_cast<CCat*>(&mycat);
return 0;
}
编译时
cannot convert from 'class CCat *' to 'class Person *'
看到这里,读者可能有点感觉了,对于旧式的转换操作,他并不检查类型不正确转换,简单的说就是告诉编译器“喂,给我把const cat转成person”,而const_cast转换操作则相对威力更小,因为它只影响类型修饰符,这样的限制同样是件好事,因为他更能精确的表达我们的意图。在const cat到person时,他只能把const去除,一旦到类型转换时,他就无能为力,编译发现类型不匹配时则会报错。
static_cast最常用的将一个继承层次结构中的基类指针或引用,向下转型成派生类的指针或引用。需要注意的是必须是继承层次中。
所以对于
Animal* an=new CCat;
CCat*cat=(CCat*)&an;
实际上由这个两步完成
Animal* cat2=const_cast< Animal*>(&mycat);
CCat * cat2=static_cast< CCat *>(&mycat);
Reinterpret_cast指它是从bit看待一个对象。从而允许将一个东西看作另一个完全不同的东西。他在低层编码时偶尔非用不可,但不具备移植性。当使用reinterpret_cast和static_cast将指向基类的指针向下转型为指向派生类的指针时的行为。Reinterpret_cast通常只是将基类指针假装成一个派生类指针而不改变其值,而static_cast和旧式转型则将执行正确的地址操作。
Animal* an=new Animal;
CCat*cat=reinterpret_cast<CCat*>(an);
cat->eat();
//此处cat虽然声明类型为CCat*,但终将是假装(实质还是Animal*),最终打印的还是//Animal eat.
CCat*cat2=static_cast<CCat*>(an);
cat2->eat();
//因为static_cast进行内存转换,cat2已经货真价实的CCat*了。
Dynamic_cast仅用于对多态类型进行向下转型,即被转型的表达式类型,必须是带有一个指向虚函数的类型的指针,并且进行运行期检查工作,而static_cast则无须付出这种代价。对指针类型进行向下转型时,转型失败会返回null,而对于引用指针类型向下转型时,转型失败则会引起异常。
Animal* an=new Cat;
if (Person* cat=dynamic_cast<Person*>(an))
{
std::cout<<"castsuccess"<<std::endl;
}else
{
std::cout<<"castfailed"<<std::endl;
}
当转型成功时会返回正确指针对象,转型错误时则会返回null作为if条件。但是在vc6.0中以下代码虽然编译能通过,但执行时确会报错,而在vs2005中则正确输出。
Cat an;
Animal& refan=an;
Person& cat=dynamic_cast<Person&>(refan);
return 0;
对了,至于引用不能作为if判断条件,很简单,引用不能为空,引用一旦声明就必须初始化。但考虑如下:
const int&a=2;
const int&b=NULL;
if (b==NULL)
{
std::cout<<"b is null"<<std::endl;
}
这确是可以的,当一个指向常量的引用采用一个字面值来初始化,该引用实际上被设置指向“采用该字面值初始化”的一个临时位置。因为a,b并非真的指向字面值,而是指向一个采用字面值初始化、类型为int的的临时量。