1.1.函数的异常声明:
在函数申明的时候,确定抛出异常的类型,增加代码的可读性,并且确定好丢的异常后,更好的捕获异常
格式 : 返回值 函数名(参数列表) throw(类型1,类型2,类型3 ){ }
注意:如果不希望函数丢异常,则throw后直接加小括号
代码:
- #include
- using namespace std;
- void fun(int x)throw(int ,char)//可以抛出的异常单位
- {
- if(x == 0)
- {
- // throw 1.3; 不行
- throw 'a';
-
- }
- }
- void show()throw()
- {
- throw 1;
- }
-
- int main(int argc, char *argv[])
- {
- try{
- fun(0);
- //show();
- }
- catch(char a)
- {
- }
-
- return 0;
- }
2.自定义异常类
定义一个类,可以用于throw中。
(补充笔记)
代码:
- #include <iostream>
- #include <cstring>
- #include <exception>
- using namespace std;
- class errorEx
- {
-
- int errId=0;
- const char* errStr[4]={"","第一种错误","第二种错误","第三种错误"};
- public:
- errorEx(int id = 0)
- {
- this->errId = id;
- cout << "我是构造函数 id="<< this->errId <<endl;
- }
-
- ~errorEx()
- {
- cout << "我是析构函数"<<endl;
- }
- errorEx show()throw(errorEx);
- string what()
- {
- if(errId == 1)
- {
- return errStr[1];
- }
- else if(errId == 2)
- {
- return errStr[2];
- }
- if(errId == 3)
- {
- return errStr[3];
- }
- }
-
- };
- errorEx errorEx::show()throw(errorEx)
- {
- if(this->errId != 0)
- {
- throw *this;
- }
- }
-
- class myexception:public exception
- {
- public:
- myexception(){
- cout << "我是构造"<<endl;
- }
- virtual const char* what() const throw()
- {
- return "我是myexception的异常";
- }
-
- };
- void fun()
- {
- myexception er;
- throw er;
- }
-
- int main(int argc, char *argv[])
- {
- try
- {
- // errorEx err(1);
- // err.show();
- fun();
- }
- catch(errorEx& e)
- {
- cout << "catch"<<endl;
- cout << e.what()<<endl;
- }
- catch(myexception& er)
- {
- try{
- cout << er.what()<<endl;
- }
- catch(...)
- {
- cout << "..."<<endl;
- }
- }
-
- return 0;
- }
-
-
3.标准异常
从标准的exception类继承的子类,处理异常。
exception中定义好的异常
std::bad_alloc 通过new抛出,
std::domain_error 使用了无效的数字域
std::length_error 当创建了一个太长的string会抛异常。
std::overflow_error 数据类型上溢出
std::underflow_error 数据类型下溢出
定义标注异常
class 异常类:public exception{}
注意:一般情况下需要重写what方法。
异常嵌套:在异常中 继续try和catch是可以的
try
{
errorEx err(1);
err.show();
}
catch(errorEx& e)
{
cout << "catch"< cout << e.what()< }
catch(myexception& er)
{
try{
cout << er.what()< }
catch(...) { }
二、模板:
1.泛型编程:用一个标志来表示类型,不是实际的类型。独立于任何特定类型的编程,c++的一个部分。
PPT模板---他是一个框架,需要填写内容--方便开发,提高了复用度。
2.函数模板:
格式:template
返回值 函数名(参数){ }
3.解释:
template ----- 声明创建模板
typename ----- (也可以用class代替)表明后的类型是一个通用类型,或许叫做虚拟类型
T1-------------- 通用类型或者虚拟类型,(理解成类型)可以在函数中直接当做普通类型使用。
4. 函数模板的调用:
3. 第一种: 函数名 <实际类型1,实际类型2>(参数列表);
显式类型推导 fun(1,2);
第二种: 函数名(参数);
隐形类型推导
5.注意:显示类型推导 参数和推导的类型必须一致。
如果有普通函数和模板函数,在调用时可以用显示调用,省略类型的方式 swap<>(1,2)
代码:
- #include <iostream>
-
- using namespace std;
- //T代替一个类型,是一个类型的代词比如T代替int
- template <typename T1,typename T2>
- T2 add(T1 a,T2 b)
- {
- return a+b;
- }
-
- template<class T>
- void swap(T x,T y)
- {
- T temp = x;
- x = y;
- y = temp;
- cout <<"x1 = "<< x << "y1 = "<< y<<endl;
- }
-
- void swap(int x,int y)
- {
- int temp = x;
- x = y;
- y = temp;
- cout <<"x = "<< x << "y = "<< y<<endl;
- }
-
- int main(int argc, char *argv[])
- {
- cout <<add('c',2.2)<<endl;
-
- swap<>(10,20);
- cout << "Hello World!" << endl;
- return 0;
- }
-
- //写一个swap交换两个变量的函数,用函数模板实现,然后写一个swap的普通函数。
- // 在main中显示类型推导和隐式类型推导分别调用
6.普通函数和函数模板的区别:
函数只可以有一种数据类型相匹配。模板有多种类型
隐式推导优先使用普通函数,只有普通函数不匹配才使用函数模板
函数模板只有在调用时,才会构建函数,而普通函数是在编译时。
普通函数调用时候可以发生自动类型转换,而函数模板不行。
7.函数模板重载
和普通函数的重载相似。也是同一个作用域类函数名相同参数列表不同。
以下是参数顺序不同的重载
template void swap1(T2 a,T1 b)
template void swap1(T1 a,T2 b)
注意,在函数参数顺序不同的重载中,实例化的时候不可以是相同类型
例如 swap1(1,2)则没法匹配哪个函数。
以下是参数个数不同的重载
template void swap1(T1 a,T2 b)
template void swap1(T2 t)
传入不同的参数,调用不同的版本
练习:写比较 大小函数模板,传入2 3 4数值都可以比较都可以返回最大值
代码:
-
- #ifndef 0
- #include <iostream>
- using namespace std;
-
-
- template<typename T1>
- void pp(T1 a)
- {
- cout<<"T0 = "<<a<<endl;
- }
- void pp(int a)//和上面的函数组成 隐式类型推导
- {
- cout<<"pp ="<<a<<endl;
- }
-
-
- /* //重载的swap1
- template <class T1,class T2>
- void swap1(T1 a,T2 b)
- {
- cout<<"1"<<endl;
- }
-
- template<class T1,class T2>
- void swap1(T2 a,T1 b)
- {
- cout<<"2"<<endl;
- }
-
- template <typename T2>
- void swap1(T2 t)
- {
- cout<<"3"<<endl;
- }
-
- */
-
- //写入一个可以比较数值大小的函数,传入的参数不限
- template <typename T1>
- T1 daxiao(T1 a,T1 b)
- {
- if(a>b)
- {
- return a;
- }
- else
- {
- return b;
- }
- }
-
- template <typename T1>
- T1 daxiao(T1 a,T1 b,T1 c)
- {
- if(a>b && a>c)
- {
- return a;
- }
- if(b>a && b>c)
- {
- return b;
- }
- if(c>b && c>a)
- {
- return c;
- }
-
- }
-
- template <typename T1>
- T1 daxiao(T1 a,T1 b,T1 c,T1 d)
- {
- if(a>b && a>c && a>d)
- {
- return a;
- }
- if(b>a && b>c && b>d)
- {
- return b;
- }
- if(c>b && c>a && c>d)
- {
- return c;
- }
- if(d>a && d>b && d>c)
- {
- return d;
- }
- }
-
-
-
- int main(int argc, char *argv[])
- {
- /*
- pp(1);//隐式类型推导测试
- pp('a');
- */
- /*重载函数的测试
- swap1<int,double>(10,10.0);
- swap1<int,double>(10.0,10);//<int->T1 double->T2>//类型匹配
- swap1<int>(1);
- */
- /*
- int a1=daxiao<int>(1,2);
- int a2=daxiao<int>(2,3,1);
- char a3=daxiao<char>('a','k','p','l');
- cout<<"a1= "<<a1<<endl;
- cout<<"a2= "<<a2<<endl;
- cout<<"a3= "<<a3<<endl;
- return 0;
- */
- return 0;
- }
- #endif
8.函数模板特化
定义:为了解决函数模板的局限性,在定义函数模板时候,直接确定好T的类型。也就是特定的类型模板。
格式: template 返回值 函数名(类名& 对象){ }
匹配:如果传入的是自定义类型,并且和特化版本匹配,会优先使用特化版本
注意:如果特化后,类型确定才可以使用自定义类型中的成员变量和方法。
代码:
- #include <iostream>
- #include <string>
- using namespace std;
- class person
- {
- public:
- int age = 0;
- string name;
- public:
- person(int age,string name):age(age),name(name) {
-
- }
- };
-
- template <class T>
- bool cmp(T a,T b)
- {
- if(a == b)
- {
- return true;
- }
- return false;
- }
-
-
- //函数模板特化
- template<class T>
- bool cmp(person& a,person& b)
- {
- if(cmp<int>(a.age,b.age) && cmp<string>(a.name,b.name))
- {
- return true;
- }
- return false;
- }
-
-
- void fun1()
- {
- int a = 10;
- int b = 12;
- cout << cmp(a,b)<<endl;
-
- }
- void fun2()
- {
- person a(10,"danny");
- person b(20,"wang");
- cmp<person>(a,b);
-
- }
-
- int main(int argc, char *argv[])
- {
- fun2();
- return 0;
- }
9.多文件实现函数模板
在定义函数或者类的时候,会.h和cpp文件,定义和申明分开。
出现连接错误的原因:函数模板是在调用时确定的版本,而调用时.h中没有函数实现,出现连接错误,找不到函数体。
如果分开后,编译会出现连接错误。
第一种办法:在main中#include 和 #include
第二种办法:模板的定义和申明都放在.h头文件中。
10. 函数模板的嵌套
定义:在函数模板中调用另外一个函数模板
练习:写两个Mymax函数模板重载,三个参数中调用两个参数的版本比较
11.函数模板的非类型参数
希望传入函数一个特定的确定参数,但不希望放在小括号中。
可以通过非类型参数实现。在模板<>中加了一个参数,但这个参数不是类型参数,是具体的某一个值。
格式:template
返回值 函数名(T& 变量)
例如:template
void showArr(T* arr);
注意:在上例子中,size是通过模板传入到函数中,可以当做普通变量使用
非类型参数都是常量,在函数中不允许修改,只可以使用,定义非类型参数变量时候,需要加const。
代码:
- #include <iostream>
-
- using namespace std;
- template<typename T>
- T mymax(T a,T b)
- {
- if(a>b)
- {
- return a;
- }
- return b;
- }
- //函数模板嵌套使用
- template<typename T>
- T max1(T a,T b,int c)
- {
- //return mymax<T>(mymax(a,b),c);
- }
-
- //函数模板的非类型参数
- template<class T,int size>
- void showArr(T* arr)
- {
- for(int i = 0;i <size;i++)
- {
- cout << arr[i]<<" ";
- }
- cout <<endl;
- }
-
-
- int main(int argc, char *argv[])
- {
- cout <<max1(1,2,3)<<endl;
- int arr[10]= {1,2,3,4,5,6,7,8,9,0};
- const int arrSize = sizeof(arr)/sizeof(arr[0]);
- showArr<int,arrSize>(arr);
-
-
-
- return 0;
- }
模板的缺省类型参数。
代码:
- #include <iostream>
-
- using namespace std;
- //模板的缺省类型参数。
- template<class T1=int,class T2=string>
- class person
- {
- T1 age;
- T2 name;
- public:
- person(T1 age,T2 name):age(age),name(name){}
- void show()
- {
- cout << "name= "<<name<<endl;
- cout << "age = "<<age <<endl;
- }
- };
-
-
- int main(int argc, char *argv[])
- {
- //实例化对象
- person<int ,string> danny(10,"danny");
- danny.show();
- person<> wang(3,"wang");
- wang.show();
- cout << "Hello World!" << endl;
- return 0;
- }
12.类模板
建立一个通用的类,如果希望成员变量的类型是任意的,用类模板实现。
类模板不是一个实际的类型,是虚拟的,和ppt模板一样,实例化的时候才会构建类。
格式:template
class test { T age; };
实例化: 类名<类型> 变量名(参数);
类模板和函数模板区别
函数模板可以使用隐式类型推导,但类模板不可以,必须显示推导
类模板在定义template时候,可以加上默认参数。template
在模板中的默认参数类型中,如果所有模板参数都是缺省,但在类进行实例化的时候,尖括号不可以省略template
class person{}; person<> wang(3,"wang");
代码:
- #include <iostream>
-
- using namespace std;
- class person1
- {
- public:
- void show1()
- {
- cout <<"我是person1"<<endl;
- }
- };
-
- class person2
- {
- void show2()
- {
- cout <<"我是person2"<<endl;
- }
- };
-
- template <class T>
- class usePerson
- {
- public:
- T per;
- //类模板中的成员函数,不是一开始就创建了,类模板调用时候创建
- void fun1()
- {
- per.show1();
- }
- void fun2()
- {
- per.show2();
- }
-
- };
-
- void myfun()
- {
- usePerson<person1> danny;
- danny.fun1(); //不出错
- //danny.fun2(); //出错
- }
-
- int main(int argc, char *argv[])
- {
- cout << "Hello World!" << endl;
- myfun();
- return 0;
- }
作业:做一个函数模板,用选择排序对不同数据类型进行排序(降序的方式)
可以传入多个参数,并且也支持非类型参数的版本。
- #include <iostream>
- using namespace std;
- template <typename T>
- void swap1(T* a,T* b)//万能的交换函数
- {
- T temp = *a;
- *a=*b;
- *b=temp;
- }
-
- template<typename T,int size>
- T sort(T num)//模板传递数组的长度
- {
- for(int i=0;i<size;i++)
- {
- for(int j=i+1;j<size ;j++)
- {
- if(num[i]<num[j])
- {
- swap1(&num[i],&num[j]);
- }
- }
- }
- return num;
- }
-
- template<typename T>
- T sort(T num,int size)//函数传递数组的长度
- {
- for(int i=0;i<size;i++)
- {
- for(int j=i+1;j<size ;j++)
- {
- if(num[i]<num[j])
- {
- swap1(&num[i],&num[j]);
- }
- }
- }
- return num;
- }
-
-
- int main()
- {
- /*
- int a=1;//整形的交换
- int b=2;
- swap1(&a,&b);
- cout<<"a = "<<a<<endl<<"b = "<<b<<endl;
-
-
- char a='a';//字符型的交换
- char b='b';
- swap1(&a,&b);
- cout<<"a = "<<a<<endl<<"b = "<<b<<endl;
-
- double a=1.0;//浮点型的交换
- double b=2.0;
- swap1(&a,&b);
- cout<<"a = "<<a<<endl<<"b = "<<b<<endl;
-
- string a("1111");//字符串类型的交换
- string b("2222");
- swap1(&a,&b);
- cout<<"a = "<<a<<endl<<"b = "<<b<<endl;
- */
- int num[]={1,5,3,9,6,45,23,96,14,100};
- const int size =sizeof(num)/sizeof(num[0]);
- int *num1=sort<int* ,size>(num);
- int *num2=sort(num,size);
- cout<<"num1 = ";
- for(int i=0;i<size;i++)
- {
- cout<<num1[i]<<" ";
- }
- cout<<endl;
- cout<<"num2 = ";
- for(int i=0;i<size;i++)
- {
- cout<<num2[i]<<" ";
- }
-
-
-
- return 0;
- }