隐式类型转换:
C++的隐式转换发生在以下四种情况:
- 在混合类型的算术表达式中。
- 在表达式赋值中。
- 表达式传给函数时,实参转换为形参的类型。
- 函数返回表达式时转换成返回值类型。
显式类型转换:
- 被称为“强制类型转换”(cast)
- C风格:(type-id)
- C++风格:
static_cast
、dynamic_cast
、reinterpret_cast
、和const_cast
1. static_cast
- 用法:
static_cast < type-id > ( expression )
- 通常用于转换数值类型,进行非多态的类型转换。
- 在转换时不进行类型检查,在编译时进行类型检查,如果转换不成功则编译错误,因此不安全。
// static_cast_Operator.cpp
// compile with: /LD
class B {};
class D : public B {};
void f(B* pb, D* pd) {
D* pd2 = static_cast<D*>(pb); // Down
// Not safe, D can have fields
// and methods that are not in B.
B* pb2 = static_cast<B*>(pd); // Up
// Safe conversion, D always contains all of B.
}
- 与 dynamic_cast 不同,pb 的 static_cast 转换不执行运行时检查。 由 pb 指向的对象可能不是 D 类型的对象,在这种情况下使用 *pd2 会是灾难性的。 例如,调用 D 类(而非 B 类)的成员函数可能会导致访问冲突。
2. dynamic_cast
- 用法:
dynamic_cast < type-id > ( expression )
- 用于指针和引用。不同类型的指针和引用之间的转换。
-
注意:
dynamic_cast
在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上。也即如果想将基类指针转换为派生类指针,如果基类不是虚类则无法实现。 - 被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。
- 向上转换和static_cast作用一样。不要求虚基类。
- 支持同一个基类的不同派生类指针之间的转换。而
static_cast
则会报错。 - 失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)。例子如下。
// static_cast_Operator_2.cpp
// compile with: /LD /GR
class B {
public:
virtual void Test(){}
};
class D : public B {};
void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
- 如果
pb
确实指向 D 类型的对象,则pd1
和pd2
将获取相同的值。 如果pb == 0
,它们也将获取相同的值。 - 如果
pb
指向 B 类型的对象,而非指向完整的 D 类,则dynamic_cast
足以判断返回零。 但是,static_cast
依赖于程序员的断言,即pb
指向 D 类型的对象,因而只是返回指向那个假定的 D 对象的指针。
3. reinpreter_cast
- 用法:
reinpreter_cast<type-id> (expression)
-
type-id
必须是一个指针、引用、算术类型、函数指针或者成员指针。 - 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
- 比较底层的转换,在非相关的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。
reinpreter_cast
是特意用于底层的强制转型,导致实现依赖(就是说,不可移植)的结果。
4. const_cast
- 用法:
const_cast<type_id> (expression)
- 该运算符用来修改类型的
const
或volatile
属性。除了const
或volatile
修饰之外,type_id
和expression
的类型是一样的。 - 常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
关于多态的类指针转换
- 向上转换:派生类指针转为基类指针。
- 向下转换:基类指针转换为派生类指针。
见下面这个例子:
class A {
public:
A() {
a = 0;
}
private:
int a;
};
class B : public A {
public:
B() {
b = 0;
}
private:
int b;
};
int main() {
A *pa1, *pa2, *pa3, *pa4;
B *pb1, *pb2, *pb3;
A a1;
pa1 = &a1;
B b1;
pb1 = &b1;
// 首先,对于指针直接指向对象:
pa2 = &b1; // Correct 指针pa直接指向B中A有的一部分
pb2 = &a1; // Error 需要进行强制类型转换来缩小pb指针的范围
// error: invalid conversion from 'A*' to 'B*'
// 指针之间的转换
pa3 = dynamic_cast<A*>(pb3); // Up Correct 见上,使得指针能够正常工作
pb3 = dynamic_cast<B*>(pa4); // Down Error
// error: cannot dynamic_cast 'pa4' (of type 'class A*') to type 'class B*' (source type is not polymorphic)
// 由于类A不是虚基类,不能将其指针pa转换为类B的指针,因为类B的指针能够对A中没有的内容进行操作,直接转换则不能做到。若A为虚基类,B是对A中方法的重写,则能够正确转换
return 0;
}
例子:
- 此转换类型称为“向上转换”,因为它将在类层次结构上的指针,从派生的类移到该类派生的类。 向上转换是一种隐式转换。
class B { };
class C : public B { };
class D : public C { };
void f(D* pd) {
C* pc = dynamic_cast<C*>(pd); // ok: C is a direct base class
// pc points to C subobject of pd
B* pb = dynamic_cast<B*>(pd); // ok: B is an indirect base class
// pb points to B subobject of pd
}
- 此转换类型称为“向下转换”,因为它将在类层次结构下的指针,从给定的类移到该类派生的类。
class B {virtual void f();};
class D : public B {virtual void f();};
void f() {
B* pb = new D; // unclear but ok
B* pb2 = new B;
D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a D
D* pd2 = dynamic_cast<D*>(pb2); // pb2 points to a B not a D
}
参考阅读
static_cast, dynamic_cast, const_cast探讨
static_cast 运算符
dynamic_cast 运算符