alin的学习之路(C++篇)(类型转换,异常,标准输入输出,文件操作)
1.类型转换
-
静态类型转换
-
静态类型转换用于内置数据类型的转换和子类和父类之间的转换
-
static_cast<目标>(源)
void test01() { char a = 'a'; double d = static_cast<double>(a); cout << d << endl; } class Base{}; class Son : public Base{}; class Other{}; void test02() { Base* base = NULL; Son* son = static_cast<Son*>(base); //向下 不安全 Base* base2 = static_cast<Base*>(son); //向上 安全 //Other* other = static_cast<Other*>(base2); //无效 }
-
-
动态类型转换
- 动态类型转换不能进行内置数据类型的转换,只能做父类和子类间向上类型转换和发生多态后的转换**(即只能做安全的转换)**
void test03() { char a = 'a'; //double d = dynamic_cast<double>(a); //报错,不能转换 //cout << d << endl; } class Base2 { virtual void func() {} }; class Son2 : public Base2 { virtual void func() {} }; class Other2 {}; void test04() { Base2* base = NULL; //Son2* son = dynamic_cast<Son2*>(base); 报错,向下类型转换不可以 Son2* son = NULL; Base2* base2 = dynamic_cast<Base2*>(son); //向上类型转换成功 Base2* base3 = new Son2; Son2* son2 = dynamic_cast<Son2*>(base3); }
-
常量转换
- 使用于指针和引用间的类型转换 用于卸掉和加上const
void test05() { int a = 10; const int* b = &a; int* c = const_cast<int*>(b); const int* d = const_cast<const int*>(c); int& e = a; const int& f = const_cast<const int&>(e); }
-
重新解释转换
- 可以任意转换,但不安全
void test06() { int a = 10; int* b = reinterpret_cast<int*>(a); }
2.异常
1.异常简介
-
异常的语法
- throw 抛出异常 抛出的是一个类型,不是具体的值
- try 代码块中放可能发生异常的语句
- catch 捕获异常并执行操作
-
代码示例
//自定义异常 class myException { public: void printError() { cout << "自定义类型捕获" << endl; } }; int myDivision(int a, int b) { if (b == 0) { //throw - 1; //抛出的是一个类型异常 //throw 'a'; throw myException(); //抛出自定义异常的匿名对象,捕获时要用对象去接收 } return a / b; } void test01() { int a = 10; int b = 0; //将可能发生异常的代码放在try代码块中 try { int ret = myDivision(a, b); } //如果没有捕获对应类型的异常,那么代码会崩 catch (int) { throw; //catch中使用throw会向上继续抛出异常,待外层捕获 cout << "int异常发生" << endl; } catch (myException e) { e.printError(); } catch (...) //代表上面所有分支判断以外的类型 { cout << "其他类型异常发生" << endl; } } int main() { try { test01(); } catch (int) { cout << "main中捕获了int异常" << endl; } system("pause"); return EXIT_SUCCESS; }
2.自定义异常多态
class BaseException
{
public:
virtual void printError() {};
};
class NULLPtrException : public BaseException
{
public:
virtual void printError()
{
cout << "空指针异常" << endl;
}
};
class OutOfRangeException : public BaseException
{
public:
virtual void printError()
{
cout << "越界异常" << endl;
}
};
void func()
{
//throw NULLPtrException();
throw OutOfRangeException(); //抛出自定义异常
}
void test01()
{
try {
func();
}
catch (BaseException & e) //使用父类的引用去接收子类的对象,发生多态
{
e.printError();
}
}
3.系统标准异常
- 系统标准异常简介
class Person {
public:
Person(int age)
{
if (age < 0 || age>150)
{
throw out_of_range("年龄必须在0-150之间"); //系统标准越界异常
}
}
};
void test01()
{
try {
Person p(1000); //年龄越界
}
catch (exception& e)
{
cout << e.what() <<endl; //what()函数返回抛出异常时的字符串
}
}
- 自定义异常继承在exception下
- 需要重写
virtual char const* what() const
- 继承在exception下
- string->const char* 使用str.c_str();
- 需要重写
class MyOutOfRange : public exception{
public:
MyOutOfRange(const char* errInfo)
{
this->m_errInfo = errInfo; //const char*可以隐式转为string
}
MyOutOfRange(const string& errInfo)
{
this->m_errInfo = errInfo;
}
virtual char const* what() const
{
//.c_str() 函数用于将string转为const char*,string不能隐式转换为const char*
return this->m_errInfo.c_str();
}
string m_errInfo;
};
void func()
{
throw MyOutOfRange("越界异常");
}
void test01()
{
try {
func();
}
catch (exception & e)
{
cout << e.what() << endl;
}
}
4.异常的接口以及栈解旋
-
使用异常的接口可以指定用什么类型去接收异常,如果抛出的类型与接收的不同,则代码崩掉
throw(int)代表抛出int,throw()表示什么都不抛
-
栈解旋:异常被抛出后,在try块开始到throw前所有的对象都会被释放,不会等到函数结束
class Person
{
public:
Person()
{
cout << "Person构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
};
void func() throw()//表示不允许抛出异常 //throw(int) //指定只能用int去接收异常
{
//栈解旋:在try块开始到throw前所有的对象都会被释放
Person p;
//throw 1;
}
void test01()
{
try {
Person p;
func();
}
catch (int)
{
cout << "int异常" << endl;
}
catch (double)
{
cout << "double异常" << endl;
}
}
5.异常的生命周期
异常抛出与接收的4种方式:
- 抛出myException() 接收:myException e 相当于值传递,会调用拷贝构造函数,产生一个新的对象
- 抛出myException() 接收:myException& e e是匿名对象的左值是一个别名,延长了该匿名对象生命周期 (推荐)
- 抛出&myException() 接收:myException* e 不能够使用catch中的指针去操作,原对象是匿名对象已释放
- 抛出new myException() 接收:myException e 将myException的对象创建在堆上,延长了生命周期,要手动delete (推荐)
class myException {
public:
myException()
{
cout << "myException构造函数调用" << endl;
}
myException(const myException& e)
{
cout << "myException拷贝构造函数调用" << endl;
}
~myException()
{
cout << "myException析构函数调用" << endl;
}
};
void func()
{
throw new myException();
}
void test01()
{
try {
func();
}
//抛出myException() 接收:myException e 相当于值传递,会调用拷贝构造函数,产生一个新的对象
//抛出myException() 接收:myException& e e是匿名对象的左值是一个别名,延长了该匿名对象的生命周期
//抛出&myException() 接收:myException* e 不能够使用catch中的指针去操作,原对象是匿名对象已释放
//抛出new myException() 接收:myException e 将myException的对象创建在堆上,延长了生命周期,要手动delete
catch (myException* e)
{
cout << "自定义异常" << endl;
delete e;
}
}
3.标准输入输出
1.标准输入
-
cin.get():从缓冲区读一个字符。注意:键盘输入的\n也放入缓冲区中
void test01() { char ch; ch = cin.get(); cout << "ch = "<< ch << endl; ch = cin.get(); cout << "ch = " << ch << endl; } void test02() { //cin.get(一个参数) 相当于cin.get()其中这个参数是传入传出参数 /*char ch; cin.get(ch); cout << "ch = " << ch << endl; cin.get(ch); cout << "ch = " << ch << endl;*/ char buf[1024] = { 0 }; cin.get(buf, 1024); //1.空格被包含进字符串,与cin>>区分。2.缓冲区中的\n会遗留在缓冲区中 cout << "buf = " << buf << endl; char ch; cin.get(ch); cout << "ch = " << ch << endl; }
-
cin.getline():读缓冲区中的字符串
void test03() { char buf[1024] = { 0 }; cin.getline(buf,1024); //与字符串一起输入缓冲区中的\n会被自动舍弃掉 cout << "buf = " << buf << endl; char ch; cin.get(ch); cout << "ch = " << ch << endl; }
-
cin.ignore:忽略缓冲区中的字符(可加参数忽略多个)
void test04() { cin.ignore(); //忽略一个字符 char ch; cin.get(ch); cout << "ch = " << ch << endl; }
-
cin.peek():“偷窥”缓冲区的一个字符,不会将看到的那个字符取出来
void test05() { cin.peek(); char ch; cin.get(ch); cout << "ch = " << ch << endl; }
-
cin.pushback():将字符放回其在缓冲区的原位置
void test06() { char buf[1024] = { 0 }; char ch; cin.get(ch); cout << "ch = " << ch << endl; cin.putback(ch); cin.getline(buf, 1024); cout << "buf = " << buf << endl; }
-
案例1:判断用户输入是数字还是字符串
//案例1:判断用户输入是数字还是字符串 void test07() { cout << "请输入一个数字或字符串:" << endl; char ch; ch = cin.peek(); if (ch >= '0' && ch <= '9') { int num; cin >> num; cout << "num = " << num << endl; } else { char buf[1024] = { 0 }; cin.getline(buf, 1024); cout << "buf = " << buf << endl; } }
-
案例2:用户输入0-9的数字,如果不是0-9则重新输入
void test08() { while (true) { int num; cout << "请输入一个0-9的数字" << endl; cin >> num; if (num >= 0 && num <= 9) { cout << "执行后面的代码" << endl; break; } else { cout << "输入有误,请重新输入" << endl; //1.重置标志位 (置0是缓冲区正常,置1是缓冲区异常) cin.clear(); //2.刷新缓冲区 //cin.sync(); VS低版本可用,VS高版本用getline刷新缓冲区 char buf[1024] = { 0 }; cin.getline(buf, 1024); } } }
2.标准输出
-
cout.put():向缓冲区写字符
-
cout.write():从buf中写n个字符到当前输入流中
void test01() { cout.put('h').put('\n'); char buf[1024] = "hello world"; cout.write(buf, 1024); //默认带换行 }
-
通过流成员函数
//通过流成员函数输出 void test02() { int number = 99; cout.width(5); //设置宽度 cout.fill('*'); //设置填充 cout.setf(ios::left); //设置左对齐 cout.unsetf(ios::dec); //卸载十进制 cout.setf(ios::hex); //装载十六进制 cout.setf(ios::showbase); //显示进制基数 cout.unsetf(ios::hex); //卸载十六进制 cout.setf(ios::oct); //装载八进制 cout << number << endl; //输出number }
-
使用控制符
//通过控制符 void test03() { int number = 99; cout << setw(5) //设置宽度 << setfill('*') //设置填充 << setiosflags(ios::showbase) //显示进制基数 << setiosflags(ios::left) //设置左对齐 << hex //十六进制 << number //输出数字 << endl; //换行 }
4.文件操作
1.写文件
//写文件
void test01()
{
ofstream ofs("test.txt", ios::out | ios::trunc); //默认单独使用ios::out的时候会隐藏的加上ios::trunc
if (!ofs.is_open())
{
cout << "文件打开失败" << endl;
}
ofs << "姓名:Tom" << endl;
ofs << "年龄:18" << endl;
ofs << "性别:男" << endl;
ofs.close();
}
2.读文件
//读文件
void test02()
{
ifstream ifs("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
}
//方法1
/*char buf[1024] = { 0 };
while (ifs >> buf) //当buf中没有内容时即文件读完时,循环结束
{
cout << buf << endl;;
}*/
//方法2
/*char buf[1024] = { 0 };
while (ifs.getline(buf,sizeof(buf)))
{
cout << buf << endl;
}*/
//方法3
//string buf;
//while (getline(ifs, buf)) //getline(src,dest)
//{
// cout << buf << endl;
//}
//方法4
char ch;
while ((ch = ifs.get()) != EOF) //ifs.get()每次读一个字符
{
cout << ch;
}
ifs.close();
}
3.二进制读写文件
class Person {
public:
Person(){}
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test01()
{
ofstream ofs("person.txt", ios::out | ios::binary);
Person p1("TOM",18);
Person p2("Jerry",19);
ofs.write((const char*)&p1, sizeof(p1));
ofs.write((const char*)&p2, sizeof(p2));
ofs.close();
ifstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
}
Person p3;
Person p4;
ifs.read((char*)&p3, sizeof(p3));
ifs.read((char*)&p4, sizeof(p4));
cout << "姓名:" << p3.m_Name << " 年龄:" << p3.m_Age << endl;
cout << "姓名:" << p4.m_Name << " 年龄:" << p4.m_Age << endl;
ifs.close();
}