c++基础学习笔记——04-c++day09

在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

04-c++day09

目录:
一、类型转换
1、静态类型
2、动态转换
3、常量转换和重新解释转换
二、C++异常
1、异常的基本使用
2、对自定义异常进行捕获
3、栈解旋
4、异常的接口声明
5、异常变量生命周期
6、异常的多态使用
7、使用系统标准异常
8、编写自己的异常类
三、C++输入输出流
1、标准输入流
2、标准输入流案例
3、标准输出流
4、格式化输出
5、文件的读写操作

一、类型转换


类型转换(cast)是将一种数据类型转换成另一种数据类型。例如,如果将一个整型值赋给一个浮点类型的变量,编译器会暗地里将其转换成浮点类型。
转换是非常有用的,但是它也会带来一些问题,比如在转换指针时,我们很可能将其转换成一个比它更大的类型,但这可能会破坏其他的数据。
应该小心类型转换,因为转换也就相当于对编译器说:忘记类型检查,把它看做其他的类型。
一般情况下,尽量少的去使用类型转换,除非用来解决非常特殊的问题。

 无论什么原因,任何一个程序如果使用很多类型转换都值得怀疑.

标准c++提供了一个显示的转换的语法,来替代旧的C风格的类型转换。
使用C风格的强制转换可以把想要的任何东西转换成我们需要的类型。那为什么还需要一个新的C++类型的强制转换呢?
新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。


1、静态类型

1)用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
>>>进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
>>>进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
2)用于基本数据类型之间的转换,如把int转换成char,把char转换成int。这种转换的安全性也要开发人员来保证。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 //静态转换
 6 //基础类型
 7 void test01()
 8 {
 9     char a = 'a';
10     
11     double d = static_cast<double>(a);
12     
13     cout << d << endl;
14 
15 }
16 
17 //父子类之间转换
18 class Base{};
19 class Child:public Base{};
20 class Other{};
21 
22 void test02()
23 {
24     Base* base = NULL;
25     Child* child = NULL;
26     
27     //把base转为Chilid*类型,向下 不安全
28     Child* child2 = static_cast<Child*>(base);
29     
30     //把child转为Base*,向上,安全
31     Base* base2 = static_cast<Base*>(child);
32     
33     //转other类型,转换无效
34     //Other* other = static_cast<Other*>(base);
35 }
36 
37 //static_cast使用:static_cast<目标类型>(原始对象)
38 
39 
40 int main()
41 {
42     test01();
43     
44     system("pause");
45     return EXIT_SUCCESS;
46 }

2、动态转换

》ynamic_cast主要用于类层次间的上行转换和下行转换;
》在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
》在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全;

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 //静态转换
 6 //基础类型
 7 void test01()
 8 {
 9     char a = 'a';
10     
11     double d = static_cast<double>(a);
12     
13     cout << d << endl;
14 
15 }
16 
17 //父子类之间转换
18 class Base{};
19 class Child:public Base{};
20 class Other{};
21 
22 void test02()
23 {
24     Base* base = NULL;
25     Child* child = NULL;
26     
27     //把base转为Chilid*类型,向下 不安全
28     Child* child2 = static_cast<Child*>(base);
29     
30     //把child转为Base*,向上,安全
31     Base* base2 = static_cast<Base*>(child);
32     
33     //转other类型,转换无效
34     //Other* other = static_cast<Other*>(base);
35 }
36 
37 //static_cast使用:static_cast<目标类型>(原始对象)
38 
39 //动态转换
40 void test03()
41 {
42     //基础类型不可以转换
43     char c = 'a';
44     //dynamic_cast非常严格,失去精度,或者不安全都不可以转换
45     //double d = dynamic_cast<double>(c);
46     
47 }
48 
49 //父子类之间转换
50 class Base2
51 {
52     virtual void func(){};
53 };
54 class Child2:public Base2
55 {
56     virtual void func(){};
57 };
58 class Other2{};
59 
60 void test04()
61 {
62     Base2* base = NULL;
63     Child2* child = NULL;
64     
65     //把base转为Chilid2*类型,向下 不安全
66     //Child2* child2 = static_cast<Child2*>(base);
67     
68     //把child转为Base2*,向上,安全
69     Base2* base2 = static_cast<Base2*>(child);
70     
71     //发生多态情况
72     //dynamic_cast如果发生了多态,那么可以让基类转为派生类,向下转换
73     Base2* base3 = new Child2;
74     Child2* child3 = dynamic_cast<Child2*>(base3);
75     
76 }
77 
78 
79 int main()
80 {
81     test01();
82     
83     system("pause");
84     return EXIT_SUCCESS;
85 }

3、常量转换和重新解释转换


常量转换(const_cast)
该运算符用来修改类型的const属性。。
》常量指针被转化成非常量指针,并且仍然指向原来的对象;
》常量引用被转换成非常量引用,并且仍然指向原来的对象;

注意:不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const.

重新解释转换(reinterpret_cast)。。。。。
这是最不安全的一种转换机制,最有可能出问题。
主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针.


  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 
  5 //静态转换
  6 //基础类型
  7 void test01()
  8 {
  9     char a = 'a';
 10     
 11     double d = static_cast<double>(a);
 12     
 13     cout << d << endl;
 14 
 15 }
 16 
 17 //父子类之间转换
 18 class Base{};
 19 class Child:public Base{};
 20 class Other{};
 21 
 22 void test02()
 23 {
 24     Base* base = NULL;
 25     Child* child = NULL;
 26     
 27     //把base转为Chilid*类型,向下 不安全
 28     Child* child2 = static_cast<Child*>(base);
 29     
 30     //把child转为Base*,向上,安全
 31     Base* base2 = static_cast<Base*>(child);
 32     
 33     //转other类型,转换无效
 34     //Other* other = static_cast<Other*>(base);
 35 }
 36 
 37 //static_cast使用:static_cast<目标类型>(原始对象)
 38 
 39 //动态转换
 40 void test03()
 41 {
 42     //基础类型不可以转换
 43     char c = 'a';
 44     //dynamic_cast非常严格,失去精度,或者不安全都不可以转换
 45     //double d = dynamic_cast<double>(c);
 46     
 47 }
 48 
 49 //父子类之间转换
 50 class Base2
 51 {
 52     virtual void func(){};
 53 };
 54 class Child2:public Base2
 55 {
 56     virtual void func(){};
 57 };
 58 class Other2{};
 59 
 60 void test04()
 61 {
 62     Base2* base = NULL;
 63     Child2* child = NULL;
 64     
 65     //把base转为Chilid2*类型,向下 不安全
 66     //Child2* child2 = static_cast<Child2*>(base);
 67     
 68     //把child转为Base2*,向上,安全
 69     Base2* base2 = static_cast<Base2*>(child);
 70     
 71     //发生多态情况
 72     //dynamic_cast如果发生了多态,那么可以让基类转为派生类,向下转换
 73     Base2* base3 = new Child2;
 74     Child2* child3 = dynamic_cast<Child2*>(base3);
 75     
 76 }
 77 
 78 //常量转换(const_cast)
 79 void test05()
 80 {
 81     const int* p = NULL;
 82     //取出const
 83     int* newp = const_cast<int*>(p);
 84     
 85     int* p2 = NULL;
 86     const int* newp2 = const_cast<const int*>(p2);
 87     
 88     //不能对非指针或非引用的变量进行转换
 89     const int a = 10;
 90     //int b = const_cast<int>(a);
 91     
 92     //引用
 93     int num = 10;
 94     int& numRef = num;
 95     
 96     const int& numRef2 = static_cast<const int&>(numRef);
 97 }
 98 
 99 //重新解释转换(reinterpret_cast)
100 void test06()
101 {
102     int a = 10;
103     int* p = reinterpret_cast<int*>(a);
104     
105     Base* base = NULL;
106     
107     Other* other = reinterpret_cast<Other*>(base);
108     //最不安全,不推荐
109 }
110 
111 
112 
113 int main()
114 {
115     test01();
116     
117     system("pause");
118     return EXIT_SUCCESS;
119 }

二、C++异常


Bjarne Stroustrup说:提供异常的基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时抛出(throw)一个异常,然后它的(直接或者间接)调用者能够处理这个问题。也就是《C++ primer》中说的:将问题检测和问题处理相分离。
一种思想:在所有支持异常处理的编程语言中(例如java),要认识到的一个思想:在异常处理过程中,由问题检测代码可以抛出一个对象给问题处理代码,通过这个对象的类型和内容,实际上完成了两个部分的通信,通信的内容是“出现了什么错误”。当然,各种语言对异常的具体实现有着或多或少的区别,但是这个通信的思想是不变的。

一句话:异常处理就是处理程序中的错误。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等)。

回顾一下:我们以前编写程序是如何处理异常?
在C语言的世界中,对错误的处理总是围绕着两种方法:一是使用整型的返回值标识错误;二是使用errno宏(可以简单的理解为一个全局整型变量)去记录错误。当然C++中仍然是可以用这两种方法的。
        这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回1表示成功,返回0表示出错;而有些函数返回0表示成功,返回非0表示出错。
        还有一个缺点就是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。当然,你也可以通过指针或者C++的引用来返回另外的值,但是这样可能会令你的程序略微晦涩难懂。

c++异常机制相比C语言异常处理的优势?
(1)函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用C语言的error宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。
(2)整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。
(3)整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
(4)异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。


1、异常的基本使用


总结:
(1)若有异常则通过throw操作创建一个异常对象并抛出。
(2)将可能抛出异常的程序段放到try块之中。
(3)如果在try段执行期间没有引起异常,那么跟在try后面的catch字句就不会执行。
(4)catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常(或继续抛出异常)
(5)如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。
(6)处理不了的异常,可以在catch的最后一个分支,使用throw,向上抛。

c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。


 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 int myDevide(int a, int b)
 6 {
 7     if(b == 0)
 8     {
 9         //如果b是异常,抛出异常
10         //return -1;
11         
12         //throw -1;//抛出int类型异常
13         throw 3.14;//抛出double类型异常,异常必须处理,如果不处理,程序就挂掉了
14         //throw 'a';
15     }
16     return a / b;
17 }
18 
19 void test01()
20 {
21     int a = 10;
22     int b = 0;
23     
24     //int ret = myDevide(a, b);//早期如果返回-1,无法区分到底是结果还是异常
25     
26     //C++中异常处理
27     
28     try//试一试
29     {
30         myDevide(a, b);
31     }
32     catch(int)//捕获异常
33     {
34         cout << "int类型异常捕获" << endl;
35     }
36     catch(double)//捕获异常
37     {
38         //如果不想处理这个异常,可以继续向上抛出
39         throw;
40         cout << "double类型异常捕获" << endl;
41     }
42     catch(...)
43     {
44         cout << "其他类型异常捕获" << endl;
45     }
46 }
47 
48 int main()
49 {
50     try
51     {
52         test01();
53     }
54     catch(char)//如果异常都没有处理,那么成员terminate函数,使程序中断
55     {
56         cout << "main函数中double类型异常捕获" << endl;
57     }
58     catch(...)
59     {
60         cout << "main函数中其他类型异常捕获" << endl;
61     }
62     
63     system("pause");
64     return EXIT_SUCCESS;
65 }

2、对自定义异常进行捕获

异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class myException//自定义异常类
 6 {
 7 public:
 8     void printError()
 9     {
10         cout << "自定义的异常" << endl;
11     }
12     
13     
14 };
15 
16 int myDevide(int a, int b)
17 {
18     if(b == 0)
19     {
20         //如果b是异常,抛出异常
21         //return -1;
22         
23         //throw -1;//抛出int类型异常
24         //throw 3.14;//抛出double类型异常,异常必须处理,如果不处理,程序就挂掉了
25         //throw 'a';
26         
27         throw myException();//匿名对象
28     }
29     return a / b;
30 }
31 
32 void test01()
33 {
34     int a = 10;
35     int b = 0;
36     
37     //int ret = myDevide(a, b);//早期如果返回-1,无法区分到底是结果还是异常
38     
39     //C++中异常处理
40     
41     try//试一试
42     {
43         myDevide(a, b);
44     }
45     catch(int)//捕获异常
46     {
47         cout << "int类型异常捕获" << endl;
48     }
49     catch(double)//捕获异常
50     {
51         //如果不想处理这个异常,可以继续向上抛出
52         throw;
53         cout << "double类型异常捕获" << endl;
54     }
55     catch(myException e)
56     {
57         e.printError();
58     }
59     catch(...)
60     {
61         cout << "其他类型异常捕获" << endl;
62     }
63 }
64 
65 int main()
66 {
67     try
68     {
69         test01();
70     }
71     catch(char)//如果异常都没有处理,那么成员terminate函数,使程序中断
72     {
73         cout << "main函数中double类型异常捕获" << endl;
74     }
75     catch(...)
76     {
77         cout << "main函数中其他类型异常捕获" << endl;
78     }
79     
80     system("pause");
81     return EXIT_SUCCESS;
82 }

3、栈解旋

异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding).

  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 
  5 class myException//自定义异常类
  6 {
  7 public:
  8     void printError()
  9     {
 10         cout << "自定义的异常" << endl;
 11     }
 12     
 13     
 14 };
 15 
 16 class Person
 17 {
 18 public:
 19     Person()
 20     {
 21         cout << "Person的构造" << endl;
 22     }
 23     ~Person()
 24     {
 25         cout << "Person的析构" << endl;
 26     }
 27     
 28 };
 29 
 30 int myDevide(int a, int b)
 31 {
 32     if(b == 0)
 33     {
 34         //如果b是异常,抛出异常
 35         //return -1;
 36         
 37         //throw -1;//抛出int类型异常
 38         //throw 3.14;//抛出double类型异常,异常必须处理,如果不处理,程序就挂掉了
 39         //throw 'a';
 40         
 41         //栈解旋
 42         //从try开始到throw抛出异常之前,所有栈上的对象,都会被释放,这个过程称为栈解旋
 43         //构造和析构的顺序相反
 44         Person p1;
 45         Person p2;
 46         
 47         throw myException();//匿名对象
 48     }
 49     return a / b;
 50 }
 51 
 52 
 53 
 54 
 55 void test01()
 56 {
 57     int a = 10;
 58     int b = 0;
 59     
 60     //int ret = myDevide(a, b);//早期如果返回-1,无法区分到底是结果还是异常
 61     
 62     //C++中异常处理
 63     
 64     try//试一试
 65     {
 66         myDevide(a, b);
 67     }
 68     catch(int)//捕获异常
 69     {
 70         cout << "int类型异常捕获" << endl;
 71     }
 72     catch(double)//捕获异常
 73     {
 74         //如果不想处理这个异常,可以继续向上抛出
 75         throw;
 76         cout << "double类型异常捕获" << endl;
 77     }
 78     catch(myException e)
 79     {
 80         e.printError();
 81     }
 82     catch(...)
 83     {
 84         cout << "其他类型异常捕获" << endl;
 85     }
 86 }
 87 
 88 int main()
 89 {
 90     try
 91     {
 92         test01();
 93     }
 94     catch(char)//如果异常都没有处理,那么成员terminate函数,使程序中断
 95     {
 96         cout << "main函数中double类型异常捕获" << endl;
 97     }
 98     catch(...)
 99     {
100         cout << "main函数中其他类型异常捕获" << endl;
101     }
102     
103     system("pause");
104     return EXIT_SUCCESS;
105 }

4、异常的接口声明


>为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func() throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型的异常。
>如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如:void func()
>一个不抛任何类型异常的函数可声明为:void func() throw()
>如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。

注意:在qt vs linux下做测试! Qt and Linux 正确!


 1 #include "mainwindow.h"
 2 #include <QApplication>
 3 #include <QDebug>
 4 //异常的接口声明
 5 void func() throw(int,char,double) //throw(int)只能抛出int类型异常  throw()不抛出任何类型异常
 6 {
 7     throw 3.14;
 8 }
 9 
10 
11 int main(int argc, char *argv[])
12 {
13     QApplication a(argc, argv);
14     MainWindow w;
15     w.show();
16 
17 
18     try
19     {
20         func();
21     }
22     catch(int)
23     {
24         qDebug() << "int类型异常捕获";
25     }
26     catch(...)
27     {
28         qDebug() << "其他类型异常捕获";
29     }
30 
31 
32     return a.exec();
33 }

5、异常变量生命周期

1)如果  MyException e,会多开销一份数据 ,调用拷贝构造
2)如果 MyExcepiton *e   , 不 new提前释放对象 new 自己管理delete
3)推荐  MyException &e  容易些 而且 就一份数据

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class MyException
 6 {
 7 public:
 8     MyException()
 9     {
10         cout << "MyException的默认构造" << endl;
11     }
12     MyException(const MyException& e)
13     {
14         cout << "MyException的拷贝构造" << endl;
15     }
16     ~MyException()
17     {
18         cout << "MyException的析构调用" << endl;
19     }
20     
21     void printError()
22     {
23         this->m_A = 100;
24         cout << "error" << this->m_A << endl;
25     }
26     
27     int m_A;
28 };
29 
30 void doWork()
31 {
32     //throw MyException();//匿名对象
33     throw new MyException();
34 }
35 
36 void test01()
37 {
38     try
39     {
40         doWork();
41     }
42     catch(MyException* e)//MyException e,会多开销一份数据,采用引用&(MyException& e),也可以采用*,但是抛出异常那里不能直接传入地址(会提前释放掉),所以要new在栈上开辟,最后还要释放delete
43     {
44         //e->printError();//多次打印也没有问题,但是e已经释放,是野指针了,指向非法内存空间
45         //e->printError();
46         
47         
48         cout << "捕获异常" << endl;
49         
50         delete e;//靠自觉,释放对象
51     }
52 }
53 
54 int main()
55 {
56     test01();
57     
58     system("pause");
59     return EXIT_SUCCESS;
60 }

6、异常的多态使用

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 //异常基类
 6 class BaseException
 7 {
 8 public:
 9     virtual void printError()
10     {
11         
12     }
13 };
14 
15 class NullPointerException:public BaseException
16 {
17 public:
18     virtual void printError()
19     {
20         cout << "空指针异常" << endl;
21     }
22 };
23 
24 class OutoofException:public BaseException
25 {
26 public:
27     virtual void printError()
28     {
29         cout << "越界异常" << endl;
30     }
31 };
32 
33 void doWork()
34 {
35     //throw NullPointerException();
36     throw OutoofException();
37 }
38 
39 void test01()
40 {
41     try
42     {
43         doWork();
44     }
45     catch(BaseException& e)
46     {
47         e.printError();
48     }
49     
50 }
51 
52 int main()
53 {
54     test01();
55     
56     system("pause");
57     return EXIT_SUCCESS;
58 }

7、使用系统标准异常


 标准库中也提供了很多的异常类,它们是通过类继承组织起来的。异常类继承层级结构图如下:

每个类所在的头文件在图下方标识出来。
标准异常类的成员:
① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
② logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述
③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。


 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 #include<string>
 4 using namespace std;
 5 //系统提供标准异常,要包含头文件
 6 #include<stdexcept>
 7 
 8 class Person
 9 {
10 public:
11     Person(string name, int age)
12     {
13         this->m_Name  = name;
14         //年龄检测
15         if(age < 0 || age > 200)
16         {
17             //抛出越界异常
18             //throw out_of_range("年龄越界了");
19             
20             throw length_error("长度越界");
21         }
22     }
23     
24     
25     string m_Name;
26     int m_Age;
27 };
28 
29 void test01()
30 {
31     try
32     {
33         Person p("张三", 300)
34     }
35     catch(out_of_range& e)
36     {
37         cout << e.what() << endl;
38     }
39     catch(length_error& e)
40     {
41         cout << e.what() << endl;
42     }
43 }
44 
45 int main()
46 {
47     test01();
48     
49     system("pause");
50     return EXIT_SUCCESS;
51 }

8、编写自己的异常类


① 标准库中的异常是有限的;
② 在自己的异常类中,可以添加自己的信息。(标准库中的异常类值允许设置一个用来描述异常的字符串)。

如何编写自己的异常类?
① 建议自己的异常类要继承标准异常类。因为C++中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。
② 当继承标准异常类时,应该重载父类的what函数和虚析构函数。
③ 因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数。


1)自己的异常类 需要继承于  exception
2)重写  虚析构   what()
3)内部维护以错误信息 字符串
4)构造时候传入 错误信息字符串,what返回这个字符串

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 #include<string>
 4 using namespace std;
 5 
 6 class MyOutOfRangeException:public exceptions
 7 {
 8 public:
 9     MyOutOfRangeException(string errorInfo)
10     {
11         this->m_ErrorInfo = errorInfo;
12     }
13     
14     
15     virtual ~MyOutOfRangeException()
16     {
17         
18     }
19     virtual const char* what() const
20     {
21         //返回错误信息
22         //string转char*(.c_str())
23         return *this->m_ErrorInfo.c_str();
24         
25     }
26     
27     string m_ErrorInfo;
28 };
29 
30 class Person
31 {
32 public:
33     Person(string name, int age)
34     {
35         this->m_Name  = name;
36         //年龄检测
37         if(age < 0 || age > 200)
38         {
39             throw MyOutOfRangeException(string("我自己的年龄越界异常"));
40         }
41     }
42     
43     
44     string m_Name;
45     int m_Age;
46 };
47 
48 void test01()
49 {
50     try
51     {
52         Person p("张三", 300)
53     }
54     catch(MyOutOfRangeException& e)
55     {
56         cout << e.what() << endl;
57     }
58 
59 }
60 
61 int main()
62 {
63     test01();
64     
65     system("pause");
66     return EXIT_SUCCESS;
67 }

三、C++输入输出流

1、标准输入流

(1)cin.get 缓冲区中读取一个字符
(2)cin.get(两个参数) 不读换行符
(3)cin.getline () 读取换行 并且扔掉
(4)cin.ignore 忽略 (N) N代表忽略字符数
(5)cin.peek 偷窥   偷看1个字符然后放回去
(6)cin.putback  放回 把字符放回缓冲区

  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 
  5 /*
  6 cin.get() //一次只能读取一个字符
  7 cin.get(一个参数) //读一个字符
  8 cin.get(两个参数) //可以读字符串
  9 cin.getline()
 10 cin.ignore()
 11 cin.peek()
 12 cin.putback()
 13 */
 14 
 15 //cin.get() //一次只能读取一个字符
 16 void test01()
 17 {
 18     //输入as,缓冲区中(a s 换行)第一个拿a,第二个拿s,第三次拿换行,第四次等待下次输入
 19     char c = cin.get();
 20     cout << "c = " << c << endl;
 21     
 22     c = cin.get();
 23     cout << "c = " << c << endl;
 24     
 25     c = cin.get();
 26     cout << "c = " << c << endl;
 27     
 28     c = cin.get();
 29     cout << "c = " << c << endl;
 30 }
 31 
 32 //cin.get(两个参数) //可以读字符串,不会把换行符拿走,还遗留在缓冲区中
 33 void test02()
 34 {
 35     char buf[1024];
 36     cin.get(buf, 1024);
 37     
 38     char c = cin.get();
 39     
 40     if(c == '
')
 41     {
 42         cout << "换行还在缓冲区" << endl;
 43     }
 44     else
 45     {
 46         cout << "换行不在缓冲区" << endl;
 47     }
 48     
 49     cout << "c = " << buf << endl;
 50 
 51 }
 52 
 53 //cin.getline()把换行符读取,并且扔掉
 54 void test03()
 55 {
 56     char buf[1024];
 57     cin.getline(buf, 1024);
 58     
 59     char c = cin.get();
 60     
 61     if(c == '
')
 62     {
 63         cout << "换行还在缓冲区" << endl;
 64     }
 65     else
 66     {
 67         cout << "换行不在缓冲区" << endl;
 68     }
 69     
 70     cout << "c = " << buf << endl;
 71 
 72 }
 73 
 74 //cin.ignore()
 75 void test04()
 76 {
 77     cin.ignore(2);//没有参数,代表忽略一个字符,带参数N,代表忽略了N个字符
 78     
 79     char c = cin.get();
 80     
 81     cout << "c = " << c << endl;
 82 
 83 }
 84 
 85 //cin.peek()偷窥
 86 void test05()
 87 {
 88     //输入as,偷看一眼a,然后再放回缓冲区,缓冲区还是as
 89     char c = cin.peek();
 90     
 91     cout << "c = " << c << endl;
 92     
 93     c = cin.get();
 94     cout << "c = " << c << endl;
 95 }
 96 
 97 //cin.putback()放回
 98 void test05()
 99 {
100     char c = cin.get();
101     cin.putback(c);
102     
103     char buf[1024];
104     
105     cin.getline(buf, 1024);
106     cout << buf << endl;
107 
108 }
109 
110 int main()
111 {
112     test01();
113     
114     system("pause");
115     return EXIT_SUCCESS;
116 }

2、标准输入流案例

案例1:判断用户输入是字符串,还是数字?

案例2:让用户输入1到10的数字,如果输入有误,重新输入

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 //案例1:判断用户输入是字符串,还是数字?
 6 void test01()
 7 {
 8     cout << "请输入一串数字或者字符串" << endl;
 9     
10     //偷窥
11     char c = cin.peek();
12     
13     if( c >= '0' && c <= '9')
14     {
15         int num;
16         cin >> num;
17         
18         cout << "你输入的是数字,数字为:" << num << endl;
19     }
20     else
21     {
22         char buf[1024];
23         cin >> buf;
24         
25         cout << "你输入的是字符串,字符串为:" << buf << endl;
26     }
27     
28 }
29 
30 //案例2:让用户输入1到10的数字,如果输入有误,重新输入
31 void test02()
32 {
33     int num;
34     
35     cout << "请输入1到10的数字" << endl;
36     
37     while(true)
38     {
39         cin >> num;
40         if(num > 0 && num <=10)
41         {
42             cout << "输入的数字为:" << num << endl;
43             break;
44         }
45 
46         cout << "对不起,请重新输入" << endl;
47         
48         //重置标志位
49         cin.clear();
50         cin.sync();//清空缓冲区
51         
52         //cout << "标志位:" << cin.fail() << endl;//标志位0是正常的,1不正常
53         
54     }
55 }
56 
57 
58 
59 int main()
60 {
61     test01();
62     
63     system("pause");
64     return EXIT_SUCCESS;
65 }

注意:在VS2015测试发现输入字符,会循环打印“对不起,请重新输入”,调试后,更改代码为:

用cin.fail()判断标志位,用getline()处理输入的缓冲区中多余的数据。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 int main()
 5 {
 6     int a;
 7     while (1)
 8     {
 9         cin >> a;
10         //cout << "标志位:" << cin.fail() << endl;
11         if (cin.fail())            //条件可改写为cin.fail()                 
12         {
13             cout << "输入有错!请重新输入" << endl;
14             cin.clear();
15             char buf[1024];
16             cin.getline(buf, 1024);
17             //cin.sync();   //清空流                 
18         }
19         else
20         {
21             cout << a;
22             break;
23         }
24         //cout << "标志位:" << cin.fail() << endl;
25     }
26 
27 
28 
29     system("pause");
30 }

3、标准输出流

>cout.flush() //刷新缓冲区 Linux下有效

>cout.put() //向缓冲区写字符

>cout.write() //从buffer中写num个字节到当前输出流中。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 /*
 6 cout.put() //向缓冲区写字符
 7 cout.write() //从buffer中写num个字节到当前输出流中。
 8 */
 9 
10 void test01()
11 {
12     //cout.put("a").put("b");
13     
14     char buf[1024] = "helloworld";
15     
16     cout.write(buf, strlen(buf));
17 }
18 
19 int main()
20 {
21     test01();
22     
23     system("pause");
24     return EXIT_SUCCESS;
25 }

4、格式化输出

两种

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 #include<iomanip>//使用控制符的头文件
 4 using namespace std;
 5 
 6 
 7 //1.通过流成员函数
 8 void test01(){
 9     
10     int number = 99;
11     cout.width(20);
12     cout.fill('*');
13     cout.setf(ios::left);//设置格式,输出的内容左对齐
14     cout.unsetf(ios::dec); //卸载十进制
15     cout.setf(ios::hex);//设置格式为16进制
16     cout.setf(ios::showbase);//设置格式,强制输出整数计数0 0x
17     cout.unsetf(ios::hex);
18     cout.setf(ios::oct);
19     cout << number << endl;
20 
21 }
22 
23 //2.使用控制符
24 void test02(){
25 
26     int number = 99;
27     cout << setw(20)
28         << setfill('~')
29         << setiosflags(ios::showbase)//基数
30         << setiosflags(ios::left)//左对齐
31         << hex //十六进制
32         << number
33         << endl;
34 
35 }
36 
37 
38 int main()
39 {
40     test01();
41     
42     system("pause");
43     return EXIT_SUCCESS;
44 }

5、文件的读写操作

(1)写文件
>ofstream  ofs
>open 指定打开方式
> isopen 判断是否打开成功
>ofs << “数据”
>ofs.close
(2)读操作
>ifstream  ifs
> 指定打开方式 ios::in
>isopen判断是否打开成功
>三种方式读取数据

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 //文件读写头文件
 5 #include<fstream>
 6 
 7 //写文件
 8 void test01()
 9 {
10     //以输出的方式打开文件
11     //ofstream ofs("./test.txt", ios::out | ios::trunc);
12     //后期指定打开方式
13     
14     ofstream ofs;
15     ofs.open("./test.txt", ios::out | ios::trunc);
16     //判断是否打开成功
17     if(!ofs.is_open())
18     {
19         cout << "打开失败" << endl;
20     }
21     
22     ofs << "姓名:abc" << endl;
23     ofs << "年龄:100" << endl;
24     ofs << "性别:男" << endl;
    ofs.close();
25 } 26 27 //读文件 28 void test02() 29 { 30 ifstream ifs; 31 ifs.open("./test.txt", ios::in); 32 33 if(!ofs.is_open()) 34 { 35 cout << "打开失败" << endl; 36 } 37 38 //第一种方式 39 /* 40 char buf[1024]; 41 42 while(ifs >> buf)//按行读取 43 { 44 cout << buf << endl; 45 } 46 */ 47 //第二种方式 48 /* 49 char buf2[1024]; 50 51 while(ifs.eof())//eof读到文件尾 52 { 53 ifs.getline(buf2, sizeof(buf2)); 54 cout << buf2 << endl; 55 } 56 */ 57 //第三种方式,按单个字符读取,不推荐 58 char c; 59 while(c = ifs.get() != EOF)//EOF文件尾 60 { 61 cout << c; 62 } 63 ofs.close(); 64 } 65 66 67 int main() 68 { 69 test01(); 70 71 system("pause"); 72 return EXIT_SUCCESS; 73 }

四、总结

1    类型转换
1.1    静态转换 static_cast
1.2    使用方式  static_cast< 目标类型>(原始数据)
1.3    可以进行基础数据类型转换
1.4    父与子类型转换
1.5    没有父子关系的自定义类型不可以转换
1.6    动态转换 dynamic_cast
1.7    不可以转换基础数据类型
1.8    父子之间可以转换
1.8.1    父转子 不可以
1.8.2    子转父 可以
1.8.3    发生多态 都可以
1.9    常量转换 const_cast
1.10    不能对非指针或者非引用进行转换
1.11    重新解释转换 reinterpret_cast
1.11.1    最不安全,最鸡肋 不推荐
2    异常
2.1    try 试图执行 try{}中的内容
2.2    在可能出现异常的地方 抛出异常  throw
2.3    try下面 catch捕获异常
2.4    catch( 捕获类型 )  …代表 所有其他类型
2.5    如果不想处理异常,继续向上抛出  throw
2.6    如果没有任何处理异常的地方,那么成员调用terminate函数,中断程序
2.7    自定义异常类 ,可以抛出自定义的对象  ,捕获自定义的异常
3    栈解旋
3.1    从try开始  到 throw 抛出异常之前  所有栈上的对象 都会被释放 这个过程称为栈解旋
3.2    栈上对象构造顺序与析构顺序相反
4    异常的接口声明
4.1    如果想抛出特定的类型异常 ,可以利用异常的接口声明
4.2    void func() throw ( int) 只能抛出 int类型
4.3    throw() 不抛出任何类型异常
5    异常变量生命周期
5.1    如果  MyException e,会多开销一份数据 ,调用拷贝构造
5.2    如果 MyExcepiton *e   , 不 new提前释放对象 new 自己管理delete
5.3    推荐  MyException &e  容易些 而且 就一份数据
6    异常的多态使用
6.1    利用多态来实现 printError同一个接口调用
6.2    抛出不同的错误对象,提示不同错误
7    使用系统标准异常
7.1    #incldue <stdexcept>
7.2    throw out_of_range(”aaa”) 。。。
7.3    catch(out_of_range & e)  cout  <<  e.what();
8    编写自己的异常类
8.1    自己的异常类 需要继承于  exception
8.2    重写  虚析构   what()
8.3    内部维护以错误信息 字符串
8.4    构造时候传入 错误信息字符串,what返回这个字符串
8.5    string 转 char *   .c_str();
9    标准的输入流
9.1    cin.get 缓冲区中读取一个字符
9.2    cin.get(两个参数) 不读换行符
9.3    cin.getline () 读取换行 并且扔掉
9.4    cin.ignore 忽略 (N) N代表忽略字符数
9.5    cin.peek 偷窥   偷看1个字符然后放回去
9.6    cin.putback  放回 把字符放回缓冲区
10    输入流案例
10.1    判断用户输入的是字符串还是数字 利用偷窥 或者 放回
10.2    让用户输入指定范围内的数字,如果不正确 重新输入
10.2.1    cin.fail() 看标志位  0正常 1不正常
10.2.2    cin.clear()重置标志位
10.2.3    cin.syne() 清空缓冲区
11    标准输出流
11.1    流对象的成员函数
11.1.1    int number = 99;
11.1.2    cout.width(20);
11.1.3    cout.fill('*');
11.1.4    cout.setf(ios::left); //设置格式  输入内容做对齐
11.1.5    cout.unsetf(ios::dec); //卸载十进制
11.1.6    cout.setf(ios::hex); //安装16进制
11.1.7    cout.setf(ios::showbase); // 强制输出整数基数  0  0x
11.1.8    cout.unsetf(ios::hex);
11.1.9    cout.setf(ios::oct);
11.1.10    cout << number << endl;
11.2    控制符
int number = 99;
        cout << setw(20)
        << setfill('~')
        << setiosflags(ios::showbase) //基数
        << setiosflags(ios::left) //左对齐
        << hex // 十六进制
        << number
<< endl;
12    文件操作
12.1    写文件
12.1.1    ofstream  ofs
12.1.2    open 指定打开方式
12.1.3    isopen 判断是否打开成功
12.1.4    ofs << “数据”
12.1.5    ofs.close
12.2    读操作
12.2.1    ifstream  ifs
12.2.2    指定打开方式 ios::in
12.2.3    isopen判断是否打开成功
12.2.4     三种方式读取数据

在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_BasicLearning_09.html