c++Day5

模板

函数模板

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; //接收要打印的那句话
}

使用方法与其他系统异常相同

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值