条款27:尽量少做转型动作

  • c++规则 的设计目标之一是,保证“类型错误”绝不可以发生。理论上如果你的程序可以很“干净地”(无错误,无警告)通过编译,就表示它并不企图在任何对象上执行不安全、无意义、愚蠢的操作。这是一个极具价值的保证,可别草率的放弃它。
  • 不幸的是,转型(casts)破坏了类型系统(type system)。那可能导致任何各种麻烦,有些容易辨识,有些非常隐晦。C++不同于JAVA,C#,C。在JAVA,C#,C,中转型(casting)比较必要而无法避免,也比较安全(相对于C++来说)。在C++中转型是一个你会带着小心谨慎态度去学习的一个特性。

旧式转型

C风格的转型如下

(T)expression   // 将expression转型成T类型

函数风格的转型如下

T(expression)   // 将expression转型为T
  • 以上两种形式的转型都称为旧式转型(old-style casts)

新式转型

const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)
  • const_cast通常被用来将对象的常量性质转除(cast away the constness)。它也是唯一一个有这种能力的C++ style转型符。
  • dynamic_cast主要用来执行“安全向下转型”(safe downcasting),多数运用在继承体系中的某个类型。它是唯一无法由旧式语法执行的操作,也是唯一可能耗费重大运行成本的转型动作(稍后细谈)。
  • reinterpret_cast意图执行低级转型,转型结果可能取决于编译器,这也就表明他不可移植。
  • static_cast用来强迫隐式转换(implicit conversions),例如将non-const对象转为const对象,或将int转为double等等。它也可以用来执行上述多种转换的反向转换,例如将void*指针转为typed指针,将pointer-to-base(基类指针)转为pointer-to-derived(派生类指针)但是它没有办法执行constnon-const的回去的转换,这只有const_cast办的到。

尽量用新式转型,而不用旧式转型

旧式转型已经很少使用了,比较常见的使用场景是调用一个explicit构造函数将一个对象传递给一个函数时,例如下面这种场景。

class Widget{
public:
    explicit Widget(int size);
    ...
};
void doSomeWork(const Widget& w);

doSomeWork(Widget(15));              // 函数风格的旧式转型
doSomeWork(static_cast<Widget>(15)); // 新式转型
  • 从某个角度说,蓄意的“对象生成”动作感觉不怎么像“转型”,所以我们很可能使用函数风格的转型动作面不使用static_cast。但是当我们写下一段日后出错导致“core dump”的代码时,撰写代码的时候可能会认为旧式网络的转型很合理,但是最后忽略你的感觉,始终理智地运用新式转型。

转型(casting)并不只是简单的标记

有时候我们会认为转型操作只是告诉编译器把某种类型视为另一种类型,但是事实绝非如此。任何一个类型转换(不论是通过转型操作而进行的显式转换,或通过编译器完成的隐式转换)往往真的令编译器产生一些运行期间的机器码,比如下面这段程序

int x, y;
double d = static_cast<double>(x)/y;
  • int x转换成double x肯定会产生一些机器码,因为在大部分计算机体系结构中,int的底层表述不同于double的底层表述。


下面是另一个有趣的例子

class Base { ... };
class Derived: public Base { ... };
Derived d;
Base* pb = &d;      // 这里会做一个隐式转换(将Derived* 转换成 Base*)
  • 这里的所做的操作是使用一个base class 指针指向一个derived class对象(运行时多态中的场景)。但是有些情况下(多重继承中),上述两个指针的值并不相同。这种情况下会有个偏移量(offset)在运行期间被施行于Derived*指针上,以此来取得正确的Base*的指针值。并且在单一继承上也可能发生!。所以这意味着你必须放弃掉假设“对象在C++中如何布局”。


下面是另一个关于在派生类虚函数中调用基类虚函数的例子。代码如下

// 基类
class Window{
public:
    // Window::onResize的实现版本
    virtual void onResize(){ ... }
    ...
};

// 派生类
class SpecialWindow: public Window{
public:
    // SpecialWindow::onResize的实现版本
    virtual void onResize(){
        static_cast<Window>(*this).onResize();    // 将(*this)转型为Windows然后调用onResize()

        // 下面是SpecialWindow::onResize的专属代码
        ...
    }
    ...
};
  • 我们可能会想要在SpecialWindow::onResize调用之前 ,首先调用Window::onResize。于是乎你可能会写出如下 代码

猜你喜欢

转载自blog.csdn.net/Pig_Pig_Bang/article/details/81035323