C语言的类型转换

类型相近的才能发生隐式类型转换,如int和double,如果不相关,而对于指针和整型,指针是地址,整型和指针类型之间不会进行隐式类型转换,只能显式的强制类型转换:

int main()
{
	int i = 1;
	//隐式类型转换
	double d = i;
	printf("%d,%.2f", i, d);

	int* p = &i;
	//显式强制类型转换
	int address = (int)p;
	printf("%x,%d\n", p, address);
	return 0;
}

C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

C++引入四种类型装换操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast


C++强制类型转换

  • static_cast

static_cast用于相近类型之间的转换,(这些类型的表示意义差不多)编译器隐式执行任何类型转换都可以使用static_cast,对于两个不相关类型之间的转换,不能使用static_cast:

int main()
{
	int i = 1;
	//隐式类型转换
	double d = static_cast<double>(i);
	printf("%d,%.2f", i, d);

	int* p = &i;
	//int address = static_cast<int>(p);//错误写法
	//printf("%x,%d\n", p, address);
	return 0;
}
  • reinterpret_cast

reinterpret_cast用于不相关的类型之间的转换:

int main()
{
    int  i = 10;
	int* p = &i;
	int address = reinterpret_cast<int>(p);
	printf("%x,%d\n", p, address);
	return 0;
}
  • const_cast

const_cast用于删除变量的const属性,转化后就可以对const变量进行修改了:比如下面的a不可以被修改,现在通过const_cast转化成int*

int main()
{
	const int a = 2;
	int* p = const_cast<int*>(&a);
	*p = 4;
	cout << a << endl;//2
	cout << *p << endl;//4
	return 0;
}

【c++】类型转换-LMLPHP

代码中使用const_cast删除变量a地址的const属性,这时候就可以通过使用指针来修改a的值了。

但是编译器会认为const修饰的变量不会被修改,所以将const修饰的变量存放在寄存器中,当需要读取const变量时会直接从寄存器中读取,而我们修改的实际上是内存中a的值,所以最终打印出来a的值是没有修改之前的。

如果不想让编译器将const变量优化到寄存器中,使用volatile关键字对const变量进行修饰即可。这时候读取const变量编译器就从内存中进行读取,保持内存的可见性

【c++】类型转换-LMLPHP

  • dynamic_cast

dynamic_cast是用于将父类的指针(引用)转换成子类的指针(引用)

向上转型:子类对象指针/引用——》父类指针/引用(不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用——》子类指针/引用(用dynamic_cast转型是安全的)

注意:

向上转型就是我们多态说的切割/切片,是语法天然支持,不需要进行转换,而向下转型语法是不支持的,需要进行强制类型转换

向下转型安全问题

使用C强制类型转换向下转型是不安全的,因为此时无论父类的指针(或引用)指向的是父类对象还是子类对象都会进行转换。

使用dynamic_cast向下转型是安全的,如果父类的指针(或引用)指向的是子类对象那么dynamic_cast会转换成功,但如果父类的指针(或引用)指向的是父类对象那么dynamic_cast会转换失败并返回一个空指针。比如:

class A
{
public:
	virtual void f(){}

	int _a = 0;
};

class B :public A
{
public:
	int _b = 0;
};
void Func(A* ptr)
{
	//直接转换是不安全的
   // B* bptre = (B*)ptr
	B* bptr = dynamic_cast<B*>(ptr);
	cout << bptr << endl;
	if (bptr)
	{
		bptr->_a++;
		bptr->_b++;
		cout << bptr->_a << endl;
		cout << bptr->_b << endl;
	}
}
int main()
{
	A aa;
	B bb;
	Func(&aa);
	Func(&bb);
	return 0;
}

如果ptr指向父类,则转换失败,返回空,如果ptr指向子类,则转换成功

如果传入Func函数的是子类对象的地址,那么转化后的bptre与bptr都会有地址,如果传入Func函数的是父类对象的地址,那么转换后的ptre也有地址,而bptr是一个空指针。

  • explicit

explicit用于修饰构造函数,用于禁止单参数构造函数的隐式转换

class A
{
public:
	explicit A(int a)
	{
		cout << "explicit A(int a)" << endl;
	}
	A(const A& A)
	{
		cout << "A(const A& a)" << endl;
	}
private:
	int _a;
};

int main()
{
	A a(10);
	//A a2 = 11;错误的写法
	return 0;
}

A a2 = 11等价于先构造A tmp(11);在拷贝A a2(tmp);

在早期编译器中,遇到A a2=11会先构造临时对象,在用临时对象拷贝构造a2;但是现在的编译器做了优化,遇到A a2=11会直接按照A a2(11)进行处理,这是隐式转换。对于单参自定义类型,A a2=11这种方式可读性不好,所以explicit修饰单参构造函数,进制单参构造函数的隐式转换。


RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。


总结

1、C++中的4种类型转换分别是:static_cast,reinterpret_cast、const_cast、dynamic_cast

2、4种类型转换的应用场景:

04-03 10:18