如何实现一个通用的交换函数呢?,交换的值是两个类型不同的数据。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
//如何实现一个通用的交换函数呢?
void swap(int& left, int &right)
{
int tmp = left;
left = right;
right = tmp;
}
void swap(double& left, double& right)
{
double tmp = left;
left = right;
right = tmp;
}
void swap(char& left, char& right)
{
char tmp = left;
left = right;
right = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
std::cout << a << " "<< b << std::endl;
int c = 1.0;
int d = 2.0;
swap(c, d);
std::cout << c << " "<< d << std::endl;
char e = 'a';
char f = 'b';
swap(e, f);
std::cout << e << " " << f << std::endl;
return 0;
}
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础:
template
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
void swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
std::cout << a << " " << b << std::endl;
double c = 1.0;
double d = 2.0;
std::cout << c << " " << d << std::endl;
swap(c, d);
char e = 'a';
char f = 'b';
swap(e, f);
std::cout << e << " " << f << std::endl;
return 0;
}
这里发现三种类型的数据都被交换了:
注意: typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。
那么如何解决上面的问题呢?大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。机器生产淘汰掉了很多手工产品。本质是什么,重复的工作交给了机器去完成。有人给出了论调:懒人创造世界。
-----------------------马云十二句经典语录之一----------------------
懒不是傻懒,如果你想少干,就要想出懒的方法。要懒出风格,懒出境界。像我从小就懒,连长肉都懒的长,这就是境界。
所以函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
int b = 20;
double c = 10.0;
double d = 20.0;
std::cout << Add(a, b) << std::endl;
std::cout << Add(c, d) << std::endl;
return 0;
}
附加问题:下面两个函数调用Add函数的地方是同一个还是两个?
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
int b = 20;
Add(a, b);
double c = 10.0;
double d = 20.0;
Add(c, d);
return 0;
}
查看反汇编代码明白两个函数调用地方的地址不一样,所以Add调用的地方是两个地方。
如果在代码中在加上一句可以编译成功吗?
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
int b = 20;
double c = 10.0;
double d = 20.0;
std::cout << Add(a, b) << std::endl;
std::cout << Add(c, d) << std::endl;
Add(a, c);
return 0;
}
如果想解决类型匹配可以使用强制类型转换:
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
int b = 20;
double c = 10.0;
double d = 20.0;
std::cout << Add(a, b) << std::endl;
std::cout << Add(c, d) << std::endl;
std::cout << Add(a, (int)c) << std::endl;
return 0;
}
注意:隐式类型转换和强制类型转换不会改变原有的类型,只是生成了一个临时变量。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
double d = 20.0;
//显示实例化
std::cout << Add<int>(a, d) << std::endl;
std::cout << Add<double>(a, d) << std::endl;
return 0;
}
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
std::cout << Add<int>(1, "123") << std::endl;
return 0;
}
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
std::cout << Add(1, 2) << std::endl; // 与非模板函数匹配,编译器不需要特化
std::cout << Add<int>(1, 2) << std::endl; // 调用编译器特化的Add版本
}
int main()
{
Test();
return 0;
}
代码如下:
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函
数
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
template<typename T>
T Add1(const T& x, const T& y)
{
return x + y;
}
int Add2(int a, double b)
{
return a + b;
}
int main()
{
int a = Add1(1, 1.1); //模板函数不允许自动类型转换
int b = Add2(1, 1.1);
return 0;
}
template
class 类模板名
{
// 类内成员定义
};
这里实现一个顺序表
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
#include
template<class T>
class SeqList
{
public:
SeqList(size_t capacity = 10)
: _array(new T[capacity + 3])
, _capacity(capacity + 3)
, _size(0)
{}
// [fist, last)区间中包含的元素要放置到顺序表中
SeqList(T* first, T* last)
{
T* it = first;
size_t count = 0;
while (it != last)
{
++it;
++count;
}
_size = count;
_capacity = count;
_array = new T[_capacity];
for (size_t i = 0; i < _size; ++i)
{
_array[i] = *first;
++first;
}
}
~SeqList()
{
if (_array)
{
delete[] _array;
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
void PushBack(const T& data)
{
_CheckCapacity();
_array[_size++] = data;
}
void PopBack()
{
if (Empty())
return;
_size--;
}
bool Empty()const
{
return 0 == _size;
}
size_t Capacity()const
{
return _capacity;
}
size_t Size()const
{
return _size;
}
T& operator[](size_t index)
{
assert(index < _size);
return _array[index];
}
const T& operator[](size_t index)const
{
assert(index < _size);
return _array[index];
}
private:
void _CheckCapacity()
{
T* tmp = new T[2 * capacity];
memcpy(tmp, array, sizeof(T) * capacity);
delete[](array);
array = tmp;
capacity *= 2;
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
template<class T>
void SeqList<T>::_CheckCapacity()
{}
int main()
{
// SeqList不是具体的类,是类模板,是编译器根据实例化结果生成具体类的模具
// SeqList s;
SeqList<int> s1;
s1.PushBack(1);
s1.PushBack(2);
s1.PushBack(3);
s1.PushBack(4);
cout << s1[2] << endl;
cout << typeid(s1).name() << endl;
int array[] = { 1, 2, 3, 4, 5 };
const SeqList<int> s3(array, array + sizeof(array) / sizeof(array[0]));
cout << s3.Size() << endl;
cout << s3[0] << endl;
SeqList<double> s2;
s2.PushBack(1.0);
s2.PushBack(2.0);
s2.PushBack(3.0);
s2.PushBack(4.0);
cout << s2.Size() << endl;
cout << s2.Capacity() << endl;
s2.PopBack();
cout << s2.Size() << endl;
cout << typeid(s2).name() << endl;
return 0;
}
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
// Vector类名,Vector才是类型
Vector s1;
Vector s2;
以上就是今天要讲的内容,本文仅仅简单介绍了模板初阶的使用,而模板提供了大量能使我们快速便捷地处理数据的函数和方法,非常的便捷,所以我们务必掌握。到现在,模板初阶已经完毕,接下就是STL的重头戏。另外如果上述有任何问题,请懂哥指教,不过没关系,主要是自己能坚持,更希望有一起学习的同学可以帮我指正,但是如果可以请温柔一点跟我讲,爱与和平是永远的主题,爱各位了。