Chinaunix首页 | 论坛 | 博客
  • 博客访问: 492739
  • 博文数量: 63
  • 博客积分: 1187
  • 博客等级: 少尉
  • 技术积分: 706
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-05 16:53
个人简介

Must Be

文章分类

全部博文(63)

文章存档

2019年(1)

2017年(4)

2016年(6)

2015年(2)

2014年(1)

2013年(3)

2012年(10)

2011年(36)

我的朋友

分类: C/C++

2013-03-25 09:30:15

1 概述

   c++的模板技术是把双刃剑,一方面,模板灵活强大,变换无穷,stl、boost给程序员展现了模板技术的匪夷所思;另一方面,模板又是c++中处理虚机制外最复杂的技术,初学者往往不知所云。本文简单的总结了以下自己工作中遇到的几个问题,希望能对读者有所帮助。

2 关键字 typename vs class

  在模板定义时的class和typename是没有区别的,因为最初发明模板时决定使用class以减少一个关键字,但后来发现还是不得不加上typename关键字,参见如下代码: 

 

[cpp]
  1. template<class C>     
  2. void f(C & rc)     
  3. {     
  4.   typename C::iterator  i  =  rc.begin();     
  5.   //   ...      
  6. }    
template void f(C & rc) { typename C::iterator i = rc.begin(); // ... }


 

       编译器不知道C的定义,所以不知道C::iterator是什么东西,此处必须有typename来告诉编译器,而不能使用class关键字。在Windows的vc6环境下,typename可以省略,编译器自动识别补全,但在Linux的gcc编译时,提示需要添加typename。

 

3 静态变量初始化

  在Windows和Linux下移植C++代码,尤其是模板,十分头疼,Windows下的VC对C++标准支持的不是很好,而且还对很多C++标准进行了修改和扩展,这给移植增加了更大困难,另外gcc不同版本之间也有区别。

  以下代码演示了多类型的静态变量初始化。

[cpp]
  1. template  <typename T>  
  2. class Image  
  3. {  
  4.   public:  
  5.     static const int flag;   
  6.   // ...   
  7. };   
  8.   
  9. const int Image<int>::flag = 10;  
  10. const int Image<float>::flag = 15;  
template class Image { public: static const int flag; // ... }; const int Image::flag = 10; const int Image::flag = 15;


 

     上面的代码,在vc6和gcc 3.2.2下均能编译通过,但在新版gcc4.2.3下出现编译器内部错误,需要采用如下方式初始化:

[cpp]
  1. template <> const int Image<int>::flag = 10;  
  2. template <> const int Image<float>::flag = 15;  
template <> const int Image::flag = 10; template <> const int Image::flag = 15;


 

4 模板之多态

  在c++中,实现类型多态有两种方式:虚函数和模板。 一般情况下,开发者习惯使用虚函数机制,这种方式采用的是动态绑定技术,即程序只有在运行时才能获得函数执行入口,所以需要由编译器自动生成一张虚函数表;而模板方式采用的是静态绑定,即在编译阶段就能获取函数入口,这种多态实现比较少见。参见如下示例:

[cpp]
  1. #include     
  2.   
  3. using namespace std;  
  4.   
  5. template <typename T>   
  6. class B1  
  7. {  
  8. public:   
  9.   void SayHi()   
  10.   {  
  11.     T* pT = static_cast(this);    
  12.     pT->PrintClassName();  
  13.   }  
  14.   
  15.   void PrintClassName() { cout << "This is B1" << endl ; }  
  16. };  
  17.    
  18. class D1 : public B1  
  19. {  
  20.   // No overridden functions at all   
  21. };  
  22.    
  23. class D2 : public B1  
  24. {  
  25. public:  
  26.   void PrintClassName() { cout << "This is D2" << endl ; }  
  27. };  
  28.    
  29. int main()  
  30. {  
  31.   D1 d1;  
  32.   D2 d2;  
  33.    
  34.   d1.SayHi();    // prints "This is B1"   
  35.   d2.SayHi();    // prints "This is D2"   
  36.    
  37.   return 0;  
  38. }  
#include using namespace std; template class B1 { public: void SayHi() { T* pT = static_cast(this); pT->PrintClassName(); } void PrintClassName() { cout << "This is B1" << endl ; } }; class D1 : public B1 { // No overridden functions at all }; class D2 : public B1 { public: void PrintClassName() { cout << "This is D2" << endl ; } }; int main() { D1 d1; D2 d2; d1.SayHi(); // prints "This is B1" d2.SayHi(); // prints "This is D2" return 0; }


 

class D1 : public B1 定义是合法的,因为c++语法支持,即D1只是被部分定义,类名D1已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数实现了编译期间的虚函数调用机制。


  上面static_cast(this)是关键所在,它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。这很像C++的多态(polymorphism),只是SayHi()方法不是虚函数。


  在第一个函数调用,对象B1被指派为D1,所以代码被解释成:

[cpp]
  1. void B1::SayHi()  
  2. {  
  3.   D1* pT = static_cast(this);  
  4.    
  5.   pT->PrintClassName();  
  6. }  
void B1::SayHi() { D1* pT = static_cast(this); pT->PrintClassName(); }


     D2实现类似。采用这种方法的优势:不需要虚函数表;静态绑定,有利于程序优化。

阅读(1384) | 评论(0) | 转发(0) |
0

上一篇:C++模板学习

下一篇:C++const用法总结

给主人留下些什么吧!~~