初识C++ · 类型转换

目录

前言:

1 C++中的类型转换

1.1 static_cast

1.2 reinterpret_cast

1.3 const_cast

1.4 dynamic_cast


前言:

C++可以说是恨死了隐式类型转换,你可能会疑问了,为什么?不是单参数隐式类型转换为自定义类型的时候人们还是津津乐道的吗?但是当我们模拟首先插入的某种情况的时候,可能会写这种代码:

int main()
{
	size_t a = 100;
	while (a >= 0)
	{
		//...
		a--;
	}

	return 0;
}

这段代码就是个死循环,因为a最后变成了-1,然后就隐式类型转为为size_t类型,最后一直重复,直到系统崩溃。C++委员会呢,可能是深受其害,所以打算在类型转换上动点东西。

在此之前,我们先看简单看看C语言的类型转换,因为很简单,这里看一段代码咱们就过:

int main()
{
	char ch = 'a';
	int a = ch;

	int* pa = &a;
	int b = (int)pa;

	return 0;
}

C语言里面类型转换分为隐式的类型转换,指类型关联性有点强的,比如int 和 char,可以类型转换,那么指针和int之间的关联性就没那么强了,但是还是有,所以只能强制转换,不能隐式类型转换,这是C语言里面的类型转换,现在我们来看C++里面的类型转换。


1 C++中的类型转换

C语言为了兼容C语言,所以C语言中的类型转换也可以使用,但是呢,C++为了有自己的一套体系,引入了四个关键字,分别表示为不同的类型转换。

1.1 static_cast

static_cast用于非多态变量之间的隐式类型转换,即同C语言的int转为char类型一样,使用隐式类型转换的时候可以加上这个关键字:

int main()
{
	int a = 2;
	double b = 1.1;
	a = static_cast<int>(b);
	cout << a << " " << b << endl;
	return 0;
}

只要是C语言里面能够隐式类型转换的,它都可以使用,同样的,强转什么的就不可以了。

1.2 reinterpret_cast

reinterpret_cast的第一个英文单词的意思是重新解释,也就是这个关键字用于C++里面的强制类型转换,比如指针转为int:

int main()
{
	int* pa = &a;
	int pb = reinterpret_cast<int>(pa);
	printf("%X\n", pb);
	return 0;
}

这里直接cout打印是不行的,因为默认十进制打印,打印出来就是负数了,这里使用static_cast就会报错了,因为条件不符合。

1.3 const_cast

const_cast就很显然已然了,取消对象的const属性,比如非const对象赋值给const对象可以,const对象赋值给非const对象就不可以了。

int main()
{
	int c = 0;
	const int cc = 1;
	c = cc;
	c = const_cast<int>(cc);

	const int d = 0;
	int* pd = const_cast<int*>(&d);

	return 0;
}

这里有一个要注意的是const_cast里面的类型只能是指针或引用,其他内置类型不可以。

那么这里提问,const修饰的对象真的不能被修改吗?

虽然不能直接被修改,但是可以间接的修改:

int main()
{
	const int a = 0;
	int* pa = const_cast<int*>(&a);
	*pa = 1;
	cout << *pa << endl;
	cout << a << endl;
	return 0;
}

这里就可以进行打印了,但是你会发现,打印*pa的时候还是正常的,打印a就不对了,这时候你带有疑惑的打开了监视窗口:

你又疑惑的发现a已经被修改了,于是你又疑惑的打开了汇编代码:

你发现编译器很暴力的给了a一个0,这时候你明白了,这时编译器的行为,但是怎么样才能做到a打印出来也是0呢?

只需要一个关键字:

int main()
{
	volatile const int a = 0;
	int* pa = const_cast<int*>(&a);
	*pa = 1;
	cout << *pa << endl;
	cout << a << endl;

	return 0;
}

 volatile,英文意思是不稳定的,所以即便被const修饰,间接修改就被允许了,编译器看到关键字就直到,哦,你要修改啊!但是即便被volatile修饰了,也不能直接修改a,只能间接的修改。

1.4 dynamic_cast

在了解这个关键字之前,我们先来想一个问题:

既然单参数可以隐式类型转换为自定义类型,那么自定义类型能不能转换为内置类型呢?

class A
{
public:
	A(int a, int b = 0)
		:_a1(a)
		,_b1(b)
	{}
	operator int()
	{
		return _a1 + _b1;
	}

private:
	int _a1 = 2;
	int _b1 = 3;
};
int main()
{
	A a1 = 1;
	int a = a1;
	cout << a1 << endl;

	return 0;
}

这里的语法类型也是有点怪的,operator + 返回值,这样就可以实现自定义类型往内置类型走了,有了内置类型隐式转换为自定义类型,也有自定义类型转为内置类型,那么,有没有自定义类型转为自定义类型呢?

当然可以,比如,构造函数用另一个类型构造即可:

class A
{
public:
	A(int a, int b = 0)
		:_a1(a)
		,_b1(b)
	{}
	operator int()
	{
		return _a1 + _b1;
	}
	int get() const
	{
		return _a1 + _b1;
	}
private:
	int _a1 = 2;
	int _b1 = 3;
};
class B
{
public:
	B(const A& aa)
		:_b2(aa.get())
	{}
private:
	int _b2;
};
int main()
{
	A a1 = 1;
	B bb = a1;
	return 0;
}

现在进入整体,什么是dyniamic_cast,这个关键字是多态对象特有的,并且要保证虚函数已经重写,一般应用的情况是父子类关系指针之间的转换

一般派生类指针赋值给基类指针是没有问题的,但是基类指针赋值给派生类指针就会有问题了,比如:

class A
{
public:
	virtual void Func()
	{
		cout << "1" << endl;
	}

private:
	int _a;
};
class B : public A
{
public:
	virtual void Func()
	{
		cout << "2" << endl;
	}
private:
	int _b;
};

void Fun(A* aa)
{
	B* pb = (B*)aa;
	pb->Func();
}

父子类之间赋值分为两种,一种是子给父,这时没问题的,一种是父给子,这个是有问题的,比如我们现在的代码里面,调用是没有问题的,但是一旦涉及访问私有的成员变量就会出错了:


void Fun(A* aa)
{
	B* pb = (B*)aa;
	pb->Func();
	pb->_b++;
}

解决方案就是使用这个关键字,一旦发现aa指向的对象是子类,就会返回空:

void Func(A* aa)
{
	B* pb = dynamic_cast<B*>(aa);
	if (pb)
	{
		pb->Func();
		pb->_b++;
	}
	else
	{
		cout << "失败" << endl;
	}
}

简单了解一下RTTI:

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

C++通过以下方式来支持RTTI: 1. typeid运算符 2. dynamic_cast运算符 3. decltype


感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值