- 一、类型转换
- 1)静态类型转换
- 2)动态类型转换
- 3)常量转换
- 二、异常处理
- 1)基本语法
- 2)异常变量生命周期
- 3)异常的多态使用
- 4)使用标准异常库
- 5)编写自己的异常类
- 三、标准输入输出流
- 1)标准输入流
- 2)标准输出流
在c++中类型转换不可以像在c语言中那样强制使用。
1)静态类型转换函数原型: static_cast <目标原型>(原对象);
- 内置数据类型
void test01() { //内置数据类型 char a = 'a'; double d = static_cast(a);//能转换成功 cout << d << endl; }
- 自定义数据类型
class base{ public: void fun(){}; }; class Son:public base{ public: void fun(){}; }; class Other{ }; void test2(){ base*base=nullptr; Son*son=nullptr; Son*son2=static_cast(base); //base 转为 Son*类型 向下类型转换 不安全 base*base2=static_cast (son); //son 转为 base * 类型 向上类型转换 安全 //base 转为 Other* //没有父子关系的两个类型之间是无法转换成功的 //Other * other = static_cast (base); }
总结:
- 语法 static_cast<目标类型>(原对象)
- 对于内置数据类型 是可以转换的
- 对于自定义数据类型,必须是父子之间的指针或者引用可以转换成功
2)动态类型转换
函数原型:dynamic_cast<目标类型>(原对象)
- 内置数据类型与自定义数据类型
void test03() { //内置数据类型 不允许内置数据类型之间的转换 //char c = 'c'; //double d = dynamic_cast(c); //自定义数据类型 base * base = NULL; Son * son = NULL; //base 转为 Son* 类型 不安全 //不安全 转换失败 //Son * son2 = dynamic_cast (base); //son 转为 base* 安全 base * base2 = dynamic_cast (son); //base 转为Other* //Other* other = dynamic_cast (base); //如果发生多态,那么父子之间的转换 总是安全的 base * base3 = new Son; //将 base3转为 Son* Son * son3 = dynamic_cast (base3); }
总结:
- 对于内置数据类型 不可以转换
- 对于自定义数据类型
- 父转子 不安全 转换失败
- 子转父 安全 转换成功
- 如果发生多态 ,那么总是安全的,都可以成功
3)常量转换
函数原型:const_cast<目标类型>(原对象)
//3、常量转换 void test04() { //指针之间的转换 const int * p = NULL; //将 const int * 转为 int * int * p2 = const_cast(p); //将 p2 转为 const int * const int * p3 = const_cast (p2); //引用之间的转换 const int a = 10; const int & aRef = a; int & aRef2 = const_cast (aRef); //不可以对非指针 或者 非引用 做const_cast转换 //int b = const_cast (a); }
总结:
- 只能对 指针 或者引用之间 使用
二、异常处理
什么是异常处理呢???其实就是程序运行时出现异常的地方先去处理然后再继续执行程序(听君一席话如听一席话???)。
1)基本语法异常的三个关键字 try throw catch try 试图执行一段可能会出现异常的代码 throw出现异常后 抛出异常的关键字 throw + 类型 catch 捕获异常 catch(类型)
- 1、用非异常处理方法处理可能异常情况
int myDivide(int a , int b) { if ( b == 0) { return -1; //C语言处理异常缺陷在于 返回的值 没有统一,返回的值可以是异常的结果,也可以是正确的结果 } return a / b; } void test01() { int a = 10; int b = 0; int c= myDivide(a,b); }
当b=0时,c就会被赋值-1,而当b=-10时,c也会被赋值-1,这样情况在众多数据运算时就很难判断b是否真的是为0,得出结论C语言处理异常缺陷在于 返回的值 没有统一,返回的值可以是异常的结果,也可以是正确的结果,所以应该用异常处理来解决。
- 2、内置数据类型的异常处理
int myDivide(int a , int b) { if ( b == 0) { //抛出异常 throw 1; //throw 3.14; //throw 'a'; } return a / b; } void test01() { int a = 10; int b = 0; //尝试执行一段 可能会出现异常的代码 try { int ret = myDivide(a, b); cout << "ret 结果为: " << ret << endl; } catch (int ) //捕获异常 { cout << "int类型的异常的捕获" << endl; } }
- 3、简单的自定义类型异常
class MyException { public: void printError() { cout << "我自己的异常类的错误" << endl; } }; int myDivide(int a , int b) { if ( b == 0) { throw MyException(); //抛出一个 MyException 匿名对象 } return a / b; } void test01() { int a = 10; int b = 0; //尝试执行一段 可能会出现异常的代码 try { int ret = myDivide(a, b); cout << "ret 结果为: " << ret << endl; } catch (int ) //捕获异常 { cout << "int类型的异常的捕获" << endl; } catch(MyException x){ x.printError(); }
- 4、异常中的栈解旋
class Person { public: Person() { cout << "Person的构造函数" << endl; } ~Person() { cout << "Person的析构函数" << endl; } }; int myDivide(int a , int b) { if ( b == 0) { Person p1; Person p2; throw MyException(); //抛出一个 MyException 匿名对象 } return a / b; } void test01() { int a = 10; int b = 0; //尝试执行一段 可能会出现异常的代码 try { int ret = myDivide(a, b); cout << "ret 结果为: " << ret << endl; } catch(...){ cout<<"出现异常"<代码中catch(…){}这样写的好处就是不用写抛出异常的类型是啥,这样就可以直接捕获其他类型的异常。
- 栈解旋: 从try代码块开始起,到 throw抛出异常前,所有栈上的对象都被释放掉,释放的顺序和构造的顺序是相反的,这个过程称为栈解旋
总结:
- 如果想捕获其他类型的异常 catch( … )
- 如果捕获到的异常不想处理,想继续向上抛出 throw
- 异常必须要有人处理,如果没有处理,程序会自动调用 terminate函数,使程序中断
- 可以抛出一个自定义类型的异常 myException
- 栈解旋:从try代码块开始起,到 throw抛出异常前,所有栈上的对象都被释放掉,释放的顺序和构造的顺序是相反的,这个过程称为栈解旋
2)异常变量生命周期class MyException { public: MyException() { cout << "MyException构造函数调用" << endl; } MyException(const MyException & e) { cout << "MyException拷贝构造函数调用" << endl; } ~MyException() { cout << "MyException的析构函数调用" << endl; } }; void doWork() { throw MyException(); } void test01() { try { doWork(); } //MyException e会调用拷贝构造 //MyException &e 引用方式 接受 建议用这种方式 节省开销 //MyException *e 指针方式 接受 抛出 &MyException();匿名对象,对象被释放掉,不可以再操作e了 //MyException *e 指针方式 接受 抛出 new MyException(); 堆区创建的对象 记得手动释放 delete e; catch (MyException &e) { cout << "MyException的异常捕获" << endl; } }总结:
- MyException e会调用拷贝构造
- MyException &e 引用方式 接受 建议用这种方式 节省开销
- MyException *e 指针方式 接受 抛出 &MyException();匿名对象,对象被释放掉,不可以再 *** 作e了
- MyException *e 指针方式 接受 抛出 new MyException(); 堆区创建的对象 记得手动释放 delete e;
- 建议使用 引用的方式 去接受对象
3)异常的多态使用//异常 基类 class baseException { public: virtual void printError() = 0; }; //空指针 异常 class NULLPointException :public baseException { public: virtual void printError() { cout << "空指针异常" << endl; } }; //越界异常 class OutOfRangeException :public baseException { public: virtual void printError() { cout << "越界异常" << endl; } }; void doWork() { //throw NULLPointException(); throw OutOfRangeException(); } void test01() { try { doWork(); } catch ( baseException & e) { e.printError(); } }
4)使用标准异常库#include//系统标准异常头文件 class Person { public: Person(int age) { if (age < 0 || age > 150) { //年龄越界异常抛出 throw out_of_range("年龄必须在 0 到 150之间!"); //throw length_error("年龄必须在 0 到 150之间!"); } this->m_Age = age; } int m_Age; }; void test01() { try { Person p1(151); } catch (exception & e) { cout << e.what() << endl; } } 总结:
- 标准异常头文件 #include< std except>
- 使用系统异常类 out_of_range(“char *”)
- 捕获 catch( exception & e ) { cout << e.what() ; };
5)编写自己的异常类//自己的异常类 class myOutOfRange :public exception { public: myOutOfRange(char * errorInfo) { //将char * 转为 string ,隐式转换 this->m_ErrorInfo = string(errorInfo); } myOutOfRange(string errorInfo) { this->m_ErrorInfo = errorInfo; } virtual ~myOutOfRange() { } const char * what() const { //string 转为 const char * 要.c_str()转换 return this->m_ErrorInfo.c_str(); } //保存住 用户传入的异常信息的 字符串 string m_ErrorInfo; }; class Person { public: Person(int age) { if (age < 0 || age > 150) { //年龄越界异常抛出 throw myOutOfRange( "我的异常类 ---- 年龄必须在 0 到 150 之间"); } this->m_Age = age; } int m_Age; }; void test01() { try { Person p1(1111); } catch (exception & e) { cout << e.what() << endl; } }总结:
- 自己编写的异常类myOutOfRange : public exception需要继承基类并且要重写虚函数what, 函数原型(const char * what() const)
- char * 和 string之间的转换
- char * 转 string string的有参构造 string(char *)
- string 转 const char * 需要用到 .c_str();
三、标准输入输出流输入输出流中的结构图
1)标准输入流
void test01() { //cin.get()一次只能读取一个字符 // a s char c = cin.get(); //第一次输出 a cout << "c = " << c << endl; c = cin.get(); //第二次输出 s cout << "c = " << c << endl; c = cin.get(); //第三次输出 换行 cout << "c = " << c << endl; c = cin.get(); //第四次 等待下一次输入 cout << "c = " << c << endl; } void test02() { //cin.get(两个参数) //可以读字符串 char buf[1024] = { 0}; cin.get(buf, 1024); //当利用cin.get读取字符串时候,并不会读走换行符,而是遗留在缓冲区中 char c = cin.get(); if (c == 'n') { cout << "换行符遗留在缓冲区了" << endl; } else { cout << "换行符不在缓冲区了" << endl; } cout << "buf = " << buf << endl; } void test03() { char buf[1024] = { 0 }; cin.getline(buf, 1024);//如果利用cin.getline()读取字符串,函数并不会读取换行符,而是将换行符从缓冲区中扔掉 char c = cin.get(); if (c == 'n') { cout << "换行符遗留在缓冲区了" << endl; } else { cout << "换行符不在缓冲区了" << endl; } cout << "buf = " << buf << endl; } //cin.ignore()忽略 void test04() { cin.ignore(); //默认忽略1个字符,如果里面有参数N,代表忽略N个字符 char c = cin.get(); //输入是 a s //输出是 c = cout << "c = " << c << endl; } //cin.peek()偷窥 void test05() { char c = cin.peek(); //输入 : as //输出 : cout << "c = " << c << endl; c = cin.get(); cout << "c = " << c << endl; } //cin.putback() 放回 void test06() { char c = cin.get(); cin.putback(c); //放回的是原来的位置 char buf[1024]; cin.getline(buf, 1024); cout << "buf = " << buf << endl; } void test07() { cout << "请输入一个字符串或者数字: " << endl; char c = cin.peek(); if (c >= '0' && c <= '9') { int num; cin >> num; cout << "您输入的是数字:" << num << endl; } else { char buf[1024] = { 0 }; cin >> buf; cout << "您输入的是字符串:" << buf<< endl; } } void test08() { cout << "请输入 0 ~ 10 之间的数字" << endl; int num; while (true) { cin >> num; if (num > 0 && num < 10) { cout << "输入正确 --- 数字为:" << num << endl; break; //输入正确 退出循环 } cout << "输入有误,请重新输入:" << endl; //fail()缓冲区中的标志位 0代表正常 1 代表异常 cin.clear(); cin.sync(); //清空标志位 并且刷新缓冲区 cin.ignore(); //VS2015以上 需要做 忽略 //cout << "cin.fail = " << cin.fail() << endl; } }总结:
- cin.get() 从缓冲区读取一个字符
- cin.get( 两个参数) 读取字符串, 换行符 遗留在缓冲区
- cin.getline(两个参数 ) 读取字符串 ,换行符 不会读取换行符,并且将换行符从缓冲区中扔掉
- cin.ignore() 忽略 ,默认忽略1个字符,如果(N)代表忽略N个字符
- cin.peek() 偷窥,从缓冲区中偷窥第一个字符,并不会取走
- cin.putback()放回,放回到缓冲区原来的位置
2)标准输出流#include//标准输入流 使用控制符方式格式化输出的头文件 void test01() { //cout.put('a').put('b').put('c'); cout << "hello world" << endl; } void test02() { //通过流成员函数 int number = 99; cout.width(20); //预留20空间 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; } //使用控制符 void test03(){ int number = 99; cout << setw(20) //设置宽度 << setfill('~') //填充 << setiosflags(ios::showbase) //显示进制基数 << setiosflags(ios::left) //设置左对齐 << hex //安装十六进制 << number << endl; } 总结:
- cout.put() cout.write() 利用成员函数 输出内容
- 通过流成员函数
- cout.width(20); //预留20空间
- cout.fill(’*’); //填充
- cout.setf(ios::left); //左对齐
- out.unsetf(ios::dec); //卸载十进制
- cout.setf(ios::hex); //安装十六进制
- cout.setf(ios::showbase); //设置显示进制 基数
- cout.unsetf(ios::hex); //卸载十六进制
- cout.setf(ios::oct); //安装八进制
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)