在看muduo源码的时候看到了implicit_cast
与down_cast
,这里顺便做个笔记。
implicit_cast
和down_cast
用在有继承关系的类间转换;implicit_cast
用于向上转型(子类指针–>父类指针);down_cast
用于向下类型转换(父类指针–>子类指针);
示例:
#include <iostream>
#ifndef NDEBUG
#include <cassert>
#endif
using namespace std;
namespace showcode {
///
/// @brief `implicit_cast` 用于向上转型
/// From: subclass
/// To : father of subclass
///
template <typename To, typename From>
inline To implicit_cast(const From& f) {
return f;
}
///
/// @brief `down_cast` 用于向下转型
/// From: father of subclass
/// To : subclass
///
template<typename To, typename From> // use like this: down_cast<T*>(foo);
inline To down_cast(From* f) // so we only accept pointers
{
// Ensures that To is a sub-type of From *. This test is here only
// for compile-time type checking, and has no overhead in an
// optimized build at run-time, as it will be optimized away
// completely.
if (false)
{
implicit_cast<From*, To>(0);
}
#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)
assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only!
#endif
return static_cast<To>(f);
}
} // namespace showcode
class Base {
public:
Base() = default;
virtual ~Base() = default;
virtual void Print() const {
cout << __FUNCTION__ << endl;
}
};
class Derived : public Base {
public:
Derived() = default;
~Derived() = default;
void Print() const override {
cout << __FUNCTION__ << endl;
}
};
int main() {
{ // 普通类型转型
cout << "----------------普通类型转型--------------------" << endl;
int a = 10;
double b = showcode::implicit_cast<double>(a);
cout << typeid(b).name() << " b: " << b << endl;
cout << endl;
}
{ // 向上转型
cout << "----------------向上转型--------------------" << endl;
Derived* pd = new Derived;
Base* pb = showcode::implicit_cast<Base*>(pd);
pb->Print();
cout << endl;
}
{ // 通常用法
cout << "----------------通常向下转型用法--------------------" << endl;
Base* pb = new Base;
pb->Print();
if (Derived* pd = dynamic_cast<Derived*>(pb)) { // 这一步是nullptr的,直到运行时才知道
pd->Print();
}
cout << endl;
}
{ // 向下转型
cout << "----------------向下转型--------------------" << endl;
Base* pb = new Base;
Derived* pd = showcode::down_cast<Derived*>(pb); // 编译期间就知道错误了
pd->Print();
cout << endl;
}
return 0;
}
运行结果:
Debug
模式下:Derived* pd = showcode::down_cast<Derived*>(pb);
报错;
Release
模式下:
没有报错,因为Release模式下的down_cast
用的就是static_cast
。
问题:
(1)为什么implicit_cast
比static_cast
安全?
implicit_cast
只用于隐式类型转换,而static_cast
向上向下都行,但显然向下类型转换是不安全的。
- 先来说说隐式转换,比如有如下的定义:
class Base {};
class Derived : public Base {
public:
Derived() {
data = 100;
}
void Print() const {
cout << data << endl;
}
private:
int data;
};
int main() {
Base* b;
Derived* d;
b = d; // ok
//d = b; // error: 不允许向上的隐式转换
d = static_cast<Derived*>(b); // ok, 但不是安全的
Base* pb = new Base;
Derived* pd = static_cast<Derived*>(pb); // ok, 但不是安全的
pd->Print(); // 打印的是一个无意义的值
return 0;
}
上面使用static_cast
向下转换是不安全的,但是程序并没有报错,而如果我们使用implicit_cast
和down_cast
的话,则可以查出错误。
Base* pb = new Base;
Derived* pd = implicit_cast<Derived*>(pb); // error
Derived* pd2 = static_cast<Derived*>(pb); // ok, 但是不安全
子类可以隐式转换成父类(借助虚函数表),而父类无法隐式转换成子类(因为父类中没有子类的信息)。