类型转换

有时候我们需要在表达式中显示地将对象强制转换为另一种类型参与运算,称作**强制类型转换(cast)。一个命名的强制类型的形式为cast-name(expression)**,其中type是转换的目标而expression是要转换的值。

static_cast

任何具有明确定义的类型转换,且不包含底层const,都可以使用static_cast。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// convert int to double
int a = 9;
int b = 8;
cout << a / b << endl;
cout << static_cast<double>(a) / b <<endl;


// 具有底层const,无法使用static_cast
const char * p = "helloworld";
char * q = static_cast<char *>(p); // compile error
*q = 'H';
cout << q << endl;

// 顶层const可以使用static_cast
char * const x = "aloha";
char * y = static_cast<char *>(x); // 去掉顶层const
y = "hello";

cout << x << endl;
cout << y << endl;
// 转换为string
cout << static_cast<string>(x) << endl;
cout << static_cast<string>(y) << endl;
cout << static_cast<const string>(y) << endl; // 添加顶层const

const_cast

const_cast只能改变运算对象的底层const。对于将常量对象转换为非常量对象,称为去掉const性质。如果对象本身不是常量,则获得写权限是合法的行为,反之对其进行写操作会产生未定义的后果。 const_cast接受的操作对象只能是指针、引用或者指向类类型对象的成员指针。
const_cast与static_cast对比

1
2
3
4
5
const char * sp;
char *q = static_cast<char *>(sp); // compile error
static_cast<string>(sp); // true.字符串字面值转换为string类型
const_cast<string>(sp); // compile error.只能改变常量属性
char * l = const_cast<char*> (sp); // true

使用const_cast获得常量的写操作权限

1
2
3
4
5
void write_const(const char * cp)
{
char * q = const_cast<char*> (cp);
*q = 'H';
}

该函数通过将指向常量的指针参数,去掉其底层const,尝试进行写操纵。
当cp指向真正的常量时,如下main函数

1
2
3
4
5
6
7
int main()
{
const char * sp = "helloworld"; // sp指向字符串常量
write_const(sp);

return 0;
}

编译通过,运行。触发segment fault:

1
2
> g++ main.cc -g -o main && ./main 
fish: Job 1, './main' terminated by signal SIGSEGV (Address boundary error)

若sp指向的是非常量

1
2
3
4
5
6
7
8
9
int main()
{
char arr[] = "helloworld";
const char * sp = arr; // sp指向字符串常量
write_const(sp);
cout << sp << endl;

return 0;
}

编译通过,运行,写入成功:

1
2
> g++ main.cc -g -o main && ./main 
Helloworld

如下程序通过去掉const特性修改string的内容:

1
2
3
4
5
6
7
8
9
10
11
12

int main(int argc, char **argv)
{
const string s = "Hello";
const string & crs = s;
string & rs = const_cast<string &>(crs);
cout << s << ' ' << crs << ' ' << rs<<endl;
rs = "world";
cout << s << ' ' << crs << ' ' << rs<<endl;

return 0;
}

运行结果如下:

1
2
3
> g++ main.cc -g -o a && ./a
Hello Hello Hello
world world world

const_cast操作对象不是指针或引用时会编译出错:

1
2
3
4
5
6
7
8

int main(int argc, char **argv)
{
string s = "hello";
const string cs = const_cast<const string>(s); // the type in a const_cast must be a pointer, reference, or pointer to member to an object type

return 0;
}

reinterpret_cast

reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。

1
2
3
4
// 将一段存储数字的内存的地址转换为字符串的地址,并输出
int arri[] = {0x6c6c6568, 0x726f776f, 0x646c};
char * p = reinterpret_cast<char *>(arri);
cout << p << endl;

运行结果为

1
2
> g++ main.cc -g -o main && ./main
helloworld

dynamic_cast

dynamic_cast运算符,用于将基类指针或引用安全转换为派生类的指针或引用。使用形式如下

  • dynamic_cast<type *>(e),e必须是一个有效的指针

  • dynamic_cast<type &>(e),e必须是一个左值

  • dynamic_cast<type &&>(e),e不能是左值

其中,type必须是一个类类型,并且通常情况下该类型应该含有虚函数。e的类型必须符合如下三个条件中任一一个:

  • e的类型是目标type的公有派生类
  • e的类型是目标typpe的公有基类
  • e的类型就说目标type的类型
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
class base
{
public:
base()
{
cout << " \n base constructor \n";
}
};

class derived : public base
{
public:
derived()
{
cout << " \n derived constructor \n";
}
};

int main()
{

base *obj = new derived; // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived *>(obj); // case 2: error

return 0;
}

此种情况下,base类没有虚函数,编译器会提示错误信息

1
the operand of a runtime dynamic_cast must have a polymorphic class typeC/C++(698)

任一添加虚函数后,程序通过编译(note: 虚函数必须有定义,不能只声明)。

对于转换类型是指针和引用,当转换失败时的行为不同:若一条 dynamic_cast语句的转换目标是指针类型,则结果为0;若转换目标是引用类型,则dynamic_cast运算符抛出bad_cast异常。

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
36
37
38
39
40
41
42
43
44
45
46
class base
{
public:
base()
{
cout << " \n base constructor \n";
}
virtual void foo()
{
}
};

class derived : public base
{
public:
derived()
{
cout << " \n derived constructor \n";
}
};

int main()
{
// case 1: pointer
base *obj = new derived; // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived *>(obj); // case 2: error

// case 2: reference
derived d;
base &rb = d;
try
{
derived &rd = dynamic_cast<derived &>(rb);
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
}

// case 3: rvalue
// derived &&rrd = dynamic_cast<derived &&>(base());

delete obj;
return 0;
}

上述例子可以运行成功。下面这个例子中的指针和引用转换失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

int main()
{
// case 1: pointer
base *obj = new base;
derived *OBJ = dynamic_cast<derived *>(obj); // fail: return nullptr
assert(OBJ == nullptr);

// case 2: reference
base b;
base &rb = b;
try
{
derived &rd = dynamic_cast<derived &>(rb);
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
}

delete obj;
return 0;
}

输出结果如下

1
std::bad_cast