泛型编程:编写的是与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。
1.函数模板:类型参数化,增加代码复用性。例如对于swap函数,不同类型之间进行交换都需要进行重载,但是函数体不变,就可以体现模板的优越性。
模板参数两种给出方法1.自动类型推导2.显示类型确定
#include<iostream>
using namespace std;
//都是进行交换
//不同类型参数的重载代码冗余,复用性低
//模板是将类型当作参数进行传递
//函数模板
template< typename T>
void Swap(T& a, T& b)
{
cout << typeid(T).name() << endl;
T tmp = a;
a = b;
b = tmp;
}
void test01()
{
int i1 = 10, i2 = 20;
char c1 = 'a', c2 = 'b';
double d1 = 1.5, d2 = 5.1;
Swap(i1, i2);//自动类型推导
Swap<char>(c1, c2);//显示类型指定
Swap<double>(d1, d2);
cout <<"i1= " << i1 <<" i2=" <<i2 << endl;
cout << "c1= " << c1 << " c2=" << c2 << endl;
cout << "d1= " << d1 << " d2=" << d2 << endl;
}
void main()
{
test01();
system("pause");
}
在自动类型推导中,T类型必须一致,不然出现二义性。
解决方案1:
void test02()
{
int a = 10;//4字节
double b = 1.23;//8字节
Swap(a, (int&)b);
//Swap((double&)a, b);//报错 涉及引用修改了原空间范围
Swap<int>(a, (int&)b);
}
解决方案2:
template< typename T1, typename T2>
void Swap(T1& a, T2& b)
{
cout << typeid(T1).name() << endl;
T1 tmp = a;
a = b;
b = tmp;
}
另一个例子:
#include<iostream>
using namespace std;
template<typename T>
T Func(T a, T b)
{
T value = a + b;
return value;
}
void test01()
{
int a = 10;
double b = 1.35;
Func(a, (int)b);
Func((double)a, b);//不修改原空间大小,可以强转
Func<int>(a, b);
}
void main()
{
test01();
system("pause");
}
在调用函数模板时,并不是直接执行函数模板,在编译器底层,模板会根据不同的类型传入生成不同的模板函数。所以母体就是函数模板,实际执行的是模板函数。所以模板看起来比较简洁,但是执行效率不高。
模板实例化:调用函数模板,生成特定版本函数的过程。隐式实例化->模板类型推导,显式实例化通过<类型>确定。
注意模板不会进行隐式类型转换,也就是类型的传入不能产生二义性。普通函数支持隐式类型转换。
当函数模板和普通函数都匹配调用时,会采用最佳匹配原则。
#include<iostream>
using namespace std;
template<typename T>
T Func(T a, T b)
{
T value = a + b;
return vale;
}
int Func(int a, int b)
{
int value = a + b;
return value;
}
void test01()
{
Func(1, 2);//调上面
Func(1.1, 2.2);//调下面
Func('a', 'b');//调下面
Func<int>('a', 'b');//调下面
Func<int>(1, 2);//调下面;
Func(1, 2.1);//调上面
}
类模板:类中的数据类型参数化就可以得到类模板。注意函数模板的每一个成员函数在类外实现时都是函数模板,需要带上template<typename T>,在作用域限定符中也要加上<T>。
c++的类中需要常量可以用这三种方法给出:宏定义、 枚举(私有成员变量)、const常量(私有成员变量)。
#define AAA 8
enum
{ BBB=8};
const int CCC = 8;
模板类的参数可以是一个已经实例化的模板类,例如使用vector实现二维数组。
vector<vector<int>> arr;
类模板:实例化时需要指定参数类型,它不能推导类型,利用类名<数据类型> 对象名来实例化。
类模板和函数模板都可以用于处理自定义数据类型,自定义类需要用到运算符重载。
内置类型的零初始化,固定写法,系统自动生成,不会发生隐式类型转换。其实就是赋各种0值。
零初始化也可以用于类模板,用于自定义数据类型。比较灵活。零初始化语法:T 变量名 = T();
调动默认构造函数(或者全缺省构造函数)进行赋值。
#include<iostream>
using namespace std;
void main()
{
int a = int();
char ch = char();
double d = double();
float f = float();
system("pause");
}
类模板不支持分离编译,类模板的成员函数的创建时机在调用时,分文件编写时链接不到,所以一般通过包含.cpp或者.hpp(声明和定义放一起)的方式来给出。
extern 关键字:让全局变量在同一工程下被不同的.cpp文件外部引入并使用。
STL:标准模板库开个头:
六大组件:容器、算法、仿函数、迭代器、适配器(配接器)、空间配置器。
STL缺点:更新慢、不支持线程安全、追求效率导致内部复杂、存在代码膨胀的问题。但是作为大佬造好的轮子,不会不行!
P.J.版本对应vc6.0 SGI版本对应STL源码剖析。
#include<iostream>
#include<list>
using namespace std;
void test01()
{
list<int> mylist;
for (int i = 0; i < 10; i++)
{
mylist.push_back(i);
}
for (auto e : mylist)
{
cout << e << "->";
}
cout << "over." << endl;
for (list<int>::iterator it = mylist.begin(); it != mylist.end(); it++)
{
cout << *it << "->";
}
cout <<"over."<<endl;
}
void main()
{
test01();
system("pause");
}