模板
1.目前函数的问题
以下函数存在的问题
1. 功能一致,但是函数参数类型不同
2. 按照目前的计数手段,只能分别实现不同的函数完成代码3. 导致代码冗余,函数冗余,编译冗余
利用 C++ 模板解决
#include <iostream>
using namespace std;
void printValue(int num);
void printValue(double num);
void printValue(char ch);
void printValue(bool ret);
void printValue(string s);
int main(int argc, char const *argv[])
{
printValue(10);
printValue(5.5);
printValue('A');
printValue(false);
printValue("233333");
return 0;
}
void printValue(int num) { cout << num << endl; }
void printValue(double num) { cout << num << endl; }
void printValue(char ch) { cout << ch << endl; }
void printValue(bool ret) { cout << ret << endl; }
void printValue(string s) { cout << s << endl; }
2.模板函数
什么是模板?
模板是泛型编程的基础,泛型编程是以一种特定类型的方式编写代码。
template
T 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。
既可以定义函数模板,也可以定义类模板
类模板在类被实例化的时候进行指定。
template <typename T,typename K>
可以用逗号分隔,定义多个数据类型。
模板案例
#include <iostream>
#include <deque>
using namespace std;
/*
template 单词汉语对应就是模版
在这里自定义模版,设置模版数据类型为 T 类型
*/
template <typename T>
void printValue(T t);
int main(int argc, char const *argv[])
{
printValue(10);
printValue(5.5);
printValue('A');
printValue(false);
printValue("233333");
return 0;
}
template <typename T>
void printValue(T t)
{
cout << "Value : " << t << endl;
}
3.模板函数细节
函数存在重载情况,分别对应模板函数和 int 具体数据类型函数,编译器会优先选择具体数据类型明确的函数,调用实现
【先实后虚】【注意数据类型一致化原则】
#include <iostream>
using namespace std;
template <typename T>
void test(T t);
template <typename T>
void test(T t1, T t2);
void test(int n);
int main(int argc, char const *argv[])
{
/*
当前代码中,针对于 test 函数没有具体参数数据类型为 double 情况。
编译器会选择模板函数为当前执行的目标,并且在当前函数调用的过程中
明确模板对应的具体数据类型为 【double】 类型
*/
test(20.5F);
/*
test 函数存在重载情况,分别对应模板函数和 int 具体数据类型函数,编译器会优先选择具体数据类型明确的函数,调用实现
【先实后虚】
*/
test(20);
/*
tempate <typename T>
void teat(T t1,T t2)
语法错误!!因为当前和散户有且只声明了一个 模板 T 要求
在函数的执行过程中,T 类型唯一且统一
test(10,20.5);(int,double)语法错误
*/
test(10, 20);
// test(10,'A');语法错误,请严格遵守【数据类型一致化原则】
test('A', 'B');
return 0;
}
template<typename T>
void test(T t)
{
cout << "模板函数触发 value : " << t << endl;
}
void test(int n)
{
cout << "具体参数为 int 类型函数触发 value : " << n << endl;
}
template<typename T>
void test(T t1,T t2)
{
cout << "Value 1 : " << t1 << ",Value 2 : " << t2 << endl;
}
template<>
告知编译器,当前函数需要使用模板函数形式没有明确的模板类型声明,函数名之后加入的中括号和具体数据类型
mySwap 明确告知编译器,当前模板对应的具体数据类型为 Person 类型
按照函数声明template <>
void mySwap(Person &t1, Person &t2)
#include <iostream>
using namespace std;
class Person
{
public:
Person() {}
Person(int id, string name, int age) : id(id), name(name), age(age) {}
Person(const Person &p) : id(p.id), name(p.name), age(p.age) {}
~Person() {}
friend ostream &operator<<(ostream &o, Person *p);
int id;
string name;
int age;
};
ostream &operator<<(ostream &o, Person *p)
{
o << "ID : " << p->id << ",Name : " << p->name << ",Age : " << p->age;
return o;
}
/*
函数中第一个参数 T1 t1 对应的是当前模板声明中的 T1
函数中第二个参数 T2 t2 对应的是当前模板声明中的 T2
都需要具体的实际参数类型来约束模板对应的具体数据类型
*/
template <typename T1, typename T2>
void test(T1 t1, T2 t2)
{
cout << "T1 : " << t1 << "T2 : " << t2 << endl;
}
template <typename T>
void mySwap(T &t1, T &t2)
{
T temp = t1;
t1 = t2;
t2 = temp;
}
/*
template<> 告知编译器,当前函数需要使用模板函数形式没有明确的模板类型声明
函数名之后加入的中括号和具体数据类型
mySwap<Person> 明确告知编译器,当前模板对应的具体数据类型为 Person 类型
按照函数声明
template <typename T>
void mySwap(T &t1,T &t2);
T 类型对应的具体数据类型在当前实现函数中为 Person 类型
定制化完成 Person 类型的 Swap 交换操作
*/
template <>
void mySwap<Person>(Person &t1, Person &t2)
{
int id = t1.id;
string name = t1.name;
int age = t1.age;
t1.id = t2.id;
t1.name = t2.name;
t1.age = t2.age;
t2.id = id;
t2.name = name;
t2.age = age;
}
int main(int argc, char const *argv[])
{
// 模板对应的类型需要根据实际情况下来进行判断
/*
void test<int,int>(int t1,int t2)
*/
test(10, 20);
/*
void test<int,char>(int t1,char t2)
*/
test(10, 'A');
int n1 = 10;
int n2 = 20;
mySwap(n1, n2);
cout << "n1 : " << n1 << "n2 : " << n2 << endl;
double d1 = 10.5;
double d2 = 25.5;
mySwap(d1, d2);
cout << "d1 : " << d1 << "d2 : " << d2 << endl;
cout << "+++++=====================" << endl;
Person *p1 = new Person(1, "郭7", 20);
Person *p2 = new Person(2, "哈登", 35);
cout << p1 << endl;
cout << p2 << endl;
cout << "0----------------------------------------" << endl;
mySwap(*p1, *p2);
cout << p1 << endl;
cout << p2 << endl;
return 0;
}
4.模板类
模板类型充当数据占位符,在整个类内,成员变量,成员函数,构造哈桑农户都可以使用
template <typename NameType, typename AgeType>
class Person {};当前 Person 声明了两个模板分别对应
NameType 模版类型,名称可以看出,用于约束成员变量 name 的类型
AgeType 模版类型,名称可以看出,用于约束成员变量 age 的类型
Person 类型带有模板修饰,需要在使用前明确模板对应的具体类型,模板具备数据类型的多样性,同时一旦确定模板类型,严格遵守数据类型一致化原则。
Person 类型确定模板对应的具体数据类型
格式:
类名<模板对应具体数据类型> * 对象 = new 类型<模板对应具体数据类型>(…);
#include <iostream>
using namespace std;
/*
当前 Person 类型,声明了两个模板
NameType 模板类型,名称可以看出用于约束成员变量 name 的类型
AgeType 模板类型,名称可以看出用于约束成员变量 age 的类型
模板类型充当数据类型占位符,在整个类内,成员变量,成员函数,构造函数都可以使用
*/
template <typename NameType, typename AgeType>
class Person
{
public:
Person() {}
Person(NameType name, AgeType age) : name(name), age(age) {}
Person(const Person &person) : name(person.name), age(person.age) {}
~Person() {}
NameType getName() { return name; }
void setName(NameType name) { this->name = name; }
AgeType getAge() { return age; }
void setAge(AgeType age) { this->age = age; }
private:
NameType name;
AgeType age;
};
int main(int argc, char const *argv[])
{
/*
Person 类型带有模板修饰,需要在使用之前明确模板对应的具体类型
模板具备数据类型支持的多样性,同时一旦确定模板类型,严格遵守数据类型一致化
原则
Person 类型如何确定模板对应的具体数据类型
格式:
类名<模板对应具体数据类型> *对象 = new 类型 <模板对应具体数据类型>(...);
Person 类型在没有约束模板具体类型的情况下,对应 C++ 编译器所见的类型为
Person 类型在没有越是模板具体类型的情况下,对应 C++ 编译器所见的类型为
Person<NameType,AgeType>
*/
// p1 明确告知编译器 NameType ==> string AgeType ==> int
// 在后续的代码中,通过 p1 调用 Person 类内函数按照以上规则完成
Person<string, int> *p1 = new Person<string, int>();
p1->setName("郭7");
p1->setAge(18);
cout << "Name : " << p1->getName() << ",Age : " << p1->getAge() << endl;
Person<string, long> *p2 = new Person<string, long>();
p2->setName("吴9");
p2->setAge(13L);
cout << "Name : " << p2->getName() << ",Age : " << p2->getAge() << endl;
delete p1;
delete p2;
return 0;
}
5.模板参数限制
双重限制
- 限制外部类型
- 限制模板类型
#include <iostream>
using namespace std;
template <typename DataType, typename MsgType>
class Data
{
public:
Data() {}
Data(DataType value, MsgType msg) : value(value), msg(msg) {}
Data(const Data &data) : value(data.value), msg(data.msg) {}
~Data() {}
DataType getValue() { return value; }
void setValue(DataType value) { this->value = value; }
MsgType getMsg() { return msg; }
void setMsg(MsgType msg) { this->msg = msg; }
private:
DataType value;
MsgType msg;
};
/*
当前 test1 函数明确告知调用者,当前所需的参数为 Data 类型
同时限制 Data 中对应的模板类型为 <int ,string>
*/
void test1(Data<int, string> *data);
/*
当前 test1 函数明确告知调用者,当前所需的参数为 Data 类型
同时限制 Data 中对应的模版类型为 <double, string>
*/
void test1(Data<double, string> *data);
int main(int argc, char const *argv[])
{
// 双重限制
Data<int, string> *d1 = new Data<int, string>(10, "什么鬼");
Data<double, string> *d2 = new Data<double, string>(10.13, "什么鬼");
Data<char, string> *d3 = new Data<char, string>('A', "什么鬼");
test1(d1);
test1(d2);
/*
test1 函数重载操作,所需参数有两种情况
Data<int, string> *
Data<double, string> *
并没有支持
Data<char, string> * 考虑到双重限制,分别对应外部类型和模版类型
d3 无法作为函数 test1 的实际参数
【注意】请严格遵守数据类型一致化原则
*/
// test1(d3);
delete d1;
delete d2;
delete d3;
return 0;
}
void test1(Data<int, string> *data)
{
cout << "Msg : " << data->getMsg() << ",Value : " << data->getValue() << endl;
}
void test1(Data<double, string> *data)
{
cout << "Msg : " << data->getMsg() << ",Value : " << data->getValue() << endl;
}
6. 模板继承
类中带有模板,同时有子类继承当前类
1.妻管严模式
子类在继承父类时,直接指定模板对应的具体数据类型,不得修改
2.自由模式
子类带有和父类同名声明模板
6.1妻管严模式
template
class MyCompare{};MyCompare 类
1.声明了模板。
2.声明纯虚函数
3.MyCompare 是一个抽象类。
class IntCompare : public MyCompare{};
IntCompare 类
1.继承了 MyCompara 抽象类
2.继承父类的过程中,同时限制了模板对应的具体类型
3.IntCpmpare 类没有声明模板
#include <iostream>
using namespace std;
/*
MyCompare 类
1.声明了模板。
2.声明纯虚函数
3.MyCompare 是一个抽象类。
*/
template <typename T>
class MyCompare
{
public:
// 纯虚函数,并且使用了类名声明的模板
virtual bool compare(T t1, T t2) = 0;
};
/*
IntCompare 类
1.继承了 MyCompara 抽象类
2.继承父类的过程中,同时限制了模板对应的具体类型
3.IntCpmpare 类没有声明模板
*/
class IntCompare : public MyCompare<int>
{
public:
bool compare(int t1,int t2)
{
return t1 > t2;
}
};
int main(int argc, char const *argv[])
{
IntCompare * ic = new IntCompare;
cout << "ret : " << ic->compare(200,30) << endl;
return 0;
}
6.2 自由模式
template
class BaseHandler定义 BaseHandler 类
1.声明了模板 T
2.使用了模板 T
3.BaseHandler 是一个抽象类
template
class MyHandler : public BaseHandler{};MyHandler 继承抽象类 Base Handler,同时
1.声明和 BaseHandeler 同名模板
2.当前模板类型尚未明确
3,必须实现 void handler(T t) 函数
实例化 MyHandler 类对象,需要指定模板对应的具体类型
MyHandler *m1 = new MyHandler;
m1->handler(“什么鬼”);
#include <iostream>
using namespace std;
/*
定义 BaseHandler 类
1.声明了模板 T
2.使用了模板 T
3.BaseHandler 是一个抽象类
*/
template <typename T>
class BaseHandler
{
public:
virtual void handler(T t) = 0;
};
/*
MyHandler 继承抽象类 Base Handler,同时
1.声明和 BaseHandeler 同名模板
2.当前模板类型尚未明确
3,必须实现 void handler(T t) 函数
*/
template <typename T>
class MyHandler : public BaseHandler<T>
{
public:
void handler(T t)
{
cout << "数据情况 : " << t << endl;
}
};
int main(int argc, char const *argv[])
{
// 实例化 MyHandler 类对象,需要指定模板对应的具体类型
MyHandler<string> *m1 = new MyHandler<string>;
m1->handler("什么鬼");
MyHandler<int> *m2 = new MyHandler<int>;
m2->handler(10006);
return 0;
}
7.模板内的模板函数
template
class Test{
template
void bt(T2 t) { cout << "value : " << t << endl;};
模板内的函数,期望可以自定义模板,使用的模板数据形式,约束与当前类不同。
#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
// 当前函数使用的模板为当前 Test 类声明的模板
void handler(T t) { cout << "Value : " << t << endl; }
/*
模板类内的函数,期望可以自定义模板,使用的模板数据形式,约束与当前类不同
*/
template <typename T2>
void bt(T2 t) { cout << "Value : " << t << endl; }
};
int main(int argc, char const *argv[])
{
Test<string> *test = new Test<string>;
test->handler("没有边界感的烦人精!");
/*
bt 函数对应的模板类型,由实际参数决定,并不是实例化 Test 类型决定
*/
test->bt(10);
return 0;
}
8.返回值类型带有模板
template <typename K1,typename V1>
friend ostream & operator<<(ostream & o,Data<K1,V1> *data);友元函数,重载运算符操作,使用的模板并不是 Data 声明的模板而是函数自定义模板,通过实际参数 Data 类型中的模板情况,限制当前函数模板的类型
【满足任意 Data 类型展示数据操作】
#include <iostream>
using namespace std;
template <typename K,typename V>
class Data
{
public:
Data(){}
Data(K key,V value) : key(key),value(value){}
Data(const Data & data):key(data.key),value(data.value){}
~Data(){}
K getKey(){return key;}
void setKey(K key){this->key = key;}
V getValue(){return value;}
void setValue(){this->value = value;}
/*
友元函数,重载运算符操作,使用的模板并不是 Data 声明的模板而是函数自定义模板,通过实际参数 Data 类型中的模板情况,限制当前函数模板的类型
【满足任意 Data 类型展示数据操作】
*/
template <typename K1,typename V1>
friend ostream & operator<<(ostream & o,Data<K1,V1> *data);
private:
K key;
V value;
};
template<typename K1,typename V1>
ostream & operator<<(ostream & o,Data<K1,V1> * data)
{
o << "key : " << data->getKey() << ",value : " << data->getValue();
return o;
}
template <typename K,typename V>
Data<K,V> * getData(K key,V value);
int main(int argc, char const *argv[])
{
string name = "BT";
Data<string , int> * data = getData(name,1096);
cout << data << endl;
Data<double,int> * data2 = getData(1.24,1039);
cout << data2 << endl;
Data<string,int> *data3 = new Data<string,int>("snns",10);
cout << data3 << endl;
delete data;
delete data2;
return 0;
}
/*
外部函数自定义模板,返回值是 Data 类型,同时利用当前函数的实际参数对 Data
中的模板数据进行约束限制,模板具体数据情况参数具体数据类型确定。
*/
template <typename K,typename V>
Data<K,V> * getData(K key,V value)
{
return new Data<K,V>(key,value);
}