c++类型转换、异常处理与标准输入输出流

一、类型转换

在c++中类型转换不可以像在c语言中那样强制使用。

1)静态类型转换

 函数原型:	static_cast <目标原型>(原对象);
  • 内置数据类型
void test01()
{
	//内置数据类型
	char a = 'a';
	
	double d = static_cast<double>(a);//能转换成功

	cout << d << endl;
}
  • 自定义数据类型
class Base{
public:
    void fun(){};
};
class Son:public Base{
public:
    void fun(){};
};
class Other{
};
void test2(){
    Base*base=nullptr;
    Son*son=nullptr;

    Son*son2=static_cast<Son*>(base);   //base 转为 Son*类型    向下类型转换  不安全
    Base*base2=static_cast<Base*>(son); //son 转为  Base * 类型      向上类型转换   安全
    
    //base 转为 Other*
	//没有父子关系的两个类型之间是无法转换成功的
	//Other * other = static_cast<Other *>(base);
}

总结:

  • 语法 static_cast<目标类型>(原对象)
  • 对于内置数据类型 是可以转换的
  • 对于自定义数据类型,必须是父子之间的指针或者引用可以转换成功

2)动态类型转换

 函数原型:dynamic_cast<目标类型>(原对象)
  • 内置数据类型与自定义数据类型
void test03()
{
	//内置数据类型 不允许内置数据类型之间的转换
	//char c = 'c';
	//double d = dynamic_cast<double>(c);
	
	//自定义数据类型
	Base * base = NULL;
	Son * son = NULL;

	//base 转为 Son* 类型  不安全  
	//不安全 转换失败
	//Son * son2 = dynamic_cast<Son*>(base);

	//son 转为  Base*   安全
	Base * base2 = dynamic_cast<Base*>(son);

	//base 转为Other* 
	//Other* other = dynamic_cast<Other*>(base);

	//如果发生多态,那么父子之间的转换 总是安全的
	Base * base3 = new Son;
	//将 base3转为  Son*
	Son * son3 = dynamic_cast<Son*>(base3);
}

总结:

  • 对于内置数据类型 不可以转换
  • 对于自定义数据类型
    • 父转子 不安全 转换失败
    • 子转父 安全 转换成功
    • 如果发生多态 ,那么总是安全的,都可以成功

3)常量转换

函数原型:const_cast<目标类型>(原对象)
//3、常量转换
void test04()
{
	//指针之间的转换
	const int * p = NULL;
	//将 const int * 转为 int *
	int * p2 = const_cast<int *>(p);

	//将 p2 转为  const int * 
	const int * p3 = const_cast<const int *>(p2);


	//引用之间的转换
	const int a = 10;
	const int & aRef = a;

	int & aRef2 = const_cast<int &>(aRef);

	//不可以对非指针 或者 非引用 做const_cast转换
	//int b = const_cast<int>(a);
}

总结:

  • 只能对 指针 或者引用之间 使用

二、异常处理

什么是异常处理呢???其实就是程序运行时出现异常的地方先去处理然后再继续执行程序(听君一席话如听一席话???)。

1)基本语法

 异常的三个关键字  try   throw   catch
 		try 试图执行一段可能会出现异常的代码
 		throw出现异常后 抛出异常的关键字   throw + 类型
 		catch  捕获异常  catch(类型)
  • 1、用非异常处理方法处理可能异常情况
int myDivide(int a , int b)
{
	if ( b == 0)
	{
		return -1; //C语言处理异常缺陷在于  返回的值 没有统一,返回的值可以是异常的结果,也可以是正确的结果
	}

	return a / b;

}

void test01()
{
	int a = 10;
	int b = 0;
	int c= myDivide(a,b);
}

当b=0时,c就会被赋值-1,而当b=-10时,c也会被赋值-1,这样情况在众多数据运算时就很难判断b是否真的是为0,得出结论C语言处理异常缺陷在于 返回的值 没有统一,返回的值可以是异常的结果,也可以是正确的结果,所以应该用异常处理来解决。

  • 2、内置数据类型的异常处理
int myDivide(int a , int b)
{
	if ( b == 0)
	{

		//抛出异常
		throw 1;
		//throw 3.14;
		//throw 'a';
	}

	return a / b;
}

void test01()
{
	int a = 10;
	int b = 0;

	//尝试执行一段 可能会出现异常的代码
	try
	{
		int ret = myDivide(a, b);
		cout << "ret 结果为: " << ret << endl;
	}
	catch (int ) //捕获异常
	{
		cout << "int类型的异常的捕获" << endl;
	}
}
  • 3、简单的自定义类型异常
class MyException
{
public:
	void printError()
	{
		cout << "我自己的异常类的错误" << endl;
	}
};
int myDivide(int a , int b)
{
	if ( b == 0)
	{	
		throw MyException(); //抛出一个 MyException 匿名对象
	}
	return a / b;

}

void test01()
{
	int a = 10;
	int b = 0;

	//尝试执行一段 可能会出现异常的代码
	try
	{
		int ret = myDivide(a, b);
		cout << "ret 结果为: " << ret << endl;
	}
	catch (int ) //捕获异常
	{
		cout << "int类型的异常的捕获" << endl;
	}
    catch(MyException x){
        x.printError();
    }
  • 4、异常中的栈解旋
class Person
{
public:
	Person()
	{
		cout << "Person的构造函数" << endl;
	}

	~Person()
	{
		cout << "Person的析构函数" << endl;
	}

};
int myDivide(int a , int b)
{
	if ( b == 0)
	{
		Person p1;
		Person p2;
		throw MyException(); //抛出一个 MyException 匿名对象
	}

	return a / b;

}

void test01()
{
	int a = 10;
	int b = 0;

	//尝试执行一段 可能会出现异常的代码
	try
	{
		int ret = myDivide(a, b);
		cout << "ret 结果为: " << ret << endl;
	}
    catch(...){
        cout<<"出现异常"<<endl;
    }
}

结果:
Person的构造函数
Person的构造函数
Person的析构函数
Person的析构函数
出现异常

代码中catch(…){}这样写的好处就是不用写抛出异常的类型是啥,这样就可以直接捕获其他类型的异常。

  • 栈解旋: 从try代码块开始起,到 throw抛出异常前,所有栈上的对象都被释放掉,释放的顺序和构造的顺序是相反的,这个过程称为栈解旋

总结:

  • 如果想捕获其他类型的异常 catch( … )
  • 如果捕获到的异常不想处理,想继续向上抛出 throw
  • 异常必须要有人处理,如果没有处理,程序会自动调用 terminate函数,使程序中断
  • 可以抛出一个自定义类型的异常 myException
  • 栈解旋:从try代码块开始起,到 throw抛出异常前,所有栈上的对象都被释放掉,释放的顺序和构造的顺序是相反的,这个过程称为栈解旋

2)异常变量生命周期

class MyException
{
public:
	MyException()
	{
		cout << "MyException构造函数调用" << endl;
	}

	MyException(const MyException & e)
	{
		cout << "MyException拷贝构造函数调用" << endl;
	}

	~MyException()
	{
		cout << "MyException的析构函数调用" << endl;
	}
};

void doWork()
{
	throw  MyException();
}

void test01()
{
	try
	{
		doWork();
	}
	//MyException e会调用拷贝构造
	//MyException &e 引用方式 接受  建议用这种方式  节省开销
	//MyException *e 指针方式 接受 抛出  &MyException();匿名对象,对象被释放掉,不可以再操作e了
	//MyException *e 指针方式 接受 抛出 new  MyException(); 堆区创建的对象  记得手动释放 delete e;
	catch (MyException &e)
	{
		cout << "MyException的异常捕获" << endl;

	}
}

总结:

  • MyException e会调用拷贝构造
  • MyException &e 引用方式 接受 建议用这种方式 节省开销
  • MyException *e 指针方式 接受 抛出 &MyException();匿名对象,对象被释放掉,不可以再操作e了
  • MyException *e 指针方式 接受 抛出 new MyException(); 堆区创建的对象 记得手动释放 delete e;
  • 建议使用 引用的方式 去接受对象

3)异常的多态使用

//异常 基类
class BaseException
{
public:
	virtual void printError() = 0;
};

//空指针 异常 
class NULLPointException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "空指针异常" << endl;
	}
};

//越界异常
class OutOfRangeException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "越界异常" << endl;
	}
};

void doWork()
{
	//throw NULLPointException();
	throw OutOfRangeException();
}

void test01()
{
	try
	{
		doWork();
	}
	catch ( BaseException & e)
	{
		e.printError();
	}
}

4)使用标准异常库

#include <stdexcept> //系统标准异常头文件

class Person
{
public:

	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			//年龄越界异常抛出
			throw out_of_range("年龄必须在 0 到 150之间!");
			//throw length_error("年龄必须在 0 到 150之间!");
		}
		this->m_Age = age;
	}

	int m_Age;
};

void test01()
{
	try
	{
		Person p1(151);
	}
	catch (exception & e)
	{
		cout << e.what() << endl;
	}

}

总结:

  • 标准异常头文件 #include< std except>
  • 使用系统异常类 out_of_range(“char *”)
  • 捕获 catch( exception & e ) { cout << e.what() ; };

5)编写自己的异常类

//自己的异常类
class myOutOfRange :public exception
{
public:
	myOutOfRange(char * errorInfo)
	{
		//将char * 转为 string	,隐式转换
		this->m_ErrorInfo = string(errorInfo);
	}
	myOutOfRange(string errorInfo)
	{
		this->m_ErrorInfo = errorInfo;
	}
	virtual  ~myOutOfRange()
	{
	}
	const char *  what() const
	{
		//string 转为 const char * 要.c_str()转换
		return this->m_ErrorInfo.c_str();
	}
	//保存住 用户传入的异常信息的 字符串
	string m_ErrorInfo;
};
class Person
{
public:
	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			//年龄越界异常抛出
			throw myOutOfRange(  "我的异常类 ---- 年龄必须在 0 到 150 之间");
		}
		this->m_Age = age;
	}

	int m_Age;
};

void test01()
{
	try
	{
		Person p1(1111);
	}
	catch (exception & e)
	{
		cout << e.what() << endl;
	}
}

总结:

  • 自己编写的异常类myOutOfRange : public exception需要继承基类并且要重写虚函数what, 函数原型(const char * what() const)
  • char * 和 string之间的转换
    • char * 转 string string的有参构造 string(char *)
    • string 转 const char * 需要用到 .c_str();

三、标准输入输出流

输入输出流中的结构图
在这里插入图片描述

1)标准输入流

/*
cin.get() //一次只能读取一个字符
cin.get(一个参数) //读一个字符
cin.get(两个参数) //可以读字符串
cin.getline()
cin.ignore()
cin.peek()
cin.putback()
*/


void test01()
{
	//cin.get()一次只能读取一个字符

	// a s 
	char c =  cin.get();
	//第一次输出 a
	cout << "c  = " << c << endl;

	c = cin.get();
	//第二次输出 s
	cout << "c  = " << c << endl;

	c = cin.get();
	//第三次输出 换行
	cout << "c  = " << c << endl;

	c = cin.get();
	//第四次  等待下一次输入
	cout << "c  = " << c << endl;

}


void test02()
{
	//cin.get(两个参数) //可以读字符串
	char buf[1024] = { 0};
	cin.get(buf, 1024); //当利用cin.get读取字符串时候,并不会读走换行符,而是遗留在缓冲区中

	char c = cin.get(); 
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区了" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区了" << endl;
	}

	cout << "buf = " << buf << endl;

}


void test03()
{
	char buf[1024] = { 0 };

	cin.getline(buf, 1024);//如果利用cin.getline()读取字符串,函数并不会读取换行符,而是将换行符从缓冲区中扔掉

	char c = cin.get();
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区了" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区了" << endl;
	}

	cout << "buf = " << buf << endl;

}

//cin.ignore()忽略
void test04()
{
	cin.ignore(); //默认忽略1个字符,如果里面有参数N,代表忽略N个字符

	char c = cin.get();
	//输入是 a s
	//输出是  c = 
	cout << "c = " << c << endl;
}

//cin.peek()偷窥
void test05()
{
	char c = cin.peek();

	//输入  : as
	//输出  :
	cout << "c = " << c << endl;

	c = cin.get();

	cout << "c = " << c << endl;
}

//cin.putback() 放回
void test06()
{
	char c = cin.get();

	cin.putback(c); //放回的是原来的位置

	char buf[1024];
	cin.getline(buf, 1024);
	cout << "buf = " << buf << endl;
}

/*
	判断用户输入的内容  是字符串还是数字  1234    abcd    
*/
void test07()
{
	cout << "请输入一个字符串或者数字: " << endl;

	char c =  cin.peek();

	if (c >= '0' && c <= '9')
	{
		int num;
		cin >> num;
		cout << "您输入的是数字:" << num << endl;
	}
	else
	{
		char buf[1024] = { 0 };
		cin >> buf;
		cout << "您输入的是字符串:" << buf<< endl;
	}

}


/*
	让用户输入一个数字,必须在0到10之间的数字,如果输入有误重新输入 
*/
void test08()
{
	cout << "请输入 0 ~ 10 之间的数字" << endl;

	int num;

	while (true)
	{
		cin >> num;

		if (num > 0 && num < 10)
		{
			cout << "输入正确 --- 数字为:" << num << endl;
			break; //输入正确 退出循环
		}

		cout << "输入有误,请重新输入:" << endl;

		//fail()缓冲区中的标志位  0代表正常    1  代表异常
		cin.clear(); 
		cin.sync(); //清空标志位 并且刷新缓冲区
		cin.ignore(); //VS2015以上 需要做 忽略
		//cout << "cin.fail = " << cin.fail() << endl;
	}

}

总结:

  • cin.get() 从缓冲区读取一个字符
  • cin.get( 两个参数) 读取字符串, 换行符 遗留在缓冲区
  • cin.getline(两个参数 ) 读取字符串 ,换行符 不会读取换行符,并且将换行符从缓冲区中扔掉
  • cin.ignore() 忽略 ,默认忽略1个字符,如果(N)代表忽略N个字符
  • cin.peek() 偷窥,从缓冲区中偷窥第一个字符,并不会取走
  • cin.putback()放回,放回到缓冲区原来的位置

2)标准输出流

#include <iomanip> //标准输入流 使用控制符方式格式化输出的头文件
/*
cout.put() //向缓冲区写字符
cout.write() //从buffer中写num个字节到当前输出流中。
*/

void test01()
{
	//cout.put('a').put('b').put('c');

	/*char buf[] = "hello world";
	cout.write(buf,strlen(buf));*/

	cout << "hello world" << endl;
}

void test02()
{
//通过流成员函数
	int number = 99;
	cout.width(20); //预留20空间
	cout.fill('*'); //填充
	cout.setf(ios::left); //左对齐
	cout.unsetf(ios::dec); //卸载十进制
	cout.setf(ios::hex);   //安装十六进制
	cout.setf(ios::showbase); //设置显示进制 基数
	cout.unsetf(ios::hex); //卸载十六进制
	cout.setf(ios::oct);  //安装八进制
	cout << number << endl;
}


//使用控制符
void test03(){

	int number = 99;
	cout << setw(20) //设置宽度
		<< setfill('~') //填充
		<< setiosflags(ios::showbase) //显示进制基数
		<< setiosflags(ios::left)  //设置左对齐
		<< hex  //安装十六进制
		<< number
		<< endl;
	
}

总结:

  • cout.put() cout.write() 利用成员函数 输出内容
  • 通过流成员函数
    • cout.width(20); //预留20空间
    • cout.fill(’*’); //填充
    • cout.setf(ios::left); //左对齐
    • out.unsetf(ios::dec); //卸载十进制
    • cout.setf(ios::hex); //安装十六进制
    • cout.setf(ios::showbase); //设置显示进制 基数
    • cout.unsetf(ios::hex); //卸载十六进制
    • cout.setf(ios::oct); //安装八进制
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值