异常基本概念
exceptions
C++比C有更多的代码检查
read a file 文件可能是网络流,共享问题,无法得到大小,文件也可能是设备,串口
调用函数需要通过返回值来判断是否正常
readFile {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
}
errorCodeType readFile {
initialize errorCode = 0;
open the file;
if (theFileIsOpen) {
determine the length of the file;
if (gotTheFileLength) {
allocate that much memory;
if (gotEnoughMemory) {
read the file into memory;
if (readFailed) {
errorCode = -1;
}
} else {
errorCode = -2;
}
} else {
errorCode = -3;
}
close the file;
if (theFileDidntClose && errorCode == 0) {
errorCode = -4;
} else {
errorCode = errorCode and -4;
}
} else {
errorCode = -5;
}
return errorCode;
}
把事务的逻辑放在一起,可读性更强,出现问题离开try,根据具体问题来处理,用异常机制来对运行时可能出现问题进行管理
readFile {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}
出现异常,有人在别的地方告诉程序该怎么做
异常的最大好处是将业务逻辑和错误处理分得很清楚
异常的抛出与捕捉
在这里插入代码片
template <class T>
T& Vector<T>::operator[] (int index){}
index小于0或者大于等于size怎么办?
1.可以不解决返回一个随机的值,不能接受
2.返回一个特殊的错误值(C的函数就是这样做的),但是这样不能将返回值直接放入运算中,需要提前检查
3.直接退出
4.assert退出,但是它是表达函数代码错误的,不合适
比较好的方式是抛出一个异常
template <class T>
T& Vector<T>::operator[](int indx){
if(indx<0||indx>=m_size){
throw<<something>>; //throw是一个关键字
}
return m_elements[indx];
}
class VectorIndexError{
public:
VectorIndexError(int v):m_badValue(v){ }
~VectorIndexError(){ }
void diagnostic(){
cerr <<"index "<< m_badValue<<"out of range!";}
private:
int m_badValue;
};
template <class T>
T& Vector<T>::operator[](int index) {
if (index<0 || index>=m_size) {
//throw is a keyword
//exception is raised at this point
throw VectorIndexError(index);
}
return m_elements[index];
}
C++中任何东西都可以扔,可以是原始类型或者对象,一般是扔出一个对象,通过它的类型可以知道发生了什么事情,同时对象也可以携带一定的数据,告诉我们异常的indx到底是什么东西
具体代码
有且仅有new出来的在堆里
全局变量,静态本地变量,静态成员变量在全局数据区里
本地变量,在函数内部产生的变量会在堆栈里
所以throw后面产生了一个对象,在函数内部,放在堆里
throw会终止和离开函数,本地变量会不存在,if语句中throw下面的语句不会被执行,会带着对象离开函数,函数相当于throw,带着对象的地址(指针),知道对象在哪儿(虽然堆栈不断退出)
try{
func();func2();//在这个函数内部发生越界,throw退出到能处理的一步
} catch(VectorIndexError& e){
e.diagnostic();
}//不会再回到try了
}
第一种流派异常发生了回去,但是后续执行会出现问题,不断地异常
第二种发生异常不回去,继续走,除非自己加个循环
异常的传播
若希望cache捉到所有的异常
改为catch(…) //万能捕捉器 拿不到具体的问题
任何的try后面都必须有至少一句catch
catch很像一个函数
多个catch如何匹配异常
void abc(int a) : throw(MathErr){…}
调用abc,最多会抛出mathErr这一个异常,是用来约束abc的,如果抛出了另外一个异常,则会抛出一个异常,调用abc的人需要对异常做出处理
如果括号里没写,则可以抛出任何异常,给了throw,括号里没异常,则表明不会抛出异常
C++的new内存不够不会给NULL,而是抛出异常
void func(){
try{
while(1){
char *p = new char[10000];
}
} catch (bad_alloc& e){}
}
构造函数里出现异常,它自己解决不了,但是构造函数不是主动调用的
class A {
public:
A() {
throw 0;
}
};
int main() {
A * p = new A();
delete p;
return 0;
}
先分配空间再构造,抛throw,没有构造也就不会析构因此delete p报错,内存垃圾
struct X{};
struct Y : public X{};
try{
throw Y();
} catch(X x){
} //子类的对象交给父类,会丢掉一些东西
try{
throw new Y();
} catch(Y* p){
}//catch的是一个指针,throw时new了一个东西,但是过了catch就没人知道p是什么,一定要记得delete,情况很复杂
所以更倾向于抛的时候是抛的堆栈的东西,在堆栈高高再上,catch的东西做一个引用,既不会发生拷贝构造,也不会了丢失成员
struct B{
virtual void print(){}
};
struct D : public B {};
try{
throw D("D error");
}
catch (B& b){
b.print() //print D's error
}
堆栈中高高再上的东西将来会被别人覆盖
流:streams
C : printf , scanf //类型不安全 ,没有办法做表达类型的事情,例如自己做一个表达类型的类,Complex,自己类的对象无法做读和写的操作
文件有文本和二进制两种格式
C++ : 流
更好的类型安全 , GNU
缺点:比较啰嗦,慢
可以重载inserters(<<)和extrators(>>)
流的特点:
1.河流沿着一个方向流
2.可以确认位置(桥下2km),是一维的
对于C来说,可以在文件里任意定位从什么时候开始读,但是流是过去了就没有了,流把它读掉或写下了,就过去了
iostream,fstream,strstream(是char的,不安全且过时,因为没有下标检查,会发生越界) sstream(string)
流的类型:
文本文件:输入是在做解析(parse),输出时是在做格式化(format)
二进制文件:不以人的阅读,不经过解析与格式化的问题
流的运算符
cin / cout/ cerr(标准错误)/clog(标准日志)
#include <iostream>
int i; float f; char c;
char buffer[80];
cin >> c;
cin >> i;
cin >> f >> buffer;
可以对自己的类型做extrator,是全局函数
istream& //返回必须是这个
operator>>(istream& is, T& obj){ //第一个参数是receiver,不是const,需要修改,因为需要position,返回也不是const,读到a的结果也必须是istream,这样才能连锁起来
...
return is;
}
也可以直接用函数
int get()
int ch;
while((ch = cin.get())!= EOF)
cout.put(ch);
istream& get(char& ch)//根据返回值,可以串在里面用
函数是以点的形式来用,manipulater是放在大括号里
getline(…) / ignore(…) / gcout() / putback() 读一个字节再放回去 / peek() 偷窥,不改变position
做自己的inseter
ostream&
operator<<(ostream& os,const T& obj) {
...return os;//连锁
}
put(char) / flush() 保证能够写到物理的介质上写
输出时关心格式化问题,用manipulate控制行为
例如endl就是一个操纵子,flush和hex也是
int n;
cout << "enter number in hexadecimal" << flush;
cin >> hex >> n;
#include <iomanip>
cout << setprecision(2) <<1000.243<<endl; //精度为2 1e03
cout << setw(20) <<"ok";
计算机中set代表置它为1,reset为0
也可以自己做manipulators
ostream& tab(ostream& out){
return out <<'\t' ;
}
cout << "hello" <<tab << "world" <<endl;
endl也是一个函数
stream flags
例如ios :: showpoint 为1则showpoint,为0则不
不同的flag值用or连接,and一下就知道各个位置的值
set flags时可以用manipulate或者函数
STL
standard Template Libary 标准模板库
包括数据结构和算法
减少开发时间,代码可读性高,健壮性(容积自动增长,自动处理越界)
可移植的,可维护的
包含了一些容器:Vector(可扩展的数组,访问没有限制,会自动增长) Deque(两头可以增长)
List(内部是双向链表)
Sets(集合,不重复,无序) and Maps(映射)
a pair class(int/char)
基础算法(sort,search) 都是函数模板
都是在std空间里的,using namespace std
容器,算法,枚举器
最主要的三个结构:map , vector , list (第一个字母都是小写的)
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> x;
for (int a = 0; a < 1000; a++)
x.push_back(a); //里面有了1000个指数
vector<int>::iterator p; //p前面是个类,::表示作用域的解析符,即vector of int类里面有个类iterator
for (p = x.begin(); p < x.end(); p++) //++也被重载过,链表中等于next
cout << *p << " "; //*可以被重载,返回int
return 0;
}
vector没有给出初始的大小;
放满了push_back会自动增大
主要是模板和重载运算符
vector<Elem> c
vector<Elem> c1(c2)//ctor
V.size()
V.empty() 是否为空
==, !=, <, >, <=, >= 比较两个vector
V.swap(v2) 交换两个vector//simple methods
I.begin() 第一个是多少
I.end() //Iterators
V.at(index)
V[index]
V.front() 第一个
V.back() 最后一个 //Element access
V.push_back(e)
V.pop_back() 说明本身就实行了stack
V.insert(pos,e)
V.erase(pos)
V.clear() 全部清空
V.find(first,last item) 判断是否在里面//Add,Remove,Find
List:
有ctor,比较两个list
x.front(), x.back()
x.push_back(item), x.push_front(item)
x.pop_bacl(), x.pop_front()
x.remove(item)
#include "pch.h"
#include <iostream>
#include <list>
#include <string>
using namespace std;
int main() {
list<string> s;
s.push_back("hello");
s.push_back("world");
s.push_front("tide");
s.push_front("crimson");
s.push_front("alabama");
list<string>::iterator p;
for (p = s.begin(); p != s.end(); p++)
cout << *p << " ";
cout << endl;
}
alabama crimson tide hello world
不能使用p < s.end() , 因为列表的元素可能不是顺序的
#include <string>
#include <map>
using namespace std;
int main() {
map<string, float> price;
price["snapple"] = 0.75;
price["coke"] = 0.50;
string item;
double total = 0;
while (cin >> item)
{
total += price[item];
cout << total;
}
return 0;
}