C++之泛型编程笔记
来源:互联网 发布:日本心神战斗机 知乎 编辑:程序博客网 时间:2024/05/18 02:37
不考虑具体数据类型的编程模式就叫泛型编程。
1. 函数模板
提供一种特殊的函数可用不同类型进行调用
看起来和普通函数很相似似 ,区别是类型可被参数化。
定义为
template <typename T>
template告诉编译器即将开始泛型编程,typename T告诉后面的程序,T是一个泛指类型。
void Swap(T &a , T &b)
{
T t = a;
a = b;;
a=t;
}
函数模板的应用:泛型调用变量有两种方式,一种是编译器自动类型推导调用。
第二种是具体类型显示调用。
Int a =1;
Int b = 2;
Swap(a,b);//自动类型推导,自动推导为int 型
Float fa =3;
Float fb =4;
Swap<float>(fa,fb);//显示类型调用,用float替换参数类型T
例如:下面是用泛型实现的排序程序。
template<typename T>
void Swap(T& a, T& b)
{
Tt = a;
a= b;
b= t;
}
template<typename T>
void SelectSort(T array[], int length)
{
for(int i=0; i<length; i++)
{
T min = array[i];
int index = i;
for(int j=i+1; j<length; j++)
{
if( array[j] < min )
{
min = array[j];
index = j;
}
}
Swap(array[i], array[index]);
}
}
调用时,
intarray[] = {3, 2, 5, 3 , 4};
SelectSort<int>(array, 5);
charca[] = {'b', 'c', 'a', 'e', 'd', 'f'};
SelectSort(ca,6);
函数模板的深入理解:编译器并不是把函数模板处理成能够处理任意类型的函数,编译器从函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
2,.函数模板与重载
函数模板可以像普通函数一样被重载,C++编译器优先考虑普通函数,如果函数模板可以产生一个更好的匹配,那么选择模板。可以通过空模板实参列表的语法限定编译器只通过模板匹配。
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
使用时
inta = 1;
intb = 2;
cout<<Max(a, b)<<endl;//优先选择原型
cout<<Max<>(a, b)<<endl;//这个地方优先选择函数模板。
cout<<Max(3.0, 4.0)<<endl;//调用函数模板,因为是float型
函数模板不允许自动类型转化,普通函数可以,例如:
Max(‘a’,100);这个函数一定会调用普通函数,不会调用模板函数,因为模板函数不会进行自动类型转换。
函数模板可以定义任意多个不同的类型参数,
例如:template<typenameT1,typename T2,typename RT>
RT Add(T1 a , T2 b)
{
Return static_cast<RT>(a +b);
}
Add<char ,float,double>(’a’,100);///函数模板返回RT类型,
当声明的类型参数为返回值类型时,无法进行自动类型推导。
多参数函数模板不完美解决方案:将返回类型参数声明到第一个参数位置,调用时只需声明返回类型参数即可。
Add< double>(’a’,100);这样调用也可以,参数位置指定了返回值类型,从而c++编译器会进行推导。
3类模版
一些类主要用于存储和组织数据元素,如:数组类,链表类,stack类,queue类等
C++中可以将模板的思想应用于类,使得类可以不关注具体所操作的数据类型,而只关注类所需要实现的功能。
C++中的类模板: 提供一种特殊的类以相同的行为处理不同的类型,在类声明前使用template进行标识。<typename T>用于说明类中使用的泛指类型 T。
声明的泛指类型 T 可用于声明成员变量和成员函数,编译器对类模板的处理方式和函数模板相同,编译器从类模板通过具体类型产生不同的类,编译器在声明的地方对类模板代码本身进行编译。编译器在使用的地方对参数替换后的代码进行编译。
类模板的应用 : 使用具体类型定义对象 比如类模板Operator<int> op1;
Operator<double> op2;
这个定义完后编译器就会编译出两个类对象,,所以下面对于这两个类的使用就和以前的类一样了
op1.add(5,4);
op2.add(1.5,0.01);
以下面的array类为例
在Array.h中:
template<typename T>//声明一个泛型T
classArray
{
private:
int mLength;
T* mSpace; //指向泛型类型的指针
public:
Array(int length);
Array(const Array& obj);
int length();
~Array();
T& operator[](int i);
Array& operator= (const Array&obj);
bool operator== (const Array& obj);
bool operator!= (const Array& obj);
};
.h文件中至少类模版的声明,,类模版的定义还需要实现,,在Array.hpp中,
#include"Array.h"
template<typename T>//在每为一个类模板的成员函数做定义时,上面都得声明,下面的函数是泛型函数。
Array<T>::Array(intlength)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new T[mLength];
}
template<typename T>
Array<T>::Array(constArray& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
template<typename T>
intArray<T>::length()
{
return mLength;
}
template<typename T>
Array<T>::~Array()
{
mLength = -1;
delete[] mSpace;
}
template<typename T>
T&Array<T>::operator[](int i)
{
return mSpace[i];
}
template<typename T>
Array<T>&Array<T>::operator= (const Array<T>& obj)
{
delete[] mSpace;
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
return *this;
}
template<typename T>
boolArray<T>::operator== (const Array<T>& obj)
{
bool ret = true;
if( mLength == obj.mLength )
{
for(int i=0; i<mLength; i++)
{
if( mSpace[i] != obj.mSpace[i] )
{
ret = false;
break;
}
}
}
else
{
ret = false;
}
return ret;
}
template<typename T>
boolArray<T>::operator!= (const Array& obj)
{
return !(*this == obj);
}
上面是一个泛型类模板的实现,确实非常高级,感觉很高大上。
类模板可以被特化,用 template <>声明一个类时,表示这是一个特化类。
template<typenameT>
classTest
{
public:
Ttest(T v)
{
cout<<"T test(Tv)"<<endl;
cout<<"sizeof(T) ="<<sizeof(T)<<endl;
return v;
}
};
特化类
template<>
class Test<int>
{
public:
int test(int v)
{
cout<<"inttest(int v)"<<endl;
cout<<"sizeof(int)= "<<sizeof(int)<<endl;
return v;
}
};
特化类模板的意义:当类模板在处理某种特定类型有缺陷时,可以通过类模板的特化来克服处理这种特定类型带来的不足。
类模版可以定义多个类型参数
4.类模板的局部特化
类模版可以定义多个类型参数
template<typenameT1, typename T2>
classTest
{
public:
void add(T1 a, T2 b)
{
cout<<(a + b)<<endl;
}
};
类模板可以被局部特化:可以指定类模板的特定实现,并要求某些类型参数仍然必须得模板的用户指定。
Template<typename T1,typenameT2>
ClassTest
{
Public :
Void add(T1 a ,T2 b)
{
Cout<<(a+b)<<endl;
}
}
类模板局部特化
template<typename T>
classTest<T, T>
{
public:
void add(T a, T b)
{
cout<<"add(T a, Tb)"<<endl;
cout<<static_cast<T>(a +b)<<endl;
}
};
template<typename T>
classTest<T, int>
{
public:
void add(T a, int b)
{
cout<<"add(T a, intb)"<<endl;
cout<<a + b<<endl;
}
};
template<typename T1, typename T2>
classTest<T1*, T2*>
{
public:
void add(T1* a, T2* b)
{
cout<<"add(T1* a, T2*b)"<<endl;
}
};
调用时:
Test<double,int> t; // <T, int>
Test<long,long> ti; // <T1, T2>
Test<float,int> tt; // <T, int>
Test<int*,int*> tp; // <T*, T*>
为什么要特化模版类而不重新定义类呢?
特化和重新定义新类看上去没有本质区别,但是如果定义新类,那么将变成一个模板和一个新类,使用的时候需要考虑究竟是使用类模版还是用新类。特化可以统一的方式使用类模板和特化类,编译器自动选择特化类。
5.非类型模板参数与特化
函数模板和类模板的模板参数可以是普通数值。
template<int N>
class Sum
{
public:
static const int VALUE = Sum<N -1>::VALUE + N;
};
template<>
classSum<1>
{
public:
static const int VALUE = 1;
};
使用这个模板类后可以快速算出1~n自然数的和。
cout<<Sum<10>::VALUE<<endl;
cout<<Sum<100>::VALUE<<endl;
速度很快,,是直接输出的,因为编译器已经算好了。
非类型模板参数的限制:变量不能作为模板参数,浮点数和类对象不能作为模板参数,全局指针不能作为模板参数。编译器的推导过程是在编译阶段完成的因此,编译器的推导必须依赖于特化类,否则推导过程无法结束。
在实际工程中内存操作的BUG主要有:未及时释放内存,产生内存泄漏,重复操作同一段内存,导致行为未知,使用越界,操作了不属于自己的内存。
内存越界的问题常发生于数组的使用中,所以在一般情况下,开发者应该预先编写数组类对象代替c语言中的原生数组。内存泄漏和内存多次释放常发生于指针的使用过程中,开发者可以预先编写智能指针类对象代替c语言中的原生指针。工程中的智能指针是一个类模板,主要实现了:通过构造函数接管申请的堆内存,通过析构函数确保堆内存被及时释放,通过重载指针运算符* 和-> 模拟指针的行为,通过重载比较运算符==和!=模拟指针的比较。- Linux+C学习笔记之IO编程
- Linux+C学习笔记之网络编程
- 《c语言编程之道》笔记
- 《c语言编程之道》笔记
- C指针编程之道 ---第一次笔记
- C指针编程之道 ---第二次笔记
- C指针编程之道 ---第三次笔记
- C指针编程之道--------笔记
- Java编程笔记之泛型
- C++之泛型编程笔记
- C语言学习笔记之C语言编程
- C++(泛型编程)学习笔记【2】
- C++(泛型编程)学习笔记【3】
- C++(泛型编程)学习笔记【1】
- C++(泛型编程)学习笔记【4】
- 《Essential C++》笔记三(2)、泛型编程风格
- (C/C++学习笔记)泛型编程基础
- C++Primer笔记 十六 模板与泛型编程
- 两个 BLE实现数据传输
- 第N次委托
- Python学习 Day 7 面向对象 类和实例 访问限制
- SAD SATD MAD SSD MSD...
- Zend Studio代码无法自动提示以及代码跟踪函数和变量问题的解决方法
- C++之泛型编程笔记
- JS-HTML基本标签
- 进程隐藏与进程保护(SSDT Hook 实现)(二) 转载自 Zachary.XiaoZhen - 梦想的天空
- org.hibernate.MappingException: Unknown entity常见问题(新手需注意)
- 【凯子哥带你做高仿】“煎蛋”Android版的高仿及优化(二)——大图显示模式、评论“盖楼”效果实现详解
- 日经社説 20150427 「大阪都」巡る住民投票の意味
- 实时统计各进程的流量
- 进程隐藏与进程保护(SSDT Hook 实现)(三) 转载自 Zachary.XiaoZhen - 梦想的天空
- eclipse中maven配置