在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败.
显式类型转化:需要用户自己处理.
int main()
{
//隐式类型转换
double d = 1.1;
int i = d;
cout << d<< endl; //1.1
cout << i << endl; //1
//显式类型转换
int* p = &i;
int address = (int)p;
cout << p << endl;
cout << address << endl;
return 0;
}
常规中c语言隐式类型转留下了一些出乎意料的坑.
例如:在字符串的插入函数中,pos为0.
void Insert(size_t pos, char ch)
{
size_t size = 5;
size_t end = size - 1;
while (end >= pos)
{
//_tr[end + 1] = _str[end];
--end;
}
}
C风格的转换格式很简单,但是有不少缺点的:
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
return 0;
}
reinterpret_cast一般用于两个不相关的类型转换.
int main()
{
int a = 10;
int* p = &a;
int address = reinterpret_cast<int>(p);
cout << address << endl;
return 0;
}
const_cast最常用的用途就是删除变量的const属性,方便赋值.
int main()
{
const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl; //2
cout << *p << endl; //3
return 0;
}
注意:
1.以上代码使用const_cast实际上是删除了变量a地址的const属性,代表着我们可以利用地址p来改变变a.
2.理论上而言,a和p的值修改后打印出来是一样的.但是,由于编译器会将const修饰的变量a加载到寄存器中,打印的时候编译器便会直接从寄存器读.所以,读取a时便还是以前的值,而p中说是从内存中获取的,具有时效性,此时便为3了.
3.为了解决这一问题,我们可以使用在长变量前加上volatile关键字,表明让编译器不要从寄存器中取长变量,而应该从内存中获取.
4.static_cast类似于c语言的隐式类型转换,reinterpret_cast,const_cast类似于c语言的强制类型转换.
dynamic_cast用于将一个父类对象的指针/引用,转换为子类对象的指针或引用(动态转换).
为什么C++要支持向下转型?
因为在Func函数中,形参为父类指针接收,可是,我们并不知道实参传的是指向父类的指针,还是指向子类的指针.
class A
{
public:
virtual void f() {};
int _a = 1;
};
class B : public A
{
public:
int _b = 3;
};
void Func( A* pa )
{
B* pb = dynamic_cast <B*>(pa);
pa->_a++;
pb->_b++;
}
int main()
{
A aa;
B bb;
Func(&aa);
Func(&bb);
}
为了访问更加安全,C++便提出了向下转型:
所以,在C++中,面对上述问题,我们可以采用dynamic_cast来强制类型转换,再通过返回值来进行判断访问范围,防止发生越界访问.
class A
{
public:
virtual void f() {};
int _a = 1;
};
class B : public A
{
public:
int _b = 3;
};
void Func( A* pa )
{
B* pb = dynamic_cast<B*>(pa);
if (pb) //pb指向子类.
{
cout << "转换成功" << endl;
pb->_b++;
pb->_a++;
cout << pb->_a << ":" << pb->_a << endl;
}
else //pb指向父类.
{
cout << "转换失败" << endl;
pa->_a++;
cout << pa->_a << endl;
}
}
int main()
{
A aa;
B bb;
Func(&aa);
Func(&bb);
}
延申问题
对于菱形继承来说,切片会产生偏移量,原本父类指针ptr1,ptr2应该都指子类bb,但是经过切片后,ptr1指向子类中的A1,ptr2指向子类中的A2.
无论采用c语言的强制转换,还是C++中的dynamic_cast强制转换.
class A1
{
public:
virtual void f() {};
int _a1 = 1;
};
class A2
{
public:
virtual void f() {};
int _a2= 1;
};
class B : public A1,public A2
{
public:
int _b = 3;
};
int main()
{
B bb;
A1* ptr1 = &bb;
A2* ptr2 = &bb;
cout << ptr1 << endl;
cout << ptr2 << endl;
B* pb1 = (B*)ptr1;
B* pb2 = (B*)ptr2;
cout << pb1 << endl;
cout << pb2 << endl;
cout << "-----------------------------" << endl;
B* pb3 = dynamic_cast<B*>(ptr1);
B* pb4 = dynamic_cast<B*>(ptr1);
cout << pb3 << endl;
cout << pb4 << endl;
return 0;
}