Effective C++ ITEM27.尽量少做转型动作
C++ 四种转型
const_cast 通常被用来将对象的常量性转除(cast away the constness)。也是唯一有能力的C++ style转型操作符
dynamic_cast 主要用来执行“安全向下转型(safe downcasing)”也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语言执行的动作,也是唯一可能耗费重大运行成本的转型动作
reinterpret_cast 意图执行低级转型,实际动作(及结果)可能取决于编译器,也就是表示它不可移植。例如将一个point to int 转型为一个int。这一类的低级代码以外很少见。本书只使用一次,讨论针对原始内存(raw memory)写出一个调试用的分配器(debugging allocator)
static_cast 用来强制隐式转换(implicit conversions), 例如将non-const 对象转换为const对象(就像条款3所为), 或将int 转换为double等等。它也可以用来执行上述多种转换的反向转换,例如将void*指针转为typed指针。将point-to-base 转为pointer-to-derived。但它无法将const 转换为non-const,只有const_cast才办得到。
单一对象可能拥有一个以上地址
多继承时
#include <iostream>
using namespace std;
class Base1 {
public:
Base1() {}
virtual ~Base1() {};
virtual void speakClearly() {
cout<<"Hello World!"<<endl;
}
virtual Base1* clone() const {
return new Base1(*this);
}
protected:
float data_Base1;
};
class Base2 {
public:
Base2() {}
virtual ~Base2() {}
virtual void mumble() {
cout<<"mumble"<<endl;
}
virtual Base2* clone() const {
return new Base2(*this);
}
protected:
float data_Base2;
};
class Derived : public Base1, public Base2
{
public:
Derived (/* args */);
virtual ~Derived ();
virtual Derived* clone() {
return new Derived(*this);
}
private:
/* data */
};
Derived ::Derived (/* args */)
{
}
Derived ::~Derived ()
{
}
int main()
{
Derived derived;
Base1* pb1 = &derived;
Base2* pb2 = &derived;
cout<<"drived: "<<&derived<<"\tpb1: "<<&pb1 <<"\tpb2: "<<&pb2<<endl;
return 0;
}
输出如下:
单继承时
#include <iostream>
#include <string>
using namespace std;
class Animals {
public:
Animals() {};
virtual ~Animals() {};
virtual void Bark() {
name = "Wu Wu Wu";
cout << name << endl;
}
protected:
string name = "Animals";
};
class Dog : public Animals {
public:
Dog() {};
virtual ~Dog() {};
virtual void Bark() {
name = "Wang Wang Wang";
cout << name << endl;
}
};
int main()
{
Dog dog;
Animals* animal = &dog;
cout<<"animal address:"<< &animal << "\tDog Address" << &dog << endl;
return 0;
}
输出:
TIPS
不要对"对象在C++中如何布局的假设"。更不应该以假设为基础做任何转型动作。例如:将对象地址转换char*指针,然后在其身上做指针算术
转型动作时调用的是"*this对象之base class 成分"的暂时副本身上的onResize
假设有代码如下:
#include <iostream>
#include <string>
using namespace std;
class Window
{
public:
Window(/* args */);
~Window();
virtual void Resize() {
posX = 100;
posY = 100;
length = 100;
width = 100;
}
protected:
int posX;
int posY;
int length;
int width;
};
Window::Window(/* args */)
{
}
Window::~Window()
{
}
class SpecialWindow : public Window
{
public:
SpecialWindow(/* args */);
~SpecialWindow();
virtual void Resize() {
static_cast<Window>(*this).Resize();
title = "Hello Window";
cout << "PosX: " << posX << "\tPosY: " << posY << "\tLength: "<< length << "\tWidth: " << width << "\ttitle: " << title << endl;
Window::Resize();
cout << "PosX: " << posX << "\tPosY: " << posY << "\tLength: "<< length << "\tWidth: " << width << "\ttitle: " << title << endl;
}
private:
string title;
};
SpecialWindow::SpecialWindow(/* args */)
{
}
SpecialWindow::~SpecialWindow()
{
}
int main()
{
SpecialWindow specialWindow;
Window* pW = &specialWindow;
pW->Resize();
return 0;
}
输出如下:
如上代码,第一次输出的时候,Window中onResize没有执行,而SpecialWindow中的onResize却执行了。方法的执行出现了一种"伤残"的状态。
如此正常的写法应当如下:
class SpecialWinow : public Window {
public:
virtual void OnResize() {
Window::OnResize();
...
}
...
};
dynamic_cast 可能会耗用多达四次的strcmp调用
- 深度继承和多重继承的成本将更会更高
- 在注重效率的代码中尽量避免使用dynamic_cast
- 连续的dynamic_cast 使用更是要避免
总结
- 尽量避免转型动作,尤其是注重效率的代码中,尽量不要使用dynamic_cast, 如果存在设计需要转型情况,尝试使用无需转型的方法
- 如果转型是必要的,试着将转型动作放在函数,调用方无需感知转型动作
- 如果真的需要转型,尽量使用C++style casts