c++模板如何学好啊?愁,看不懂,求指导

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++模板如何学好啊?愁,看不懂,求指导相关的知识,希望对你有一定的参考价值。

想借着寒假好好补一下这块,求大侠指导一下

C++ 模板基础谈1. 什么是模板模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。我们知道,C++ 是一种“强类型”的语言,也就是说一个变量,编译器必须确切的知道它的类型,而模板就是构建在这个强类型语言基础上的泛型系统。2. 模板的语法模板函数template< typename 类型参数名称, [ int Name=...][, ...] >函数定义模板类template< typename ... , [ int Name=...] >class ...模板的参数可以是类型,或者是一个 int 型的值(或者可以转换为int 型的,比如 bool)。3. 模板的使用显式类型参数:对于模板函数,在函数名后添加 < 类型参数表 >。对于模板类,在类后添加 < 类型参数表 >隐式类型参数:对于模板函数,如果类型参数可以推导,那么可以省略类型参数表举个例子:template< typename T >T max( T a, T b ) return a < b ? b : a;这个 max 函数就是一个模板函数,它可以传入一个 “类型”的参数,以便实现任意类型求最大值的效果。假设我们这样使用它:int x=5, y=10;int z=max<int>( x, y );这时候发生了什么呢?我们传入的“类型参数”是int,因此编译器在编译这段代码时会使用 int 来构造一个新函数:int max( int a, int b ) return a < b ? b : a;后面的事就和编译普通的函数一样了,C++编译器继续使用强类型系统编译这个函数,由强类型系统来检查这个函数是否正确。这个过程叫做模板的“特化”,它发生在编译期,当编译器发现模板函数、模板类被使用(注意,不是定义)的时候进行的。这个系统实际上比较像宏,但是比宏更为智能。很明显,编译器必须知道模板如何特化这个函数,因此模板函数的实现,必须在“使用点”之前,因此模板库只能通过头文件库的形式来提供。4. 模板的类型推导对于函数,编译器是知道传入参数的类型的,比如上面的max,max< ? >( x, y ),由于第一个参数 x 是 int 类型的,那么 ? 这里需要填写什么呢?我们可以很明显的推断出应该是 "int",否则,后面的强类型系统将无法编译这个函数。编译器同样知道 x 的类型,因此它也能推导出“类型参数”,这时候我们调用时就可省略模板参数了。这个推导是按顺序来的,因此如果上面的 y 是其他类型,? 仍然会被推导为 int,如果y无法隐性转换为int,强类型编译时就会报错。5. 类型推导的隐式类型转换在决定模板参数类型前,编译器执行下列隐式类型转换: 左值变换 修饰字转换 派生类到基类的转换 见《C++ Primer》([注2],P500)对此主题的完备讨论。简而言之,编译器削弱了某些类型属性,例如我们例子中的引用类型的左值属性。举例来说,编译器用值类型实例化函数模板,而不是用相应的引用类型。同样地,它用指针类型实例化函数模板,而不是相应的数组类型。它去除const修饰,绝不会用const类型实例化函数模板,总是用相应的非 const类型,不过对于指针来说,指针和 const 指针是不同的类型。底线是:自动模板参数推导包含类型转换,并且在编译器自动决定模板参数时某些类型属性将丢失。这些类型属性可以在使用显式函数模板参数申明时得以保留。6. 模板的偏特化如果我们打算给模板函数(类)的某个特定类型写一个函数,就需要用到模板的偏特化,比如我们打算用 long 类型调用 max 的时候,返回小的值(原谅我举了不恰当的例子):template<> // 这代表了下面是一个模板函数long max<long>( long a, long b ) // 对于 vc 来说,这里的 <long> 是可以省略的 return a > b ? b : a;实际上,所谓偏特化,就是代替编译器完成了对指定类型的特化工作,现代的模板库中,大量的使用了这个技巧。7. 仿函数仿函数这个词经常会出现在模板库里(比如 STL),那么什么是仿函数呢?顾名思义:仿函数就是能像函数一样工作的东西,请原谅我用东西这样一个代词,下面我会慢慢解释。void dosome( int i )这个 dosome 是一个函数,我们可以这样来使用它: dosome(5);那么,有什么东西可以像这样工作么?答案1:重载了 () 操作符的对象,比如: struct DoSome void operator()( int i ); DoSome dosome;这里类(对 C++ 来说,struct 和类是相同的) 重载了 () 操作符,因此它的实例 dosome 可以这样用 dosome(5); 和上面的函数调用一模一样,不是么?所以 dosome 就是一个仿函数了。实际上还有答案2: 函数指针指向的对象。 typedef void( *DoSomePtr )( int ); typedef void( DoSome )( int ); DoSomePtr *ptr=&func; DoSome& dosome=*ptr; dosome(5); // 这里又和函数调用一模一样了。当然,答案3 成员函数指针指向的成员函数就是意料之中的答案了。8. 仿函数的用处不管是对象还是函数指针等等,它们都是可以被作为参数传递,或者被作为变量保存的。因此我们就可以把一个仿函数传递给一个函数,由这个函数根据需要来调用这个仿函数(有点类似回调)。STL 模板库中,大量使用了这种技巧,来实现库的“灵活”。比如:for_each, 它的源代码大致如下:template< typename Iterator, typename Functor >void for_each( Iterator begin, Iterator end, Fucntor func ) for( ; begin!=end; begin++ ) func( *begin );这个 for 循环遍历了容器中的每一个元素,对每个元素调用了仿函数 func,这样就实现了 对“每个元素做同样的事”这样一种编程的思想。特别的,如果仿函数是一个对象,这个对象是可以有成员变量的,这就让 仿函数有了“状态”,从而实现了更高的灵活性。 参考技术A 首先要有一本好书,模板好书推荐c++ template,多练习,有问题想不明白时去看看stl是怎么搞的。再多看看,多写写,时间久了慢慢也明白了。然后看stl源码剖析,两本都是侯捷翻译的。如果c++不是很精通,需要先静心学好c++,看c++primer。
模板正如其名,它只是一个样板,目的是指导编译器。编译器会根据模板样式依葫芦画瓢,帮助你节约代码书写量(模板发明的最初目的,当然现在远远不止这点用处)。例如函数模板,在你写出函数调用代码后,编译器就可根据你的函数调用代码中的参数类型或其他类型信息推导并生成一个确定的函数,最后调用这个新生成的函数。模板精髓就是编译器做的编译时期类型推导。对于每一种类型的调用,编译器都根据调用给生成了对应的唯一的函数,同类型调用不重复生成。当然生成具体函数时也会检查你的函数的正确性(参数类型,内部变量类型啊这些)。
认证努力,模板不是一个高峰,它只是下一个高峰的起点。
参考技术B C++模版?你是说MFC吗

用回溯法求01背包问题,怎样使用C++模板啊,迫切求指点!

#include<iostream>
#include<algorithm>
using namespace std;

template<class Typew,class Typep>
class Knap

friend Typep Knapsack(Typep *,Typew *,Typew,int);

private:
Typep Bound(int i);
void Backtrack(int i);
Typew c;
int n;
Typew *w;
Typep *p;
Typew cw;
Typep cp;
Typep bestp;
;

template<class Typew,class Typep>
void Knap<Typew,Typep>::Backtrack(int i)

if(i>n)

bestp=cp;
return;

if(cw+w[i]<=c)

cw+=w[i];
cp+=p[i];
Backtrack(i+1);
cw-=w[i];
cp-=p[i];

if(Bound(i+1)>bestp)
Backtrack(i+1);


template<class Typew,class Typep>
Typep Knap<Typew,Typep>::Bound(int i)

Typew cleft=c-cw;
Typep b=cp;

while(i<=n && w[i]<=cleft)

cleft-=w[i];
b+=p[i];
i++;

if(i<=n)
b+=p[i]*cleft/w[i];
return b;


class Object

friend int Knapsack(int *,int *,int,int);
public:
int operator<=(Object a)const

return d>=a.d;


private:
int ID;
float d;
;

template<class Typew,class Typep>
Typep Knapsack(Typep p[],Typew w[],Typew c,int n)

Typew W=0;
Typep P=0;
Object *Q=new Object[n];
for(int i=1;i<=n;i++)

Q[i-1].ID=i;
Q[i-1].d=1.0*p[i]/w[i];
P+=p[i];
W+=w[i];


if(W<=c)
return P;

sort(Q,Q+n);
Knap<Typew,Typep>K;
K.p=new Typep[n+1];
K.w=new Typew[n+1];
for(int j=1;j<=n;j++)

K.p[i]=p[Q[i-1].ID];
K.w[i]=w[Q[i-1].ID];


K.cp=0;
K.cw=0;
K.c=c;
K.n=n;
K.bestp=0;

K.Backtrack(1);
delete[]Q;
delete[]K.w;
delete[]K.p;
return K.bestp;


int main()

int n=3;
int w[3]=16,15,15;
int p[3]=45,25,25;
int c=30;
int bestprice=Knapsack(p,w,c,n);
cout<<"最优价值:"<<bestprice<<endl;

return 0;

参考技术A #include<iostream>
using namespace std;

class Knap

friend int Knapsack(int p[],int w[],int c,int n );
public:
//输出当前最优解
void print()

for(int m=1;m<=n;m++)

cout<<bestx[m]<<" ";

cout<<endl;
;
private:
int Bound(int i); //计算右子树的上界
void Backtrack(int i);
int c;//背包容量
int n; //物品数
int *w;//物品重量数组
int *p;//物品价值数组
int cw;//当前重量
int cp;//当前价值
int bestp;//当前最优值
int *bestx;//当前最优解
int *x;//当前解
;

int Knap::Bound(int i)

//计算上界
int cleft=c-cw;//剩余容量
int b=cp;
//以物品单位重量价值递减序装入物品
while(i<=n&&w[i]<=cleft)

cleft-=w[i];
b+=p[i];
i++;

//剩余物品取部分来装满背包
if(i<=n)
b+=p[i]/w[i]*cleft;
return b;


void Knap::Backtrack(int i)

if(i>n)//到达叶结点

if(bestp<cp)

for(int j=1;j<=n;j++)
bestx[j]=x[j];//更新当前最优解
bestp=cp;

return;

if(cw+w[i]<=c) //搜索左子树

x[i]=1;//装入
cw+=w[i];
cp+=p[i];
Backtrack(i+1);
cw-=w[i];
cp-=p[i];

if(Bound(i+1)>bestp)//搜索右子树

x[i]=0;//不装入
Backtrack(i+1);



class Object

friend int Knapsack(int p[],int w[],int c,int n);
public:
int operator<=(Object a)const

return (d>=a.d);

private:
int ID;
float d;//单位重量价值
;

int Knapsack(int p[],int w[],int c,int n)

//为Knap::Backtrack初始化
int W=0;
int P=0;
int i=1;
Object *Q=new Object[n];
for(i=1;i<=n;i++)

Q[i-1].ID=i;
Q[i-1].d=1.0*p[i]/w[i];//计算单位重量价值
P+=p[i];
W+=w[i];

if(W<=c)
return P;//装入所有物品
//依物品单位重量排序
float f;
for( i=0;i<n;i++)
for(int j=i;j<n;j++)

if(Q[i].d<Q[j].d)

f=Q[i].d;
Q[i].d=Q[j].d;
Q[j].d=f;



Knap K;
K.p = new int[n+1];
K.w = new int[n+1];
K.x = new int[n+1];
K.bestx = new int[n+1];
K.x[0]=0;
K.bestx[0]=0;
for( i=1;i<=n;i++)

K.p[i]=p[Q[i-1].ID];
K.w[i]=w[Q[i-1].ID];

K.cp=0;
K.cw=0;
K.c=c;
K.n=n;
K.bestp=0;
//回溯搜索
K.Backtrack(1);
K.print();
delete [] Q;
delete [] K.w;
delete [] K.p;
return K.bestp;

void main()

int *p;
int *w;
int c=7;
int n=4;
int i=0;
p=new int[n+1];
w=new int[n+1];
p[0]=0;
w[0]=0;
p[1]=9;w[1]=3;
p[2]=10;w[2]=5;
p[3]=7;w[3]=2;
p[4]=4;w[4]=1;
cout<<Knapsack(p,w,c,n)<<endl;
system("pause");
本回答被提问者采纳

以上是关于c++模板如何学好啊?愁,看不懂,求指导的主要内容,如果未能解决你的问题,请参考以下文章

请C++高手指导 IntelliSense: 缺少 类模板 "std::iterator" 的参数列表 。如何添加类模板到集成环境

在JAVA中是如何体现C++的函数模板这种机制的?

C++中函数模板怎么用

在进行添加时如何通过使用 c++ 模板来避免临时对象? [关闭]

C++归并排序求逆序对_模板

c++ 泛型 模板继承问题