c++基础09

类型转换

静态转换 static_case

使用方式:static_case<目标类型> (原始数据)

可以进行基础数据类型和父子之间的转换

没有父子关系的自定义类型不能静态转换

动态转换 dynamic_case

不可以转换基础类型

dynamic_cast非常严格,失去精度或者不安全都不可转换

父子之间:

​ 父转子:不可以

​ 子转父:可以

​ 发生多态:都可以

常量转换 const_cast

不能对非指针或者非引用的变量就进行转换

重新解释转换 reinterpret_cast

最不安全,不推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

using namespace std;


//基础类型
void ()
{
char a = 'a';
double d = static_cast<double>(a);
cout<<"d= "<<d<<endl;
}

//父子之间转换
class Base
{
virtual void func(){}
};
class Child :public Base
{
virtual void func(){}
};
class Other{};

void test02()
{
Base *base = NULL;
Child *child = NULL;

//把base转为Child*类型 向下 不安全
Child *child2 = static_cast<Child*>(base);
//把child转为Base*类型 向上 安全
Base *base2 = static_cast<Base*>(child);

//转为other类型
//Other *other = static_cast<Other*>(base);//无效
}

//动态转换 dynamic_cast
void test03()
{
//基础类型不可以转换
char c = 'a';
//dynamic_cast非常严格,失去精度或者不安全都不可转换
//double d = dynamic_cast<double>(c);
}

void test04()
{
Base *base = NULL;
Child *child = NULL;

//把base转为Child*类型 向下 不安全
//Child *child2 = dynamic_cast<Child*>(base);
//把child转为Base*类型 向上 安全
Base *base2 = dynamic_cast<Base*>(child);

//发生多态
Base *base3 = new Child;
Child *child3 = dynamic_cast<Child*>(base3);

}
// dynamic_cast如果发生了多态,那么可以让基类转为派生类

//常量转换 const_cast
void test05()
{
//指针
const int *p = NULL;
//去除const
int *newp = const_cast<int *>(p);

int *p2 = NULL;
//加上const
const int *newp2 = const_cast<const int *>(p2);

//不能对非指针或者非引用的变量就进行转换

//引用
int num = 10;
int &numref = num;
const int &numref2 = const_cast<const int &>(numref);
}

//重新解释转换 reinterpret_cast
void test06()
{
int a = 10;
int *p = reinterpret_cast<int *>(a);

Base *base = NULL;
Other *other = reinterpret_cast<Other *>(base);
//最不安全,不推荐
}
int main(void)
{
test01();
return 0;
}

类型异常的基本使用

try试图执行try{}中的内容

在可能出现异常的地方抛出异常 throw

try下面catch捕获异常

catch( 捕获类型 ) … 代表所有其他类型

如果不想处理异常,继续向上抛出 throw;

如果没有任何处理异常的地方,那么成员调用terminate函数,中断程序

自定义异常类,可以抛出自定义的对象,捕获这种自定义的异常

栈解旋:从try开始到throw抛出异常之前,所有栈上的对象都会被释放的过程

栈上构造顺序和析构顺序相反

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

using namespace std;

class myException //自定义异常类
{
public:
void printError()
{
cout<<"自定义的异常"<<endl;
}
};

class Person
{

public:
Person()
{
cout<<"构造函数调用"<<endl;
}
~Person()
{
cout<<"析构函数调用"<<endl;
}
};
int myDevide(int a, int b)
{
if(b == 0)
{
//throw -1;//抛出int类型异常
//throw 3.14; //抛出double类型异常,异常必须处理,否则编译出错
//throw 'a';
//栈解旋:从try开始到throw抛出异常之前,所有栈上的对象都会被释放的过程
Person p1, p2;
throw myException();
}
return a/b;
}
void test()
{
int a = 10;
int b = 0;

//int ret = myDevide(a, b);//早期如果返回-1,无法区分到底是结果还是异常
try
{
myDevide(a, b);
}
catch(int)
{
cout<<"int类型异常捕获"<<endl;
}
catch(double)
{
throw;
cout<<"double类型异常捕获"<<endl;
}
catch(myException e)
{
e.printError();
}
catch(...)
{
cout<<"其他类型异常捕获"<<endl;
}
}

int main(void)
{

try
{
test();
}
catch(double)//如果异常都没有处理,那么成员调用terminate函数,使程序中断
{
cout<<"main函数中double类型异常捕获"<<endl;
}
catch(...)
{
cout<<"main函数中其他类型异常捕获"<<endl;
}
return 0;
}

异常的接口声明

如果想抛出特定的异常,可以利用异常的接口声明

throw()不抛出任何异常

1
2
3
4
5
6
7
8
9
void func() throw(int)//throw(int)只能抛出int类型异常
{
throw 1;
}

void func() throw(int,char,double)//throw(int)只能抛出int类型异常
{
throw 1;
}

异常变量的生命周期

如果 MyException e,会多开销一份数据

如果 MyException *e,不new会提前释放对象,new的话要自己释放对象delete

推荐 MyException &e, 容易些,而且就一份数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

using namespace std;

class MyException
{

public:
MyException()
{
cout<<"MyException的默认构造"<<endl;
}
MyException(const MyException &myexception)
{
cout<<"MyException的拷贝构造"<<endl;
}
~MyException()
{
cout<<"MyException的析构调用"<<endl;
}
};

void doWork()
{
//throw MyException();
throw new MyException();
}
void test()
{
try
{
doWork();
}
// catch(MyException &e)//MyException e会多开销一份数据,加引用就不会了
// {
// cout<<"捕获异常"<<endl;
// }
catch(MyException *e)//MyException e会多开销一份数据,加引用就不会了
{
cout<<"捕获异常"<<endl;

delete e;//靠自觉释放对象
}
}

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

异常的多态使用

利用多态来实现printError同一个接口的使用

抛出不同的错误对象,提示不同的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

using namespace std;

//异常基类
class BaseException
{
public:
virtual void printError()
{

}
};

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

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

void doWork()
{
throw NullPointerException();
}
void test()
{
try
{
doWork();
}
catch(BaseException &e)
{
e.printError();
}
}

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

系统提供的标准异常

系统提供标准异常,要包含头文件#include < stdexcept >

throw out_of_range(“年龄越界了”);

catch(out_of_range &e){ cout<<e.what()<<endl; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

using namespace std;
//系统提供标准异常,要包含头文件
#include <stdexcept>
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
if(age<0 || age>200)
{
//throw out_of_range("年龄越界了");
throw length_error("长度越界了");
}

}
~Person(){}

string m_Name;
int m_Age;
};
void test()
{
try
{
Person p("张三", 222);
}
catch(out_of_range &e)
{
cout<<e.what()<<endl;
}
catch(length_error &e)
{
cout<<e.what()<<endl;
}
}

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

编写自己的异常类

自己的异常类需要继承于 exception 类

重写 虚析构 和 what()函数

内部维护以错误信息 字符串

构造的时候传入 错误信息字符串,在what方法返回这个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
using namespace std;

class MyOutofRangeException :public exception
{
public:
MyOutofRangeException(string effInfo)
{
this->m_ErrorInfo = effInfo;
}
virtual ~MyOutofRangeException()
{

}
virtual char const* what() const throw()
{
return this->m_ErrorInfo.c_str();
}

string m_ErrorInfo;
};

class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
if(age<0 || age>200)
{
throw MyOutofRangeException("我自己的越界异常");
}

}
~Person(){}

string m_Name;
int m_Age;
};
void test()
{
try
{
Person p("张三", 222);
}
catch(MyOutofRangeException &e)
{
cout<<e.what()<<endl;
}
}

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

标准输入流

cin.get()缓冲区中读取一个字符

cin.get(两个参数)不读换行符

cin.getline()读取换行,并且扔掉

cin.ignore() 忽略(N)N代表忽略字符数

cin.peek()偷窥 偷看1个字符然后放回去

cin.putback()放回,把字符放回缓冲区

输入流案例

  1. 判断用户输入的是字符串还是数字,利用偷窥或者放回

  2. 让用户输入指定返回内的数字,如果输入不正确,重新输入

    cin.fail() 标志位 0正常 1不正常

    cin.clear() 重置标志位

    cin.sync() 清空缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
using namespace std;

void ()
{
//输入as 缓冲区中 a s 换行 1.拿a 2.拿s 3.拿换行 4.等待输入
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()
{
//cin,get(两个参数)读取字符串时,不会把换行符拿走,遗留在缓冲区中
char buf[1024];
cin.get(buf, 1024);
char c = cin.get();
if (c == 'n')
{
cout << "换行还在缓冲区" << endl;
}
else
{
cout << "换行不在缓冲区" << endl;
}

cout << buf << endl;
}

void test03()
{
//cin.getline()把换行符读取,并且扔掉
char buf[1024];
cin.getline(buf, 1024);
char c = cin.get();
if (c == 'n')
{
cout << "换行还在缓冲区" << endl;
}
else
{
cout << "换行不在缓冲区" << endl;
}
cout << buf << endl;
}

void test04()
{
cin.ignore(); //没有参数代表忽略一个字符,带参数n,代表忽略n个字符
char c = cin.get();
cout << "c= " << c << endl;
}

void test05()
{
//输入as,偷看一眼,然后再放回缓冲区,缓冲区中还是as
char c = cin.peek();
cout << "c= " << c << endl;

c = cin.get();
cout << "c= " << c << endl;
}

void test06()
{
//放回,把字符放回缓冲区
char c = cin.get();
cin.putback(c);

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

//案例一:判断用户输入的是字符串还是数字?
void test07()
{
cout << "请输入一串数字或者字符串" << endl;
//偷窥
char c = cin.peek();
if (c >= '0' && c <= '9')
{
int num;
cin >> num;
cout << "您输入的是数字" << num << endl;
}
else
{
char buf[1024];
cin >> buf;
cout << "您输入的是字符串" << buf << endl;
}
}
//案例二: 让用户输入1到10的数字,如果输入有误重新输入
void test08()
{
int num;

while (true)
{
cin >> num;
if (num > 0 && num < 10)
{
cout << "输入的数字为: " << num << endl;
break;
}
cout << "对不起,请重新输入" << endl;
//cout<<"标志位: "<<cin.fail()<<endl;//标志位 0正常,1不正常
cin.clear();
cin.sync();
}
}
int main(void)
{
//test01();
//test02();
//test03();
//test04();
//test05();
//test06();
//test07();
test08();
return 0;
}

标准输出流

流对象的成员函数

控制符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;

void ()
{
cout.put('a').put('b') << endl;

char buf[1024] = "helloworld";
cout.write(buf, strlen(buf));
}

void test02()
{
//通过流成员函数

int num = 99;
cout.width(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;
}
void test03()
{
int num = 99;
cout << setw(20)
<<setfill('~')
<< setiosflags(ios::showbase)
<<setiosflags(ios::left)
<< hex
<< num
<< endl;
}
int main(void)
{
//test01();
//test02();
test03();
return 0;
}

文件读写

写文件

​ ofstream ofs;

​ open 指定打开方式

​ is_open()判断是否打开成功

​ ofs<<”数据”

​ ofs.close();

读文件

​ ifstream ifs;

​ 指定方式打开

​ 判断是否打开成功

​ 三种方式读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

void ()
{
//以输出的方式打开文件
//ofstream ofs("./test.txt", ios::out | ios::trunc);

ofstream ofs;
ofs.open("./test.txt", ios::out | ios::trunc);
if(!ofs.is_open())
{
cout<<"打开失败"<<endl;
}

ofs<<"姓名:abc"<<endl;
ofs<<"年龄:100"<<endl;
ofs<<"性别:男"<<endl;
}

void test02()
{
ifstream ifs;
ifs.open("./test.txt", ios::in);
if(!ifs.is_open())
{
cout<<"打开失败"<<endl;
}
//第一种方式
char buf[1024];
while (ifs >> buf)
{
cout << buf << endl;
}
//第二种方式
char buf2[1024];
while (!ifs.eof())
{
ifs.getline(buf2, sizeof(buf2));
cout<<buf2<<endl;
}
//第三种 不推荐
char c;
while((c = ifs.get()) != EOF)
{
cout<<c;
}

}

int main(void)
{
test01();
test02();
return 0;
}