类型转换
C++提供一种新的类型转换语法,好处是更清晰的表明自己要干什么,程序员可以扫一眼就知道一个强制转换的目的。
静态类型转换 static_cast
用途
1. 允许内置数据类型转换
2. 允许父子之间的指针或者引用的转换
语法
static_cast<目标类型>(原变量/原对象)
动态类型转换 dynamic_cast
**不允许内置数据类型转换*8
允许父子之间指针或者引用的转换(在下行转换时 多一个类型检查)
父转子 不安全的 转换失败
子转父 安全 转换成功
如果发生多态,总是安全,可以成功
语法
dynamic_cast<目标类型>(原变量/原对象)
常量转换 const_cast
只允许 指针或者引用 之间转换
语法
const _cast<目标类型>(原变量/原对象)
重新解释转换(啥都能转)
reinterpret_cast 最不安全一种转换,不建议使用
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//1. 静态类型转换 static_cast
void test01()
{
//1. 允许内置数据类型之间转换
char a = 'a';
double d = static_cast<double>(a);
cout << d << endl;
}
class Base {};
class Son :public Base {};
class Other {};
void test02()
{
Base* base = NULL;
Son* son = NULL;
// 语法: static_cast<目标类型>(原对象)
// 父子之间的指针或者引用可以转换
//将base 转为Son* 父转子, 向下类型转换 不安全
Son* son2 = static_cast<Son*>(base);
//son 转为 Base * 子转父 向上类型转换 安全
Base* base2 = static_cast<Base*>(son);
//base转为Other* 报错
Other* other = static_cast<Other*>(base);
}
//2. 动态类型转换 dynamic_cast
void test03()
{
//char c = 'c';
//double d = dynamic_cast<double>(c);
}
void test04()
{
Base* base = NULL;
Son* son = NULL;
//将base 转为Son* 父转子, 向下类型转换 不安全
Son* son2 = dynamic_cast<Son*>(base);
//son 转为 Base * 子转父 向上类型转换 安全
Base* base2 = dynamic_cast<Base*>(son);
}
//3. 常量转换 const_casr
void test05()
{
const int* p = NULL;
int* pp = const_cast<int*>(p);
const int* ppp = const_cast<const int*>(pp);
//不可以将非指针或非引用做const_cast 转换,因为放的地方不一样,变量放在栈上
//对引用
int num = 10;
int& numRef = num;
const int& num2 = const_cast<const int&>(numRef);
}
// 4. 重新解释转换 reinterpret_cast 最不安全一种转换,不建议使用。啥都能转
void test06()
{
int a = 10;
int* p = reinterpret_cast<int*>(a);
Base* base = NULL;
//base 转 Other *
Other* other = reinterpret_cast<Other*>(base);
}
异常的基本语法
C语言中处理异常的两种方法
- 用整型的返回值标识错误。
- 使用errno宏(可以简单理解为一个全局整型变量)去记录错误。
缺点: 一是不一致,有的返回0是成功,有的返回值0是失败。 二是函数返回值只能有一个,返回错误代码就不能返回其他值。
C++异常的处理关键字
关键字
try throw catch
-
可以出现异常的代码 放到 try块
-
利用throw抛出异常
-
利用catch捕获异常
-
catch( 类型) 如果想捕获其他类型 catch(…)
-
如果捕获到的异常不想处理,而继续向上抛出,利用 throw
-
异常必须有函数进行处理,如果都不去处理,程序自动调用 terminate函数,中断掉
-
异常可以是自定义数据类型
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyExcept
{
public:
void printError()
{
cout << "我自己的异常" << endl;
}
};
int myDivision(int a, int b)
{
if (b == 0)
{
//return -1;//C返回异常的方式
//throw - 1;//抛出int类型的异常
//throw 'a';//抛出char类型的异常
//throw 3.13;//抛出double类型的异常
//string str = "abc";
//throw str;//抛出string类型的异常
throw MyExcept();//抛出MyException的匿名对象,此处是懒得起名了
}
return a / b;
}
void test01()
{
int a = 10;
int b = 0;
// C语言处理异常有缺陷: 返回值不统一,返回值只有一个无法区分是结果还是异常
//int ret = myDivision(a, b);
//if (ret == -1)
//{
// cout << "异常" << endl;
//}
try
{
myDivision(a, b);
}
catch (int)
{
cout << "int 类型异常的捕获" << endl;
}
catch (char)
{
cout << "char类型异常的捕获" << endl;
}
catch (double)
{
//捕获到了异常但是不想处理,继续向上抛出异常,直接用throw就可以了
//异常必须有函数进行处理,如果没有任何处理,程序自动调用terminate函数,让程序中断
throw;
cout << "double类型异常的捕获" << endl;
}
catch (MyExcept e)
{
e.printError();
}
catch (...)
{
cout << "其他类型异常的捕获" << endl;
}
}
int main()
{
try
{
test01();
}
catch (double)
{
cout << "main函数中double类型异常的捕获" << endl;
}
catch (...)
{
cout << "main函数中其他类型异常的捕获" << endl;
}
system("pause");
return EXIT_SUCCESS;
}
栈解旋
从try代码块开始,到throw抛出异常之前,所有栈上的数据都会被释放掉,释放的顺序和创建顺序相反的,这个过程我们称为栈解旋
异常的接口声明
在函数中 如果限定抛出异常的类型,可以用异常的接口声明
语法: void func()throw(int ,double)
throw(空)代表 不允许抛出异常
异常变量的生命周期
-
抛出的是 throw MyException(); catch (MyException e) 调用拷贝构造函数 效率低
-
抛出的是 throw MyException(); catch (MyException &e) 只调用默认构造函数 效率高 推荐
-
抛出的是 throw &MyException(); catch (MyException *e) 对象会提前释放掉,不能在非法操作
-
抛出的是 new MyException(); catch (MyException *e) 只调用默认构造函数 自己要管理释放
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyExcept
{
public:
MyExcept()
{
cout << "MyExcept的默认构造函数调用" << endl;
}
MyExcept(const MyExcept &e)
{
cout << "MyExcept的拷贝构造函数调用" << endl;
}
~MyExcept()
{
cout << "MyExcpet的析构函数调用" << endl;
}
};
void doWork()
{
throw new MyExcept();
}
void test01()
{
try
{
doWork();
}
//抛出是 throw MyExcept(); catch (MyExcept e) 调用拷贝构造函数 效率低
//抛出是 throw MyExcept(); catch (MyExcept &e) 只调用默认构造函数 效率高 推荐
//抛出是 throw MyExcept(); catch (MyExcept *e) 对象会提前释放掉,不能在非法操作
//抛出是 throw new MyExcept(); catch (MyExcept *e) 只调用默认构造函数,要自己管理释放 delete
catch (MyExcept * e)
{
cout << "自定义异常捕获" << endl;
delete e;
}
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
异常的多态使用
1. 提供基类异常类
class BaseException
纯虚函数 virtual void printError() = 0;
2.子类空指针异常 和 越界异常 继承 BaseException
3. 重写virtual void printError()
4. 测试 利用父类引用指向子类对象
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//异常的基类
class BaseExcept
{
public:
virtual void printError() = 0;
};
//空指针异常
class NULLPointerExcept :public BaseExcept
{
public:
virtual void printError()
{
cout << "空指针异常" << endl;
}
};
//越界异常
class OutOfRangeExcept :public BaseExcept
{
public:
virtual void printError()
{
cout << "越界异常" << endl;
}
};
void doWork()
{
//throw NULLPointerExcept();
throw OutOfRangeExcept();
}
void test01()
{
try
{
doWork();
}
catch (BaseExcept& e)
{
e.printError();
}
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
系统标准异常
-
引入头文件
#include <stdexcept> -
抛出越界异常
throw out_of_range(“…”) -
获取错误信息
catch( exception & e ) e.what();
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<stdexcept>
class Person
{
public:
Person(int age)
{
if (age < 0 || age>150)
{
throw out_of_range("年龄必须在0~150之间");
}
else
{
this->m_Age = age;
}
}
int m_Age;
};
void test01()
{
try
{
Person p(151);
}
//catch (out_of_range& e) 也可以像下面这样直接用多态,省得记;一般我们主要做catch操作
catch(exception & e)
{
cout<<e.what()<<endl;
}
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
编写自己的异常类
-
编写myOutofRange 继承 Exception类
-
重写 virtual const char * what() const
-
将sting 转为 const char *
.c_str() -
const char * 可以隐式类型转换为 string 反之不可以
-
测试,利用多态打印出错误提示信息
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyOutOfRangeExcept :public exception
{
public:
MyOutOfRangeExcept(const char * str)
{
//const char * 可以隐式类型转换为string,反之不可以
this->m_errorInfo = str;
}
MyOutOfRangeExcept(string str)
{
this->m_errorInfo = str;
}
//重写父类中的虚函数
virtual const char* what() const
{
return m_errorInfo.c_str();//c_str函数可以将str转为const char *
}
string m_errorInfo;
};
class Person
{
public:
Person(int age)
{
if (age < 0 || age>150)
{
throw MyOutOfRangeExcept(string("年龄必须在0~150之间"));
}
else
{
this->m_Age = age;
}
}
int m_Age;
};
void test01()
{
try
{
Person p(1000);
}
catch (exception& e)
{
cout<<e.what() << endl;
}
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
标准输入流
C++输入输出三方面内容
cin.get() 获取一个字符
cin.get(两个参数) 获取字符串
利用cin.get获取字符串时候,换行符遗留在缓冲区中
cin.getline() 获取字符串
利用cin.getline获取字符串时候,换行符不会被取走,也不在缓冲区中,而是直接扔掉
cin.ignore() 忽略 默认忽略1个字符, 如果填入参数X,代表忽略X个字符
cin.peek() 偷窥
cin.putback() 放回 放回原位置
示例
void test01()
{
//输入 a s "回车" 第一次 a ,第二次 s 第三次 换行 第四次 等待下次输入
char c = cin.get();
cout << "c=" << c << endl;
c = cin.get();
cout << "c=" << c << endl;
c = cin.get();
cout << "c=" << c << endl;
c = cin.get();
cout << "c=" << c << endl;
}
void test02()
{
char buf[1024] = {0};
cin.get(buf, 1024);
//利用cout.get()取字符串的时候,换行符会遗留在缓冲区中
char c = cin.get();
if (c == '\n')
{
cout << "换行符遗留在缓冲区" << endl;
}
else
{
cout << "换行符不在缓冲区" << endl;
}
cout << buf << endl;
}
void test03()
{
char buf[1024] = { 0 };
//利用cin.getline获取字符串时候,换行符不会被取走,也不在缓冲区中
cin.getline(buf, 1024);
char c = cin.get();
if (c == '\n')
{
cout << "换行符遗留在缓冲区" << endl;
}
else
{
cout << "换行符不在缓冲区" << endl;
}
cout << buf << endl;
}
//cin.ignore() 忽略,默认忽略1个字符,如果填入参数x,代表忽略x个字符
void test04()
{
cin.ignore();
char c = cin.get();
cout << "c=" << c << endl;
}
//cin.peek() 偷窥,可以看第一个字符,不从缓冲区取走
void test05()
{
char c = cin.peek();
cout << "c= " << c << endl;
c = cin.get();
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] = { 0 };
cin.getline(buf, 1024);
cout << buf << endl;
}
案例1
判断用户输入的内容 是字符串还是数字
//案例1. 判断用户输入的内容是字符串还是数字
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;
}
}
案例2
用户输入 0 ~ 10 之间的数字,如果输入有误,重新输入
标志位 cin.fail() 0代表正常 1代表异常
cin.clear() cin.sync() 清空缓冲区 重置标志位
// 案例2. 用户输入 0 到 10 之间的数字,如果输入有误,重新输入
void test08()
{
cout << "请输入0-10之间的数字" << endl;
int num;
while (true)
{
cin >> num;
if (num >= 0 && num <= 10 && cin.fail()==0)
{
cout << "输入正常,输入值为:" << num << endl;
break;
}
//重置标志位,清空缓冲区
cin.clear();
cin.sync();
cin.ignore(); //VS2013以上版本加入这句
//如果标志位为0,如果标志位为1,缓冲区异常
//cin.fail()
cout << "输入有误,请重新输入" << endl;
}
}
标准输出流
cout.put() //向缓冲区写字符
cout.write() //从buffer中写num个字节到当前输出流中。
格式化输出
-
通过 流成员函数 格式化输出
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; -
通过控制符 格式化输出
引入头文件 #include< iomanip> int number = 99; cout << setw(20) //设置宽度 << setfill('~') //设置填充 << setiosflags(ios::showbase) //显示基数 << setiosflags(ios::left) //设置左对齐 << hex //显示十六进制 << number << endl;
示例
//cout.put() 向缓冲区写字符
//cout.write() 从buffer中写num个字节到当前输出流中
void test01()
{
//cout.put('h').put('e');
char buf[] = "hello world";
cout.write(buf, strlen(buf));
}
//1. 通过流成员函数 格式化输出
void test02()
{
int num = 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 << num << endl;
}
//2. 使用控制符 格式化输出 需要包含头文件 <iomanip>
void test03()
{
int num = 99;
cout << setw(20) <<setfill('^') <<setiosflags(ios::showbase) <<setiosflags(ios::left) <<hex<< num << endl;
}
文件读写
头文件 #inlcude < fstream>
写文件
ofstream ofs (文件路径,打开方式 ios::out )
判断文件是否打开成功 ofs.is_open
ofs << “…”
关闭文件 ofs.close();
读文件
ifstream ifs(文件路径,打开方式 ios::in)
判断文件是否打开成功 ofs.is_open
利用4种方式 对文件进行读取
关闭文件 ifs.close();
示例
void test01()
{
//写文件 o --输出
ofstream ofs("./test.txt", ios::out | ios::trunc);
//ofs.open("./test.ext", ios::out | ios::trunc); 设置打开方式 以及路径
if (!ofs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
ofs << "姓名:孙悟空" << endl;
ofs << "年龄:999" << endl;
ofs << "性别:男" << endl;
//关闭文件
ofs.close();
}
void test02()
{
//读文件 i——输入
ifstream ifs;
ifs.open("./test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//第一种方式 读 推荐
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种方式 读
//char buf[1024] = { 0 };
//while (ifs.getline(buf, sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种方式
//string buf;
//while (getline(ifs,buf)) //全局函数 getline
//{
// cout << buf << endl;
//}
//第四种方式 不推荐
char c;
while ((c = ifs.get()) != EOF)
{
cout << c;
}
ifs.close();
}