所有的类模板函数写在类的内部
#include "iostream"
using namespace std;
template <typename T>
class Complex
{
public:
friend Complex MySub(Complex &c1, Complex &c2)
{
Complex temp(c1.a - c2.a, c1.b - c2.b);
return temp;
}
friend ostream & operator<<(ostream &out, Complex &c)
{
out << c.a << " + " << c.b << endl;
return out;
}
Complex(T a , T b )
{
this->a = a;
this->b = b;
}
//重载+操作符
Complex& operator+(Complex &c2)
{
Complex temp(a+c2.a, b+c2.b);
return temp;
}
//重载<<运算符
void printComplex()
{
cout << "a: " << a << "b: " << b << endl;
}
private:
T a;
T b;
};
//运算符重载的正规写法
//重载 << >>只能用友元函数,其他的运算符重载有要写成成员函数, 不要滥用友元函数
/*
ostream & operator<<(ostream &out, Complex &c)
{
out << "c.a: " << c.a << "c.b: " << c.b << endl;
return out;
}
*/
void main061()
{
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
//Complex& operator+(Complex &c2);
Complex<int> c3 = c1 + c2;
cout << c3 << endl;
//滥用友元函数案例
{
Complex<int> c4 = MySub(c1, c2);
cout << c4 << endl;
}
system("pause");
}
所有的类模板函数写在类的外部,在一个 cpp 中
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <string>
using namespace std;
template <typename T>
class Complex; //类的前置声明
template <typename T>
Complex<T> MySub<T>(Complex<T> &c1, Complex<T> &c2); //非<< >>重载的友元函数的前置声明
template <typename T>
class Complex
{
public:
friend Complex<T> MySub<T>(Complex<T> &c1, Complex<T> &c2);
//重载<<运算符 operator<<<T>
friend ostream & operator<<<T>(ostream &out, Complex &c);
Complex(T a, T b);
void printComplex();
//重载+操作符
Complex operator+(Complex &c2);
//Complex operator-(Complex &c2);
private:
T a;
T b;
};
//构造函数的实现写在了类的外部
template <typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <typename T>
void Complex<T>::printComplex()
{
cout << "a: " << a << "b: " << b << endl;
}
//成员函数 实现+运算符重载
template <typename T>
Complex<T> Complex<T>::operator+ (Complex<T> &c2)
{
Complex temp(a + c2.a, b + c2.b);
return temp;
}
/*template <typename T>
Complex<T> Complex<T>::operator-(Complex<T> &c2)
{
Complex<T> temp(a - c2.a, b - c2.b);
return temp;
}*/
//友元函数实现 重载<<运算符
/* 本质是:模板函数是两次编译生成的,第一次生成的函数头和第二次生成的函数头不一样
解决方法: friend ostream & operator<<<T>(ostream &out, Complex &c);
错误 LNK2019 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > &
__cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Complex<int> &)"
(??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Complex@H@@@Z),
该符号在函数 _main 中被引用 泛型编程课堂操练
*/
template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c)
{
out << c.a << " + " << c.b << endl;
return out;
}
/
//滥用友元函数
/*
错误 LNK2019 无法解析的外部符号 "class Complex<int> __cdecl MySub(class Complex<int> &,class Complex<int> &)"
(?MySub@@YA?AV?$Complex@H@@AAV1@0@Z),该符号在函数 _main 中被引用
*/
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2)
{
Complex<T> temp(c1.a - c2.a, c1.b - c2.b);
return temp;
}
//运算符重载的正规写法
//重载 << >>只能用友元函数,其他的运算符重载有要写成成员函数, 不要滥用友元函数
/*
ostream & operator<<(ostream &out, Complex &c)
{
out << "c.a: " << c.a << "c.b: " << c.b << endl;
return out;
}
*/
void main()
{
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
//Complex& operator+(Complex &c2);
Complex<int> c3 = c1 + c2;
cout << c3 << endl;
//滥用友元函数案例
{
Complex<int> c4 = MySub<int>(c1, c2);
cout << c4 ;
}
system("pause");
return;
}
//构造函数 没有问题
//普通函数 没有问题
//友元函数:用友元函数重载 << >>
// friend ostream& operator<< (ostream &out, Complex &c3) ;
//友元函数:友元函数不是实现函数重载(非 << >>)
//1)需要在类前增加 类的前置声明 函数的前置声明
template
class Complex;
template
Complex mySub(Complex &c1, Complex &c2);
//2)类的内部声明 必须写成:
friend Complex mySub (Complex &c1, Complex &c2);
//3)友元函数实现 必须写成:
template
Complex mySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b-c2.b);
return tmp;
}
//4)友元函数调用 必须写成
Complex c4 = mySub(c1, c2);
cout<<c4;
结论:友元函数只用来进行 左移 友移操作符重载。
所有的类模板函数写在类的外部,在不同的.h 和.cpp 中
#include "iostream"
using namespace std;
#include "dem09_Complex.cpp" //注意:模板类的声明和实现分开写时,此处包含的是.cpp文件,否则会报错找不到函数实现
void main()
{
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
//Complex& operator+(Complex &c2);
Complex<int> c3 = c1 + c2;
cout << c3 << endl;
//滥用友元函数案例
{
//Complex<int> c4 = MySub<int>(c1, c2);
//cout << c4;
}
system("pause");
return;
}
要包含.cpp,否则编译不通过
总结
归纳以上的介绍,可以这样声明和使用类模板:
- 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。
轻松入门 实战应用 传智播客 C++课程 - 将此类中准备改变的类型名(如 int 要改变为 float 或 char)改用一个自己指定的虚拟类型名
(如上例中的 numtype)。 - 在类声明前面加入一行,格式为:
template <class 虚拟类型参数>
如:
template //注意本行末尾无分号
class Compare
{…}; //类体 - 用类模板定义对象时用以下形式:
类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
如:
Compare cmp;
Compare cmp(3,7); - 如果在类模板外定义成员函数,应写成类模板形式:
template <class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}
关于类模板的几点说明:
- 类模板的类型参数可以有一个或多个,每个类型前面都必须加 class,如:
template <class T1,class T2>
class someclass
{…};
在定义对象时分别代入实际的类型名,如:
someclass<int,double> obj; - 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。
- 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。