例子1 复数类—-所有函数写的类的内部
#include
using namespace std;
template
class Complex
{
firend ostream & operatora = a;
this->b = b;
}
Complex operator+(Complex &c2)
{
Complex tmp(a + c2.a, b + c2.b);
return tmp;
}
void printComplex()
{
cout >只能用友员函数,其他运算符重载,都要写成成员函数,不要滥用友员函数
void main()
{
//需要把模板类进行具体化以后,才能定义对象,c++编译器要分配内存
Complex c1(1,2);
Complex c2(3, 4);
Complex c3=c1+c2;
cout
例子2 复数类—-所有函数写的类的外部,但在一个cpp里
#include
using namespace std;
template
class Complex
{
firend Complex MySub (ostream &out, Complex &c3);
public:
Complex(T a, T b)
Complex operator+(Complex &c2)
void printComplex();
private:
T a;
T b;
};
template//构造函数的实现,写在了类的外部
Complex::Complex(T a,T b)
{
this->a = a;
this->b = b;
}
template
void Complex::printComplex()
{
cout
Complex Complex::operator+(Complex &c2)//成员函数实现+运算符重载
{
Complex tmp(a + c2.a, b + c2.b);
return tmp;
}
template
ostream & operator&c3)//友员函数实现运算符重载
{
cout c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
cout
归纳以上的介绍,可以这样使用.声明类模板
1)先写出一个实际的类.由于其语义明确,含义清楚,一般不会出错.
2)将此类中准备改变的类型名(如int 要改为char)改用一个自己指定的虚拟类型名字
3)在类声明前加入一行,格式为:
template
4)用类模板定义对象使用以下形式:
类模板名对象名
类模板名对象名(实参列表)
如:
Compare
Compare
5)如果在类模板外定义成员函数,应该写成类模板形式:
template
函数类型 类模板名::成员函数名(参数形参列表)(……)
关于类模板的几点说明
1)类模板的类型参数可以有一个或多个,每个类型前面都必须加class,
如:
template
class someclass
{……}
定义对象时分别带入实际的类型名,如;
someclass
2)和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象.
3)模板可以有层次,一个类模板可以作为基类,派生出派生类的模板,有关这方面的知识实际应用比较少,感兴趣的可以自行查阅.
类模板中的ststic关键字
从类模板比例实例化的每个模板类都有自己的类模板数据成员,该模板类的所有对象共享一个
ststic数据成员.
和非模板类的ststic数据成员一样,模板类的ststic数据成员,也应该在文件范围定义和初始化.
每个模板类都有自己的类模板和ststic数据成员副本.
例子
#include
using namespace std;
template
class AA
{
public:
static T m_a;
private:
};
class AA1
{
public:
static int m_a;
private:
};
template
int AA1::m_a = 0;
class AA2
{
public:
static char m_a;
private:
};
char AA2::m_a = 0;
void main()
{
AA a1, a2, a3;
a1.m_a = 10;
a2.m_a++;
a3.m_a++;
cout ::m_a b1, b2, b3;
b1.m_a = 'a';
b2.m_a++;
b2.m_a++;
cout ::m_a
异常问题
一、为什么要有异常——WHY?
1.通过返回值表达错误
像malloc会返回0或1.
局部对象都能正确的析构
层层判断返回值,流程繁琐
例子:
#include
#include
using namespace std;
int func3 (void) {
FILE* fp = fopen ("none", "r");//fopen失败会返回控指针NULL。
if (! fp)
return -1;
// ...
fclose (fp);
return 0;
}
int func2 (void) {
if (func3 () == -1)
return -1;
// ...
return 0;
}
int func1 (void) {
if (func2 () == -1)
return -1;
// ...
return 0;
}
int main (void) {
//层层判断返回值
if (func1 () == -1) {
cout
2.通过setjmp/longjmp远程跳转
一步到位进入错误处理,流程简单
局部对象会失去被析构的机会
例子:
#include
#include
#include //标c的函数,跳转
using namespace std;
jmp_buf g_env; //jmp是专门为c量身定造的,有类的情况不适用,会跳转,因为不执行右括号,局部对象失去执行析构的机会,不会调用析构函数,会造成内存泄露
class A {
public:
A (void) {
cout
———————————————————————
3.异常处理
局部对象都能正确的析构
一步到位进入错误处理,流程简单
———————————————————————
二、异常的语法——WHAT?
1.异常的抛出
throw 异常对象;
异常对象可以是基本类型的变量,也可以是类类型的对象。
当程序执行错误分支时抛出异常。
2.异常的捕获
try {
可能抛出异常的语句块;
}
catch (异常类型1 异常对象1) {
处理异常类型1的语句块;
}
catch (异常类型2 异常对象2) {
处理异常类型2的语句块;
}
…
catch (…) {
处理其它类型异常的语句块;
}
异常处理的流程,始终沿着函数调用的逆序,依次执行右花括号,直到try的右花括号,保证所有的局部对象都能被正确地析构,然会根据异常对象的类型,匹配相应的catch分支,进行有针对性的错误处理。
例子:
#include
#include
using namespace std;
class A {
public:
A (void) {
cout