c++ - 类型转换

文章目录


一、内置类型与内置类型

1、隐式类型转换:一般是类型与类型之间关联性大的才能进行转换,如:整形与整形、整形与浮点型。

2、显示类型转换:当有关联但是不大时,不能进行隐式类型转换时,就需要进行显示类型转换了,如:指针与整形,不同类型的指针之间。

3、演示:

cpp 复制代码
//隐式类型转化:整形与整形 整形与浮点型
//显示类型转化:指针与整形 不同指针之间指针
void test01()
{
	int a = 10;
	double b = 1.1;
	char c = 'a';
	
	
	//整形与整形
	a = c;

	//整形与浮点型
	a = b;

	int* p2 = &a;
	//int d = p;	err
	int d = (int)p1;

	//指针与指针
	//char* cp = p; err
	char* cp = (char*)p1;
}

二、内置类型与自定义类型

1、内置类型转换为自定类型:这个能不能转换是通过自定义类型的构造函数决定的。

2、自定义类型转换为内置类型:需要自定义类型进行重载,(不需要写返回类型)operator type(转化的内置类型) () 。

3、演示:

cpp 复制代码
class Base1
{
public:
	Base1(int a = 10,int b = 10):_a(a),_b(b)
	{}

	//用与转化的函数
	operator int()
	{
		return _a + _b;;
	}

	void Printf()
	{
		cout << "_a:" << _a << "_b:" << _b << endl;
	}

private:
	int _a = 10;
	int _b = 10;

};

void test02()
{
	//内置类型转化为自定义类型
	Base1 b = { 1,1 };
	//Base1 b = Base1(1,1);
	b.Printf();
	
	//自定义类型转化为内置类型
	int a = Base1();
	//int a = b.operator int();
	cout << a << endl;

}

三、内置类型与内置类型

1、自定义类型与自定义类型之间转化:看相互的构造函数,如:普通迭代器转换为const迭代器。

2、演示:

cpp 复制代码
//声明Base3
class Base3;

class Base2
{
	// Base3作为Base2的友元
	friend class Base3;
public:
	Base2(int a = 1, int b = 1) :_a(a), _b(b)
	{}
	void Printf()
	{
		cout << "_a:" << _a << "_b:" << _b << endl;
	}

private:
	int _a ;
	int _b ;

};

class Base3
{
public:
	
	Base3(int c = 2, int d = 2) :_c(c), _d(d)
	{}
	//实现用Base2构造Base3的构造函数
	Base3(const Base2& b2) :_c(b2._a), _d(b2._b)
	{}

	void Printf()
	{
		cout << "_c:" << _c << "_d:" << _d << endl;
	}

private:
	int _c ;
	int _d ;

};

void test03()
{

	//使用Base2转化为Base3
	Base3 b3 = Base2();
	//Base3 b3(Base2());	Base2用匿名对象构造Base3

	b3.Printf();
}

四、c++标准中的4种类型类型转换

1、c++为什么要有这4种转换

C风格的转换格式很简单(上面的一、二、三都是c语言的风格),但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰
  3. 因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

2、 static_cast

(1)介绍

static_cast 是 C++中用于基本数据类型之间转换以及具有明确转换路径的类之间的转换的运算符。它主要用于在编译时检查转换的安全性,如果转换不合法,编译器将报错。static_cast 相比于 C 风格的类型转换(如 (type)value)提供了更好的类型检查。

(2)语法格式

cpp 复制代码
T a = static_cast<T(需要转换到的类型)> (b)	//由b类型转到a类型

(3)使用

cpp 复制代码
//基本数据类型之间的转换
double pi = 3.14159;  
int ip = static_cast<int>(pi); // 将 double 类型的 pi 转换为 int 类型的 ip,并截断小数部分

// 类的向上转换(派生类到基类)
class Base {};  
class Derived : public Base {};  
Derived d;  
Base* b = static_cast<Base*>(&d); // 将 Derived 类型的指针转换为 Base 类型的指针

//类的向下转换(基类到派生类) ,注意:这种类型转换是不安全的
Base* b = new Derived();  
Derived* d = static_cast<Derived*>(b); // 将 Base 类型的指针转换为 Derived 类型的指针

3、reinterpret_cast

(1)介绍

reinterpret_cast 是 C++中的一个类型转换运算符,它用于进行各种不安全的类型转换,包括指针类型之间的转换、指针与足够大的整数类型之间的转换,以及函数指针之间的转换等。reinterpret_cast 几乎可以转换任何类型的指针(或引用)为任何其他类型的指针(或引用),但它不保证转换后的指针(或引用)的安全性或有效性。

(2)语法

static_cast语法格式一样。

(3)使用

cpp 复制代码
//指针类型之间的转换
int* ptr1 = new int(10);  
char* ptr2 = reinterpret_cast<char*>(ptr1); // 将 int* 转换为 char*

//指针与整数类型之间的转换
int* ptr = new int(42);  
char* addr = reinterpret_cast<uintptr_t>(ptr); // 将 int* 转换为 char* 

4、 const_cast

(1)介绍

const_cast 是 C++ 中的一个类型转换运算符,用于修改类型的 const(常用) 或 volatile (不常用)属性。具体来说,const_cast可以用来移除一个对象的常量性(const-ness)或易变性(volatileness),但不能用于创建常量性或易变性。

(2)什么是易变性?

1、 在C/C++编程语言中,易变性(volatileness)是通过volatile关键字来实现的。volatile关键字是一种类型修饰符,用于声明一个变量是"易变的",即这个变量的值可能会在意料之外被改变,通常是由于外部因素(如操作系统、硬件或其他线程)的作用。

2、易变性(Volatileness)的含义

(1)变量值的不可预测性:当一个变量被声明为volatile时,编译器会意识到这个变量的值可能会在任何时候被外部因素改变,因此它不会对这个变量的访问进行优化。这意味着,每次访问这个变量时,编译器都会直接从其内存地址中读取值,而不是使用可能已经缓存在寄存器中的旧值。

(2)防止编译器优化:volatile关键字还告诉编译器不要对这个变量进行任何形式的优化,包括但不限于删除看似无用的读写操作、重排序指令等。这保证了程序中的指令会按照编写的顺序执行,尽管这并不意味着它会阻止硬件级别的指令重排序。

(3)多线程和并发编程:在多线程或并发编程中,volatile关键字可以用来标记那些可能会被其他线程修改的共享变量。然而,需要注意的是,volatile并不等同于同步机制(如互斥锁),它不能保证操作的原子性或内存可见性。因此,在需要确保数据一致性和线程安全的场合,应该使用更强大的同步机制。

(3)语法格式

static_cast语法格式一样。

(4)使用

移除常量性(常用)

当你有一个指向常量对象的指针或引用,但你需要通过该指针或引用来修改对象时,可以使用 const_cast来移除常量性。但是,必须确保你实际上有权限修改该对象,否则行为是未定义的。

cpp 复制代码
const int a = 10;  
int* ptr = const_cast<int*>(&a); // 移除 a 的常量性  
*ptr = 20; // 未定义行为,因为 a 实际上是常量

注意:上面的例子展示了 const_cast 的用法,但尝试修改 a 的值是未定义行为,因为 a 是在栈上声明的常量,下面使用才是合理的:

cpp 复制代码
const int* ptrToConst = &someNonConstInt; // someNonConstInt 是 int 类型的非 const 对象  
int* ptrToNonConst = const_cast<int*>(ptrToConst);  
*ptrToNonConst = 42; // 现在可以安全地修改对象

5、 dynamic_cast

(1)介绍

dynamic_cast 是 C++中的一种类型转换运算符,主要用于在类的层次结构中进行安全的向下转型(即,将基类指针或引用转换为派生类指针或引用)。与 static_cast 相比,dynamic_cast 在运行时检查转换的有效性,如果转换不安全或不合法,则转换失败。

(2)语法格式

static_cast语法格式一样。

(3)使用

向下转型

当使用 dynamic_cast 将基类指针或引用转换为派生类指针时,如果转换成功,则返回指向派生类对象的指针;如果转换失败(即,基类指针实际上不指向派生类对象),则返回 nullptr(对于指针类型)或抛出 std::bad_cast 异常(对于引用类型,但通常不推荐使用引用进行dynamic_cast,因为它会导致程序异常终止)。

cpp 复制代码
class Base {  
public:  
    virtual ~Base() {} // 需要虚析构函数  
};  
  
class Derived : public Base {  
};  
  
Base* basePtr = new Derived();  
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 转换成功  
if (derivedPtr != nullptr) {  
    // ... 使用 derivedPtr  
}  
  
delete basePtr;

向上转型

虽然 dynamic_cast 主要用于向下转型,但它也可以用于向上转型(即,将派生类指针或引用转换为基类指针或引用)。然而,在这种情况下,dynamic_cast实际上与 static_cast 的行为相同,并且不会进行运行时检查。因此,对于向上转型,通常推荐使用 static_cast

cpp 复制代码
class Base {  
public:  
    virtual ~Base() {} // 需要虚析构函数  
};  
  
class Derived : public Base {  
};  
  
Derived* derivedPtr = new Derived();  
Base* basePtr = dynamic_cast<Base*>(derivedPtr); // 转换成功  

delete derivedPtr;

(4)注意:

1、虚函数:为了使用 dynamic_cast 进行向下转型,基类必须至少有一个虚函数(通常是虚析构函数)。这是因为 dynamic_cast 依赖于运行时类型信息(RTTI),而 RTTI 是通过虚函数表(vtable)实现的。

2、 性能:由于 dynamic_cast 需要在运行时进行类型检查,因此它比 static_cast 有更高的性能开销。因此,在不需要运行时类型检查的情况下,应优先使用 static_cast。

3、引用和指针:当使用引用进行 dynamic_cast 时,如果转换失败,将抛出 std::bad_cast

异常。然而,由于异常处理通常比返回 nullptr并检查它更昂贵,并且异常处理机制可能不适用于所有情况(例如,在构造函数或析构函数中),因此通常建议对指针使用 dynamic_cast而不是引用。

4、 多重继承:在多重继承的情况下,dynamic_cast 的行为可能会变得更加复杂,因为需要指定要转换到的派生类的确切路径。

6、RTTI

1、概念: RTTI(Run-Time Type Information,运行时类型信息)是 C++ 中的一个特性,它允许程序在运行时查询对象的类型信息。这种能力是通过 C++ 的类型系统提供的,特别是通过虚函数和虚函数表(vtable)来实现的。RTTI 主要通过 typeid 操作符和 dynamic_cast 运算符来体现。

2、typeid 操作符

(1)typeid 是 C++ 中的一个操作符,用于在运行时获取一个表达式的类型信息。它返回一个对std::type_info 对象的引用,该对象包含了类型的信息。std::type_info 类定义在 < typeinfo > 头文件中。

(2)对于基本数据类型(如 int、float 等),typeid 在编译时就能确定类型。

(3) 对于指针和引用,typeid返回的是指针或引用所指向的对象的实际类型(如果是指向多态类型的指针或引用)。

(4)如果表达式是多态类型的对象(即包含至少一个虚函数的类),则 typeid 会在运行时检查对象的实际类型。 注意,typeid 不能用于非多态类型的指针或引用,因为这种情况下它无法确定对象的实际类型(除非进行了显式的类型转换)。

3、dynamic_cast

(1)概念:dynamic_cast 是 C++中的一个类型转换运算符,它主要用于在类的层次结构中进行安全的向下转型(即将基类指针或引用转换为派生类指针或引用)。与 static_cast

不同,dynamic_cast 会在运行时检查转换的有效性。

(2)如果转换成功,dynamic_cast 将返回指向派生类对象的指针或引用。

(3)如果转换失败(即,基类指针实际上不指向派生类对象),则对于指针类型,dynamic_cast 返回 nullptr;对于引用类型,它抛出 std::bad_cast 异常(但通常不推荐使用引用进行 dynamic_cast,因为它会导致程序异常终止)。

(4) dynamic_cast 需要基类至少有一个虚函数(通常是虚析构函数),以便能够利用运行时类型信息(RTTI)来检查转换的安全性。

4、注意事项

(1)RTTI 增加了程序的复杂性和运行时开销,因为它需要在运行时维护类型信息。 在性能敏感的应用程序中,应谨慎使用

(2)RTTI,并考虑是否有其他替代方案。 在某些情况下,可以通过设计模式(如访问者模式、工厂模式等)来避免对 RTTI 的依赖。 禁用

(3)RTTI:在某些编译器中,可以通过编译器选项来禁用 RTTI(例如,GCC 的 -fno-rtti 选项)。禁用 RTTI

(4)可以减少程序的大小和运行时开销,但也会限制 typeid 和 dynamic_cast 的使用。

相关推荐
JSU_曾是此间年少3 分钟前
数据结构——线性表与链表
数据结构·c++·算法
sjsjs1110 分钟前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode
许野平16 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨19 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
狂奔solar27 分钟前
yelp数据集上识别潜在的热门商家
开发语言·python
密码小丑44 分钟前
11月4日(内网横向移动(一))
笔记
朱一头zcy1 小时前
C语言复习第9章 字符串/字符/内存函数
c语言
此生只爱蛋1 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧1 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++