Intro

无论是在cpp中,还是c#中,静态强类型语言都会涉及到一个问题,就是强类型转换。 譬如下面的代码

1
2
double d = 3.3;
int i = (int)d; // i = 3;

当然,c#中,是存在比较安全的转换的,就是as

as operator(c# 7.0)

使用as,如果可以转换,会发生转换。如果不可以转换,那么就会返回一个null。

它等价于下面的式子

1
2
E as T
E is T ? (T)(E) : (T)null

它的好处是,不会在类型转换失败的时候抛出异常。

cpp11 cast

在cpp11中,引入了新的概念来进行更安全的cast。 在讨论新的cast(也不算新,已经快10年了)之前,我们先讨论已有的转换

隐式转换、显示转换和算术转换

隐式转换(implicit conversion)

1
2
3
int i = 6;
double d = i; // d = 6.0
i = d; // error, mscpp会报错

显示转换(explicit conversion)

1
2
double c = 3.1415
int pai = (int)c; // pai = 3;

算术转换(arithmetic conversion)

1
2
3
double d = 3.1415926;
int j = 10;
double res = d * j; // res = 31.415926

整个式子都会被作为double来计算。

整型提升(integral promotion)

1
2
3
int j = 10;
long long p = 1000;
long long res = p + j; // res = (long long)p + (long long)j

其他c++中特有的转换

1
2
int ia[10];
int* ip = ia; //ia被转换成pointer然后带入ip中

但是,array被用作decltype关键字参数,或者&、sizeof、typeid等的运算对象时候,不会进行上述转换。 (与c不同之处)

显示转换

正因为显示转换存在危险性,所以cpp11中引入了新的概念。

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

cpp11的理由

cpp11的理由主要是要把旧的cast的几种不同的cast分解开来, 把危险的reinterpret_cast分解到一个单独的操作中去,以避免显式cast所造成的不必要的困扰。

标准库中的范例

1
2
3
4
5
6
7
//atltime.h
static const ULONGLONG Millisecond = 10000;
static const ULONGLONG Second = Millisecond * static_cast<ULONGLONG>(1000);
static const ULONGLONG Minute = Second * static_cast<ULONGLONG>(60);
static const ULONGLONG Hour = Minute * static_cast<ULONGLONG>(60);
static const ULONGLONG Day = Hour * static_cast<ULONGLONG>(24);
static const ULONGLONG Week = Day * static_cast<ULONGLONG>(7);

static_cast

适合除了const值以外的几乎所有情况。
我们必须确保转换的正确性(手动确认)

1
2
int slo = static_cast<int>(3.3);
double dd = static_cast<double>(slo);

const_cast

可以转换const的pointer,但是注意,只是转换,不能写值。

1
2
3
const char *cp;
char* p = const_cast<char*>(cp); // OK, but cannot write p
char* tp = static_cast<char*>(cp); //error

reinterpret_cast

可以理解为一种重新定义的cast。譬如

1
2
3
int *ip;
char *pc = reinterpret_cast<char*>(ip);
string str(pc); // error, cause pc is not REAL char*

由于这样的转换,编译器不会报错,但是执行时会导致不可逆神奇的后果,所以一般不推荐使用。

附: 在mscpp中 char的大小是int的4分之1

Type Size
bool, char, unsigned char, signed char, __int8 1 byte
__int16, short, unsigned short, wchar_t, __wchar_t 2 bytes
float, __int32, int, unsigned int, long, unsigned long 4 bytes
double, __int64, long double, long long 8 bytes

dynamic_cast

专门转换指针和引用的cast。这里不详细说明。也不推荐使用 它是RTTI(run-time type identification)的一个运算符。它在以下的情况下使用;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct Mom {
public:
  virtual int sub(int a, int b) { return a - b; }
};

struct Son : Mom {
public:
  int sub(int a, int b) {
    return a + b;
  }
  virtual int add(int a, int b) {
    return a + b;
  }
};

int main() {
  Mom* m = new Mom;
  // failed
  if (Son* s = dynamic_cast<Son*>(m)) {
    cout << s->add(1, 2) << endl;
  }
  else {
    cout << "failed" << endl;
  }
  delete m;
  m = new Son;
  // successed
  if (Son* s = dynamic_cast<Son*>(m)) {
    cout << s->add(1, 2) << endl;
  }
  else {
    cout << "failed" << endl;
  }
  return 0;
}

cons

这里并没有详细讨论隐式转换(implicit conversion)


creativ common license