两种类型转换
隐式类型转换:编译器自动执行,相近类型才能成功
显示的强制类型转换
缺点
隐式类型转换转换可能会出现精度的丢失
显示类型转换将所有情况混合在一起,代码不够清晰
用于相近之间的类型,与上述隐式类型转换差不多。
double a = 3.14;
int b = static_cast<int>(a);
不相近之间的类型转换,对应C语言之前的强制类型转换
double a = 3.14;
int b = static_cast<int>(a);
int* p = reinterpret_cast<int*>(b);
去掉对象const属性,方便赋值
const int x = 2;
int* ptr = const_cast<int*>(&x);
*ptr = 3;
(前提是必须要有虚函数)
用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转换:子类对象指针/引用——>父类对象指针/引用(切片)
向下转换:用dynamic_cast更安全
dynamic_cast只能用于含父类虚函数的类
dynamic_cast会先检查受否转换成功,能成功则转换,不能则返回0
应用场景:
如果指针原本是指向子类的,然后由于切片传参,进入函数后再转换成子类是可以的,但是如果始终是指向父类的,如果不用dynamic_cast此时就是强制类型转换,会出些野指针的问题,用dynamic_cast会检查转换是否成功。
,
class A
{
public:
virtual void f(){}
};
class B :public A
{
public:
virtual void f() {
}
};
void Fun(A* ptr)
{
if (dynamic_cast<B*>(ptr))
cout << "转换成功" << endl;
else
{
cout << "转换失败" << endl;
}
}
int main()
{
A a;
B b;
A* pa = &a;
B* pb = &b;
Fun(pa);//转换失败
Fun(pb);//转换成功
RTTL(Run-time Type identification)运行时类型识别
拷贝只会发生在两个场景中:拷贝构造和赋值运算符重载
- 将拷贝构造和赋值运算符重载设计成私有并且只声明不定义
设置成私有是为了防止用户在类外定义,不定义是为了万一定义了,在类内部调用
- c++11直接在拷贝和赋值重载函数后面加delete,编译器删除该默认成员函数
class OnlyHeap
{
public:
static OnlyHeap* CreatObject()
{
return new OnlyHeap;
}
static void DelObj(OnlyHeap* ptr)
{
delete ptr;
}
private:
//构造和拷贝构造都是设置为私有,此时无法调用
OnlyHeap(){}
OnlyHeap(const OnlyHeap& st);
~OnlyHeap() {}
/*析构函数私有的话,栈和静态的对象就不能
创建,因为此时其不能调用析构函数,同时
堆创建的对象也无法delete,此时写一个静态
析构函数如上即可*/
};
void test1()
{
//OnlyHeap h1;//栈上
//static OnlyHeap h2;//静态区
//OnlyHeap* h3 = new OnlyHeap;//堆区
上述都要调用构造函数,此时将构造函数设置为私有即可
//OnlyHeap h4(h1);//拷贝构造在栈上生成对象、
此时将拷贝构造也设置为私有即可
OnlyHeap* h5 = OnlyHeap::CreatObject();//此时达到了只能在堆上创建对象
//delete h5;//此时delete也会调用私有的析构函数,一样报错
OnlyHeap::DelObj(h5);
}
class OnlyStack
{
public:
static OnlyStack CreatObject()
{
return OnlyStack();
}
//禁掉operator new
/*如果类重载了operator new此时new这个对象时
就会走重载的operator而不会走库中的operator new
被禁掉了之后就无法再调用一个对象了。*/
private:
//构造和拷贝构造都是设置为私有,此时无法调用
OnlyStack():_a(0) {}
OnlyStack(const OnlyHeap& st) = delete;
//删除拷贝构造是因为防止出现以下这种情况
//sattic OnlyStack copy(h1);//此时也是拷贝构造
private:
int _a;
};
void test_Only_Stack()
{
OnlyStack h1 = OnlyStack::CreatObject();//允许通过
OnlyStack* h2 = new OnlyStack;//报错
static OnlyStack h3;//报错
sattic OnlyStack copy(h1)//报错
}
class A final
{};
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
无论程序后面是否使用,程序启动时就创建一个唯一的实例对象。
class SingLeton
{
public:
static SingLeton* GetInstance()
{
return m_instance;
}
void Print()
{
cout << "单例对象调用" << endl;
}
private:
//构造函数私有此时无法创建其他对象
SingLeton(int a):_a(a)
{
//信息输入
}
//防拷贝
SingLeton(const SingLeton& st) = delete;
SingLeton& operator=(const SingLeton& st) = delete;
static SingLeton* m_instance;//静态成员变量
int _a;
};
SingLeton* SingLeton::m_instance = new SingLeton(2);
//静态成员变量定义在类域中不受访问限定符的限制
//所以即使构造函数是私有的,此时单例对象依旧可以初始化
void test_SingLeton()
{
SingLeton::GetInstance();//这个类的单例对象
SingLeton::GetInstance()->Print();
/*SingLeton h1;
SingLeton *h2 = new SingLeton;
SingLeton copy(*SingLeton::GetInstance());
此时构造函数和拷贝构造都是没用的,保证
整个类中只有一个实例化对象*/
}
优点:不需要加锁,时间简单
缺点:可能会导致进程启动慢,且多个单例类对象实例启动顺序不一定。
eg:如果在main函数前就初始化单例对象,有可能构造函数需要进行的操作太多,这样的话,就可能会影响main函数的启动,所以一个大型程序启动慢的话,我们不清楚是构造函数太复杂(连接数据库什么的)还是整个程序卡死了。
eg:要求先初始化数据库对象,再初始化缓存对象,饿汉模式可能因为控制不住初始化的顺序而报错,都是在main函数之前初始化,顺序不确定。
但懒汉模式可以控制——第一次调用时初始化
一开始不创建对象,第一调用GetInstance再创建对象
涉及到加锁问题,实现后面补上
总结
推荐
流即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据(其单位可以是bit,byte,packet)的抽象描述。
C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。
特性:有序连续,具有方向性
c++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类
c++标准库提供了四个全局对象cin,cout,cerr,clog
cin:标准输入即数据通过键盘输入到程序中
cout:标准输出即数据从内存流流向控制台(显示器)
cerr:用来标准错误的输出
clog:进行日志的输出
其中cout,cerr,clog是ostream类的三个不同对象,因此三个对象现在基本没区别,指示应用场景不同。
cout << "hello world" << endl;
cerr << "hello world" << endl;
clog << "hello world" << endl;
//三者输出都是一样的结果
cin与cout可以直接输入和输出内置类型数据,标准库已经将所有内置类型的输入和输出全部重载了。
为什么在大数据输入的时候cin要比scanf慢?
因为cin是c++,它要同步C语言的输入流,如果输入的时候一下是cin,一下是scanf,两边的输入都不是直接到控制台,而是到缓存区,此时cin需要控制输入顺序什么的,而且scanf输入的时候类型%就确定了,而cin还需要推断。
C++文件数据格式分为二进制文件和文本文件
ifstream ifile(只输入用)
ofstream ofile(只输出用)
fstream iofile(既输入用又输出用)
struct ServerInfo
{
char _address[32];
//string _address;
int _port;
Date _date;
};
struct ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
void WriteBin(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);
//out:打开文件是为了写
//binary:以二进制的方式打开
//总体就是以二进制的方式将info写入_filename
ofs.write((const char*)&info, sizeof(info));
}
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
//in:打开文件是为了读
//binary:以二进制的方式打开
//总体就是以二进制的方式将_filename中的内容读入info
ifs.read((char*)&info, sizeof(info));
}
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename);//默认是文本的方式
//将info中的内容以文本的方式写入到文件中
ofs << info._address << " " << info._port << " " << info._date;
}
void ReadText(ServerInfo& info)
{
ifstream ifs(_filename);
//将文件中的内容以文本的方式读入到info中
ifs >> info._address >> info._port >> info._date;
}
private:
string _filename; // 配置文件
};
int main()
{
ServerInfo winfo = { "192.0.0.1", 80, { 2021, 4, 10 } };
// 二进制读写
//二进制写
ConfigManager cf_bin("test.bin");
cf_bin.WriteBin(winfo);
//二进制读
ServerInfo rbinfo;
cf_bin.ReadBin(rbinfo);
cout << rbinfo._address << " " << rbinfo._port << " "
<< rbinfo._date << endl;
// 文本读写
ConfigManager cf_text("test.text");
//文本写
cf_text.WriteText(winfo);
ServerInfo rtinfo;
//文本读
cf_text.ReadText(rtinfo);
cout << rtinfo._address << " " << rtinfo._port << " " <<
rtinfo._date << endl;
return 0;
}
包含头文件sstream
该头文件下,标准库三个类:istringstream、ostringstream 和 stringstream,分别用来进行流的输入、输出和输入输出操作
C语言中将整型变量转换成一个字符串
int n = 123456;
char s1[32];
_itoa(n, s1, 10);
char s2[32];
sprintf(s2, "%d", n);
char s3[32];
sprintf(s3, "%f", n);
stringstream s;
s << "a" << " " << "b";
cout << s.str() << endl;//输出a b
s.clear();
s << "c";
cout << s.str() << endl;//输出a bc
s.str("");
s << "d";
cout << s.str() << endl;//输出d
空间配置器,顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的默默工作。