C++笔记8.26

异常基本概念
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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值