一、一个案例引入模板
1、模板和泛型编程系类文章介绍
(1)模板编程是C++的一种高级特性,很常用,很有用,可以说是 C++的精髓,是C++被广泛使用的关键
(2)泛型在C++、java、C#等多种语言中都有,是现代编程语言典型特性
(3)本章开始不像以前以理论知识讲解为主,而是以写代码和实践为主,在实践中学为主。因为开始变得抽象起来了
2、案例和传统解决方案
(1)问题:swap函数交换2个数 (2)传统解法:定义重载函数,用引用(或指针)来实现 (3)代码实战
#include <iostream>
using namespace std;
void myswap(int& a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void myswap(double& a, double& b)
{
double tmp;
tmp = a;
a = b;
b = tmp;
}
int main(int argc, char *argv[])
{
int a =3, b = 4;
double c = 3.3, d = 4.4;
myswap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
myswap(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
return 0;
}
3、传统方案的缺点
(1)要为每个数据类型提供一个重载函数,麻烦
(2)所有重载函数除了类型外其他完全一致,重复而浪费
4、解决办法思考与模板引入
(1)用到什么类型再实现相应重载。虽然可行但无法类库化,无法分工
(2)函数编写时进一步抽象化,调用时再给定具体类型,由编译器在编译时再绑定形成代码,这就是模板
(3)代码实践,用函数模板来实现swap
#include <iostream>
using namespace std;
template <typename X>
void myswap(X& a, X& b)
{
X tmp;
tmp = a;
a = b;
b = tmp;
}
int main(int argc, char *argv[])
{
int a =3, b = 4;
double c = 3.3, d = 4.4;
myswap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
myswap(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
return 0;
}
二、模板的理解和几个细节
1、模板是一种编译时的多态
(1)模板是关键是编写函数代码原型时不给定具体类型,每次调用时再给定具体类型
(2)模板特性由编译器提供,编译时根据调用时的实参具体类型推导匹配的原型中模板的本次具体类型
(3)模板在最终生成的可执行程序中是不可见的
(4)模板是编译时的多态,也可以叫“静态”多态
(5)模板是一种语法糖,是编译器提供给我们的减少编程劳动量的一种语法特性
2、模板有什么用
(1)让我们编写与类型无关的函数
(2)模板是一种抽象的维度,让我们能够写出抽象度更高的代码
(3)用模板来写库函数更好,因为这样的模板库可以在调用库函数时再指定各种数据类型
(4)模板没那么简单,抽象是拿复杂度换劳动量,所以学起来难,学会了用处大
3、模板的几个小细节
(1)函数模板定义时typename和class效果是一样的,视个人习惯而使用
template <typename X>
template <class X>
两个都可以,效果相同
(2)函数模板中可以有多个typename,使用时按名称匹配。
template <typename T1, typename T2>
三、函数模板和类模板
1、模板分2种
(1)函数模板。模板类型在函数参数列表中使用
(2)类模板。模板类型在类中(定义类成员变量,或类成员函数的参数列表)使用
2、类模板举例
单模板参数的类模板定义与使用
#include <iostream>
using namespace std;
template <typename T> class People
{
private:
int age;
public:
People(){};
People(int myage);
void print(T x);
};
template <typename T> People<T>::People(int myage)
{
this->age = myage;
}
template <typename T> void People<T>::print(T x)
{
cout << "age = " << age << ", x = " << x << endl;
}
int main(int argc, char *argv[])
{
People<int> p1(4);
People<double> p2(5);
p1.print(4444);
p2.print(5.55);
return 0;
}
四、多模板参数的类模板
1、同类型多参数
#include <iostream>
using namespace std;
template <typename T> class People
{
private:
int age;
public:
People(){};
People(int myage);
void print(T x, T y);
};
template <typename T> People<T>::People(int myage)
{
this->age = myage;
}
template <typename T> void People<T>::print(T x, T y)
{
cout << "age = " << age << ", x = " << x << ", y = " << y <<endl;
}
int main(int argc, char *argv[])
{
People<int> p1(4);
People<double> p2(5);
p1.print(4444, 0000);
p2.print(5.55, 1.122);
return 0;
}
2、不同类型多参数
#include <iostream>
using namespace std;
template <typename T1, typename T2>class People
{
private:
int age;
public:
People(){};
People(int myage);
void print(T1 x, T2 y);
};
template <typename T1, typename T2> People<T1, T2>::People(int myage)
{
this->age = myage;
}
template <typename T1, typename T2> void People<T1, T2>::print(T1 x, T2 y)
{
cout << "age = " << age << ", x = " << x << ", y = " << y <<endl;
}
int main(int argc, char *argv[])
{
People<int, string> s(4);
s.print(111, "one_one_one");
return 0;
}
3、成员函数在类内部的模板类
#include <iostream>
using namespace std;
template <typename T1, typename T2>class People
{
private:
int age;
public:
People(){};
People(int myage)
{
this->age = myage;
}
void print(T1 x, T2 y)
{
cout << "age = " << age << ", x = " << x << ", y = " << y <<endl;
}
};
int main(int argc, char *argv[])
{
People<int, string> s(4);
s.print(111, "one_one_one");
return 0;
}
五、模板友元函数
1、友元函数参数中不带模板的情况
(1)友元函数声明在class内,定义实现写在class外 (2)友元函数参数中类的直接给出具体类型,譬如 (3)这种友元函数实际是削弱了模板参数在使用
#include <iostream>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
friend void friend_function(People<int>& s)
{
cout << "age = " <<s.age << endl;
}
};
int main(int argc, char *argv[])
{
People<int> a(4);
friend_function(a);
return 0;
}
2、友元函数参数中带模板参数方法1
(1)友元函数声明和定义都写在class内部 (2)虽然写在class内,但仍然是友元,而不是member function (3)友元可以适配类的各种模板参数
#include <iostream>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
friend void friend_function1(People<T>& s)
{
cout << "age = " << s.age << endl;
}
};
int main(int argc, char *argv[])
{
People<double> c(1.1223);
friend_function1(c);
People<string> b("wwwwedsfs");
friend_function1(b);
return 0;
}
3、友元函数参数中带模板参数方法2
(1)友元函数声明在class内,定义在class外
(2)声明时函数名加后缀,而定义时不用加
(3)需要class和friend function的2个前置声明
(4)调用friend function时可加<实参类型>后缀,也可以不加,但是加就必须加对了
#include <iostream>
using namespace std;
template <typename T> class People;
template <typename T> void friend_function1(const People<T>& s);
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
friend void friend_function1<T>(const People<T>& s);
};
template <typename T> void friend_function1(const People<T>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223);
friend_function1(c);
People<string> b("wwwwedsfs");
friend_function1(b);
return 0;
}
4、友元函数参数中带模板参数方法3
#include <iostream>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
template <typename U> friend void friend_function1(const People<U>& s);
};
template <typename U> void friend_function1(const People<U>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223);
friend_function1(c);
People<string> b("wwwwedsfs");
friend_function1(b);
return 0;
}
六、模板运算符重载函数
1、实现+和+=的运算符重载函数
+号如果返回引用,
一种是返回*this
另一种是返回新建的对象的引用
前者导致加数也被改
后者导致返回垃圾
所以只能返回对象
=号返回引用,
保证了效率,
支持了连续的=号操作
若返回对象,
效率低下,
并不支持连续=号操作
#include <iostream>
#include <string>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
template <typename U> friend void friend_function1(const People<U>& s);
People<T> operator+(People<T>& other);
People<T>& operator+=(People<T>& other);
};
template <typename T> People<T> People<T>::operator+(People<T>& other)
{
People<T> tmp;
tmp.age = this->age + other.age;
return tmp;
}
template <typename T> People<T>& People<T>::operator+=(People<T>& other)
{
this->age += other.age;
return *this;
}
template <typename U> void friend_function1(const People<U>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223), c1(2.222), c2;
c2 = c + c1;
friend_function1(c2);
c2 += c;
friend_function1(c2);
People<string> b("wwwwedsfs"), b1("ooosdfsdfsdqw"), b2;
b2 = b + b1;
friend_function1(b2);
b2 += b;
friend_function1(b2);
return 0;
}
七、模板运算符友元函数重载实现
1、+作为友元运算符重载
(1)友元函数在class内实现,ok,但是因为是友元实现的,所以参数要有2个
(2)友元函数在class外实现,不ok,编译无法通过
(3)友元函数的第三种实现,ok
上述三种情况下的代码实现 在《2、+=作为友元运算符重载》中 给出了
2、+=作为友元运算符重载
(1)友元函数在class内实现,ok,但必须带2个参数,带1个不行
#include <iostream>
#include <string>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
template <typename U> friend void friend_function1(const People<U>& s);
friend People<T> operator+(People<T>& a, People<T>& b)
{
People<T> tmp;
tmp.age = a.age + b.age;
return tmp;
}
friend People<T>& operator+=(People<T>& a, People<T>& b)
{
a.age += b.age;
return a;
}
};
template <typename U> void friend_function1(const People<U>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223), c1(2.222), c2;
c2 = c + c1;
friend_function1(c2);
c2 += c;
friend_function1(c2);
People<string> b("wwwwedsfs"), b1("ooosdfsdfsdqw"), b2;
b2 = b + b1;
friend_function1(b2);
b2 += b;
friend_function1(b2);
return 0;
}
(2)友元函数在class外实现,ok
#include <iostream>
#include <string>
using namespace std;
template <typename T> class People;
template <typename T> People<T>& operator+=(People<T> &a, People<T> &b);
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
template <typename U> friend void friend_function1(const People<U>& s);
friend People<T> operator+(People<T>& a, People<T>& b)
{
People<T> tmp;
tmp.age = a.age + b.age;
return tmp;
}
friend People<T>& operator+=<T>(People<T>& a, People<T>& b);
};
template <typename T> People<T>& operator+=(People<T> &a, People<T> &b)
{
a.age += b.age;
return a;
}
template <typename U> void friend_function1(const People<U>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223), c1(2.222), c2;
c2 = c + c1;
friend_function1(c2);
c2 += c;
friend_function1(c2);
People<string> b("wwwwedsfs"), b1("ooosdfsdfsdqw"), b2;
b2 = b + b1;
friend_function1(b2);
b2 += b;
friend_function1(b2);
return 0;
}
(3)友元函数的第三种实现,ok
#include <iostream>
#include <string>
using namespace std;
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T myage):age(myage){};
template <typename U> friend void friend_function1(const People<U>& s);
template <typename U> friend People<U> operator+(People<U>& a, People<U>& b);
template <typename U> friend People<U>& operator+=(People<U>& a, People<U>& b);
};
template <typename U> People<U> operator+(People<U>& a, People<U>& b)
{
People<T> tmp;
tmp.age = a.age + b.age;
return tmp;
}
template <typename U> People<U>& operator+=(People<U>& a, People<U>& b)
{
a.age += b.age;
return a;
}
template <typename U> void friend_function1(const People<U>& s)
{
cout << "age = " << s.age << endl;
}
int main(int argc, char *argv[])
{
People<double> c(1.1223), c1(2.222), c2;
c2 = c + c1;
friend_function1(c2);
c2 += c;
friend_function1(c2);
People<string> b("wwwwedsfs"), b1("ooosdfsdfsdqw"), b2;
b2 = b + b1;
friend_function1(b2);
b2 += b;
friend_function1(b2);
return 0;
}
注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。
|