共计 3453 个字符,预计需要花费 9 分钟才能阅读完成。
在上篇与中篇中,咱们探讨了隐式类型转换及其与函数重载之间的相干话题。本篇将要探讨的即为类型转换的另一大分支——强制类型转换。
9. C 格调的强制类型转换
在 C 语言中,强制类型转换存在两种等价模式:Type(Value) 或 (Type)Value。
参考以下代码:
int main()
{(int *) malloc(0); // (Type)Value 模式的强制类型转换
int(0.); // Type(Value) 模式的强制类型转换
}
上述代码中,咱们别离应用了 C 语言的提供的两种强制类型转换的等价模式将 void 转为了 int ,以及将 double 转为了 int。
10. static_cast
在 C ++ 中,static_cast 相当于 C 语言中的强制类型转换语法。static_cast 用于在编译期对某种类型的变量进行强制类型转换。
参考以下代码:
int main()
{static_cast<int *>(malloc(0));
static_cast<int>(0.);
}
上述代码中,咱们应用了 static_cast 别离将 void 转为了 int ,以及将 double 转为了 int。
11. const_cast
const_cast 是 C ++ 中专用于解决与 const 相干的强制类型转换关键字,其性能为:为一个变量从新设定其 const 形容。即:const_cast 能够为一个变量强行减少或删除其 const 限定。
须要明确的是,即便用户通过 const_cast 强行去除了 const 属性,也不代表以后变量从不可变变为了可变。const_cast 只是使得用户接管了编译器对于 const 限定的管理权,故用户必须恪守“不批改变量”的承诺。如果违反此承诺,编译器也不会因而而引发编译时谬误,但可能引发运行时谬误。
上面探讨 const_cast 的主要用途。
考查以下代码:
struct A {A &test() {return *this;} };
int main()
{A().test();}
这段代码看上去运行失常。但如果:
struct A {A &test() {return *this;} };
int main()
{
const A a;
a.test(); // Error!}
咱们试图用一个 const 对象去调用非 const 成员函数,此时,为了调用此成员函数,const A this 就须要转换为 A this,这显然是不行的。
通过上述探讨,咱们能够将代码批改为如下:
struct A {const A &test() const {return *this;} };
int main()
{
const A a;
a.test();}
咱们将 this 指针申明为 const A ,解决了此问题。但不难发现,如果咱们通过一个非 const 对象调用此办法,其返回值也会被转为 const,从而不再能够持续调用任何承受 A this 的成员函数。这显著不是咱们想要的后果:
struct A
{const A &test() const {return *this;}
A &test2() { return *this;}
};
int main()
{A().test().test2(); // Error!
}
怎么解决此问题呢?依据 C ++ 函数重载的规定,咱们能够为 test 成员函数同时定义 const 与非 const 版本:
struct A
{A &test() {return *this;}
const A &test() const { return *this;}
A &test2() { return *this;}
};
int main()
{A().test().test2();
const A a;
a.test();}
对于 A 的非 const 实例而言,test 的非 const 版本是准确匹配,故编译器将抉择此版本,从而返回一个 A &;同时,对于 A 的 const 实例而言,const 版本的 test 是其惟一可用的版本,返回一个 const A &。
至此,问题解决了。咱们基于 const 的有无重载出了两个版本的成员函数,从而使得 const 对象与非 const 对象可能各自调用不同的版本,互不影响。
在理论状况中,咱们定义的两个版本的重载函数除了有无 const 以外往往没有任何区别,此时就能够应用 const_cast 定义第二个重载版本,而无需写两遍截然不同的函数体。
参考以下代码:
struct A
{A &test() {...}
// 通过 const_cast 强行去除 this 的 const 限定后调用非 const 版本
// 返回值通过隐式类型转换再转回 const A &
const A &test() const { return const_cast<A *>(this)->test();}
};
上述代码中,咱们首先定义了一个非 const 版本的 test 成员函数,这个成员函数将提供给 A this 调用;在定义 test 成员函数的 const 版本时,咱们通过 const_cast<A >(this),将此版本的 const A this 指针转换为非 const 版本须要的 A this 类型指针,而后调用了非 const 版本的 test 成员函数,并返回其调用后果,非 const 版本的 test 成员函数的返回值将通过隐式类型转换转为 const A &。
由此可见,通过 const_cast,咱们仅需一行代码就能够实现第二个函数重载版本的定义。
12. dynamic_cast
上文提到,动静类型为继承类的指针或援用能够存储在动态类型为基类的变量中,且不会产生隐式类型转换。对于一个变量而言,尽管其动静类型的确是继承类,但因为编译期与运行期的差异,其也无奈逾越“可应用的成员名称由动态类型决定”这一规定。尽管继承类能够通过虚函数的形式肯定水平上解决此种状况,然而,如果某个成员函数不是基类虚函数,而只存在于继承类中呢?dynamic_cast 为咱们提供了解决方案。
当一个动态类型为基类指针或援用的变量的确寄存了继承类指针或援用时,从基类向继承类的类型转换,即向下类型转换实践上是可行的,dynamic_cast 即用于在运行时实现向下类型转换。须要留神的是,dynamic_cast 的应用必须同时满足以下所有条件:
- 被转换的变量的类型为基类指针或援用,且其的确寄存了一个继承类指针或援用
- 基类具备虚表,即基类必须至多定义了一个虚函数
参考以下代码:
struct A {virtual void test() {}}; // 基类含有虚函数
struct B: A {void test2() {}}; // 继承类特有函数
int main()
{
// 动态类型为基类指针的变量寄存继承类指针
A *b = new B;
// 通过向下类型转换调用继承类特有函数
dynamic_cast<B *>(b)->test2();}
上述代码中,咱们首先定义了具备虚函数的基类 A,而后定义了具备继承类特有函数的类 B。此时,因为 test2 成员函数并未在基类中注册为虚函数,咱们将无奈通过动态类型为 A 的变量 b 调用此函数。但因为咱们能够确定变量 b 的动静类型为 B ,则能够于运行时通过 dynamic_cast 将变量 b 的动态类型转为 B *,而后调用继承类的特有函数 test2。
13. reinterpret_cast
reinterpret,即“从新解释”,顾名思义,这个强制类型转换的作用是提供某个变量在底层数据上的从新解释。当咱们对一个变量应用 reinterpret_cast 后,编译器将忽视任何不合理行为,强行将被转换变量的内存数据重解释为某个新的类型。须要留神的是,reinterpret_cast 要求转换前后的类型所占用内存大小统一,否则将引发编译时谬误。
参考以下代码:
int main()
{reinterpret_cast<int *>(0); // 强行将一个整数的内存数据解释为一个 int *
}
14. 探讨
编程语言的强类型与弱类型相干话题,多年来业界始终探讨不休,有的语言倒退出了高度弱类型的语法体系,而有的语言则绝对谨严,要求用户尽可能多的应用显式类型转换。C++ 作为一门经典的弱类型语言,其类型转换的相干话题天然非常宏大。
纵观 C ++ 的类型转换语法体系,其连续了 C ++ 一贯的无所不包格调,不仅为用户提供了自定义类型转换的极大自由度,也在语法层面为类型转换可能会带来的各种盘根错节的状况作出了谨严的规定。激进看来,如果对 C ++ 的类型转换没有深刻的了解,或不心愿大量应用隐式类型转换时,咱们不应适度的依赖诸如非 explicit 转换构造函数,自定义的类型转换操作符,以及波及隐式类型转换的各种重载确定等语法组分。但作为 C ++ 语法体系的一个重要局部,深刻了解 C ++ 对于类型转换的各种话题,必然是非常重要的。
樱雨楼
2019.8 于苏州