单例模式
单例模式是指一个类只能有一个对象。
饿汉模式
在单例模式下,在程序开始(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;
}