目录
1.1 C++类型转换
1.1.1 静态转换
1.用处
用于类层次结构中父类与子类之间的指针或引用的转换。
上行(子转父)是安全的,下行是不安全的。
2.code:静态类型转换 static_cast---内置数据类型转换
语法:static_cast<目标数据类型>(要转成目标数据类型的变量)
void test01() { char a = 'a'; double d = static_cast<double>(a); cout << "d = " << d << endl; } int main() { test01(); return 0; }
/home/lhw/桌面/C++/day9/cmake-build-debug/day9 d = 97
允许内置数据类型转换
3. 类的下行转换(不安全但不报错)
lass Base{}; class Son : public Base{}; class Other{}; void test02() { Base * base = NULL; Son * son = NULL; Son * son2 = static_cast<Son*>(base); }
但base不能转换成other类型的,只支持父子类型的转换。
1.1.2 动态转换
1.语法及相比于静态类型转换的区别
dynamic_cast主要用于类层次的上行或下行转换
在类上行转换时,dynamic_cast与static_cast 是一致的
在类下行转换时,dynamic_cast比static_cast 更安全
2. code:动态类型转换 dynamic_cast---内置数据类型转换
void test03() { char a = 'a'; double d = dynamic_cast<double>(a); cout << "d = " << d << endl; }
报错,不允许内置数据类型转换!
3.类的下行转换
void test03() { Base * base = NULL; Son * son = NULL; Son * son2 = dynamic_cast<Son*>(base); }
父转子,不安全,报错!
4.发生多态类型转换总是安全的
下面这段代码可以成功运行
class Base { virtual void func() { } }; class Son : public Base { void func() { } }; class Other{}; void test03() { Base * base = new Son; Son * son = NULL; Son * son2 = dynamic_cast<Son*>(base); }
1.1.3 常量转换
1.用处
该运算符用来修改类型的const属性
常量指针被转化成非常量指针,并且仍然指向原来的对象
常量引用被转化为非常量引用,并且仍然指向原来的对象
warning:不能直接对非引用和非指针的变量使用const_cast操作符去直接移除它的const
2.code
void test05() { const int * p =NULL; int * pp = const_cast<int*>(p); const int * ppp = const_cast<const int *>(pp); }
3.语法
const转为非const语法
const_cast<类型 * >(const 类型 * )
非const转为const语法
const_cast<const 类型 * >(类型 * )
4. 不能直接对非引用和非指针的变量使用const_cast操作符去直接移除它的const
void test06() { const int a = 10; int b = const_cast<int>(a); }
报错
5.引用转化为const引用
void test07() { int num = 10; int & numref = num; const int & num2 =const_cast<const int &>(numref); }
1.1.4 重新解释转换(极其不安全,不推荐)
1.int转换为int *
void test08() { int a = 10; int * p = reinterpret_cast<int*>(a); }
2.相同地,不同类也能相互转换。十分不安全。
1.2 C++异常
1.2.1 什么是异常
异常就是处理程序中的错误。所谓错误就是指在程序运行中发生的一些异常事件(除0、数足下标越界)
1.2.2 C语言处理异常缺陷
int mydivision(int a,int b) { if(b==0) { return -1; } else { return a/b; } } int main() { int a = 10; int b = -10; int ret = mydivision(a,b); if(ret == -1) { cout << "yichang" << endl; } return 0; }
返回值不统一,返回值无法区分是结果还是异常。
1.2.3 C++处理异常
1.C++处理异常代码
int mydivision(int a,int b) { if(b==0) { throw 1; } else { return a/b; } } int main() { int a = 10; int b = -10; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } return 0; }
运行:无运行结果
代码修改为:
int mydivision(int a,int b) { if(b==0) { throw 1; } else { return a/b; } } int main() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } return 0; }
执行:
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 type int exxception
代码修改为:
int mydivision(int a,int b) { if(b==0) { throw 'a'; } else { return a/b; } } int main() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch(...) { cout << "type others exxception" << endl ; } return 0; }
运行:
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 type char exxception 进程已结束,退出代码0
2.总结
I .必须有处理异常的catch函数,否则报错。(比如throw 3.14,必须要有catch double)
II.catch用...表示其他异常的处理,类似于switch default ...
3.嵌套异常
int mydivision(int a,int b) { if(b==0) { throw '3.14'; } else { return a/b; } } void test01() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch (double ) { cout << "type double exxception" << endl ; } } int main() { try { test01(); } catch (double) { cout << "type double exxception in main" << endl ; } return 0; }
我们看下面这一段代码,这里的结构是这样的,main函数调用test01,test01中有异常,这里异常由谁处理呢?
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 type double exxception
明显,由test01处理,但如果想向上抛出异常,要加一个throw。
int mydivision(int a,int b) { if(b==0) { throw 3.14; } else { return a/b; } } void test01() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch (double ) { throw; cout << "type double exxception" << endl ; } } int main() { try { test01(); } catch (double) { cout << "type double exxception in main" << endl ; } return 0; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 type double exxception in main
4.设计一个我的异常类
class myException { public: void ex() { cout << " my exception !" << endl ; } };
int mydivision(int a,int b) { if(b==0) { throw myException(); } else { return a/b; } }
这里抛出一个异常的匿名对象。
void test01() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch (double ) { throw; cout << "type double exxception" << endl ; } catch(myException e) { e.ex(); } }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 my exception !
5.总结
若有异常则通过throw操作创建一个异常对象并输出
将可能抛出异常的程序放在try块之中
如果在try块执行期间没有引起异常,那么跟在try后面的catch语句不会执行
catch字句会根据出现的先后顺序检查,匹配的catch语句捕获并处理异常(或者向上抛出异常)
如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。
处理不了的异常,可以在catch的最后一个分支,使用throw向上抛
6.栈解旋
从try代码块开始,到throw抛出异常之前,所有栈上的数据都会被释放掉。
1.2.4 异常接口声明
当只允许抛出某种类型的异常时,我们看看代码
int mydivision(int a,int b) throw(int) { if(b==0) { throw (3); } else { return a/b; } } void test01() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch (double ) { throw; cout << "type double exxception" << endl ; } catch(myException e) { e.ex(); } } int main() { test01(); }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 type int exxception 进程已结束,退出代码0
这里我们只允许throw出int类型的异常。若我们更改代码如下:
class myException { public: void ex() { cout << " my exception !" << endl ; } }; int mydivision(int a,int b) throw(int) { if(b==0) { throw (3.14); } else { return a/b; } } void test01() { int a = 10; int b = 0; try { mydivision(a,b); } catch (int) { cout << "type int exxception" << endl ; } catch (char) { cout << "type char exxception" << endl ; } catch (double ) { throw; cout << "type double exxception" << endl ; } catch(myException e) { e.ex(); } }
代码运行报错
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 terminate called after throwing an instance of 'double'
当然也可以捕获很多异常
int mydivision(int a,int b) throw(int,double)
1.2.5 异常的生命周期
class myException { public: myException() { cout << "myexception default begin " << endl ; } myException(const myException & e) { cout << "myexception copy begin " << endl ; } ~myException() { cout << "myexception end " << endl; } void ex() { cout << " my exception !" << endl ; } }; void dowork() { throw myException(); } void test02() { try { dowork(); } catch (myException e) { cout << "myexception buhuo" << endl; } }
我们看这段代码的运行结果
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 myexception default begin myexception copy begin myexception buhuo myexception end myexception end
其实是这样运行的,调用dowork时会调用默认构造,catch里面以值传递参数会调用拷贝构造,然后打印信息,再释放掉。但此时代码运行效率不高,我们可以加一个引用,提高效率。
void test02() { try { dowork(); } catch (myException&e) { cout << "myexception buhuo" << endl; } }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 myexception default begin myexception buhuo myexception end
当然我们也可以通过指针。
void dowork() { throw &myException(); }
void test02() { try { dowork(); } catch (myException * e) { cout << "myexception buhuo" << endl; } }
但这么调用的话,不推荐,再在catch调用myexception的方法会报错。
1.2.6 异常的多态使用
class BaseException { public: virtual void printError() = 0; }; class nullptrException : public BaseException { public: virtual void printError() { cout << "nullptrException" << endl; } }; class arrayoverflowException : public BaseException { public: virtual void printError() { cout << "arrayoverflowException" << endl; } }; void dowork() { throw nullptrException(); } void test01() { try { dowork(); } catch (BaseException & exception) { exception.printError(); } }
1.2.7 系统标准异常
1.C++标准异常库
引入头文件
#include <stdexcept>
2.越界异常
class Person { public: Person(int age) { if(age <0 || age>150) { throw out_of_range("age error!"); } this -> age = age; } int age; }; void test02() { try { Person p(151); } catch(out_of_range & e) { cout << e.what() << endl; } }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 age error!
利用多态原理,可以直接传入父类对象。
catch(exception & e)
其他异常可以参考手册。
1.2.8 自定义异常类
1.自定义异常类是干什么的
扩充系统标准异常,重写C++标准异常的虚函数。
2.重写什么及重写注意事项
void test11()
{
ifstream ifs;
ifs.open("/home/liuhongwei/桌面/1.txt",ios::in);
if(!ifs.is_open())
{
cout << "fail to open the file" << endl;
return;
}
char buf[1024] = {0};
while(ifs.getline(buf,sizeof(buf)))
{
cout << buf << endl ;
}
}
1.3 C++输入输出流
1.3.1 流的概念和流类库的结构
1.流的概念
程序的输入流是指从输入文件将数据传给程序,程序的输出流是指从程序将数据传送给输出文件。
2.C++的输入输出包含以下三方面的内容
标准I/O:对系统指定的标准设备的输入和输出,即从键盘输入数据,输出到屏幕显示器。
文件I/O:以外存磁盘文件为对象进行输入和输出
串I/O:对内存中指定的空间进行输入和输出
1.3.2 标准输入流
1.举例子:cin(无参数)
void test02() { char c = cin.get(); cout << "c = " << c << endl; }
等待从键盘上输入字符,并从缓冲区读走一个字符
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 as c = a
void test02() { char c = cin.get(); cout << "c = " << c << endl; c = cin.get(); cout << "c = " << c << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 caa c = c c = a
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 c c = c c =
第二次输出的是换行符
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 c = a c = a 进程已结束,退出代码0
第一次回车,读取换行符,第二次输入a,读取a
2.举例子:cin(两参数)
void test02() { char buf[1024]; cin.get(buf,1024); cout << buf << endl; }
将键盘输入读入buf中,
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 hahhahnefu hahhahnefu 进程已结束,退出代码0
但它对换行符是如何操作的呢?我们做以下测试:
void test02() { char buf[1024]; char c; cin.get(buf,1024); cout << buf << endl; c = cin.get(); if(c == '\n') { cout << "\\n at buffer" << endl; } else { cout << "\n not at buffer" << endl; } }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 ccc ccc \n at buffer
因此得出结论:利用cin.get()获取字符串的时候会把换行符留在缓冲区中
3. 举例子:cin.getline(缓冲区,长度)
void test02() { char buf[1024]; char c; cin.getline(buf,1024); cout << buf << endl; c = cin.get(); if(c == '\n') { cout << "\\n at buffer" << endl; } else { cout << "\n not at buffer" << endl; } }
利用cin.getline()获取字符串的时候会把一行值读入并把换行符遗弃,也不会留在缓冲区。
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 hello world hello world a \n not at buffer
4. 举例子:cin.ignore()
void test03() { cin.ignore(); char c = cin.get(); cout << "c = " << c << endl; }
忽略一个字符
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 as c = s
ignore也可以有参数,我们来演示一下:
void test03() { cin.ignore(2); char c = cin.get(); cout << "c = " << c << endl; }
忽略了两个字符
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 ming c = n
5. 举例子:cin.peek()
void test04() { char c = cin.peek(); cout << "c = " << c << endl; c = cin.get(); cout << "c = " << c << endl; c = cin.get(); cout << "c = " << c << endl; }
作用,偷窥一眼一个字符,但不把字符从缓冲区拿走。
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 cs c = c c = c c = s
6. 举例子:cin.pushback()
void test05() { char c = cin.get(); cin.putback(c); char buf[1024] = {0}; cin.getline(buf,1000); cout << buf << endl; }
放回到缓冲区一个字符
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 lhwnb lhwn
7.案例:判断用户输入的是数字还是字符串
void test06() { cout << "Please input a string or number:" << endl; char c = cin.peek(); if(c>=0&&c<=9) { int num; cin >> num; cout << "number input : " << num << endl; } else { char buf[1024] = {0}; cin >> buf; cout << "string input :" << buf << endl; } }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 Please input a string or number: ccccc string input :ccccc 进程已结束,退出代码0
8.案例2:输入0-10错误重新输入
获取当时的标志位:0为正常,1为异常 cin.fail()来获取
清空缓冲区 cin.clear() 重置标志位 cin.sync()
1.3.3 标准输出流
1.cout.put()向缓冲区写一个字符
void test08() { cout.put('c'); } int main() { test08(); }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 c
也可以连环使用
void test08() { cout.put('c').put('e'); }
2. cout.write()向buffer中写num个字节到当前输出流中
void test08() { char buf[] = "hello world"; cout.write(buf, strlen(buf)); }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 hello world
3. 通过流成员格式化输出
void test08() { int num = 99; cout.width(20); cout << num << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 99
99前面有18个空格。
void test08() { int num = 99; cout.width(20); cout.fill('*'); cout << num << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 ******************99
填充为*
void test08() { int num = 99; cout.width(20); cout.fill('*'); cout.setf(ios::left); cout << num << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 99******************
左对齐
void test08() { int num = 99; cout.width(20); cout.fill('*'); cout.setf(ios::left); cout.unsetf(ios::dec); cout.setf(ios::hex); cout << num << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 63******************
卸载十进制,安装十六进制。
void test08() { int num = 99; cout.width(20); cout.fill('*'); cout.setf(ios::left); cout.unsetf(ios::dec); cout.setf(ios::hex); cout.setf(ios::showbase); cout << num << endl; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 0x63****************
显示基数
4.使用控制符格式化输出
要包含头文件 iomanip
void test09() { int num = 99; cout << setw(20) << num ; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 99
void test09() { int num = 99; cout << setw(20) << setfill('*') << num ; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 ******************99
void test09() { int num = 99; cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << num ; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 ******************99
void test09() { int num = 99; cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << setiosflags(ios::left) << num ; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 99******************
void test09() { int num = 99; cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << setiosflags(ios::left) << hex << num ; }
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 0x63****************
1.4 文件读写
1.包含输入输出流
fstream
2.写文件
void test10() { ofstream ofs; ofs.open("/home/liuhongwei/桌面/1.txt",ios::out|ios::trunc); //设置打开方式和路径 if(!ofs.is_open()) { cout << "fail to open the file" << endl; } ofs << "name : sunwukong" << endl; ofs << "age : 16" << endl; ofs.close(); }
我们运行这段代码,发现桌面上多了一个1.txt文件,里面出内容是:
name : sunwukong age : 16
3.读文件
void test11() { ifstream ifs; ifs.open("/home/liuhongwei/桌面/1.txt",ios::in); if(!ifs.is_open()) { cout << "fail to open the file" << endl; return; } char buf[1024] = {0}; while(ifs >> buf) { cout << buf << endl ; } }
方式1:利用右移运算符的重载,一个一个读出来。
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9 name : sunwukong age : 16
方式2:用getline方法
void test11() { ifstream ifs; ifs.open("/home/liuhongwei/桌面/1.txt",ios::in); if(!ifs.is_open()) { cout << "fail to open the file" << endl; return; } char buf[1024] = {0}; while(ifs.getline(buf,sizeof(buf))) { cout << buf << endl ; } }