文章目录
0、前言
在C语言中,会遇到不同类型数据一起运算的情况,在C语言中有自动类型转换和强制数据类型转换,在类型转换中会有一些精度丢失。但是随着C++的出现,C中的两种类型转换已经不能满足要求了,以下重点介绍一些C++的4种类型转换。
1、C语言数据类型转换
1.1、自动类型转换
(1)算术表达式中的自动类型转换
在实际运算中,整型(int,short,long,char)和浮点型(float,double)是可以混合运算的,例如下面这段代码就是合法的。
10+'a'+1.5-8765.1234*'b'
只是在算术表达式进行运算的过程中,不同类型的数据要自动转换成同一类型,然后进行运算。
算术表达式中自动类型转换的具体规则:
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
横向向左的箭头表示必定的转换,如char,short型数据在运算时必定先自动类型转换为int类型(char,short自动类型转换为int也称为整型提升),float型数据在运算时一律先自动类型转换为double类型以提高运算精度。
纵向的箭头表示当运算对象为不同类型时转换的方向,类型转换方向由低到高,如某类型数据与double型数据进行运算时,该类型数据自动类型转换为double类型,然后在两个double型数据间进行运算,结果为double型。如果表达式中,类型转换的最高级别是long,那么其它类型的数据就会自动类型转换为long类型。以此类推…
注:
- 不要错误地将该图理解为int型先转换成unsigned int型,再转换成long型,再转成double型
- 在算术表达式中进行自动类型转换的时候,原数据类型并无发生变化,实际上是得到一个自动类型转换后的中间变量,表达式运算的过程是对转换后的中间变量进行运算,运算结果为转换后的数据类型
(2)赋值运算中的自动类型转换
C语言规定:如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时会进行自动类型转换。
赋值运算中自动类型转换的规则:
- 将浮点型数据赋值给整型变量时,舍弃浮点数的小数部分。如这段代码:int i = 8.24;i的值为8,在内存中以整数形式存储
- 将整型赋值给浮点型变量时,数值不变,但以浮点数的形式存储到变量中,如float f =
35;,先将35转换成35.00000,再存储到f中。double d =
35;,则将35转换成35.00000000000000,然后以双精度浮点数形式存储到d中。 - 将一个double型数据赋值给float类型变量时,截取前面7位有效数字,存放到float变量的存储单元(32位)中,但是应注意数值范围不能溢出
- 将一个float类型数据赋值给double类型变量时,数值不变,有效位数扩展到16位,在内存中以64位存储。
- 将字符型数据赋值给其它整型变量时,由于字符只有1个字节,而其它整型变量至少为2个字节,因此将字符数据放到整型变量的低八位中,将其他整形变量赋值给char型变量时,只将其低8位原封不动地送到char型变量(高位截断)。
1.2、强制类型转换
自动类型转换常被称为隐式类型转换,强制类型转换常被称为显示类型转换,强制类型转换的一般形式为(类型标识符)(操作数)或(类型标识符)(表达式)。其功能就是把操作数或表达式结果的数据类型暂时地强制转换为圆括号()中的类型。
类型标识符两边的圆括号就是C语言中的强制类型转换符。
在进行强制类型转换时,原来变量的类型未发生变化,我们所得到的是一个所转类型的中间变量。 如:
int main()
{
float x;
int i;
x = 3.6;
i = (int)x;
printf("x = %f,i = %d", x, i);
return 0;
}
在进行强制类型转换后得到的是一个int型的中间变量,它的值等于x的整数部分,而x的类型不变,仍为float型,x的值也不变。
输出结果如下:
2、C++数据类型转化
C风格的转换格式虽然很简单,但也有很多缺点:
- 隐式类型转换在某些情况下可能会出问题,比如数据精度丢失。
- 显式类型转换将所有情况混合在一起,转换的可视性比较差。
1、static_cast
static_cast 是 C++ 的一种类型转换运算符,它用于执行一些基本的、非安全的类型转换。static_cast 可以进行一些基本的类型转换,如整数和浮点数的相互转换、指针转换、空指针转换、任何类型的转换(除了类层次间的转换,这是dynamic_cast的特权)、以及无操作(任何类型到void类型,以及void类型到void类型)等。
使用 static_cast 的情况通常包括:
- 基础数据类型之间的转换(如 int 到 double,double 到 int 等)。
- 空指针和空指针之间的转换。
- 同一个继承层次内不同类型的对象之间的转换。
- 同一个继承层次内不同类型的指针之间的转换。
- 同一类型的不同指针之间的转换。
- 同一类型的不同引用之间的转换。
- 同一类型的不同常量引用之间的转换。
- 同一类型的不同非常量引用之间的转换。
- 同一类型的不同非常量引用和常量引用之间的转换。
- 同一类型的不同非常量引用和非常量引用之间的转换。
- 任何类型的转换到 void 和 void 的转换。
语法:
static_cast< new-type expression>(...)
返回类型为“new-type”的值。
使用示例:
#include <iostream>
#include <vector>
struct B
{
int m = 42;
const char* hello() const
{
return "Hello world, this is B!\n";
}
};
struct D : B
{
const char* hello() const
{
return "Hello world, this is D!\n";
}
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
// 1. static downcast
D d;
B& br = d; // upcast via implicit conversion
std::cout << "1) " << br.hello();
D& another_d = static_cast<D&>(br); // downcast
std::cout << "1) " << another_d.hello();
// 2. lvalue to xvalue
std::vector<int> v0{1,2,3};
std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
std::cout << "2) after move, v0.size() = " << v0.size() << '\n';
// 3. initializing conversion
int n = static_cast<int>(3.14);
std::cout << "3) n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "3) v.size() = " << v.size() << '\n';
// 4. discarded-value expression
static_cast<void>(v2.size());
// 5. inverse of implicit conversion
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "5) *ni = " << *ni << '\n';
// 6. array-to-pointer followed by upcast
D a[10];
[[maybe_unused]]
B* dp = static_cast<B*>(a);
// 7. scoped enum to int
E e = E::TWO;
int two = static_cast<int>(e);
std::cout << "7) " << two << '\n';
// 8. int to enum, enum to another enum
E e2 = static_cast<E>(two);
[[maybe_unused]]
EU eu = static_cast<EU>(e2);
// 9. pointer to member upcast
int D::*pm = &D::m;
std::cout << "9) " << br.*static_cast<int B::*>(pm) << '\n';
// 10. void* to any type
void* voidp = &e;
//[[maybe_unused]]
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}
可能的输出:
1) Hello world, this is B!
1) Hello world, this is D!
2) after move, v0.size() = 0
3) n = 3
3) v.size() = 10
5) *ni = 3
7) 2
9) 42
2、const_cast
const_cast 是 C++ 中的一种类型转换运算符,用于从 const 类型去除 const 属性。const_cast 可以用于修改 const 对象的值,也可以将非 const 对象的 const 属性去掉,从而可以修改其值。
使用 const_cast 的情况通常包括:
- 去除 const 修饰符,以便可以对 const 对象进行修改。
- 强制转换指针类型,即使指针所指向的数据是 const 类型。
语法:
const_cast< new-type expression>(...)
返回new-type类型的值。
使用示例:
#include <iostream>
struct type
{
int i;
type(): i(3) {}
void f(int v) const
{
// this->i = v; // compile error: this is a pointer to const
const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
}
};
int main()
{
int i = 3; // i is not declared const
const int& rci = i;
const_cast<int&>(rci) = 4; // OK: modifies i
std::cout << "i = " << i << '\n';
type t; // if this was const type t, then t.f(4) would be undefined behavior
t.f(4);
std::cout << "type::i = " << t.i << '\n';
const int j = 3; // j is declared const
[[maybe_unused]]
int* pj = const_cast<int*>(&j);
// *pj = 4; // undefined behavior
[[maybe_unused]]
void (type::* pmf)(int) const = &type::f; // pointer to member function
// const_cast<void(type::*)(int)>(pmf); // compile error: const_cast does
// not work on function pointers
}
可能的输出:
i = 4
type::i = 4
3、dynamic_cast
dynamic_cast 是 C++ 中的一种类型转换运算符,用于在类层次结构中执行安全的向下转换。它主要用于将基类指针或引用转换为派生类指针或引用,以确保转换的有效性。dynamic_cast 在运行时检查转换是否成功,如果转换失败,则返回空指针或抛出 std::bad_cast 异常。
使用 dynamic_cast 的情况通常包括:
- 在类层次结构中执行安全的向下转换,将基类指针或引用转换为派生类指针或引用。
- 检查一个对象是否是指定类型的实例。
语法:
沿继承层次结构安全地向上、向下和横向转换对类的指针和引用。
dynamic_cast< target-type expression>(...)
参数:
- target-type:指向完整类类型的指针、对完整类类型的引用或指向(可选符合 CV 条件)void 的指针
- expression:lvalue(直到 C++11)glvalue (自 C++11) 如果目标类型是引用,则指向完整类类型的指针的PR值(如果目标类型是指针)。
如果强制转换成功,dynamic_cast返回目标类型的值。如果强制转换失败并且目标类型是指针类型,则返回该类型的空指针。如果强制转换失败并且目标类型是引用类型,则会引发与 std::bad_cast 类型的处理程序匹配的异常。
使用示例:
#include <iostream>
struct V
{
virtual void f() {} // must be polymorphic to use runtime-checked dynamic_cast
};
struct A : virtual V {};
struct B : virtual V
{
B(V* v, A* a)
{
// casts during construction (see the call in the constructor of D below)
dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
}
};
struct D : A, B
{
D() : B(static_cast<A*>(this), this) {}
};
struct Base
{
virtual ~Base() {}
};
struct Derived: Base
{
virtual void name() {}
};
int main()
{
D d; // the most derived object
A& a = d; // upcast, dynamic_cast may be used, but unnecessary
[[maybe_unused]]
D& new_d = dynamic_cast<D&>(a); // downcast
[[maybe_unused]]
B& new_b = dynamic_cast<B&>(a); // sidecast
Base* b1 = new Base;
if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
{
std::cout << "downcast from b1 to d successful\n";
d->name(); // safe to call
}
Base* b2 = new Derived;
if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
{
std::cout << "downcast from b2 to d successful\n";
d->name(); // safe to call
}
delete b1;
delete b2;
}
输出:
downcast from b2 to d successful
4、reinterpret_cast
reinterpret_cast 是 C++ 中一种更为底层的类型转换运算符,它提供了在指针之间进行转换的能力,并能够处理任何类型的指针,包括函数指针、数据指针、对象指针等。reinterpret_cast 可以将指针转换为任何其他类型的指针,也可以将任何整数类型转换为任何类型的指针。
使用 reinterpret_cast 的情况通常包括:
- 在不同类型指针之间进行转换。
- 将指针与整数之间进行转换。
- 对指针进行算术运算。
然而,需要注意的是,使用 reinterpret_cast 应当非常谨慎,因为它可能导致不安全的代码。不正确的使用可能会导致各种问题,包括空指针引用、内存访问错误等。
语法:
reinterpret_cast< new-type expression>(...)
返回new-type类型的值。
使用示例:
#include <cstdint>
#include <cassert>
#include <iostream>
int f() { return 42; }
int main()
{
int i = 7;
// pointer to integer and back
std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error
std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
int* p1 = reinterpret_cast<int*>(v1);
assert(p1 == &i);
// pointer to function to another and back
void(*fp1)() = reinterpret_cast<void(*)()>(f);
// fp1(); undefined behavior
int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
std::cout << std::dec << fp2() << '\n'; // safe
// type aliasing through pointer
char* p2 = reinterpret_cast<char*>(&i);
std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
: "This system is big-endian\n");
// type aliasing through reference
reinterpret_cast<unsigned int&>(i) = 42;
std::cout << i << '\n';
[[maybe_unused]] const int &const_iref = i;
// int &iref = reinterpret_cast<int&>(
// const_iref); // compiler error - can't get rid of const
// Must use const_cast instead: int &iref = const_cast<int&>(const_iref);
}
可能的输出
The value of &i is 0x7fff352c3580
42
This system is little-endian
42
3、面试相关
1、C++中的4种类型转换分别是:____ 、____ 、____ 、____。
分别是static_cast、reinterpret_cast、const_cast和dynamic_cast。
2、说说4种类型转换的应用场景。
- static_cast用于相近类型的类型之间的转换,编译器隐式执行的任何类型转换都可用static_cast。
- reinterpret_cast用于两个不相关类型之间的转换。
- const_cast用于删除变量的const属性,方便赋值。
- dynamic_cast用于安全的将父类的指针(或引用)转换成子类的指针(或引用)。
4、参考文章
4.1 C/C++数据类型转换详解
4.2 C++的类型转换
4.3 C++中常用的四种类型转换方式