大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++将强制转型减到最少

C++将强制转型减到最少(1)

关键词:C++强制转型  阅读(1052) 赞(12)

[摘要]本文主要是对C++将强制转型减到最少的讲解,希望对您学习C++有所帮助!

C++ 的规则设计为保证不会发作类型错误。在理论上,假定你的顺序想顺利地经过编译,你就不应该试图对任何对象做任何不安全的或有意义的操作。这是一个非常有价值的保证,你不应该随意地坚持它。

不幸的是,强迫转型破坏了类型系统。它会惹起各种各样的费事,其中一些容易被察觉,另一些则格外地巧妙。假定你从 C,Java,或 C# 转到 C++,请一定留意,由于强迫转型在那些言语中比在 C++ 中更有必要,风险也更少。但是 C++ 不是 C,也不是 Java,也不是 C#。在这一言语中,强迫转型是一个你必需全神贯注才可以接近的特性。

我们就从回想强迫转型的语法末尾,由于关于异常的强迫转型通常有三种不同的写法。C 作风(C-style)强迫转型如下:

(T) expression // cast expression to be of type T

函数作风(Function-style)强迫转型运用这样的语法:

T(expression) // cast expression to be of type T

这两种方式之间没有本质上的不同,它地道就是一个把括号放在哪的效果。我把这两种方式称为旧作风(old-style)的强迫转型。

C++ 同时提供了四种新的强迫转型方式(通常称为新作风的或 C++ 作风的强迫转型):

  const_cast(expression)

dynamic_cast(expression)

reinterpret_cast(expression)

static_cast(expression)

每一种适用于特定的目的:

·const_cast 普通用于强迫消弭对象的常量性。它是独一能做到这一点的 C++ 作风的强迫转型。

·dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象能否是一个承袭体系中的一个特定类型。它是独一不能用旧作风语法执行的强迫转型。也是独一可以有严重运转时代价的强迫转型。(过一会儿我再提供细节。)

·reinterpret_cast 是特意用于底层的强迫转型,招致完成依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强迫转型在底层代码以外应该极为稀有。在本书中我只用了一次,而且还仅仅是在讨论你应该如何为裸内存(raw memory)写一个调谐分配者(debugging allocator)的时分。

·static_cast 可以被用于强迫隐型转换(例如,non-const 对象转型为 const 对象(就像 Item 3 中的),int 转型为 double,等等)。它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象。(只需 const_cast 能做到。)

旧作风的强迫转型依然合法,但是新的方式更可取。首先,在代码中它们更容易识别(无论是人还是像 grep 这样的工具都是如此),这样就简化了在代码中寻觅类型系统被破坏的地方的进程。第二,更精确地指定每一个强迫转型的目的,使得编译器诊断运用错误成为可以。例如,假定你试图运用一个 const_cast 以外的新作风强迫转型来消弭常量性,你的代码将无法编译。

当我要调用一个 explicit 构造函数用来传递一个对象给一个函数的时分,大约就是我仅有的运用旧作风的强迫转换的时分。例如:

class Widget {

public:

explicit Widget(int size);

...

};

void doSomeWork(const Widget& w);

doSomeWork(Widget(15)); // create Widget from int

// with function-style cast   

doSomeWork(static_cast(15)); // create Widget from int

// with C++-style cast

由于某种缘由,有条不紊的对象创建觉得上不像一个强迫转型,所以在这个强迫转型中我多半会用函数作风的强迫转型替代 static_cast。反过来说,在你写出那些招致中心崩溃(core dump)的代码时,你通常都觉得你有恰当的缘由,所以你最好忽略你的觉得并不时都运用新作风的强迫转型。

很多顺序员以为强迫转型除了告诉编译器将一种类型看作另一种之外什么都没做,但这是错误的。任何种类的类型转换(无论是经过强迫转型的显式的还是编译器添加的隐式的)都会招致运转时的可执行代码。例如,在这个代码片断中,   

int x, y;

...

double d = static_cast(x)/y; // divide x by y, but use

// floating point division

int x 到 double 的强迫转型理所当然要生成代码,由于在大多数系统架构中,一个 int 的底层表示与 double 的不同。这可以还不怎样令人吃惊,但是下面这个例子可以会让你稍微开一下眼:   

class Base { ... };   

class Derived: public Base { ... };   

Derived d;  

Base *pb = &d; // implicitly convert Derived* → Base*

这里我们只是创建了一个指向派生类对象的基类指针,但是有时分,这两个指针的值并不相反。在当前情况下,会在运转时在 Derived* 指针上运用一个偏移量以失掉正确的 Base* 指针值。

这后一个例子标明一个单一的对象(例如,一个类型为 Derived 的对象)可以会有不止一个地址(例如,它的被一个 Base* 指针指向的地址和它的被一个 Derived* 指针指向的地址)。这在 C 中就不会发作,也不会在 Java 中发作,也不会在 C# 中发作,它仅在 C++ 中发作。实际上,假定运用了多承袭,则一定会发作,但是在单承袭下也会发作。与其它事情合在一同,就意味着你应该总是避免对 C++ 如何摆放事物做出假定,你当然也不应该基于这样的假定执行强迫转型。例如,将一个对象的地址强迫转型为 char* 指针,然后对其运用指针运算,这几乎总是会招致未定义行为。

但是请留意我说一个偏移量是“有时”被需求。对象摆放的方法和他们的地址的计算方法在不同的编译器之间有所变化。这就意味着仅仅由于你的“我知道事物是如何摆放的”而使得强迫转型能义务在一个平台上,并不意味着它们也能在其它平台义务。这个世界被经过痛苦的路途学得这条阅历的不幸的顺序员所充溢。 关于强迫转型的一件幽默的事是很容易写出看起来对(在其它言语中也许是对的)实际上错的东西。例如,许多运用框架(application framework)要求在派生类中完成虚成员函数时要首先调用它们的基类对应物。假定我们有一个 Window 基类和一个 SpecialWindow 派生类,它们都定义了虚函数 onResize。进一步假定 SpecialWindow 的 onResize 被希冀首先调用 Window 的 onResize。这就是完成这个的一种方法,它看起来正确实际并不正确:   

class Window { // base class

public:

virtual void onResize() { ... } // base onResize impl

...

};

class SpecialWindow: public Window { // derived class

public:

virtual void onResize() { // derived onResize impl;

static_cast(*this).onResize(); // cast *this to Window,

// then call its onResize;

// this doesn’t work!  

... // do SpecialWindow-

} // specific stuff   

...   

};

«上一页12下一页»


相关评论