IO流与单例模式

发布于:2025-08-17 ⋅ 阅读:(11) ⋅ 点赞:(0)

单例模式

单例模式是指一个类只能有一个对象。

饿汉模式

在单例模式下,在程序开始(main函数运行前)的时候创建一个对象,这之后就不能再创建这个对象。

class HungryMan
{
public:
	static HungryMan* getinstance()
	{
		return &inst;
	}
private:
	HungryMan()
	{}
	HungryMan(const HungryMan& it) = delete;
	const HungryMan& operator=(const HungryMan& it) = delete;
	static HungryMan inst;//这里是声明,并不是定义,所以可以在类里面包含
	//实际上是全局变量与类封装融合的结果
};

HungryMan HungryMan::inst;//在类外定义

饿汉模式的实现方式较为简单,但存在线程安全(如果没加锁,可能会出现new了几个单例对象出来),可能导致进程启动慢(main函数启动之前需要创建对象),无法控制单例的初始化顺序(如果不同的单例模式在不同的文件,就无法确认初始化顺序,包括单例之间有可能存在依赖,也可能存在问题)的问题。

懒汉模式

在第一次使用的时候创建。

class LazyMan
{
public:
	static LazyMan* getinstance()
	{
		if (inst == nullptr)
		{
			inst = new LazyMan();
			//new出来的数据是需要释放的,但懒汉对象大部分不需要手动释放
			//但这个对象并不是在程序层面上释放的,而是在系统回收资源的时候释放的
			//那如果我们需要保存一些数据的话,要保证main函数之后要自动调用懒汉类的析构函数
		}
		return inst;
	}

	static void delinstance()
	{
		if (inst == nullptr)
		{
			delete inst;
			inst=nullptr;
		}
	}

private:
	LazyMan()
	{}
	LazyMan(const LazyMan& it) = delete;
	const LazyMan& operator=(const LazyMan& it) = delete;

	static LazyMan* inst;

	class gc
	{
		~gc()
		{
			delinstance();
		}
	};

	static gc delgc;
};

LazyMan* LazyMan::inst = nullptr;

LazyMan::gc LazyMan::delgc;

IO流

c++的IO流

C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类,c++这个IO流实际上是和C语言保持一致的,比如说istream对标的就是scanf和printf,fstream对标的是fscanf和fprintf,sstream对标的是sscanf和sprintf。
在这里插入图片描述
在类型转换中,对于内置类型,相近的类型可以互相转换,其他的可以由构造函数来支持转换,就像自定义类型可以转化为自定义类型,内置类型也可以转换为自定义类型,自定义类型也可以转化为自定义类型。

class tmp
{
public:
	operator int()
	{
		if(/*输入为ctrl+z*/)
		{
			return 0;
		}
		else
		{
			return /*非0*/
		}
	}
};

int main()
{
	tmp t;
	int i = t;
	return 0;
}

标准读写

所以cin可以直接使用while(cin>>tmp),当输入其他字符,返回值就为真,如果输入ctrl+z,返回值就是假,循环就退出了。
c++默认是兼容C语言的,当c++的cin和C语言的scanf混用的时候,并不会因为缓冲区的问题而导致读取混乱,如果想关掉这种兼容,可以调用下图的函数。

cin.sync_with_stdio(false);
cout.sync_with_stdio(false);

在这里插入图片描述

文件读写

在C语言中,fgetc和fputc是用来读单个字符的,fread和fwrite是用来读写文件的,fprintf和fscanf是用来格式化读写的,但这些只能用来对内置类型做操作,在c++里,ifstream是从文件里读,ofstream是写到文件里,可以应付内置类型和自定义类型的输入和输出。
二进制读写:如果要把内存中的数据存到磁盘里,内存中的数据是由类型的,而磁盘没有,我们可以使用二进制读写,也可以使用文本读写。比如说我们要把一个结构体按二进制的形式写到磁盘里:
在这里插入图片描述

class A
{
private:
	int a;
	char b;
	string c;
};


class Bin
{
public:
	Bin(const char* filename="./info.bin")
		:_filename(filename)
	{}

	void Write()
	{
		ofstream ofs(_filename, ios_base::out | ios_base::binary);
		A data;
		ofs.write((const char*)&data, sizeof(data));
	}

	void Read(A & rA)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);
		ifs.read((char*)&rA, sizeof(rA));
	}

private:
	string _filename;
};

在这里插入图片描述
我们可以用read再次读取,但这样是把数据原封不动的读回来,包括地址,但这样在同一个进程就会发生浅拷贝问题,析构两次,而如果是不同的进程读取,就会出现野指针问题,因为到后来的进程读取的时候,前面进程的空间早就销毁了,根据文件拷贝回来的地址就是一个野指针,所以只要是容器都要注意不能用二进制进行读取,因为容器底层都相对复杂。
文本读写:如果要正常读写到文件里和从文件里读出来,都要和字符串相互转化。但这样转化太麻烦,所以在C语言中,我们可以使用sscanf和sprintf转化完后用fwrite和fread写到文件里和读取。而在c++中重载了流插入和流提取,所以也不需要自己去做转换。

class Text
{
public:
	Text(const char* filename = "./info.text")
		:_filename(filename)
	{}

	void Read(A& rA)
	{
		ifstream ifs(_filename);
		ifs >> rA._a >> rA._b >> rA._c;
	}

	void Write()
	{
		A data(1, 'a', "hello world");
		ofstream ofs(_filename);
		ofs << data._a;
		ofs << data._b;
		ofs << data._c;//可以使用getline
	}
private:
	string _filename;
};

stringstream

自定义类型不方便转为字符串,所以可以调用stringstream。

class A
{
public:
	A(int a=0,char b='\0', string c="")
		:_a(a)
		,_b(b)
		,_c(c)
	{}
	int _a;
	char _b;
	string _c;
};


ostream& operator << (ostream & os, A aa)//必要手动写格式
{
	os << aa._a << '/' << aa._b << '/' << aa._c;
	return os;
}

int main()
{
	A tmp(20, 'a', "hello world");
	ostringstream oss;
	oss << tmp;

	string out = oss.str();
	cout << out << endl;
	return 0;
}

在这里插入图片描述
但stringstream并不会用于比较复杂的情景,比较复杂的情景可以使用json。

class Date
{
public:
	Date(int _year,int _month,int _day)
		:year(_year)
		,month(_month)
		,day(_day)
	{}
	int year;
	int month;
	int day;
};
istream& operator>>(istream& is, Date& d)
{
	is >> d.year >> d.month >> d.day;
	return is;
}

ostream& operator<<(ostream& os, Date& d)
{
	os << d.year <<' ' << d.month<<' ' << d.day << ' ';
	return os;
}
class Parent
{
public:
	Parent(int a1,char b1,Date c1)
		:a(a1)
		,b(b1)
		,c(c1)
	{}
	int a;
	char b;
	Date c;
};


ostream& operator<<(ostream& os, Parent& p)
{
	os << p.a <<' ' << p.b<<' ' << p.c << ' ';
	return os;
}

istream& operator>>(istream& is, Parent& p)
{
	is >> p.a >> p.b >> p.c;
	return is;
}

int main()
{
	Date d1(2025, 8, 16);
	Parent p1(10, 'a', d1);
	ostringstream os;
	os << p1;

	Date d2(2024, 9, 18);
	Parent p2(20, 'b', d2);
	istringstream is(os.str());
	is >> p2;

	return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到