您的位置:首页 > 编程语言 > C语言/C++

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

2014-10-28 21:22 176 查看
【模板】

函数模板定义的一般形式:

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种类型:语法错误、连接错误、运行时错误 和 逻辑错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: