C++模板与泛型编程

3,328 views
3,163 views

Published on

C++ template.

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,328
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
31
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

C++模板与泛型编程

  1. 1. C++ トレーニング<br />C++模板与泛型编程<br />之初级篇<br />Poseidon项目组<br />
  2. 2. 基本Thread<br /><ul><li>模板的编译模型
  3. 3. 为什么引入泛型
  4. 4. C++函数模板定义</li></ul>1<br />3<br />4<br />2<br />6<br />5<br /><ul><li>实际的案例分析
  5. 5. 还为涉及的模板领域
  6. 6. C++类模板定义</li></ul>2<br />
  7. 7. 为什么要引入泛型<br />泛型是什么?<br />泛型编程又是什么呢?<br />3<br />
  8. 8. 为什么要引入泛型<br />泛型<br />就是C++模板的一种表现,也即类型参数化。<br />泛型编程(Generic Programming) <br />是建立在C++的Template机制基础上的一种完全不同于面向对象的程序设计思维模式。<br />典型作品:STL<br />4<br />
  9. 9. 为什么要引入泛型<br />为什么需要泛型编程?<br />5<br />
  10. 10. 为什么要引入泛型<br />C++ template的最初发展动机很直接:让我们得以建立类型安全(type-safe)的通用容器。<br />如:vecotr,list,map等。<br />当愈多人用上template时,他们发现template有能力完成愈多可能的变化。容器当然很好。<br />但是泛型编程(Generic Programming)--写出的代码和其所处理的对象类型彼此独立。<br />于是STL的各种算法就应运而生。<br />如:for_each, find, merge等。<br />6<br />
  11. 11. 为什么要引入泛型<br />简单的一个开发场景说明<br />主角:小逗童鞋<br />1<br />简单的一个开发任务<br />任务内容:编写compare函数,分别比较int、string、double三种类型值的大小。要求比较的两个值通过参数传入compare函数中,如果第一个值大于第二个值,就然会1,如果小于就返回-1,否则返回0。<br />2<br />日本项目中抽取的一个实例<br />模板函数实例<br />3<br />
  12. 12. 简单的一个案例分析<br />小逗提交了如下代码:<br />int compare(const int& v1, cosnt int& v2)<br />{<br /> if (v1 > v2) return 1;<br /> else if (v2 > v1) return -1;<br /> return 0;<br />}<br />int compare(const string& v1, const string& v2)<br />{<br /> if (v1 > v2) return 1;<br /> else if (v2 > v1) return -1;<br /> return 0;<br />}<br />int compare(const double& v1, const double& v2)<br />{<br /> if (v1 > v2) return 1;<br /> else if (v2 > v1) return -1;<br /> return 0;<br />}<br />8<br />
  13. 13. 简单的一个案例分析<br />小逗的主管帮小逗一起评审了这个几个函数的问题:<br /><ul><li> 仅仅是函数声明部分,严格说是参数不同而已,但是三个函数的函数体都是相同的。造成了代码的赘余。
  14. 14. 那有什么办法解决这个问题吗?</li></ul>9<br />
  15. 15. 简单的一个案例分析<br />Google之后的结论是,可以通过如下两种解决办法来处理:<br /><ul><li> 单根继承</li></ul>Java以及C#中的超类,所有的类都继承与它。<br />通过存储基类的引用,从而通过超类的引用访问。<br /><ul><li> 类型参数化</li></ul>这也正是泛型的简单定义。通过C++的模板template方式来实现。<br />小逗在学习了模板之后,用模板的方法解决了主管提出的问题,重构了之前的代码:<br />template<typename T><br />int compare(const T& v1, const T& v2)<br />{<br /> if (v1 > v2) return 1;<br /> else if (v2 > v1) return -1;<br /> return 0; <br />}<br />小逗通过自己的努力,逐渐掌握了如何来编写一个模板函数方法。<br />10<br />
  16. 16. 简单的一个案例分析<br />日本项目的一个实例应用:<br />简单需求:处理市场发送过来的广播消息,消息数量大于有7,8种。<br />每种广播消息的格式的定义如下:<br />typedef struct broadcast_message_type<br />{<br /> broadcast_type_t broadcast_type;<br /> uint16_t items_n;<br /> char filler_2_s [2];<br /> broadcast_message_type_item_t item [9]; 每个item对应一只股票。<br />} broadcast_message_type _t;<br />现在需求是,需要把每个广播消息中对应items下面的每一只股票发送给其他线程处理。<br />11<br />
  17. 17. 简单的一个案例分析<br />发送消息的模板函数:<br />template <typename msgT, typename memberT><br />void BOMXFFHBroadcastController::sendMessage(<br /> char* broadcast, <br /> ENDataEventType event_type)<br />{<br /> msgT* message_serise = (msgT*)broadcast;<br /> char* data_ptr = broadcast;<br /> uint16_t items = message_serise->items_n;<br /> series_t series_key;<br /> data_ptr += offsetof(msgT, item);<br /> for (uint16_t index = 0; index < items; index++) {<br /> series_key = message_serise->item[index].series; <br /> sendDirectMessage(data_ptr, sizeof(memberT), event_type, series_key);<br /> data_ptr += sizeof(memberT);<br /> }<br />}<br />12<br />
  18. 18. 如何引入泛型<br />1、C++引入单根继承<br />2、参数化类型<br />实现通用容器的方案就是使用“参数化类型”。<br />一个容器需要能够存放任何类型的对象,那干脆就把这个对象的类型“抽”出来,参数化它:<br /> <br />template<class T> class vector<br />{<br /> T* v;<br /> int sz;<br /> public:<br /> vector(int);<br /> T& operator[](int);<br /> T& elem(int i) { return v[i]; }<br /> // ...<br />};<br />13<br />
  19. 19. 如何引入泛型<br />宏不能解决这个问题吗?<br />那么与模板相比,宏有什么缺点呢?<br />14<br />
  20. 20. 如何引入泛型<br />跟模板相比,宏的最大的缺点就是它并不工作在C++的语法解析层面,宏是由预处理器来处理的,而在预处理器的眼里没有C++,只有一堆文本,只做简单的替换。因此C++的类型检查根本不起作用。<br />比如上面的定义如果用宏来实现,那么就算你传进去的T不是一个类型,预处理器也不会报错;只有等到文本替换完了,到C++编译器工作的时候才会发现一堆莫名其妙的类型错误,但那个时候错误就已经到处都是了。往往最后会抛出一堆吓人的编译错误。<br />更何况宏基本无法调试。<br /> <br />15<br />
  21. 21. 为什么要引入泛型<br />我们再来看一看通用算法,这是泛型的另一个动机。比如我们熟悉的C的qsort:<br /> <br />void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));<br /> <br />16<br />
  22. 22. 为什么要引入泛型<br />这个算法有如下几个问题:<br /> <br />1.  类型安全性:使用者必须自行保证base指向的数组的元素类型和compar的两个参数的类型是一致的;使用者必须自行保证size必须是数组元素类型的大小。<br />2.  通用性:qsort对参数数组的二进制接口有严格要求——它必须是一个内存连续的数组。如果你实现了一个巧妙的、分段连续的自定义数组,就没法使用qsort了。<br />3.  接口直观性:如果你有一个数组char* arr = new arr[10];那么该数组的元素类型其实就已经“透露”了它自己的大小。然而qsort把数组的元素类型给“void”掉了(void *base),于是丢失掉了这一信息,而只能让调用方手动提供一个size。为什么要把数组类型声明为void*?因为除此之外别无它法,声明为任意一个类型的指针都不妥(compar的参数类型也是如此)。qsort为了通用性,把类型信息丢掉了,进而导致了必须用额外的参数来提供类型大小信息。在这个特定的算法里问题还不明显,毕竟只多一个size参数而已,但一旦涉及的类型信息多了起来,其接口的可伸缩性(scalability)问题和直观性问题就会逐渐显现。<br />4.  效率:compar是通过函数指针调用的,这带来了一定的开销。但跟上面的其它问题比起来这个问题还不是最严重的。<br />17<br />
  23. 23. 函数和类模板<br />那该如何编写一个C++模板呢?<br /><ul><li> 函数模板
  24. 24. 类模板 </li></ul>18<br />
  25. 25. 函数模板(Function template)大解剖<br />定义C++ 模板的关键字<br />模板定义都以它开头。<br />template <typename T, size_t N><br />T sumArray(T (&arr)[N])<br />{<br /> T sum=0;<br />for (int i = 0; i < N; ++i) {<br /> sum += arr[i];<br /> }<br /> return sum;<br />}<br />模板形参表<br />表示类型的类型形参(T)<br />表示常量表达式的非类型形参(N)。<br />模板函数体<br />在模板函数内部,可以使用类型形参 T 引用一个类型,T 表示哪种实际类型由编译器根据所用的函数而确定。<br />sumArray<int, 10>(array) <<--称之为模板的实例化(显示的模板实参),在编译阶段完成。<br />inline函数模板:<br />template <typename T> inline T min(const T&, const T&); ○<br />inline template <typename T> T min(const T&, const T&); ×<br />注意inline关键字的位置<br />19<br />
  26. 26. 函数模板(Function template)大解剖<br />C++ Function Template Overloading<br />template <class T><br />inline const T& min(const T& a, const T& b)<br />{ <br /> return b < a ? b : a;<br />}<br />例如:<br />r3 = min(r1, r2);<br />编译器做参数推导。<br />■任何类型对象都可以<br />运用min(),<br />需要支持 operator<。<br />template <class T, class C><br />inline const T& min(const T& a, const T& b, C comp)<br />{ <br /> return comp(b, a) ? b : a;<br />}<br />例如:<br />r3 = min(r1, r2, compare);<br />编译器做参数推导。<br />■需要根据类型自定义<br />比较函数,传入函数指针。<br />20<br />
  27. 27. 类模板(class template)大解剖<br />定义C++ 类模板的关键字<br />类模板定义都以它开头。<br />template < typename T, size_t N = 32><br />class BFixedArray<br />{<br />public: <br /> explicit BFixedArray(size_t dim = N)<br /> : _cItems(dim), _buffer(_internal) <br /> {<br /> if(dim > THRESHOLD) {<br /> _buffer = new T[dim];<br /> } <br /> }<br /> ~BFixedArray() {<br /> if (!isOnStack()) {<br /> delete[] _buffer;<br /> }<br /> }<br /> size_t size() const {<br /> return _cItems;<br /> }<br />模板形参表<br />表示类型的类型形参(T)<br />表示常量表达式的非类型形参(N)。<br />类模板定义体<br />在类模板内部,可以使用类型形参 T 引用一个类型,T 表示哪种实际类型由编译器根据所用的函数而确定。<br />编译器将使用用户提供的实际类型来实例化这个类模板。<br />BFixedArray<char, 128> sendbuffer(length);<br />BFixedArray<int, 128> fixedBuffer(length);<br />类型形参由关键字class或typename后街说明符构成。在模板形参表中,这两个关键字具有相同的含义,都指出后面所接的名字表示一个类型。<br />当然在模板的使用上,也是有不同之处(在模板定义内部指定类型) <br />21<br />
  28. 28. 类模板(Class template)大解剖<br />C++ Class Member Template<br />22<br />
  29. 29. 编写泛型程序的简单原则<br />如何做两个<br />数字的比较呢<br />一般的程序思维里<br />if (v1 > v2)<br />或者<br />else if (v1 < v2)<br />但是在泛型思维中<br /> if (v1 > v2) <br />或者<br />else if (v2 > v1)<br />类型只需要支持 ><br />而不必支持 <<br />简单原则:编写独立于类型的代码,对实参类型的要求尽可能少时有很有益的。<br />23<br />
  30. 30. 编写泛型程序的简单原则<br />一般编程思维中的代码:<br />template<typename T><br />int compare(const T& v1, const T& v2)<br />{<br /> if (v1 > v2) return 1; <br />else if (v1 < v2) return -1;<br /> return 0; <br />}<br />根据泛型编程原则重构代码:<br />template<typename T><br />int compare(const T& v1, const T& v2)<br />{<br /> if (v1 > v2) return 1; <br /> else if (v2 > v1) return -1;<br /> return 0; <br />}<br />24<br />
  31. 31. 模板的编译模型<br />为啥STL的源代码中只有头文件呢?<br />25<br />
  32. 32. 模板编译模型<br />标准C++为编译模板代码定义了两种编译模型。<br />包含编译模型(Inclusion compilation model)<br />几乎所有的编译器都支持的编译模式。<br />简单的说,就是模板定义放在头文件中。如STL。<br />不过,头文件中提供模板定义存在几个缺点:<br />函数的实现细节暴露。<br />如果大量的模板定义放于头文件中,会增加文件间的编译依赖,增加编译时间。<br />分离编译模型(Separate compilation model)<br />只有一部分编译器支持。<br />简单说,模板的声明放于头文件中,定义放于源文件中。<br />此种组织形式,和我们常规的非内联函数的定义组织方式相同<br />26<br />
  33. 33. 模板编译模型<br />export(可选) template <class elemType><br />void Array<elemType>::init( const elemType *array, int sz )<br />{<br /> if ( ! array ) { _size = 0; _ia = 0; }<br /> if ( sz < 1 ) sz = 1;<br /> _size = sz;<br /> _ia = new elemType[ _size ];<br /> for ( int ix = 0; ix < _size; ++ix )<br /> _ia[ ix ] = array[ ix ];<br />}<br />日本项目开发环境:<br />perseus ose_omx_order_book_detail_server/src> gcc -v<br />gcc version 3.3.3 (SuSE Linux)<br />27<br />
  34. 34. 关于泛型编程参考书籍:<br />
  35. 35. C++模板与泛型编程<br />谢 谢。<br />

×