C++提供了函数模板:所谓的函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可。在调用函数时系统会根据实参类型来取代模板中的虚拟类型,从而实现不同函数的功能。C++提供了两种模板机制:类模板和函数模板
模板把函数或者类要处理的数据类型参数化,表现为参数的多态性,称为类属。模板用于表达逻辑结构相同,但具体数据元素不同的数据对象的通用行为
#include
#include
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
#include
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
#include
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
#include
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 &p)
{
p.showPerson();
}
类模板派生普通类的时候必须指定类型,否则无法派生
#include
#include
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
#include
#include
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
类模板会经过两次编译,第一次是类模板本身的编译,第二次是在函数在调用的时候编译,确定对应的类型
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
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
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
实现一个简单的vector