第一章,基础议题

条款2 最好使用C++转型操作符

强制转换带来的问题:

(1)直接将任意类型转为其他任意类型,意图不明确且显得拙劣,如:将基类类型转为派生类类型,或者将常量类型转为非常量类型,但仅仅通过小括号强转并不能很好的表明意图

(2)强转使用小括号来实现,但小括号在编码中使用到的地方很多,很难直接一眼看出有哪些是类型转换,哪些又是函数调用等使用到小括号的地方,这显得很不专业

使用C++转型操作符:

为解决强转带来的问题,C++导入了4个新的转型操作符:

(1)static_cast

(2)const_cast

(3)dynamic_cast

(4)reinterpret_cast

与C强制转换的比较

C++转型操作符基本拥有强转一样的威力和意义,以及相同的限制,如: 不能将double转为pointer,或者将struct转为int; static_cast不能移除常量性,因为专门有const_cast来负责

C++转型操作符的使用:

(1)C++转型操作符更加集中他们自己负责的转型内容,const_cast只负责将类型转为常量或者非常量,其他的并不关注,如果被用于这个场景之外,就会转型失败

class Widget{...};
class SpecialWidget : public Widget{...};
void update(SpecialWidget*);

SpecialWidget sw;
const SpecialWidget& csw = sw;

update(&csw);//Worng! 函数update不接受const类型

update(const_cast<SpecialWidget*>(&csw));// Right! const_cast去除常量性,转型成功

update((SpecialWidget*)&csw); // 可读性比较差的旧式强转换

Widget *pw = new SpecialWidget();
update(pw); // Worng! 子类SpecialWidget转换为基类Widget,向上转型;对象是Widget*类型;函数update不接受Widget*类型

update(const_cast<SpecialWidget*>(pw));// Worng! const_cast并不负责将Widget*类型转为SpecialWidget*类型,他只关注和负责类型的常量性

(2)dynamic_cast 将指向基类对象的指针或引用转型为指向派生类对象的指针或引用,转型失败将得到null指针

class Widget{...};
class SpecialWidget : public Widget{...};
void update(SpecialWidget*);
void updateViaRef(SpecialWidget&);

Widget *pw;
... ... ...
update(dynamic_cast<SpecialWidget*>(pw)); // Right!
updateViaRef(dynamic_cast<SpecialWidget&>(*pw)); // Right!

注意: dynamic_cast 只能协助于继承体系中,无法应用在缺乏虚函数表的类型上,也不能改变常量性

int fnumb,snumb;
dynamic_cast<double>(fnumb)/snumb; // Worng! 未涉及继承机制,没有虚函数表

//-----------------------------------------------------------

class Widget{...};
class SpecialWidget : public Widget{...};
void update(SpecialWidget*);

const SpecialWidget csw;
update(dynamic_cast<SpecialWidget*>(&csw));// Worng! dynamic_cast不关注常量性转换

(3)不涉及继承体系和常量性的类型转换使用static_cast

(4)reinterpret_cast与编译平台息息相关,不具有移植性;最常用用途是转换“函数指针”类型

typedef void (*FuncPtr)(); //FuncPtr指针指向某个函数
FuncPtr funcPtrArray[10]; //funcPtrArray数组中有10个FuncPtr指针

int doSomething(); //如果现在要将指向doSomething函数的指针放入funcPtrArray数组,就需要将funcPtrArray转型,因为funcPtrArray中的指针指向函数的返回类型是void,但是doSomething返回类型的int

funcPtrArray[0] = &doSomething; // Worng!

funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); // Right!

注意: reinterpret_cast不具有移植性,应该尽量避免函数指针转型,除非走投无路