c++(25)类型转换、异常机制

1、类型转换

通过改变一个变量的类型为别的类型,从而改变该变量的表达方式。

c++提供四种类型转换操作符

 基本语法:int a = 100;  char c = static_cast<char>(a);

  例程

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

class animal{};
class people
{
public:
	int a;
};
class student:public people
{
public:
	int b;
};

int main(void)
{	
	//static_cast
	//基础类型转换,允许
	int a1=99;
	char c1 = static_cast<char>(a1);
	cout<<"c1="<<c1<<endl;
	
	//基础类型指针, 不允许转换
	//int *np = NULL;
	//char *cp = static_cast<char *>(np);
	
	//对象指针,不允许转换
	//people *p = NULL;
	//animal *st = static_cast<animal *>(p);
	
	//转换具有继承关系的对象指针, 允许
	people *p = NULL;
	student *st = static_cast<student *>(p);
	
	//父类转子类,子类转父类。都允许转换
	student *st1 = NULL;
	people *p1 = static_cast<people *>(st1);
	
	//对象引用,同对象指针
	people p2;
	people &rep2 = p2;
	student &st2 = static_cast<student &>(rep2);
	
	//------------------------------------------------
	//dynamic_cast做类型安全检查,大空间转小空间是允许的。子类转父类
	student *st3 = NULL;
	people *p3 = dynamic_cast<people *>(st3);
	
	//但是小空间转大空间的时候(父类转子类),指针访问成员就会不安全,也就不被允许转换。
	//people *p4 = NULL;
	//student *st4 = dynamic_cast<student *>(p4);
	
	//------------------------------------------------
	//const_cast一般用于取消const修饰的指针、引用、或对象指针、对象引用。或者对非const的加上const修饰。
	int a = 10;
	const int &b = a; //此时已经不允许 修改b了  b=20是不被允许的
	int &c = const_cast<int &>(b);
	c = 20;
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
	cout<<"c="<<c<<endl;//打印出来都是一样的
	
	int aa = 100;
	int *bb = &aa;
	const int *cc = const_cast<const int *>(bb);
	//*cc = 200;   已经不被允许
	
	//------------------------------------------------
	
	
	return 0;
}


(1)、static_cast 用于内置的类型转换(int、char),或者是有继承关系的对象指针、对象引用的转换。

(2)、dynamic_cast 只能转换有继承关系的对象指针、或引用。但是dynamic_cast会做类型安全检查。

(3)、const_cast一般用于取消const修饰的指针、引用、或对象指针、对象引用。或者对非const的加上const修饰。

(4)、reintrpret_cast 就是c语言的强转,要注意自己进行安全检查

结论:1、程序员必须清楚的知道要转换的变量,转换前是什么类型,转换后是什么类型,以及转换后有什么后果

2、一般情况下,不建议类型转换,避免进行类型转换。

2、c++异常机制

我们在C语言中处理异常,一般都是通过函数返回值来进行判断并处理。且C语言的异常处理逻辑完全交由程序员管理,必须逐级处理。如果func1中调用func2,又func2调用func3,如果func3返回的异常,func2没有处理,则在func1中可能发生不可预期的问题。

c++中提供了异常处理机制。当出现异常时,由程序员抛出异常,可以跳级捕获,并且一旦发生捕获异常,就必须处理异常。且异常抛出的类型没有做限定,可以是一个int、char等基础数据类型,也可以是一个类的对象、或者是结构体,并且异常本身也是一个类,拥有自己的成员,可以传递足够的信息。

下面看一个简单的案例,介绍了异常的语法

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

int divide(int x, int y)
{
	if (0 == y)
	{
		throw y;//抛出异常
	}
	
	return x/y;
}

void test()
{
	//尝试捕获异常
	try
	{
		divide(10, 0);
	}
	
	//异常处理
	catch(int err)
	{
		//异常是根据类型进行匹配的,所以形参要加上int
		cout<<"错误:除数为"<<err<<endl;
	}
}


void CallDivide(int x, int y)
{
	divide(x, y);
}

//异常处理可以不用逐级响应,可以跳级处理。
void test1()
{
	try
	{
		CallDivide(10, 0);
		//异常应该是出现在CallDivide中,但是c++会跳级、跨函数处理,且一旦有异常出现,就必须会处理
	}
	
	//异常处理
	catch(int err)
	{
		//异常是根据类型进行匹配的,所以形参要加上int
		cout<<"错误:除数为"<<err<<endl;
	}
}

int main(void)
{	
    test();
	test1();
	
	return 0;
}


throw关键字抛出一个异常。

try{  do func...  },执行方法,并尝试捕获异常

catch(数据类型){ 处理异常,do something ...},关键字根据数据类型匹配异常,并进行处理。catch抓住的数据,就是throw抛出的数据。

异常是跨函数的,异常必须被处理,如果不处理,程序会崩溃。有些编译器甚至不允许编译通过。

3、栈解旋

概念意义:在调用func中,出现了异常。异常被抛出且处理掉的时候,func中的局部对象、变量等会被释放。相当于return之前释放栈空间

4、异常接口声明

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

(2)如果函数声明中没有包含异常接口声明,则此函数可以抛出任何类型的异常

(3)一个不抛出任何类型异常的函数可以声明为void func()throw();

(4)如果一个函数抛出了他的异常接口声明锁不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。

注意:C++11已经弃用动态异常规范。

下面是一个抛出类对象的案例,注意分析对象的生命周期

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

class myException
{
public:
	myException(const char *str)
	{
		cout<<"myException(char *str)..."<<endl;
		this->Err = new char[strlen(str)+1];
		strcpy(this->Err, str);
	}
	
	myException(const myException &another)
	{
		cout<<"myException(const myException &another)..."<<endl;
		if (NULL == another.Err)
		{
			this->Err = new char[0+1];
			strcpy(this->Err, "");
		}
		else
		{
			this->Err = new char[strlen(another.Err)+1];
			strcpy(this->Err, another.Err);
		}
	}
	
	myException & operator=(const myException &another)
	{
		cout<<"myException & operator=(const myException &another)..."<<endl;
		if (this->Err == another.Err)
		{
			return *this;
		}
		
		if (this->Err != NULL)
		{
			delete[] this->Err;
			this->Err = NULL;
		}
		
		this->Err = new char[strlen(another.Err)+1];
		strcpy(this->Err, another.Err);
		return *this;
	}
	
	~myException()
	{
		cout<<"~myException()..."<<endl;
		if (this->Err != NULL)
		{
			delete[] this->Err;
			this->Err = NULL;
		}
	}
	
	void showErr()
	{
		cout<<this->Err<<endl;
	}
public:
    char *Err;
};

void func() 
{
	myException p("异常!!!");
	
	throw p;
	//throw "exceptions!!!";
}

int main(void)
{	
	try
	{
		func();
		
	}
	
	catch(char const *str)
	{
		cout<<str<<endl;
	}
	
	//如果使用指针  或者引用来接异常的时候,要注意栈变量的生命周期
	catch(myException err)
	{
		err.showErr();
	}
	
	return 0;
}


执行结果 

5、c++的标准异常类

 (1)在实际使用中,我们往往自己定义个一个异常类,继承自标准异常类。这样有更好的使用便捷性。

(2) 在继承标准异常类时,应当重载父类的what函数和虚析构函数

6、案例:自定义异常类

#include <iostream>
#include <cstring>
#include <memory>
#include <stdexcept>
using namespace std;

class myOutOfRange : public exception
{
public:
	myOutOfRange(const char *str)
	{
		cout<<"myOutOfRange(const char *str)..."<<endl;
		this->err = new char[strlen(str)+1];
		strcpy(this->err, str);
	}

	virtual const char* what() const noexcept
	{
		return err;
	}
	
	virtual ~myOutOfRange()
	{
		/*if (this->err != NULL)
		{
			delete[] this->err;
		}编译器差异,实际上vs上是需要自己手动释放的*/
	}
public:
	char *err;
};

class people
{
public:
	people()
	{
		m_age = 0;
	}
	
	void setAge(int age)
	{
		if (age < 0 || age > 200)
		{
			throw myOutOfRange("年龄应该在0-200之间");
		}
		
		this->m_age = age;
	}
public:
	int m_age;
};

void func() 
{
	people p;
	
	try
	{
		p.setAge(1000);
	}
	
	
	catch(exception err)
	{
		cout<<err.what()<<endl;
	}
}

int main(void)
{	
	func();
	return 0;
}

执行结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值