类型转化分为两大类:旧风格与新风格
C++提供了4种新的类型转化:
const_cast<T> (表达式)
dynamic_cast<T> (表达式)
reinterpret_cast<T> (表达式)
static_cast<T> (表达式)
它们的用处分别如下:
const_cast<T> 用来去除指向某个对象的指针或者引用的const属性。只有它有这个能力。
dynamic_cast<T> 主要用来安全向下转型。这个转型可能耗费很大的成本。
reinterpret_cast<T> 意图执行低级转型,结果取决于编译器,故不可移植。
static_cast用来完成隐式类型转化。
首先,我们倾向于使用新的转型方式。因为1.他很容易被人发现。2.每种方式都有特定的使用范围,这使得编译器很容易判断这种转型是否错误。
其中派生类中同过两种方式来调用基类中的say函数:一种是通过类型转化,一种是直接声明作用域。我们可以用下面的程序检测结果:
虽然static_cast<Base>(*this).say();的打印结果都是11,但是使用cout<<d.bVal<<endl;时,d.bVal的值却不一样了:使用强制类型转化后,d.bVal的还是10;而调用基类函数时,d.bVal的值就变成了11。这中间的问题出在哪里了呢?就是static_cast<Base>(*this).say();是在当前对象基类成分的副本上调用say函数,而不是当前对象本身。所以cout<<++bVal<<endl;修改的是副本的bVal值,而不是d中的。总而言之一句话:当在派生类中想调用基类的某些成分时,直接通过作用域操作符告诉使用的是基类的成员,而不是通过类型转换。
下面再说说dynamic_cast。这个函数效率很低,能不用就不用吧。通常,只有当你想在一个认定为派生类对象的身上执行派生类操作,而你却只有一个基类指针或者引用时,才需要dynamic_cast:
注意,这个转化这里要求基类必须函有虚函数。
通常,有两种办法解决这个问题:
1.在基类里添加对应的虚函数:
然后就可以通过动态绑定来调用派生类的函数了:
2.使用容器存储只想直接指向派生类的对象的指针(通常是智能指针)。这便消除了通过基类接口处理对象的需要,假设继承派生体系中只有派生类才有say函数,那么
最后,避免连续使用dynamic_cast。因为这样效率很低,而且基础不稳定。
总而言之,好的代码很少使用类型转化。如果非要类型转换,则应该使用C++风格的,且应该把它隐藏在某个函数中,而不是暴漏给用户。
旧风格是从C语言继承过来的,分为C-style和function-style。举个例子:
double d = 1.2;
int i = (int) d;//C-style
int j = int(d);//函数风格
C++提供了4种新的类型转化:
const_cast<T> (表达式)
dynamic_cast<T> (表达式)
reinterpret_cast<T> (表达式)
static_cast<T> (表达式)
它们的用处分别如下:
const_cast<T> 用来去除指向某个对象的指针或者引用的const属性。只有它有这个能力。
dynamic_cast<T> 主要用来安全向下转型。这个转型可能耗费很大的成本。
reinterpret_cast<T> 意图执行低级转型,结果取决于编译器,故不可移植。
static_cast用来完成隐式类型转化。
首先,我们倾向于使用新的转型方式。因为1.他很容易被人发现。2.每种方式都有特定的使用范围,这使得编译器很容易判断这种转型是否错误。
然后看一个让人无比纠结的例子:
class Base
{
public:
Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
virtual void say()
{
cout<<++bVal<<endl;
}
int bVal;
};
class Drived:public Base
{
public:
Drived(int i = 10, int j = 5):Base(i),dVal(j){cout<<"派生类构造函数"<<endl;}
void say()
{
cout<<"基类值为:"<<endl;
static_cast<Base>(*this).say(); //错误做法
// Base::say(); //正确做法
cout<<"派生类值为:"<<endl;
cout<<dVal<<endl;
}
private:
int dVal;
};
其中派生类中同过两种方式来调用基类中的say函数:一种是通过类型转化,一种是直接声明作用域。我们可以用下面的程序检测结果:
int main()
{
Drived d;
d.say();
cout<<d.bVal<<endl;
return 0;
}
虽然static_cast<Base>(*this).say();的打印结果都是11,但是使用cout<<d.bVal<<endl;时,d.bVal的值却不一样了:使用强制类型转化后,d.bVal的还是10;而调用基类函数时,d.bVal的值就变成了11。这中间的问题出在哪里了呢?就是static_cast<Base>(*this).say();是在当前对象基类成分的副本上调用say函数,而不是当前对象本身。所以cout<<++bVal<<endl;修改的是副本的bVal值,而不是d中的。总而言之一句话:当在派生类中想调用基类的某些成分时,直接通过作用域操作符告诉使用的是基类的成员,而不是通过类型转换。
下面再说说dynamic_cast。这个函数效率很低,能不用就不用吧。通常,只有当你想在一个认定为派生类对象的身上执行派生类操作,而你却只有一个基类指针或者引用时,才需要dynamic_cast:
class Base
{
public:
Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
/* virtual void say()
{
cout<<"基类函数"<<endl;
}*/
virtual void fun(){}
private:
int bVal;
};
class Drived:public Base
{
public:
Drived(int i = 10, int j = 5):Base(i),dVal(j){cout<<"派生类构造函数"<<endl;}
void say()
{
cout<<"派生函数"<<endl;
}
private:
int dVal;
};
int main()
{
Base *pd = new Drived;
Drived* pd1 = dynamic_cast<Drived*>(pd);
pd1->say();
return 0;
}
注意,这个转化这里要求基类必须函有虚函数。
通常,有两种办法解决这个问题:
1.在基类里添加对应的虚函数:
public:
Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
virtual void say(){}//什么也不做
然后就可以通过动态绑定来调用派生类的函数了:
Base *pd = new Drived;
pd->say();
2.使用容器存储只想直接指向派生类的对象的指针(通常是智能指针)。这便消除了通过基类接口处理对象的需要,假设继承派生体系中只有派生类才有say函数,那么
typedef vector<std::tr1::shared_ptr<Drived>> VPD;
VPD d(1);
for(VPD::iterator iter = d.begin(); iter != d.end();++iter)
(*iter)->say();
最后,避免连续使用dynamic_cast。因为这样效率很低,而且基础不稳定。
总而言之,好的代码很少使用类型转化。如果非要类型转换,则应该使用C++风格的,且应该把它隐藏在某个函数中,而不是暴漏给用户。