C++模板
C++提供了函数模板:所谓的函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可。在调用函数时系统会根据实参类型来取代模板中的虚拟类型,从而实现不同函数的功能。C++提供了两种模板机制:类模板和函数模板
模板把函数或者类要处理的数据类型参数化,表现为参数的多态性,称为类属。模板用于表达逻辑结构相同,但具体数据元素不同的数据对象的通用行为
函数模板
示例代码
#include <iostream> #include <string> template<typename T> auto testSwap(T &a, T &b) -> void { T tmp = a; a = b; b = tmp; } int main(int argc, char* argv[]) { int a = 10; int b = 20; // 自动推导类型 testSwap(a, b); std::cout << a << " " << b << std::endl; std::string aa = "aaa", bb = "bbb"; testSwap(aa, bb); std::cout << aa << " " << bb << std::endl; // 显示指定类型 int c = 30; int d = 40; testSwap<int>(c, d); std::cout << c << " " << d << std::endl; return 0; }
函数模板和普通函数区别
你可以使用以下代码来测试调用顺序
#include <iostream> #include <string> template<typename T> auto testSwap(T &a, T &b) -> void { std::cout << "函数模板" << std::endl; T tmp = a; a = b; b = tmp; } auto testSwap(int &a, int &b) -> void { std::cout << "普通函数" << std::endl; int tmp = a; a = b; b = tmp; } int main(int argc, char* argv[]) { int a = 10; int b = 20; testSwap(a, b); std::cout << a << " " << b << std::endl; std::string aa = "aaa", bb = "bbb"; testSwap(aa, bb); std::cout << aa << " " << bb << std::endl; int c = 30; int d = 40; testSwap<int>(c, d); std::cout << c << " " << d << std::endl; return 0; }
- 函数模板和普通函数都识别的时候,优先调用普通函数,但是指定模板类型的时候是调用函数模板的。
执行下面代码:注释放开和不放开时候试试
template<typename T> auto testSwap(T a, T b) -> void { std::cout << "函数模板" << std::endl; } //auto testSwap(int a, int b) -> void //{ // std::cout << "普通函数" << std::endl; //} int main(int argc, char* argv[]) { int a = 30; char b = 'a'; // testSwap(a, b); testSwap<int>(a, b); return 0; }
- 函数模板的参数类型不能自动转换
- 函数模板也可以重载
函数模板的局限性
如果代码定义了赋值操作a=b, 但是T为数组,这种假设就不成立了,同样的,如果里面的语句为判断语句 if(a>b),但是T如果有结构体,该假设也不成立,另外如果传入的是数组,数组名为地址,因此比较的是地址,而这也不是我们需要的操作。总之编写函数模板无法处理某些类型。另一方面,有时候通用化是有意义的,但是C++不允许这么做。为了解决这种问题,可以提供模板的重载为这些特定的类型提供具体化的模板。
#include <iostream> #include <string> using namespace std; template<typename T> auto testSwap(T &a, T &b) -> void { T tmp = a; a = b; b = tmp; } class Person { public: Person() {} Person(const Person &other) = delete; void operator=(const Person& out) = delete; friend ostream& operator<<(ostream& out, const Person&p) { return out << "(" << p.age << "," << p.name << ")"; } int age; std::string name; }; int main(int argc, char* argv[]) { Person p1; Person p2; p1.age = 10; p1.name = "aaa"; p2.age = 20; p2.name = "bbb"; testSwap(p1, p2); std::cout << p1 << p2 << std::endl; return 0; }
上述代码会报错。
类模板
示例代码
#include <iostream> #include <string> using namespace std; template<typename T, typename U> class Data { public: Data(const T &t, const U &u) { this->name = t; this->num = u; cout << "有参构造" << endl; } ~Data() { cout << "析构函数" << endl; } void showPerson() { std::cout << this->name << " " << this->num << endl; } private: T name; U num; }; int main(int argc, char* argv[]) { Data<string, int> data("aa", 5); data.showPerson(); Data<string, int> data2("bb", 6); data2.showPerson(); return 0; }
类模板做函数参数
void test(Data<string, int> &p) { p.showPerson(); }
类模板派生普通类
类模板派生普通类的时候必须指定类型,否则无法派生
#include <iostream> #include <string> using namespace std; template<class T> class Base { public: Base(const T &t) : name(t){} T name; }; class Derive : public Base<string> { public: Derive(const string &s) : Base<string>(s){} }; int main(int argc, char* argv[]) { Derive d("张三"); std::cout << d.name << std::endl; return 0; }
类模板成员函数在类外实现
类模板的作用域是Class<T>
#include <iostream> #include <string> using namespace std; template<class T1, class T2> class Person { public: Person(const T1&t, const T2 &t2) ; void showPerson(); T1 name; T2 name2; }; template<class T1, class T2> Person<T1, T2>::Person(const T1 &t, const T2 &t2) : name(t) , name2(t2) { } template<class T1, class T2> void Person<T1, T2>::showPerson() { std::cout << name << " " << name2 << endl; } int main(int argc, char* argv[]) { Person<int, int> a(10, 10); a.showPerson(); return 0; }
模板类的类型是 Person<T1, T2>
类模板头文件和源文件分离
类模板会经过两次编译,第一次是类模板本身的编译,第二次是在函数在调用的时候编译,确定对应的类型
C/C++都是独立文件编译,因此第二次的编译的时候会替换对应的文件,但是因为没有包含对应的cpp,因此无法获取到对应的实现,因此没有定义过程,因为我们正常不包含cpp,所以模板类的实现和声明放在一个文件内
// person.h #ifndef ALGOTEST_PERSON_H #define ALGOTEST_PERSON_H template<class T1, class T2> class Person { public: Person(const T1&t, const T2 &t2) ; void showPerson(); T1 name; T2 name2; }; #endif //ALGOTEST_PERSON_ // person.cpp #include "person.h" #include <iostream> using namespace std; template<class T1, class T2> Person<T1, T2>::Person(const T1 &t, const T2 &t2) : name(t) , name2(t2) { } template<class T1, class T2> void Person<T1, T2>::showPerson() { cout << name << " " << name2 << endl; }
// person.hpp #ifndef ALGOTEST_PERSON_HPP #define ALGOTEST_PERSON_HPP #include <iostream> template<class T1, class T2> class Person { public: Person(const T1&t, const T2 &t2) ; void showPerson(); T1 name; T2 name2; }; using namespace std; template<class T1, class T2> Person<T1, T2>::Person(const T1 &t, const T2 &t2) : name(t) , name2(t2) { } template<class T1, class T2> void Person<T1, T2>::showPerson() { cout << name << " " << name2 << endl; } #endif //ALGOTEST_PERSON_HPP