C语言强制类型转换是有一定风险的,有的转换并不一定安全,如
总结:C语言强制类型转换缺点;主要是为了克服C语言强制类型转换的以下三个缺点。
例如,将int 强制转换成 double是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分
举例1. 把整型数值转换成指针,编译阶段不报错,运行阶段报错
C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换
语法:(目标类型)表达式或目标类型(表达式);
用途:基本等价于隐式转换的一种类型转换运算符,可使用于需要明确隐式转换的地方。
可以用于低风险的转换
不可以用与风险较高的转换
#include
using namespace std;
class CInt
{
public:
operator int()
{
this->m_Int = 128;
return m_Int;
}
int m_Int;
};
int main(int argc, char* argv[])
{
int i = 3;
float f = 10.0f;
f = i; //可以隐式转换,会出现警告int”转换到“float”,可能丢失数据
long m = i; // 绝对安全,可以隐式转换,不会出现警告。
double dd = 1.23;
long m1 = dd; // 可以隐式转换,会出现可能丢失数据的警告。
long m2 = (long)dd; // C风格:显式转换,不会出现警告。
long m3 = static_cast<long>(dd); // C++风格:显式转换,不会出现警告。
cout << "m1=" << m1 << ",m2=" << m2 << ",m3=" << m3 << endl;
//低风险的转换:整型与浮点型;字符型与整型;void *指针转换为任意类型指针
//字符型与整型
char ch='a';
int n = 5;
n = static_cast<int>(ch);
//void *指针转换为任意类型指针
void *p = nullptr;
int *p1 = static_cast<int *>(p);
//转换运算符,类与其他类型
CInt Obj;
//int k=Obj ;//可以隐式转换
int k = static_cast<int>(Obj);
cout << "k=" << k << endl;
}
C风格可以把不同类型的指针进行转换。
C++不可以,需要借助void *。
#include
using namespace std;
class CInt
{
public:
operator int()
{
this->m_Int = 128;
return m_Int;
}
int m_Int;
};
int main(int argc, char* argv[])
{
int i = 3;
float f = 10.0f;
f = i; //可以隐式转换,会出现警告int”转换到“float”,可能丢失数据
long m = i; // 绝对安全,可以隐式转换,不会出现警告。
double dd = 1.23;
long m1 = dd; // 可以隐式转换,会出现可能丢失数据的警告。
long m2 = (long)dd; // C风格:显式转换,不会出现警告。
long m3 = static_cast<long>(dd); // C++风格:显式转换,不会出现警告。
cout << "m1=" << m1 << ",m2=" << m2 << ",m3=" << m3 << endl;
//低风险的转换:整型与浮点型;字符型与整型;void *指针转换为任意类型指针
//高风险的转换:整型与指针类型转换
//字符型与整型
char ch='a';
int n = 5;
n = static_cast<int>(ch);
//void *指针转换为任意类型指针
void *p = nullptr;
int *p1 = static_cast<int *>(p);
//转换运算符,类与其他类型
CInt Obj;
//int k=Obj ;//可以隐式转换
int k = static_cast<int>(Obj);
cout << "k=" << k << endl;
//
}
int main()
{
CFather* pFather = nullptr;
CSon* pSon = nullptr;
//父类转子类(不安全)
//pSon = pFather;
pSon = static_cast<cson*>(pFather); //不安全,没有提供运行时的检测,编译会通过
//子类转父类(安全)
pFather = pSon;
pFather = static cast<CFather*>(pSon);
}
const
属性的转换,它也是四个强制类型转换运算符中唯一能够去除 const
属性的运算符。
示例1改为
#include
#include
int main()
{
const int n = 5;
const std::string s = "Inception";
//const_cast 只针对指针,引用,this指针
int *k = const_cast<int*>(&n);//const_cast指针类型 &n取出变量地址
*k = 123456;
std::cout <<"改变后的值 "<< *k << std::endl;
}
#include
#include
int main()
{
const int n = 5;
const std::string s = "Inception";
//const_cast 只针对指针,引用,this指针
int *k = const_cast<int*>(&n);//const_cast指针类型 &n取出变量地址
int &k1 = const_cast<int&>(n);//const_cast引用类型
*k = 123456;
k1 = 10000;
std::cout <<"改变后的值 "<< *k << std::endl;
std::cout << "改变后的值 " << k1 << std::endl;
}
常成员函数——不能修改成员变量的值,使用const_cast
让常成员函数可以修改成员变量的值,这个做法感觉有点无聊
#include
#include
class CTest
{
public:
int m_test=100;
void foo(int test) const
{
//m_test = test;
//void *p = this;
const_cast<CTest* const>(this)->m_test = test;
//const_cast(this)->m_test = test;//报错
}
};
int main()
{
int n = 5;
int* const p=&n;
//p = 0x123;
CTest t;
t.foo(1);
std::cout << t.m_test << std::endl;
//const int n = 5;
//const std::string s = "Inception";
const_cast 只针对指针,引用,this指针
//int *k = const_cast(&n);//const_cast指针类型 &n取出变量地址
//int &k1 = const_cast(n);//const_cast引用类型
//*k = 123456;
//k1 = 10000;
//std::cout <<"改变后的值 "<< *k << std::endl;
//std::cout << "改变后的值 " << k1 << std::endl;
}
用途:用于进行各种不同类型的转换
不同指针之间
不同类型引用之间
指针和能容纳指针的整数类型之间的转换
编译期处理,执行的是逐字节复制的操作
类似于显式强转,后果自负
语法:reinterpret_cast<目标类型>(表达式);
<目标类型>和(表达式)中必须有一个是指针(引用)类型。
reinterpret_cast不能丢掉(表达式)的const或volitale属性。
应用场景:
1)reinterpret_cast的第一种用途是改变指针(引用)的类型。
2)reinterpret_cast的第二种用途是将指针(引用)转换成整型变量。整型与指针占用的字节数必须一致,否则会出现警告,转换可能损失精度。
3)reinterpret_cast的第三种用途是将一个整型变量转换成指针(引用)。
#include
using namespace std;
class CFather
{
};
class CSon :public CFather
{
};
int main(int argc, char* argv[])
{
int n = 1;
int *p = (int *)n;//显式强转
int *p1 = reinterpret_cast<int*>(n);//整型转整型指针
char *ph = reinterpret_cast<char*>(p1);//各种类型之间的转换
CSon* pSon;
CFather* pFather = nullptr;
pSon = reinterpret_cast<CSon*>(pFather); //父类强转为子类指针 ,不存在检查
return 0;
}
动态转换(dynamic_cast)用于基类和派生类之间的指针或引用的转换,但只能在运行时确定类型信息,因此只能用于多态类型。如果转换失败,将返回一个空指针。
语法:
dynamic_cast<目标类型> (原始类型)
原因: dynamic cast
是运行时类型检查,需要运行时类型信息(RTTI
),而这个信息是存储与类的虚函数表关系紧密,只有一个类定义了虚函数,才会有虚函数表。
即运行阶段类型识别(RTTI RunTime Type Identification)为程序在运行阶段确定对象的类型,只适用于包含虚函数的类。
常见的转换方式
#include
using namespace std;
class CFather
{
public:
virtual void foo()
{
cout << "CFather::void foo()" << endl;
}
int m_father;
};
class CSon :public CFather
{
public:
virtual void foo()
{
cout << "CSon::void foo()" << endl;
}
int m_son;
};
int main(int argc, char* argv[])
{
CFather f;
CSon s;
CFather* pFather = &f;//父类指针指向父类对象
CSon* pSon = &s;//子类指针指向子类对象
//子类指针转换为父类,安全
//pFather = static_cast(pSon);//子类指针转换为父类
//父类指针转换为子类,不安全
//pSon = static_cast(pFather);//父类指针转换为子类
//pSon->m_son = 125;//越界访问子类的成员变量,只有在程序运行阶段产生这个错误,编译器可能不报错
//我们希望编译器可以检测出父类转换为子类是不安全的,使用dynamic_cast
//在程序运行时做检测
pSon = dynamic_cast<CSon *>(pFather);//父类指针转换为子类
pSon->m_son = 125;//越界访问子类的成员变量,只有在程序运行阶段产生这个错误,编译器可能不报错
return 0;
}
dynamic_cast
,确保程序的健壮性和安全性。#include
using namespace std;
class CFather
{
public:
virtual void foo()
{
cout << "CFather::void foo()" << endl;
}
int m_father;
};
class CSon :public CFather
{
public:
virtual void foo()
{
cout << "CSon::void foo()" << endl;
}
int m_son;
};
int main(int argc, char* argv[])
{
CFather f;
CSon s;
CFather* pFather = &f;//父类指针指向父类对象
CSon* pSon = &s;//子类指针指向子类对象
//子类指针转换为父类,安全
//pFather = static_cast(pSon);//子类指针转换为父类
//父类指针转换为子类,不安全
//pSon = static_cast(pFather);//父类指针转换为子类
//pSon->m_son = 125;//越界访问子类的成员变量,只有在程序运行阶段产生这个错误,编译器可能不报错
//我们希望编译器可以检测出父类转换为子类是不安全的,使用dynamic_cast
//在程序运行时做检测
//pSon = dynamic_cast(pFather);//父类指针转换为子类
//pSon->m_son = 125;//越界访问子类的成员变量,只有在程序运行阶段产生这个错误,编译器报错
//改变为
pSon = dynamic_cast<CSon *>(pFather);//父类指针转换为子类
if (pSon != nullptr)
{
pSon->m_son = 125;//加入判断,确保程序的健壮性和安全性。
}
return 0;
}
#include
using namespace std;
class CFather
{
public:
void foo()
{
cout << "CFather::void foo()" << endl;
}
int m_father;
};
class CSon :public CFather
{
public:
virtual void foo()
{
cout << "CSon::void foo()" << endl;
}
int m_son;
};
int main(int argc, char* argv[])
{
CFather f;
CSon s;
CFather* pFather = &f; //父类指针指向父类对象
CSon* pSon = &s; //子类指针指向子类对象
//dynamic_cast能够在运行的时刻,检测出被转换的指针的类型
//pFather = dynamic_cast(pSon);//父类指针转换为子类,不推荐使用,避免开销
pSon = dynamic_cast<CSon *>(pFather);//父类指针转换为子类
if (pSon != nullptr)
{
pSon->m_son = 125;//加入判断,确保程序的健壮性和安全性。
}
return 0;
}
//4.2 举例2_子类指针转换为父类,安全
#include
using namespace std;
class CFather
{
public:
virtual void foo()
{
cout << "CFather::void foo()" << endl;
}
int m_father;
};
class CSon :public CFather
{
public:
virtual void foo()
{
cout << "CSon::void foo()" << endl;
}
int m_son;
};
int main(int argc, char* argv[])
{
CFather f;
CSon s;
CFather* pFather = &f; //父类指针指向父类对象
CSon* pSon = &s; //子类指针指向子类对象
//dynamic_cast能够在运行的时刻,检测出被转换的指针的类型
pFather = dynamic_cast<CFather *>(pSon);//父类指针转换为子类,不推荐使用,避免开销
return 0;
}
class Base {};
class Derived : public Base {};
Base* b1 = new Derived();
Derived* d1 = dynamic_cast<Derived*>(b1);
if (d1 != nullptr) {
// b1 是 Derived 类型的。
}
需要注意的是,如果指向的基类指针并不真正指向派生类,或者目标类型与原始类型之间的类型转换无法完成,dynamic_cast会返回null指针或抛出std::bad_cast异常。因此,在使用dynamic_cast时需要非常小心,确保程序的健壮性和安全性。