类型转换和IO流
一、c语言的类型转换
int i = 1;
// 隐式类型转换,这些类型都是表示数据的大小,整型家族包括char相互转化是因为他们只是范围不一样,浮点数与整型支持是因为浮点数本质也是表示数据的大小,只是加了精度而已;
double d = i;
printf("%d, %.2f\n" , i, d);
int* p = &i;
// 显示的强制类型转换,基本上内置类型都支持显式类型转换,整型和指针是支持显式类型转换的,因为地址是编号是支持显式类型转换的;
int address = (int) p;
printf("%x, %d\n" , p, address);
c隐式类型转换:
自定义类型基本上是不支持隐式类型转换的,但是c++98支持单参数的构造函数是支持隐式类型转换的,之后c++11支持了多参数的隐式类型转换;
即想要支持隐式类型转换要有一定的关联度,内置类型之间也是支持隐式类型转换的,如果不想支持隐式类型转换,就要在构造函数之前用explict关键字修饰;
size_t 与int发生隐式类型转换可能会导致比较大小出现问题;
c强制类型转换:
常变量是可以使用强制类型转换获得地址,然后修改的。编译器认为堆常变量类型转换是有风险的会做出优化,对于常变量没有去内存中去取,而是放到了寄存器中去,读取也是从寄存器中进行读取,vs2019将常变量用宏替换成常量实现;
使用关键字volatile可以强制去内存中进行查找或者不能使用宏替换;
二、c++的强制类型转换
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换 ,即静态转换是相关类型的转换;
// 相关类型的转换
double d = 2.1;
int i = static_cast<int>(d);
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型,即用重新解释转换是不相关类型的转换;
// 不相关类型的转换
int *p = &i;
int x = reinterpret_cast<int>(p);
const_cast最常用的用途就是删除变量的const属性,方便赋值;const转换是带有一定的风险的;
//去掉const属性
const int a = 10;
int b = static_cast<int>(a);
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的),如果父类指针/引用是指向子类对象的就可以转换成功,正常返回地址,是指向父类对象的就会失败,返回空指针,否则就会存在越界访问的问题;强烈建议:避免使用强制类型转换。
三、RTTI
RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
typeid 运算符
dynamic_cast 运算符
decltype
四、c语言常见IO流
常见IO函数:
对于键盘显示器,printf/scanf;
对于文件,fprintf/fscanf;fwrite/fread,进行的是二进制的读写;fputs/fgets;
对于字符串,sprintf/sscanf;
五、c++常见IO流
c++IO流真正的优势是支持自定义类型;
cout的本质就是将自定义类型的成员转换成字符串,然后载入控制台;
istream/ostream对象只支持用istream/ostream对象进行构造;
常见IO对象:
//支持下列操作即要实现自定义类型转换成内置类型,是因为c++98重载了operator!,c++11重载了operator bool
//c++98
bool operator!() const;
//c++11
//ios类实现的
operator void*() const;
explicit operator bool() const;//不允许隐式类型转换
string str;
while(cin>>str)
{cout<<str<<endl;
}
如何实现自定义类型转换成内置类型或者自定义类型转自定义类型,使用operator 类型(),注意不需要写返回值;
class A
{
public:operator int(){return a_;}
private:int a_ = 0;
};
//istream使用了流标志ctrl+z会被识别成结束
六、c++文件流
头文件fstream,支持使用string对象进行构造;
如下是枚举常量,表示打开的方式;
open()
close()
write()//以二进制的方式写,要注意读写是要使用字符数组,因为string内部是一个指向堆空间的指针,也就是说发送的是一个地址(野指针),且是上一个进程的
ofs<<str;//以文本方式写,直接使用流插入实现重载operator<<实现,ofstream继承了ostream可以直接使用ostream的运算符重载;要注意用空格隔开,因为读取默认以空格或者换行分割;
get()//读取字符
getline()//读取一行
ignore()//到达某个位置或者文件结尾就停止读取
七、c++字符流
使用不同的类型进行存储是为了除了便于描述之外更重要的是为了支持运算;
头文件sstream;
支持使用string对象进行构造;
要对stringstream对象多次转换时要clear并且str(“”),将状态clear成good,然后将缓冲区的空间str(“”)清理干净;
//直接初始化一个对象,然后直接流插入,然后使用str这个函数拿到字符串
A a;
ostringstream oss;
oss << a;
cout << oss.str();
A a;
istringstream iss(string("3 4 5"));
iss >> a;
cout << a;
字符流常用于序列化和反序列化;
1.stringstream实际是在其底层维护了一个string类型的对象用来保存结果。
2.多次数据类型转化时,一定要用clear()来清空,才能正确转化,但clear()不会将
stringstream底层的string对象清空。
3.可以使用s. str(“”)方法将底层string对象设置为""空字符串。
4.可以使用s.str()将让stringstream返回其底层的string对象。
5.stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参
数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更
安全。
八、序列化和反序列化
序列化:将数据换换成字符串;反序列化:按需将字符串转换成不同类型的数据;由于数据库、网络中很多数据是不