【无标题】

Day9

dynamic_cast

#include <iostream>
#include <typeinfo>
using namespace std;
class A
{
public:
	virtual ~A() {} // 若无此虚函数, 则称运行时信息不成立
};
class B :public A {
};
class C :public A {
};
int main()
{
	A* p = new B;
	B* pb;
	pb = dynamic_cast<B*>(p);
	if (pb != nullptr)cout << "pb->new B" << endl;
	C* pc;
	pc = dynamic_cast<C*>(p);
	if (pc != nullptr)cout << "pc->new C" << endl;
	else cout << "nullptr" << endl;
}
终端输出:
pb->new B
nullptr

赋值兼容层面的语义,即 upcast(上转),不需要显示的转化。那么存在 dynamic_cast 主要意义,就是在于 downcast(下转)。 注意在多态体系中,要想应用 dynamic_cast,必须虚析构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wYokbKIM-1655360235077)(E:\博客\photo\Snipaste_2022-06-06_17-16-35.png)]

基类指针下转时,如果发现指向的不是一个发生在多态中的子类对象,则会返回 NULL 指针以后判断使用。

多态实现原理

C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table。在这个 表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆写的问题,保证其真实反 应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所 以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就 像一个地图一样,指明了实际所应该调用的函数。

Day10

Template模板

重载函数,固然实现了泛化的一些特性,但是不彻底,且有二义性(ambiguous)的存在。 如下,示例中 long 类型可以隐式的转化为 int 或是 double,编译会出现二义性,导致 编译失败

#include<iostream>
using namespace std;

void myswag(int& a, int& b)
{
	int t = a;
	a = b;
	b = t;
}
void myswag(double& a, double& b)
{
	double t = a;
	a = b;
	b = t;
}

int main()
{
	long a = 100;
	long b = 200;
	//myswag(a,b);   //既可以调用第一个也可以调用第二个,二义性,便不过
	//myswag((int)a,(int)b);
    这个也编不过,因为在强转之后会产生一个临时变量,临时变量的引用属于常引用要加const,但是,const承诺修饰的成员不被改变,这意味这a 与 不可以被赋值,显然不可能,故无法编过。忽略这个,交换的也是临时变量而不是真正的a和b
}

那么考虑下指针版本:
#include<iostream>
using namespace std;
void myswag(int* a, int* b)
{
	int t = *a;
	*a = *b;
	*b = t;
}
void myswag(double& a, double& b)
{
	double t = a;
	a = b;
	b = t;
}
int main()
{
	long a = 1;
	long b = 2;
	myswag((int*) & a,(int*) & b);
	//先取地址,之后产生了临时变量,临时变量的值为a的地址,调用函数之后交换的就是a与b的值而不是临时变量
	cout << a << "  " << b << endl;
}
函数模板
语法:
template<typename/class 类型参数 T1,typename/class 类型参数 T2,...>
返回类型 函数模板名(函数参数列表)
{
	函数模板定义体
}

template< typename T>,既可以与函数同行,也可以将函数另起一行来书写。T 即为范化的类型。 其过程,相当于经历了两次编译,先依据实参类型,实例化函数,然后再将实例化的 函数,参与编译。

#include<iostream>
using namespace std;
class A
{
public:
	A(int x, int y):x_(x), y_(y) {}
	void dis()
	{cout << x_ << "  " << y_;}
protected:
	int x_; int y_;
};
template<typename T> void myswag(T& a, T& b)
{
	T t = a;
	a = b;
	b = t;
}
int main()
{
	int a = 1;int b = 2;
	myswag(a,b);
	cout << a << "  " << b << endl;
	string s1 = "aa"; string s2 = "bb";
	myswag(s1, s2);
	cout << s1 << "  " << s2 << endl;
	A a1(1, 2); A a2(3, 4);
	myswag(a1, a2);
	a1.dis();
	//char c = 3;
	//myswag(a,c)       //编译出错,类型不同
}
终端输出:
2  1      int成功交换
bb  aa    string成功交换
3  4      自定义类A成功交换

以 myswag(a,b) 为例过程中先实例化即变为myswag< int >(a,b)在进行调用

原理:

编译器并不是把函数模板处理成能够处理任意类的函数。比如,自定义类型,如何处 理呢? 编译器遇到模板方法定义时,会进行语法检查,但是并不编译模板。编译器无法编译 模板定义,因为它不知道使用什么类型。比如 T a,b; 在 不知晓 T 具体类型时,是无法分配 内存,更无从谈编译 a = b; T 获取类型的过程,称为模板的实例化,函数模板通过具体类型产生不同的函数;编 译器会对函数模板进行两次编译:在声明的地方对模板代码本身进行编译(类型检查),在调 用的地方对参数替换后的代码进行编译(代码生成)

函数模板的特化
template<typename T> int compare( T &a, T &b)
template<> int compare <const char*>( const char* &a, const c)
#include <iostream>
#include <string.h>
using namespace std;
template<typename T> int compare( T &a, T &b)
{
if(a > b) return 1;
else if(a < b)return -1;
else return 0;
}
//实参为两个 char*时,比较的是指针的大小,
//而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特//殊
处理的版本:
template<> int compare < const char * >( const char* &a, const char* &b)
//                        对参数类型为const char*的函数特化,即子实现
{
	return strcmp(a,b);
}
int main()
{
int a = 3; int b = 5;
cout<<compare(a,b)<<endl;
string str1 = "abc",str2 ="abc";
cout<<compare(str1,str2)<<endl;
//char * 与 const char* 亦属于不匹配
const char * p1 = "abc",*p2= "def";
cout<<compare(p1,p2)<<endl;
cout<<compare(p2,p1)<<endl;
return 0;
}
类模板即类模板的友元
模板类
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
template<typename T>     
class Stack
{
public:
	Stack(int size = 1024)
	{
		space = new T[size];
		top = 0;
	}
	~Stack() {
		delete[]space;
	}
	bool isEmpty() {
		return top == 0;
	}
	bool isFull() {
		return top == 1024;
	}
	void push(T data) {
		space[top++] = data;
	}
	T pop() {
		return space[--top];
	}
private:
	T* space;
	int top;
};
int main()
{
	Stack<int> s(100);           //必须要实例化,即生成类模板
	for (int i = 0; i < 10; ++i) 
	{
		if (!s.isFull())
			s.push(i);
	}
	while (!s.isEmpty())
		cout << s.pop() << endl;
	return 0;
}

模板函数:myswap 函数模板 实例化------> myswag< int> 模板函数 ------->myswag< int>(a,b)调用

模板类 :Stack 类模板 实例化------------>Stack< int > 模板类 -------------> Stack< int> s 生成对象

格式
template<typename T> class ClassName
{
	void func(T );
};
template<typename T> void ClassName<T>::func(T)   //func这个函数属于实例化的类
{
	实现
}

例如:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
template<typename T>
class Stack
{
public:
	Stack(int size = 1024);
	~Stack();
	bool isEmpty();
	bool isFull();
	void push(T data);
	T pop();
private:
	T* space;
	int top;
};
int main()
{
	Stack<int> s(100);
	for (int i = 0; i < 10; ++i) 
	{
		if (!s.isFull())
			s.push(i);
	}
	while (!s.isEmpty())
		cout << s.pop() << endl;
	return 0;
}
template<typename T> Stack<T>::Stack(int size)
{
	space = new T[size];
	top = 0;
}
template<typename T> Stack<T>::~Stack() {
	delete[]space;
}
template<typename T> bool Stack<T>::isEmpty() {
	return top == 0;
}
template<typename T> bool Stack<T>::isFull() {
	return top == 1024;
}
template<typename T>void Stack<T>::push(T data) {
	space[top++] = data;
}
template<typename T>T Stack<T>::pop() {
	return space[--top];
}

这只适用于单文件。多文件的话既要包含.h 文件 也要包含 .cpp 文件。因为由于编译器需要通过这些"模板"为实例化类型生成实际的方法代码,因此任何使用了 模板的源代码文件中,编译器都应该能同时访问类模板定义和实现。

友元

在 .h文件中写就好了,在 .cpp 内写有点难

mylist.h
#ifndef __MYLIST_H__
#define __MYLIST_H__
#include <iostream>
using namespace std;
namespace listspace{
template<class ItemType>
class GenericList
{
public:
GenericList(int max);
~GenericList( );
int length( ) const;
void add(ItemType new_item);
bool full( ) const;
void erase( );
friend ostream&operator <<(ostream& outs, const GenericList<ItemType>& the_list)
{
for (int i = 0; i < the_list._curIdx; i++)
outs << the_list._item[i] << endl;
return outs;
}
private:
ItemType *_item;
int _maxLength;
int _curIdx;
};
}//listspace
#endif //__MYLIST_H__

mylist.cpp
#ifndef __MYLIST_CPP__
#define __MYLIST_CPP__
#include <iostream>
#include <cstdlib>
#include "mylist.h"
using namespace std;
namespace listspace{
template<class ItemType>
GenericList<ItemType>::GenericList(int max)
: _maxLength(max),_curIdx(0)
{
_item = new ItemType[max];
}
template<class ItemType>
GenericList<ItemType>::~GenericList( )
{
delete [] _item;
}
template<class ItemType>
int GenericList<ItemType>::length( ) const
{
return (_curIdx);
}
template<class ItemType>
void GenericList<ItemType>::add(ItemType new_item)
{
if ( full( ) )
{
cout << "Error: adding to a full list.\n";
exit(1);
}
else
{
_item[_curIdx] = new_item;
_curIdx = _curIdx + 1;
}
}
template<class ItemType>
bool GenericList<ItemType>::full( ) const
{
return (_curIdx == _maxLength);
}
template<class ItemType>
void GenericList<ItemType>::erase( )
{
_curIdx = 0;
}
}
#endif

main.cpp
#include <iostream>
#include "mylist.h"
#include "mylist.cpp"
using namespace std;
using namespace listspace;
int main( )
{
GenericList<int> first_list(2);
first_list.add(1);
first_list.add(2);
cout << "first_list = \n"
<< first_list;
GenericList<char> second_list(10);
second_list.add('A');
second_list.add('B');
second_list.add('C');
cout << "second_list = \n"
<< second_list;
return 0;
}
hpp

由于编译器需要通过这些"模板"为实例化类型生成实际的方法代码,因此任何使用了 模板的源代码文件中,编译器都应该能同时访问类模板定义和方法定义。 C++中的编译是以文件为单位的,然后链接阶段完成链接。如果模板的声明与实现分开, 这种机制显然会导致看不到模板的全貌,而致编译失败。所以常将类模板定义和方法定义 放到一起,该类模板文件的后缀常为.hpp,以示与普通文件的区别。在使用的时候, #include"xx.hpp"。

#ifndef __MYLIST_HPP__
#define __MYLIST_HPP__
#include <iostream>
using namespace std;
namespace listspace{
template<class ItemType>
class GenericList;
template<class ItemType>
ostream&
operator <<
(ostream& outs,const GenericList<ItemType>& the_list);
template<class ItemType>
class GenericList
{
public:
GenericList(int max);
~GenericList( );
int length( ) const;
void add(ItemType new_item);
bool full( ) const;
void erase( );
friend ostream& operator << <>
(ostream& outs, const GenericList<ItemType>& the_list);
private:
ItemType *_item;
int _maxLength;
int _curIdx;
};
}//listspace
#include <iostream>
#include <cstdlib>
#include "mylist.h"
using namespace std;
namespace listspace{
template<class ItemType>
GenericList<ItemType>::GenericList(int max)
: _maxLength(max), _curIdx(0)
{
_item = new ItemType[max];
}
template<class ItemType>
GenericList<ItemType>::~GenericList( )
{
delete [] _item;
}
template<class ItemType>
int GenericList<ItemType>::length( ) const
{
return (_curIdx);
}
template<class ItemType>
void GenericList<ItemType>::add(ItemType new_item)
{
if ( full( ) )
{
cout << "Error: adding to a full list.\n";
exit(1);
}
else
{
_item[_curIdx] = new_item;
_curIdx = _curIdx + 1;
}
}
template<class ItemType>
bool GenericList<ItemType>::full( ) const
{
return (_curIdx == _maxLength);
}
template<class ItemType>
void GenericList<ItemType>::erase( )
{
_curIdx = 0;
}
template<class ItemType>
ostream& operator << (ostream& outs,const GenericList<ItemType>&
the_list)
{
for (int i = 0; i < the_list._curIdx; i++)
outs << the_list._item[i] << endl;
return outs;
}
}
#endif

即在一个文件中即声明又实现,调用时只包含 .hpp文件

io stream

我们一直再用cin 和 cout 充当了printf 与 scanf 的功能,但他们并不是函数,而是类对象

流类的特性
不可赋值和复制
#include <iostream>
#include <fstream>
using namespace std;
void print(fstream fs)    //只能写fstream& fs
{
}
int main()
{
	fstream fs1,fs2;
	fs1 = fs2;
	fstream fs3(fs2);
	print(fs3);
	return 0;
}
编译错误

这也就是为什么在类中重载 >> 和 << 时要写为 void operator<<(ostream& out,A a){},这里ostream &的原理就是流类不可复制和赋值

缓冲

下面几种情况会导致刷缓冲

1,程序正常结束,作为 main 函数结束的一部分,将清空所有缓冲区。

2,缓冲区满,则会刷缓冲。

3,endl, flush 也会刷缓冲。

重载了 >> 和 <<
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	fstream fs("abc.txt", ios::out | ios::in | ios::trunc);
	if (!fs)
		cout << "open error" << endl;
	fs << 1<<" " << 2 << " " << 3;  //像文件fs内输入
	fs.seekg(0, ios::beg);  //将指针指向开头位置
	int a, b, c;
	fs >> a >> b >> c;              //文件fs向a b c 变量内输入 
	cout << a << b << c;
	return 0;
}
状态位
状态位的意义

大部分情况下,我们可能并不关心这些标志状态位,比如我们以前用到的 cin/cout。 但是在循环读写中,这些标志位确实大用用途。比如,用于判断文件结束标志的位

状态位操作函数
  • eof() :如果读文件到达文件末尾,返回 true

  • bad() : 如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态 的文 件进行写入时,或者我们要写入的设备没有剩余空间的时候。

  • fail() :除了与 bad() 同样的情况下会返回 true 以外,加上格式错误时也返回 true , 例如当想要读入一个整数,而获得了一个字母的时候。或是遇到 eof

  • good() :这是最通用的:如果调用以上任何一个函数返回 true 的话,此函数

  • clear :标识位一旦被置位,这些标志将不会被改变,要想重置以上成员函数所检查的状态标志,你可以使用成员函数 clear(),没有参数。比如:通过函数移动文件指针,并不会使 eofbit 自动重置

cin 的 get函数

声明:

  • int get();
  • istream& get(char& c);
  • istream& get(char* s,streamsize n);
  • istream& get(char* s,streamsizse n,char delim);

解释:

前两个:

int main()
{
	char c;
	c = cin.get(); 相当于将 输入字符的ASCII码赋值给字符c
	cout << c;
}

int main()
{
	char c;
	cin.get(c);
	cout << c;
}

后两个:

int main()
{
	char buf[10];
	while(cin.get(buf,3))
	{
		cout<<buf<<endl;
	}
}

int main()
{
	char buf[1024];
	while (cin.get(buf, 20,'x'))  遇到字符x就停止,不读 x
	{
		cout << buf << endl;
	}
}

cin.get(buf,3):是从输入流中读取两个字符后在其后方也就是第三个字符加上 ‘\0’

getline

声明:

istream& getline (char* s, streamsize n );
istream& getline (char* s, streamsize n, char delim );

get 和 getline 解决了 cin 在输入时不能有空格的情况

char buf[20];
cin>>buf;    //在输入时遇到空格就结束
cin.getline(buf,20)    //解决了出入带空格的问题
another

get 越不过去的截止字符,getline 越过丢弃的截止字符

getline 版本:
int main()
{
	char buf[1024];
	cin.getline(buf, 20, '/');
	cout << buf << endl;
	cin.getline(buf, 20, '/');
	cout << buf;
}
终端输入:i like C/ i like cPP also/
终端输出:  i like C
 		  i like cPP also
 		  
get版本:
int main()
{
	char buf[1024];
	cin.get(buf, 20, '/');
	cout << buf << endl;
	cin.get(buf, 20, '/');
	cout << buf;
}
终端输入:i like C/ i like CPP also/
终端输出:i like C

ignore函数:

int main()
{
	char ch[20];
	cin.get(ch, 20, '/'); // i like c/ i like C++ also/
	cout << "the first part is :" << ch << endl;
	cin.ignore(10, 'i');
	cin.get(ch, 20, '/');
	cout << "this second part is:" << ch << endl;
	return 0;
}
终端输入:
i like c/ i like C++ also/
终端输出:
the first part is :i like c
this second part is: like C++ also    // 这里没有 i

cin.ignore( a, ch )。它表示从输入流 cin 中提取字符,提取的字符被忽略,不被使用。而每抛弃一个字符,它都要进行计数和比较字符:如果计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。

由于会把截至的字符也忽略掉,这让人很不爽

解决办法:

int main()
{
	char ch[20];
	cin.get(ch, 20, '/'); // i like c/ i like C++ also/
	cout << "the first part is :" << ch << endl;
	cin.ignore(10, 'i');
	cin.putback('i');   //在输入流指针的地方加入一个字符
	cout<<(char)cin.peek()<<endl;
	//窥视当前指针,文件指针未发生移动,返回int
	cin.get(ch, 20, '/');
	cout << "this second part is:" << ch << endl;
	return 0;
}
终端输入:
i like c / i like c++ also/
终端输出:
the first part is :i like c
i
this second part is:i like c++ also
if(!fs) / while(cin)

在判断文件打开成功与否或是连续从流中读取数据时,就要用到对流对像的操作,比 如 if(!fs)和 while(cin>>val),我们都知道 cin 是一个流对象,而>>运算符返回左边的流对象, 也就是说 cin>>val 返回 cin,于是 while(cin>>val)就变成了 while(cin),问题就变成了一个 流对象在判断语句中的合法性。我们自己定义一个类,然后定义该类的对象,然后使用 if 语句来判断它是不合法的。 这说明,流对象具有某种转换函数,可以将一个流对象转换成判断语句可以识别的类型。

打开 iostream.h 文件,找到 cin 的定义,发现是来自于 istream.h,其中的模板类 basic_istream 继承自 basic_ios,打开 basic_ios 的定义,发现它有两个重载函数。operator void *() const 和 operator!() const。这两个函数使得流对象可作为判断语句的内容。

operator void*() const //转化函数 A 类对象-> void *对象
{
return this->fail() ? 0 : const_cast<basic_ios*>(this);
}
bool operator!() const //运算符重载函数 对象调用 operator!()
{
return this->fail();
}

自实现:

#include<iostream>
using namespace std;
class A
{
public:
A(){}
~A(){}
operator void* ()const     //操作符转化函数,将流类转化为unsinged int 类型
{
cout<<"operator void* () cast to void*; 
return (void *)this;
}
bool operator!() const      //重载了 !号
{
cout<<"bool operator!() return bool;
return true;
}
};
int main()
{
A a;
if(a) cout<<"first"<<endl;
if(!a) cout<<"second"<<endl;
return 0;
}
终端输出:
operator void* () cast to void*; first
bool operator!() return bool; second
FILE stream 文件流

对文件的操作过程可按照四步 ,即:定义文件流类的对象,打开文件,对文件进行读写操作,关闭文件

流对象的定义
ifstream ifile  //定义文件输入流对象
ofstream ofile  //定义文件输出流对象
fstream iofile  //定义文件输入输出流对象
流对象相关文件

open 函数:

定义文件流对象后,就可以利用其成员函数 open() 打开需要操作的文件,

void open(const unsigned char *filename,int mode,int access:openprot)

其中:filename 是一个字符型指针,指定了要打开的文件名;mode 指定了文件的打开方式,其值如下表所示;access 指定了文件的系统属性,取默认即可

ios::in		0x01	以输入(读)方式打开文件,若文件不存在则报错
ios::out	0x02	以输出(写)方式打开文件,若文件不存在则创建
ios::app	0x08	打开一个文件件使新的内容始终添加在文件的末尾,若文件不存在,则创建
ios::trunc	0x10	若文件存在,则清除文件所有内容;若文件不存在,则创建新文件。
ios::binary	0x80	以二进制方式打开文件,缺少时以文本方式打开文件
ios::nocreat0x20	打开一个已有文件,若该文件不存在,则打开失败。
ios::noreplace	0x40	打开的文件已经存在,则打开失败
  1. 在实际使用过程中,可以根据需要将以上打开文件的方式用"|"组合起来
  2. 如果未指明以二进制方式打开文件,则默认是以文本方式打开文件。
  3. 构造函数打开文件
对于 ifstream 流, mode 参数的默认值为 ios::in,
对于 ofstream 流,mode 的默 认值为 ios::out|ios::trunc, 
对于 fstream 流, mode 的默认值为 ios::int|ios::out|ios::trunc
  1. ios::int|ios::out是命名空间 ios 中的一堆枚举
单个字符的输入输出:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	fstream fs("xxx.txt", ios::in | ios::out | ios::trunc);
	for (char i = 'a'; i < 'z'; i++)
		fs << i;
	fs.seekg(0, ios::beg);
	char ch;
	while (fs >> ch)       //  >>是流类的重载不能写为 ch << fs,因为流类对象是fs
		cout << ch << endl;
	return 0;
}
一行的输入输出:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char buf[1024];
	fstream fs;
	fs.open("xxx.txt", ios::in | ios::out | ios::trunc);
		fs << "aaaaaaaaaaa"<<endl;
		fs << "bbbbbbbbbbb" << endl;
		fs << "cccccccccccccc" << endl;
	fs.seekg(0, ios::beg);
	while (fs >> buf)
		cout << buf << endl;
	/*while(fs.getline(buf, 1024))
		cout<<buf<<endl;*/
		fs.close()
	return 0;
}

异常

C 语言中错误的处理,通常采用返回值的方式或是置位全局变量的方式。C++通过异常实现了返回与错误处理的分离

例子

C语言版:

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{
double area;
double s = (x+y+z)/2;
if(x+y>z && y+z>x && x+z >y)
area = sqrt(s*(s-x)*(s-y)*(s-z));
else
return -1.0;
return area;
}
int main()
{
int a, b,c;
float area;
while(1)
{
cin>>a>>b>>c;
if(a>0&&b>0&&c>0)
{
area = triangleArea(a,b,c);
if(area == -1.0)
cout<<"输入的三角形不合法"<<endl;
else
cout<<"Area:"<<area<<endl;
}
}
return 0;
}

C++版:

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{
double area;
double s = (x+y+z)/2;
if(x+y>z && y+z>x && x+z >y)
area = sqrt(s*(s-x)*(s-y)*(s-z));
else
throw -1.0;
return area;
}
int main()
{
int a, b,c;
float area;
while(1)
{
try{
cin>>a>>b>>c;
if(a>0&&b>0&&c>0)
{
area = triangleArea(a,b,c);
cout<<"Area:"<<area<<endl;
}
}catch(double e){
cout<<"return "<<e<<endl;
cout<<"输入的三角形不合法"<<endl;
}
}
return 0;
}
  1. 把可能发生异常的语句放在 try 语句声当中。try 不影响原有语句的执行流程。
  2. 若未发生异常,catch 子语句并不起作用。程序会流转到 catch 子句的后面执行。
  3. 若 try 块中发生异常,则通过 throw 抛出异常。throw 抛出异常后,程序立即离开本函数,转到上一级函数。所以 triangleArea 函数中的 return 语句不会执 行。
  4. throw 抛出数据,类型不限。既可以是基本数据类型,也可以是构造数据类型。
  5. 程序流转到 main 函数以后,try 语句块中 抛出进行匹配。匹配成功,执行 catch 语句,catch 语句执行完毕后。继续执行后面的语句。
  6. 如无匹配,系统调用 terminate 终止程序。
语法:
try{
	被检测抛出异常的语句
}
catch(异常信息的类型 变量名){异常处理语句}
使用条例
  1. 被检测语句必须放在try快中,否则不起作用
  2. try catch 中花括号不可省略
  3. 在一个 try catch 中只能有一个try 快,却可以有多个 catch快。以便不同类型信息匹配
  4. throw 抛出的类型,既可以是系统预定义的标准类型也可以是自定义类型。从 抛出到 catch 是一次复制拷贝的过程。如果有自定义类型,要考虑自定义类型的拷 贝问题。
  5. ,异常匹配,不作类型转化。如果 catch 语句没有匹配异常类型信息,就可以用(…) 表示可以捕获任何异常类型的信息
catch(...)
{
	cout<<"没有匹配的类型"<<endl;
}
  1. try-catch 结构可以与 throw 在同一个函数中,也可以不在同一个函数中,throw 抛出异常后,先在本函数内寻找与之匹配的 catch 块,找不到与之匹配的就转到上 一层,如果上一层也没有,则转到更上一层的 catch 块。如果最终找不到与之匹配 的 catch 块,系统则会调有系统函数 terminate 使程序终止。
抛出异常声明

(1)一个不抛掷任何类型异常的函数可以声明为: void func() throw(); //推荐使用

(2)如果在函数声明中没有包含异常接口声明,则函数可以抛掷任何类型的异常, 例如: void func(); //不推荐

(3)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型。 例如: void func() throw (A, B, C , D); //这个函数 func()能够且只能抛出类型 A B C D 及其子类型的异常。

(4)如果一个函数抛出了它的异常接口声明所不允许抛出的异常,该函数默认行为调 用 terminate 函数中止程序

栈自旋

异常被抛出后,从进入 try 块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值