前言
IO流:
- 数据从内存输出到硬盘、显示器等其他的输出设备:ostream,这叫流提取写入,ouput
- 硬盘、键盘等把数据输入到内存:istream,这叫流插入读取,input
类型转换
- 相近类型的转换,不相关类型的转换,const属性的取消,基类指针转成派生类指针。
- 标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
- static_cast、reinterpret_cast、const_cast、dynamic_cast
- 有符号向无符号转,可以隐式类型转换,小转大;
- 无符号向有符号转,要显式类型转换,大转小;
static_cast
:用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout<<a<<endl;
return0;
}
reinterpret_cast
:操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。
int main()
{
int a = 110;
int b = reinterpret_cast<int>(&a);
cout << b << endl;
cout << typeid(&a).name() << endl;
return 0;
}
const_cast
:最常用的用途就是删除变量的const属性,方便赋值
void Test ()
{
const int a = 2;
int* p = const_cast< int*>(&a );
*p = 3;
cout<<a <<endl;
}
dynamic_cast
:用于将一个基类对象的指针/引用转换为派生类类对象的指针或引用(动态转换)
- 向上转型:派生类对象指针/引用->基类指针/引用(不需要转换,赋值兼容规则)
- 向下转型:基类对象指针/引用->派生类指针/引用(用dynamic_cast转型是安全的)
- dynamic_cast只能用于含有虚函数的类
- dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
class A
{
public:
virtual void ptinf()
{
cout << _a << endl;
}
protected:
int _a = 24;
};
class C :public A
{
public:
int _b = 25;
};
int main()
{
//C b;
//A* pa = dynamic_cast<C*>(&b);
A a;
C* pc = dynamic_cast<C*>(&a);
if (pc)
{
pc->_b=26;
cout << pc->_b << endl;
}
else
{
cout << "false" << endl;
}
return 0;
}
explicit:关键字阻止经过转换构造函数进行的隐式转换的发生
struct A
{
int _a;
explicit A(int b)
:_a(b)
{
cout << "A(int b)" << endl;
}
A(const A& a)
{
cout << "A(const A& a)" << endl;
}
};
int main()
{
//A a = 1;
A b(1);
return 0;
}
RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:1. typeid运算符;2. dynamic_cast运算符
IO流
- “流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述。
- C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
- 它的特性是:有序连续、具有方向性
- 为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能
内置类型的IO
-
空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。getline可以忽略空格,读取一行。
-
循环输入字符串要怎样才能结束?
void StringTest()
{
string s;
//cin >> s;
//cout << s;
/*getline(cin, s);
cout << s;*/
while ((cin >> s).operator bool())//ctrl Z结束
{
cout << " ctrl Z 结束输入" << endl;
}
}
自定义类型的IO
- 重载
>> 和 <<
class Date
{
friend ostream& operator<<(ostream& ou, const Date& d);
friend istream& operator>>(istream& is, Date& d);
public:
Date(int age = 24, int y = 2022, int m = 7)
:_age(age)
,_y(y)
,_m(m)
{
}
private:
int _age;
int _y;
int _m;
};
ostream& operator<<( ostream& ou, const Date& d)
{
ou << d._age<<"-"<< d._y <<"-"<< d._m << endl;
//ou << d._age << d._y << d._m << endl;
return ou;
}
istream& operator>>(istream& is, Date& d)
{
is >> d._age >> d._y >> d._m;
return is;
}
void test_date_io()
{
/*Date d1= 25;
cout << d1;*/
Date d2;
cin >> d2;
cout << d2;
}
文件流IO
- 头文件
#include<fstream>
ifstream ifile(只输入用)
ofstream ofile(只输出用)
fstream iofile(既输入又输出用)
- 声明一个文件流操作的类型,用一个文件名构造一个对象,该文件流类型的对象调用成员函数,完成文件读写。
- 数据信息:
struct ServerInfo
{
char _address[32];
int _port;
Date _date;
};
- 文件流类型:
struct ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
....
private:
string _filename; // 配置文件
};
- 文件的二进制读写
void WriteBin(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);
ofs.write((const char*)&info, sizeof(info));//按单个字节序写入
}
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs.read((char*)&info, sizeof(info));
}
- 二进制读写的测试:
ServerInfo winfo = { "192.0.0.1", 80, {2022, 4, 10} };//数据信息的初始化
ConfigManager cf_bin("test.bin");//建立关系
cf_bin.WriteBin(winfo);
ServerInfo rbinfo;
cf_bin.ReadBin(rbinfo);
cout << rbinfo._address << ":" << rbinfo._port << endl;
- 文本方式读写
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename);
ofs << info._address << " " << info._port<< " "<<info._date;
}
void ReadText(ServerInfo& info)
{
ifstream ifs(_filename);
ifs >> info._address >> info._port>>info._date;
}
- 文本读写的测试
ServerInfo winfo = { "192.0.0.1", 80, {2022, 4, 10} };//数据信息的初始化
ConfigManager cf_text("test.text");
cf_text.WriteText(winfo);
ServerInfo rtinfo;
cf_text.ReadText(rtinfo);
cout << rtinfo._address << ":" << rtinfo._port <<":"<<rtinfo._date<<endl;
- 关于文件操作中结构化信息的字符串,为何不能用string:写入文件的是个地址,不是原字符串内容;且再将文件读取到该类型的对象中则会产生多次析构报错。
struct Config1
{
string _a;
int _pos;
//Date _d;
};
- 测试
void write_binTest1()
{
Config1 con{ "自定义类型的数据分格式往文件里写入",24 };
config_manger fbin("tessssst.bin");
fbin.write_bin1(con);//写入文件的是个地址
Config1 rcon1;
config_manger rbin("tessssst.bin");
rbin.read_bin1(rcon1);//会被多次析构报错
}
JSON
- JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。
- 采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
- 可扩展标记语言 (Extensible Markup Language, XML) ,标准通用标记语言的子集,在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
stringstream
#include <sstream>
-
标准库三个类:istringstream、ostringstream 和 stringstream,分别用来进行流的输入、输出和输入输出操作。
-
stringstream:将一个整形变量转化为字符串,存储到string类对象中;字符串拼接
//将一个整形变量转化为字符串
void test_stringstream()
{
int a = 123456789;
string sa;
stringstream ss;
ss << a;
ss >> sa;
cout << sa << endl;
//多次转换时,要将字符串流清空
//原因:stringstream 在转换结尾时,会将其内部状态设置成badbit
//clear是将状态重置为goodbit才能以继续转换
ss.clear();
double d = 12.34;
ss<< d;
ss >> sa;
cout << sa << endl;
}
//字符串拼接
void test_String_splicing()
{
stringstream ss;
//将多个字符串放入 ss里面;
ss << "Can" << " u ";
ss << " make it ? ";
cout << "the result is : " << ss.str() <<endl;
ss.str("");//不置空还有前一个的遗留
ss << "i dont konw either";
cout << "after str("") :" << ss.str() << endl;
}
- ostringstream:将结构化信息序列化为字符串
- istringstream:将字符串转换成结构化信息
- 结构化信息:
struct Info
{
string _name;
int _id;
Date _date;
string _msg;
};
- IO场景测试:
void stringstreamTest2()
{
Info winfo{ "大周",2,{2022,7,11},"你能成吗?" };
ostringstream oss;
oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg;
string str = oss.str();
cout << str << endl;
//通过网络把字符串发送给对象,解析成结构化信息
Info rinfo;
istringstream iss(str);
iss >> rinfo._name >> rinfo._id >> rinfo._date >> rinfo._msg;
cout << "姓名:" << rinfo._name << " ( " << rinfo._id << " ) ";
cout << rinfo._date << endl;
cout << rinfo._name << ":>" << rinfo._msg <<endl;
}