Review cpp day10

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Review cpp day10相关的知识,希望对你有一定的参考价值。

回顾:
Review cpp day01
Review cpp day02
Review cpp day03
Review cpp day04
Review cpp day05
Review cpp day06
Review cpp day07
Review cpp day08
Review cpp day09

二十三、运行时的类型信息//了解

1、typeid操作符

#include <typeinfo>
typeid(类型/对象);
  • 返回typeinfo的对象,用于描述类型信息,里面包含一个name的成员函数,可以将类型信息转化为字符串形式。
  • typeinfo提供了对“==”和“!=”操作符重载的支持,通过它们可以直接进行类型之间的比较。

06typeid.cpp

#include <iostream>
#include <typeinfo>
#include <cstring>
using namespace std;

class X{
	virtual void foo(){}
};
class Y:public X{
	void foo(){}
};
class Z:public X{
	void foo(){}
};

void func(X& x){
	//if(!strcmp(typeid(x).name(), "1Y")){
	if(typeid(x) == typeid(Y)){
		cout << "针对Y对象处理" << endl;
	}
	//else if(!strcmp(typeid(x).name(), "1Z")){
	else if(typeid(x) == typeid(Z)){
		cout << "针对Y对象处理" << endl;
	}
	else{
		cout << "针对X对象处理" << endl;
	}
}
int main(void){
	int a;
	cout << typeid(int).name() << endl;//i
	cout << typeid(a).name() << endl;//i
	
	int arr[10];
	cout << typeid(arr).name() << endl;//A10_i;

	int* p;
	cout << typeid(p).name() << endl;//Pi
	
	int *arr1[10];
	int (*arr2)[10];
	cout << typeid(arr1).name() << endl;//A10_Pi
	cout << typeid(arr2).name() << endl;//PA10_i

	cout << typeid(int (*)(char)).name() << endl;//PFicE

	cout << typeid(Y).name() << endl;//1Y

	Y y;
	Z z;
	X x;
	func(y);
	func(z);
	func(x);

	return 0;
}

在这里插入图片描述

2、动态类型转换操作符

  • 语法:目标类型变量 = dynamic_cast<目标类型>(源类型变量);
  • 适用场景:用于具有多态继承关系的父子类指针或引用的显式转换。

注: 在转换过程中,会检查目标对象的类型和期望转换的类型是否一致,如果一致转换成功,否则转换失败。如果转换的是指针,返回NULL表示失败,如果转换的是引用,抛出“bad_cast”异常表示转换失败。

07dynamic.cpp

#include <iostream>
using namespace std;

class A{
	virtual void foo(void){}
};
class B:public A{
	void foo(void){}
};
class C:public A{
	void foo(void){}
};
int main(void){
	B b;
	A* pa = &b;
	//B* pb = static_cast<B*>(pa);//合理
	//C* pc = static_cast<C*>(pa);//编译器能编译通过,但这种转换不合理
	B* pb = dynamic_cast<B*>(pa);
	//C* pc = dynamic_cast<C*>(pa);//error,可以发现转换不合理而报错
	cout << "pa=" << pa << endl;
	cout << "pb=" << pb << endl;

	A& ra = b;
	//C& rc = dynamic_cast<C&)(ra);//error,可以发现转换不合理而报错
	
	cout << "pc=" << pc << endl;//NULL
	return 0;
}

二十四、异常(EXception)

1、开发中常见的错误

  • 1)语法错误
  • 2)逻辑错误
  • 3)功能错误
  • 4)设计缺陷
  • 5)需求不符
  • 6)环境异常
  • 7)操作不当

2、传统C中错误处理机制//了解

  • 1)通过返回值表示错误
    • 优点:函数调用路径中所有的局部函数对象都能被正确地析构,不会发生内存泄漏。
    • 缺点:错误处理的流程比较复杂,需要逐层判断,代码臃肿。

01error.cpp

#include <iostream>
#include <cstdio>
using namespace std;


class A{
publuc:
	A(void){
		cout << "A::A()" << endl;
	}
	~A(void){
		cout << "A::~A()" << endl;
	}
};
int func3(void){
	A a;
	FILE* fp = fopen("xx.txt", "r");
	if(fp  = NULL){
		cout << "文件打开失败!" << endl;
		return -1;
	}
	//...
	fclose fp;
	fp = NULL;
}
int func2(void){
	A = a;
	if(func3() == -1){
		return -1;
	}
	//...
	return 0;
}
int func1(void){
	A a;
	if(func2 == -1){
		return -1;
	}
	//...
	return 0;
}
int main(void){
	if(func1 == -1){
		return -1;
	}
	return 0;
}
A::A()
A::A()
A::A()
文件打开失败!
A::~A()
A::~A()
A::~A()
  • 2)通过远程跳转处理错误
    • 优点:不需要逐层判断,一步到位的错误处理,代码精炼。
    • 缺点:函数调用路径中的局部对象失去被析构的机会,有内存泄漏的风险。

02error.cpp

#include <iostream>
#include <cstdio>
#include <csetjmp>
using namespace std;

jmp_buf g_env;

class A{
publuc:
	A(void){
		cout << "A::A()" << endl;
	}
	~A(void){
		cout << "A::~A()" << endl;
	}
};
int func3(void){
	A a;
	FILE* fp = fopen("xx.txt", "r");
	if(fp  = NULL){
		longjmp(g_env, -1);
	}
	//...
	fclose(fp);
	fp = NULL;
}
int func2(void){
	A  a;
	func3();
	//...
	return 0;
}
int func1(void){
	A a;
	func2();
	//...
	return 0;
}
int main(void){
	if(setjmp(g_env) == -1){
		cout << "文件打开失败" << endl;
	}
	func1();
	return 0;
}
A::A()
A::A()
A::A()
文件打开失败!

3、C++异常机制

  • 结合两种传统错误处理的优点,同时避免它们的缺点,在形式实现一步到位的错误处理,同时保证所有的局部对象得到正确的析构。

4、异常语法

  • 1)异常抛出: throw 异常对象;

eg:

	throw -1;
	thow "error";
	//--------------------
	class FileError();
	throw FileError();
  • 2)异常捕获
	try{
		可能引发异常的语句;
	}
	catch(异常类型1){
		针对异常类型1的处理;
	}
	catch(异常类型2){
		针对异常类型2的处理;
	}
	catch(...){
		针对其他异常类型的处理;
	}

03error.cpp

#include <iostream>
#include <cstdio>
using namespace std;

class A{
public:
	A(void){
		cout << "A::A()" << endl;
	}
	~A(void){
		cout << "A::~A()" << endl;
	}
};
class FileError{
public:
	FileError(const string& file, int line){
		cout << "出错位置:" << file << "," << line << endl;
	}
};
int func3(void){
	A a;
	FILE* fp = fopen("xx.txt", "r");
	if(fp == NULL){
		throw FileError(__FILE__, __LINE__);
		throw -1;//抛出异常
		cout << "test1" << endl;
	}
	//...
	fclose(fp);
	fp = NULL;
	return 0;
}
int func2(void){
	A a;
	func3();
	cout << "test2" << endl;
	//...
	return 0;
}
int func1(void){
	A a;
	func2();
	cout << "test3" << endl;
	//...
	return 0;
}
int main(void){
	try{
		func1();	
		//.....
	}
	catch(int ex){
		if(ex == -1){
			cout << "文件打开失败" << endl;
			return -1;
		}
	}
	catch(FileError& ex){
		cout << "文件打开失败!" << endl;
		return -1;
	}

	return 0;
}
A::A()
A::A()
A::A()
出错位置:03exception.cpp,21
A::~A()
A::~A()
A::~A()
文件打开失败!

注: 子类的异常处理应该写在基类处理的前面,否则会发生向上造型提前捕获异常。(catch子句根据异常的类型自上而下顺序匹配,而不是最匹优配,所以对子类类型的异常捕获不要放在对基类类型异常捕获的后面,否则前者将被后者提前截取。)

04exception.cpp

#include <iostream>
using namespace std;

class ErrorA{};
class ErrorB:public ErrorA{};

int main(void){
	try{
		//...
		throw ErrorA();
		throw ErrorB();
		//...
	}
	catch(ErrorB& ex){
		cout << "捕获到ErrorB的异常!" << endl;
		return -1;
	}
	catch(ErrorA& ex){
		cout << "捕获到ErrorA的异常!" << endl;
		return -1;
	}
	return 0;
}

5、函数异常说明

  • 1)定义: void func(形参表) throw(异常类型表){//函数体}
  • 2)函数的异常说明是一种承诺,表示该函数所抛出的异常不会超出所说明的范围,如果抛出了异常以外的类型,该异常无法正常捕获,而会被系统捕获终止进程。
  • 3)函数异常说明的两种极端形式
    • 不写异常说明,表示可以抛出任何异常;
    • 空异常说明,throw(),表示不会抛出任何异常。
  • 4)函数的声明和定义如果分开写,要保证异常说明一致。

05exception.cpp

#include <iostream>
using namespace std;

class FileError{};
class MemoryError{};
//函数的声明
void func(void) throw(int, FileError, MemoryError);
//函数的定义
void func(void) throw(int, FileError, MemoryError){
	//...
	throw FileError();
}
int main(void){
	try{
		func();
	}
	catch(int ex){
		cout << "int类型异常" << endl;
		return -1;
	}
	catch(FileError& ex){
		cout << "FileError类型异常" << endl;
		return -1;
	}
	catch(MemoryError& ex){
		cout << "MemoryError类型异常" << endl;
		return -1;
	}
	return 0;
}
  • 5)如果基类中虚函数带有异常说明,那么该函数在子类中的覆盖版本不能说明比基类抛出更多的异常,否则将会因为“放松throw限定”导致编译失败。

06exception.cpp

#include <iostream>
using namespace std;

class Base{
public:
	virtual void func(void)throw(int, char){}
	virtual ~Base(void) throw(){}
};
class Derived:public Base{
	void func(void) throw(int,char){}
	//void func(void) throw(int){}//ok
	//void func(void) throw(int,char,double){}//error
	~Derived(void) throw(){}
	//~Derived(void){}//error
};
int main(void){
	return 0;
}

6、标准异常类:exception

class exception{
public:
	exception() throw() {}
	virtual ~exception() throw() {}
	virtual const char* what() const throw();
};

07exception.cpp

#include <iostream>
using namespace std;

class FileError:public exception{
public:
	~FileError(void)throw(){};
	const char* what(void) const throw(){
		cout << "针对文件错误的处理" << endl;
		return "FileError";
	}
};
class MemoryError:public exception{
public:
	~MemoryError(void)throw(){};
	const char* what(void) const throw(){
		cout << "针对内存错误的处理" << endl;
		return "MemoryError";
	}
};
void func() throw(FileError, MemoryError){
	throw FileError();
	//throw MemoryError();
}
int main(void){
	try{
		//char* p = new char[0xffffffff];
		func();
	}	
	catch(exception& ex){
		cout << ex.what() << endl;
	}
	return 0;
}

7、构造和析构函数中的异常

  • 1)构造函数可以抛出异常,但是对象将会被不完整构造,这样对象析构函数无法正常被执行;所以在构造函数抛出异常之前,需要手动地销毁在异常产生之前所分配的动态资源。
  • 2)析构函数不要抛出异常。

08exception.cpp

#include <iostream>
#include <cstdio>
using namespace std;

class A{
public:
	A(void){
		cout << "A::A()" << endl;
	}
	~A(void){
		cout << "A::~A()" << endl;
	}
Review cpp day04

Review cpp day09

Review cpp day07

Review cpp day03

Review cpp day08

Review cpp day06