【模板】
函数模板定义的一般形式:
template <类型形式参数表>
返回类型 函数名(形式参数表) { ... //函数体 }
范例:
定义一个求和的函数模板
template <class type> type Sum(type xvar, type yvar) { return xvar + yvar; }
在定义完函数模板后,需要在程序中调用函数模板。下面的代码演示了Sum函数模板的调用。
int iRet = Sum(10, 20);
int dRet = Sum(1.2, 3.45);
消除模板调用产生歧义的方法-->在调用函数模板时显示标识模板类型。如:
int iRet = Sum(10.5, 20);
double dret = Sum<double>(10, 20.5);
用函数模板生成实际可执行的函数又称为模板函数。
例:使用数组作为模板参数
#include <iostream> using namespace std; template <class type,int len> //定义一个模板类型 type Max(type array[len]) { //定义函数模板 type ret = array[0]; for(int i=1;i<len;i++) { ret = ret > array[i] ? ret : array[i]; } return ret; } int main(int argc,char* argv[]) { int array[5] = {1,2,3,4,5}; //定义一个整型数组 int iret = Max<int,5>(array); //调用函数模板Max double dset[3] = {1.2, 3.45, 6.789}; //定义实数数组 double dret = Max<double,3>(dset); //调用函数模板Max cout << dret << endl; return 0; }
重载函数模板举例:
#include <iostream> #include <cstring> using namespace std; template<class type> type Min(type a, type b) { //定义函数模板 return a < b ? a : b; } char* Min(char* a, char* b) { //重载函数模板 if(strcmp(a, b) < 0) return a; return b; } int main(int argc, char* argv[]) { cout << Min(10, 3) << endl; cout << Min('a','b') << endl; cout << Min("lasolmi","hello world") << endl; return 0; }
类模板的定义形式:
template<类型形式参数表>
class 类模板名
{
... //类模板体
};
类模板成员函数的定义形式:
template<类型形式参数表>
返回类型 类模板名 <类型名表>::成员函数名(形式参数列表)
{
... //函数体
}
生成类的形式如下:
类模板名<类型实在参数表>
用新生成的类定义对象的形式如下:
类模板名<类型实在参数表> 对象名
在类模板定义中,类型形式参数表中的参数也可以是其他类模板。例如:
template<template<class A> class B> class CBase { private: B<int> m_n; };
类模板也可以进行继承。例如:
t
emplate<class T> class CDerived : public T { public: CDrived(); }; template<class T> CDerived<T>::CDerived(): T() { cout << :: <<endl; } int main(int argc,char* argv[]) { CDerived<CBase1> D1; return 0; }
例:简单类模板
#include <iostream> using namespace std; template<class T1, class T2> class MyTemplate { T1 t1; T2 t2; public: MyTemplate(T1 tt1, T2 tt2) {t1 = tt1; t2 = tt2;} void display() {cout << t1 << " " << t2 <<endl;} }; int main(int argc, char* argv[]) { int a = 123; double b = 3.14159; MyTemplate<int,double> mt(a, b); mt.display(); return 0; }
默认模板参数,例:
template<class T1, class T2=int>
为具体了女性的参数提供默认值,例:
template<class T1, class T2, int num=10>
有界数组模板:
#icnlude <cassert> template<class T, int b> class Array { T& operator[] (int sub) { return sub >=0 && sub < b; } };
模板的定制:定义完模板后如果想扩展模板新类的功能,需要对类模板进行覆盖,是模板类能
够完成功能。覆盖操作可以针对整个类模板、部分类模板以及类模板的成员函数。这种覆盖操
作成为定制。
对于同一类型的模板实例,其静态数据成员是共享的。
【STL标准模板库】
序列容器:
向量类模板
双端队列类模板
链表类模板
结合容器
set类模板
multiset类模板
map类模板
multimap类模板
算法
非修正序列算法
修正序列算法
排序算法
数值算法
迭代器
输出迭代器
输入迭代器
前向迭代器
双向迭代器
随机访问迭代器
【RTTI与异常处理】
运行是类型识别(Run-time Type Identification, RTTI)是在只有一个指向基类的指针或引用
时确定的一个对象的类型。
范例:CBint类和CBString类都继承与CBase类,这3个类存在一个公共方法GetName(),而CBint
类有自己的特有方法GetInt(),CBString类有自己的特有方法GetString()。如果想通过CBase
类的指针调用CBint类或CSString类的特有方法时就必须确定指针的具体类。字啊面的代码完成
了这样的功能。
#include <cstdio> class CBase { //基类 public: virtual char * GetName() = 0; //虚方法 }; class CBint : public CBase { public: char * GetName() {return "CBint";} int GetInt() {return 1;} }; class CBString : public CBase { public: char * GetName() {return "CBString";} char * GetString() {return "Hello";} }; int main(int argc, char* argv[]) { CBase* B1 = (CBase*)new CBint(); printf(B1->GetName()); CBint* B2 = static_cast<CBint*>(B1); //静态替换 if(B2) printf("%d" , B2->GetInt()); CBase* C1 = (CBase*)new CBString(); printf(C1->GetName()); CBString* C2 = static_cast<CBString*>(C1); if(C2) printf(C2->GetString()); return 0; }
从上面代码可以看出基类CBase的指针B1和C1分别指向了CBint类与CBString类的对象,并且在
程序运行时基类通过static_cast进行了转换,这样就形成了一个运行时类型是别的过程。
RTTI与引用
#include <typeinfo>
typeid(p); //p是某类型指针
typeid()获取的指针是基类类型,而不是子类类型或派生类类型,typeid()获取的引用是子类
类型。
【RTTI映射语法】
dynamic_cast:用于安全类型的向下映射。
例如,通过dynamic_cast实现基类指针的向下转型。
const_cast:用于映射常量和变量。
例如,在常方法中修改成员变量和常量的值。
static_cast:为了行为良好和行为较好使用的映射,如向上转型和类型自动转换。
例如,通过static_cast将子类指针向上转成基类指针。
reinterpret_cast:将某一类型映射回原有类型时使用。
例如,将整型转成字符型,再由reinterpret_cast转换回原类型。
【异常处理】
抛出异常:
例:将错误ID和错误信息以类对象的形式进行异常抛出。
#include <iostream> #include <cstring> using namespace std; class CCustomError { private: int m_ErrorID; char m_Error[255]; public: CCustomError(int ErrorID, char* Error) { m_ErrorID = ErrorID; strcpy(m_Error, Error); } int GetErrorID() {return m_ErrorID;} char* GetError() {return m_Error;} }; int main(int argc, char* argv[]) { try { throw (new CCustomError(1,"出现异常!")); } catch(CCustomError* error) { //输出异常信息 cout << "异常ID:" << error->GetErrorID() <<endl; cout << "异常信息:" << error->GetError() << endl; } return 0; }
异常捕获:
异常抛出信号发出后,一旦被异常处理器接收到就被销毁。异常处理器应具备接受任何异常的
能力。异常处理器紧随try块之后,处理的方法由关键字catch引导。
有时并不一定在列出的异常处理中包含所有可能发生的异常类型,所以C++提供了可以处理任何
类型异常的方法,就是在catch后面的括号内添加“...”代码如下:
int main(int argc, char* argv[]) { try { throw "字符串异常"; } catch (CCustomError* error) { //输出异常信息 cout << "异常ID:" << error->GetErrorID() <<endl; cout << "异常信息:" << error->GetError() << endl; } catch (char* error) { cout << "异常信息:" << error << endl; } catch (...) { cout << "未知异常信息" << endl; } return 0; }
异常匹配:
能匹配到基类,而不能匹配到其派生类。
为了正确的进入指定的异常处理器,在对异常处理器进行排列时应将派生类排在前面,而将基
类排在后面。
标准异常:
下面给出了C++提供的一些标准异常:
namespace std { //exception 派生 class logic_error; //logic_error 派生 class domain_error; class invalid_argument; class length_error; class out_of_range; class bad_cast; class bad_typeid; //exception 派生 class runtime_error; //runtime_errot 派生 class range_error; class overflow_error; class bad_alloc; }
【程序调试】
程序错误常见的4种类型:语法错误、连接错误、运行时错误 和 逻辑错误。