背景:
C++规则的设计目标之一:保证 类型错误 绝不可发生。(非常重要!)
一个程序能干净地通过编译,表示:该程序不会在任何对象身上执行 任何不安全,无意义、愚蠢的操作。
但转型破坏类型系统。可能会导致麻烦。
c++提供的转型方案:
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)
各自分工不同:
const_cast<T>(expression):将对象的常量性转除。意思就是将一个对象类型是const的,经过该转换之后,就不是const类型的。
dynamic_cast<T>(expression):用于执行安全向下类型转换。意味:决定某对象是否归属继承体系中的某个类型。也是唯一可能消耗重大运行成本的转型动作。
reinterpret_cast<T>(expression):执行低级转型。实际转型动作以及结果可能取决于编译器。这表示它不可移植。比如将指向int型的指针,转型为int类型。很少见~~~
static_cast<T>(expression):强迫隐式转换。比如:将非const类型转型为const,将int转为double类型等。但无法将const类型转型为非const类型,只有const_cast可以实现。
为何少用或不用转型?
1. 隐式转型或显示转型,有时候需要一个偏移量来获取到转型后的对象。
示例:
class Base{ ... };
class Derived:public Base{ ... };
Derived d;
Base* pb = &d; //隐式将Derived*转换为Base*
或
Base* pb = static_cast<Derived*>d;//显示类型转换
此处目的:将一个基类指针指向一个派生类对象。
编译器实际执行的动作:
这种情况下会有个偏移量在运行期被施行于Derived* 指针身上,用于取得正确的Base*类型指针值。
意思就是:
Base* pb指向的地址是 Derived* d指针的地址经过一个offset偏移量计算得到。此时的&d的地址代表的是Base 在Derived类中的偏移位置。
但是&d本身有个地址,是指向自身Derived的。
这也就出现一个指针,有可能有多个不同的地址。这就是C++。 其他语言是不可能发生这种情况的。
因此:
在这种情况下,不要将对象地址转型为其他类型的指针,比如char*,然后进行指针算术,会导致无定义行为。
背景:
想使用dynamic_cast转型,通常是想在派生类身上执行派生类成员函数,但当前环境中只有一个指向基类的指针或引用,只能靠dynamic_cast转型实现。
解决方案:
1. 使用容器并在其中存储直接指向派生类对象的指针(通常是智能指针)
举例:
class A{ ... };
class B: public A{
public:
void M();
};
先假设按照以下方法实现:
typedef std::vector< std::shared_ptr<A> > VPA;
VPA vPtrs;
....
for(VPA::itertor iter = vPtrs.begin();iter != vPtrs.end();++iter){
if(B* pb = dynamic_cast<B*>(iter->get())){
pb->M();
}
}
该方案就是直接使用dynamic_cast实现访问派生类的成员函数方法。但是这个弊端,上面已述:采用普遍的比较类名称来找到目标对象,耗费的成本太高,效率不好。
推荐使用方案:
typedef std::vector< std::shared_ptr<B> > VPB;
VPB vPtrs;
....
for(VPA::itertor iter = vPtrs.begin();iter != vPtrs.end();++iter){
(*iter)->M();
}
存在的缺点:
该方案的容器无法存储指向所有可能的组合的A派生类的指针。意思就是A的其他派生类C,D,E,F等就不能通过上诉一个容器
typedef std::vector< std::shared_ptr<B> > VPB;搞定。
有多少个派生类型,就可能需要多个容器。并且具备类型安全。
2. 在基类提供virtual函数,一份什么也不实现的代码。
举例:
class A{
public:
virtual void M(){};//什么也不实现。
};
总结:
1.如果可以,尽量避免转型。特别是在注重效率的代码中避免dynamic_cast操作。如果有设计需要转型动作,
尝试无需转型的设计方案:
B:在基类提供virtual函数,一份什么也不实现的代码。
3.不要欺骗编译器,请真实表达自己想做的事情