首页 > 代码库 > C++学习笔记3--一些应用

C++学习笔记3--一些应用

【模板】
函数模板定义的一般形式:
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种类型:语法错误、连接错误、运行时错误 和 逻辑错误。



C++学习笔记3--一些应用