【cpp--->类型转换与io流】

文章目录

    • @[TOC](文章目录)
  • 一、C++强制类型转换
    • 1.static_cast
    • 2.reinterpret_cast
    • 3.const_cast
    • 4.4 dynamic_cast
  • 二、C++io流
    • 1.流的概念
    • 2.标准io流
    • 3.文件io流
    • 4.stringio流

一、C++强制类型转换

当运算符两边的变量类型不一致的时候就需要发生类型转换,类型转换有隐式类型转换和强制类型转换,c语言提供的强制类型转换比较简洁,但是还不够规范,可读性不高,所以C++就增加了更加规范的强制类型转换方法.

1.static_cast

static_cast用于类型相关的类型之间的转换.

int main()
{
	int a = 10;
	double b = 11.1;
	a = static_cast<int>(b);
	cout << a << endl;
	return 0;
}

2.reinterpret_cast

reinterpret_cast是C++中的一种类型转换操作符,它用于在不同类型之间进行强制类型转换,尤其在涉及指针类型的转换时非常有用。它主要用于以下几种情况:

  1. 将一个指针转换为一个不同类型的指针。例如,将一个void指针转换为int指针。
  2. 将一个整数类型转换为指针类型。例如,将一个整数转换为指向对象的指针。
  3. 将一个指针转换为一个整数类型。例如,将一个对象的指针转换为整数。
  4. 将一个指针转换为一个不同类型的指针,无论是否相关。例如,将一个基类指针转换为派生类指针。

需要注意的是,reinterpret_cast执行的是一种底层的、不安全的类型转换,它会忽略类型之间的任何关系和限制。因此,在使用reinterpret_cast时要格外小心,确保转换的类型和目标类型之间存在合理的关系,以避免潜在的错误和不确定行为。

int a = 10;
	string str("string");
	string* pstr = &str;
	a = reinterpret_cast<int>(pstr);
	printf("0x%x\n", a);

3.const_cast

const_cast只能用于去除指针或引用类型的常量属性。
volatile 是一个关键字,用于修饰变量,用于告诉编译器该变量可能会被意外的改变,因此编译器在编译时不能对该变量进行优化。

当一个变量被声明为 volatile,意味着该变量可能会被其他线程、中断或硬件设备修改,编译器会确保每次访问该变量时都会从内存中读取最新的值,而不会使用之前缓存的值。

int main()
{
	//不加volatile编译器会优化,流提取中a的值
	volatile const int a = 10;
	int *b = const_cast<int*>(&a);
	*b = 20;
	cout << a << endl;
	return 0;
}

4.4 dynamic_cast

dynamic_cast是C++中用于运行时类型识别和转换的操作符。它主要用于在继承关系中进行安全的向下转型(downcasting)。其中,目标类型是你希望将表达式转换成的类型,表达式是要被转换的对象或指针。

dynamic_cast的用法示例:

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    void specificFunction() {
        // ...
    }
};

int main() {
    Base* basePtr = new Derived();
    
    // 使用dynamic_cast将basePtr转换为Derived指针
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    
    if (derivedPtr) {
        // 转换成功,可以调用Derived类的特定函数
        derivedPtr->specificFunction();
    } else {
        // 转换失败,basePtr指向的对象不是Derived类型
        // 处理转换失败的情况
    }
    
    delete basePtr;
    return 0;
}

需要注意的是:

  1. dynamic_cast只能用于指针或引用类型的转换。
  2. dynamic_cast只能用于具有多态性(至少有一个虚函数)的类类型。
  3. 如果目标类型是指针类型,而表达式的类型不是指针类型,dynamic_cast会返回一个空指针。
  4. 如果目标类型是引用类型,而表达式的类型不是引用类型,dynamic_cast会抛出std::bad_cast异常。
  5. dynamic_cast在运行时进行类型检查,因此会带来一些性能开销。在性能敏感的场景中,应该谨慎使用。

二、C++io流

1.流的概念

“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述。
C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
它的特性是:有序连续、具有方向性

2.标准io流

C++标准库提供了4个全局流对象cin、cout、cerr、clog;cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。

clog和cerr都是标准错误,都是向错误设备输出错误信息,但是clog有行缓冲,cerr没有缓冲.

cin对象支持类型转换为bool类型的,用于逻辑比较,因为cin类中实现了bool类型重载.当对象进行隐式类型转换的时候,编译器首先会先找对应的单参数构造函数,其次是类型重载函数.

#include
#include
#include
using namespace std;

class A
{
public:
	A(int a)
		:_a(a)
	{}
	operator int() const
	{
		return _a;
	}
private:
	int _a;
};

int main()
{
	// 内置类型转换成自定义类型
	//A aa1 = static_cast(1);
	A aa1 = 1;
	// 自定义类转换成内置类型
	int x = aa1;
	return 0;
}
class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	operator bool() const
	{
		// 这里是随意写的,假设输入_year为0,则结束
		if (_year == 0)
			return false;
		else
			return true;
	}

private:
	int _year;
	int _month;
	int _day;
};

istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;

	return out;
}

3.文件io流

文件流有三个类用于文件的读写**,ifstream和fstream以及ofstream**,它们都是继承了io_base类的写法,在io_base类中定义了in和out两个全局静态对象,用于确定文件是读还是写.因为io_base是三个文件读写类的基类,所以这三个类域中都继承了in和out对象,ifstream用于文件读取,ofstream用于文件写入,fstream同时具备ifstream和ofstream的功能.

**构造:**有无参构造和初始化构造两种,无参构造是先创建对象,然后再打开文件.初始化构造是在构造对象的时候就打开文件.

打开文件的模式有读方式in和写方式out,app和ate以及trunc与binary模式;ate(append to end)是文件流打开模式的一种选项,它将文件流的初始位置设置为文件末尾。这意味着在读/写操作之前,文件指针将定位在文件的末尾,可以直接进行写入操作。如果文件不存在,则会创建一个新文件。
app与ate类似,将文件流的初始位置设置为文件末尾,不同的是,app模式下的写入操作将始终追加到文件的末尾,而不管文件指针的位置。如果文件不存在,则会创建一个新文件。
ate允许在追加之前进行随机访问,而app则始终将写入操作追加到文件末尾。trunc模式作用是清空文件之前的内容.binary用于二进制数据的读写.

需要注意的是在进行二进制文件读写的时候,读写文件不可以是string对象,因为写入的是string对象中的str指针,而不是str本身,当程序结束时str指针会被析构成野指针,所以会导致程序崩溃.

#include 
#include
#include
using namespace std;
class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	operator bool() const
	{
		// 这里是随意写的,假设输入_year为0,则结束
		if (_year == 0)
			return false;
		else
			return true;
	}

private:
	int _year;
	int _month;
	int _day;
};
struct ServerInfo
{
	//char _address[32];
	string _address;

	int _port;
	Date _date;
};

struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}

	// 二进制读写,读写对象中,不能有string
	void WriteBin(const ServerInfo& info)
	{
		ofstream ofs(_filename, ofstream::out | ofstream::binary);
		ofs.write((char*)&info, sizeof(info));
	}

	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ofstream::in | ofstream::binary);
		ifs.read((char*)&info, sizeof(info));
	}

	// 文本读写 C++文本读写更简单
	// 文本读写本质,内存中任何类型都是转成字符串在写
	// c语言文本读写很不方便,因为要不断转字符串
	// c++封装了以后就有很大的优势
	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		ofs << info._address << " ";
		ofs << info._port << endl;
		ofs << info._date << endl;
	}

	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address;
		ifs >> info._port;
		ifs >> info._date;
	}

private:
	string _filename; // 配置文件
};

int main()
{
	ServerInfo winfo = { "192.0.0.1xxxxxxxxxxxxxxxxxxxxx", 80, { 2023, 7, 11 } };

	string str;
	cin >> str;
	if (str == "二进制写")
	{
		ConfigManager cm("test.txt");
		cm.WriteBin(winfo);
	}
	else if (str == "二进制读")
	{
		ServerInfo rinfo;
		ConfigManager cm("test.txt");
		cm.ReadBin(rinfo);
		cout << rinfo._address << endl;
		cout << rinfo._port << endl;
		cout << rinfo._date << endl;
	}
	else if (str == "文本写")
	{
		ConfigManager cm("test.txt");
		cm.WriteText(winfo);
	}
	else if (str == "文本读")
	{
		ServerInfo rinfo;
		ConfigManager cm("test.txt");
		cm.ReadText(rinfo);

		cout << rinfo._address << endl;
		cout << rinfo._port << endl;
		cout << rinfo._date << endl;
	}
	

	return 0;
}

4.stringio流

构造函数有默认构造,有缺省参数打开模式参数,默认为in,带参构造,string对象加缺省参数打开模式参数,默认为in.


默认构造
explicit istringstream (ios_base::openmode which = ios_base::in);

带参构造
explicit istringstream (const string& str,ios_base::openmode which = ios_base::in);

str成员函数,用于获取或设置stringstream对象的字符串内容

stringstream可以将将数值类型数据格式化为字符串,也可以拼接字符串,也可以分割序列化字符串,格式化数据在格式化的时候应该用空格或者换行隔开.


int main()
{
	ChatInfo winfo = { "张三", 135246, { 2022, 4, 10 }, "晚上一起看电影吧" };
	stringstream oss;
	oss << winfo._name << " ";
	oss << winfo._id << " ";
	oss << winfo._date << " ";
	oss << winfo._msg;
	string str = oss.str();
	cout << str << endl;

	stringstream iss(str);
	ChatInfo rinfo;
	iss >> rinfo._name;
	iss >> rinfo._id;
	iss >> rinfo._date;
	iss >> rinfo._msg;

	cout << "-------------------------------------------------------" << endl;
	cout << "姓名:" << rinfo._name << "(" << rinfo._id << ") ";
	cout << rinfo._date << endl;
	cout << rinfo._name << ":>" << rinfo._msg << endl;
	cout << "-------------------------------------------------------" << endl;

	return 0;
}
  1. stringstream实际是在其底层维护了一个string类型的对象用来保存结果。
  2. 多次数据类型转化时,一定要用clear()来清空,才能正确转化,但clear()不会将
    stringstream底层的string对象清空。
  3. 可以使用s. str(“”)方法将底层string对象设置为""空字符串。
  4. 可以使用s.str()将让stringstream返回其底层的string对象。
  5. stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参
    数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更
    安全。

你可能感兴趣的:(开发语言,c++)