模板
函数模板
template<class T>
template<typename T>//每一个函数都要声明一下模板
void func(T &a,T &b){}
main()
{
int a,int b;
char c;
func(a,c);//错误,推导不出来T是什么类型
func(a,b);//正确
fun<int>(a,b);//显式指定类型
//模板必须要指定出T
}
利用模板实现数组排序
template<class T>
void sort(T a[],int len)
{
for(int i = 0; i < len; i++){
int max = i;
for(int j = i + 1; j < len; j++){
if(a[max] < a[j]){
max = j;
}
}
if(max != i){
swap(a[i],a[max]);
}
}
}
template<class T>
void printf(T a[],int len){
....
}
main()
{
char s[] = "helloworld";
sort(s,10);
int a[] = {1,2,3,4,5};
sort(a,5);
}
函数模板与普通函数的区别
template<class T>
T add(T a,T b)
int add2(int a,int b)
main()
{
char c;
int a;
add(a,c);//错误
add2(a,c);//正确,普通函数可以进行隐式类型转换
}
template<class T>
void func(T a,T b){}
void func(int a,int b){}
template<class T>
void func(T a,T b,T c){}
main
{
int a,b;
fun(a,b);//如果出现重复,优先使用普通函数调用,如果没有实现,出现错误
//如果想强制调用函数模板,那么可以使用空参数列表
func<>(a,b);//<>代表空参数列表
int c;
fun(a,b,c);//模板函数可以发生重载
char d,e;
func(d,e);//如果函数模板可以产生更好的匹配,那么优先调用函数模板,此时调用的就为模板函数,因为普通函数为int,会产生隐式转换,而模板函数不会。
}
函数模板实现机制
1.编译器并不是把函数模板处理成能够处理任何类型的函数。
2.函数模板通过具体类型产生不同的函数。
3.编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
模板的局限性及解决
class person{};
template<class T>
bool comp(T a,T b)
template<> bool comp<person>(person &p1,person &p2)
{
if (p1.name == p2.name)
....
}//如果具体化能够优先匹配,那么就选择具体化
main()
{
person p1,p2;
comp(p1,p2);//此时是不能通过编译的(没写template<> bool之前)
//通过具体化自定义数据类型,就觉上述类型
comp(p1,p2);//写了之后,直接去找template<> bool这个函数
}
template<> 返回值 参数名<具体类型>(参数)
类模板
基本使用
template<class nametype,class agetype = int>//类模板可以有默认参数类型
class person
{
public;
person(nametype name,agetype age){
this->name = name;
this->age = age;
}
nametype nae;
agetype age;
};
main()
{
person("zzz",100);//自动类型推导,不支持
person<string,int>("zzz",100);//这样写支持
}
class person1{
public:
void showperson1();
}
class person2{
public:
void showperson2();
}
template<class T>
class person
{
public:
T.obj;
void func1()
{
obj.showperson1;
}
void func2()
{
obj.showperson2;
}
}
main()
{
person<person1> p;
p.showperson1;//当执行这个代码时不会出现问题
p.showperson2;//执行这个代码会出现问题
}//说明成员函数一开始不会创建出来,而是在运行时才创建
类模板做函数的参数
template<class nametype,class agetype = int>//类模板可以有默认参数类型
class person
{
public;
person(nametype name,agetype age){
this->name = name;
this->age = age;
}
void show();
nametype nae;
agetype age;
};
1.指定传入类型
void dowork(person<string,int> &p)
{
p.show();
}
2.参数模板化
template<class T1,class T2>
void dowork2(person<T1,T2> &p)
{
//如何查看类型
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
//
p.show();
}
3.整体类型化
template<class T>
void dowork3(T &p)
{
p.show();
}
类模板碰到继承
template<class T>
class Base
{
public:
T a;
};
class child:public Base<int>;
//这样才能继承,必须告诉base中T的类型,否则T无法分配内存
template<class T1,class T2>
class child2:public Base<T2>
{
T b;
};
main()
{
child2<int,double> c;
}
类模板类外实现成员函数
template<class T1,class T2>
class person
{
public:
person(T1 n,T2 a);
void showperson();
T1 name;
T2 age;
};
//类外实现成员函数
template<class T1,class T2>
person<T1,T2>::person(T1 name,T2 age);
template<class T1,class T2>
void person<T1,T2>::showperson();
类模板的分文件编写
//person.h
#pragama once
template<class T1,class T2>
class person
{
public:
person(T1 n,T2 a);
void showperson();
T1 name;
T2 age;
};
//person.cpp
#include "person.h"
template<class T1,class T2>
person<T1,T2>::person(T1 n,T2 a)
{
...//实现
}
void person<T1,T2>::showperson()
//person.hpp
#pragama once
template<class T1,class T2>
class person
{
public:
person(T1 n,T2 a);
void showperson();
T1 name;
T2 age;
};
template<class T1,class T2>
person<T1,T2>::person(T1 n,T2 a)
{
...//实现
}
template<class T1,class T2>
void person<T1,T2>::showperson()
//此时头文件不能写person.h,因为只能声明,不能编译
#include "person.cpp"//声明与实现都能编译,但一般不使用
#include "person.hpp"
main()
{
person<string,int> p("zzz",100);
p.showperson;
}
建议模板不要做分文件编写,写到一个类中即可。类内进行声明和实现,最后把后缀名改为hpp,约定俗成。
类模板碰到友元函数-类内实现
template<class T1,class T2>
class person
{
friend void printperson(person<T1,T2> &p){
cout << name << age << endl;
}//默认为全局函数,不是成员函数,不用对象去调用,直接调用
public:
person(T1 n,T2 a)
private:
T1 name;
T2 age;
};
main()
{
person<string,int> p(...);
printperson(p);
}
类模板碰到友元函数-类外实现
//让编译器提前看到person声明
template<class T1,class T2>
class person;
//让编译器提前看到printperson声明,不用知道是不是模板函数
template<class T1,class T2>
void printperson(person<T1,T2> &p);
template<class T1,class T2>
class person
{
friend void printperson<>(person<T1,T2> &p);//利用空参数列表让它成为模板函数
public:
person(T1 n,T2 a)
private:
T1 name;
T2 age;
};
template<class T1,class T2>
void printperson(person<T1,T2> &p){
cout << name << age << endl;
}
c++类型转换
一般情况下,少用转换
静态转换
只能用于基础数据的转换和有父子关系之间的转换。
//使用方式
static_cast<目标类型>(原始对象)
char a = 'a';
double d = static_cast<double>(a);//普通类型转换
class base;
class child:public base;
main()
{
base *b = NULL;
child *c = NULL;
child *c2 = static<child*>(b);//向下转换,不安全
base *b2 = static<base*>(c);//向上转换,安全
}
动态转换
基础类型不可以转换。非常严格,失去精度或者不安全都不可以转换。
dynamic_cast如果发生了多态(有虚函数,不光有继承),那么可以让基类转为派生类,向下转换。(因为多态永远是安全的)
dynamic_cast<目标类型>(原始对象);
常量转换
注意:不能对非指针和非引用的变量使用const_cast操作符。
int *p = NULL;
int *newp = const_cast<int *>(p);//这样就去掉了const
int *p2 = NULL;
const int * newp2 = const_cast<const int *>(p2);//加上了const
重新解释转换
reinterpret_cast<目标类型>(原始类型)
//最不安全,不推荐,什么都可以转,乱转
c++异常
异常基本处理
class myexception
{
public:
void printerror()
cout << "自定义的异常" << endl;
}
int test01(int a,int b)
{
if(b == 0){
//return -1;
throw -1;//抛出int类型异常
throw 3.14;//抛出double类型异常
throw myexpection();//抛出匿名对象,自定义的异常
}
return a / b;
}
void test02()
{
int a = 10, b = -10;
//int ret = test01();//早期如果返回-1,不不知道是异常还是结果
try{
test01();
}catch(int){
cout << "int 类型异常捕获" << endl;
}catch(double){
//如果不想处理,可以继续向main函数抛出,但如果main函数不处理,terminate函数会使程序中断
throw;
//
cout << "double 类型异常捕获" <<endl;
}catch(myexpection e){
e.printerror;
}//自定义类型异常捕捉
}
main()
{
test02();
try{
test01();
}catch(int){
cout << "int 类型异常捕获" << endl;
}catch(double){
cout << "double 类型异常捕获" <<endl;
}catch(...){
cout << "其他类型异常捕获" <<endl;
}
}
异常必须处理,不处理就会挂掉。
栈解旋
异常接口声明
抛出特定的类型异常
void func() throw(int)//只能抛出int异常
throw(int,char)//抛出int,char
{
throw 1;
}
void func() throw()//不抛出任何类型异常
异常变量生命周期
如果myexception e,会多开销一份数据,调用拷贝构造。
如果myexception *e,不new会提前析构对象,new就自己管理在堆上的数据(栈解旋)
推荐myexception &e 就一份数据,还可供操作。
异常的多态使用
//异常基类
class base
{
public:
virtual void printerr{
cout<<"error"<<endl;
}
};
class nptr:public base
{
public:
virtual void printerr{
cout<<"ptr error"<<endl;
}
};
class outofrange:public base
{
public:
virtual void printerr{
cout<<"range error"<<endl;
}
};
void func()
{
throw nptr();//抛出子类对象
}
main()
{
try{
func();
}catch(base &e){
e.printerr();//父类引用使用子类对象,发生多态
}
}
使用系统标准异常
#include <stdexcept>//引入头文件
class person
{
public:
person(int age){
if(age < 0)
throw out_of_range("年龄太小了,建议入土");
}
int age;
}
main()
{
try{
person(-1);//这个傻逼,还没出生,真的脑残
}catch(out_of_range &e)//out_of_range也是一个类
{
cout << e.what() << endl;//what()就是类中的("年龄太小了,建议入土")
}
}
使用系统的要提供打印信息
扩展自己的系统异常类
class myerror:public exception//继承于系统的异常类
{
public:
myerror(string errorinfo){
this->errorinfo = errorinfo;
}
virtual ~myerror(){}
virtual const char* what() const{
//返回错误信息
//string 转 char * .c_str()
return this->errorinfo.c_str();
}
string errorinfo; //接收要打印的那句话
}
使用方法与其他系统异常相同