泛型程式设计(Generic Programming)

元智大学 90-2

课程备忘录 / 侯捷

每周三 18:30~21:20
工程一馆1102


课程教材:
1.《C++ Primer 3e》chap6,10,12,15,16, appnedix
2. 五篇文章 by 侯捷  见左视窗「STL系列文章」 2000/02~2000/06. 自行列印
3.《泛型程式设计与STL
4.《C++ 标准程式库
5.《STL源码剖析

1,2 为教材,3,4,5 为课外辅助叁考书籍

注:单纯以本课程主旨而言,4 为最佳教材。但估计许多同学还需补强 C++ template 语法基础,故以 1+2为教材。


■第1周:02/27   缺书

1 课程介绍,游戏规则,书籍评介,期末作业题目
2 GP (Generic Programming) and STL overview
★练习1:设定好你的编译环境,建议使用 console mode.
★练习2:试用 C runtime library 的 qsort() 写个程式
         感受一下 function pointer.
本周程式例:STL 的运用
#include <vector>
#include <algorithm>
#include <iostream>
#include <functional>
using namespace std;

int main()
{
   vector<int>  vi;
   vi.push_back(23);
   vi.push_back(14);
   vi.push_back(39);
   vi.push_back(20);
   vi.push_back(9);
   vi.push_back(10);
   vi.push_back(36);

   for(int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;

   vector<int>::iterator  ite1 = vi.begin();
   vector<int>::iterator  ite2 = vi.end();
   sort(ite1,ite2);

   for(int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;

   sort(ite1,ite2, greater<int>());
   for(int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;
}
■第2周:03/06 书籍七折

请助教和同学提早15分钟到达,领取书籍。同学请自备零钱,避免给助教太多困扰。980 * 0.7 = 686

Q : 同学提出一个问题,他将 qsort()放在某个class中,并於呼叫时指定某个member function做为qsort()的最後引数,结果编译器报错,要求该member function必须设定为static。同学不解。

A : 这是因为 qsort() 的最後叁数为一个 function pointer,可视为一个 callback 函式。而 class member function会多出一个 this 指标做为隐藏叁数,如此一来将与 qsort()的要求不符。改为 static member function 便可去除隐藏的 this 指标叁数。我将於课中更详细说明。

Q: 可否以优惠折扣购买其他叁考书籍。

A: 可以。上课时登记,我把名单转给出版社。折扣为 7.5 折。此为出版社对侯捷课程的特殊服务,其他读者请勿来信要求代购。

GP vs. OO,见读者来函与回应

本周仍着重在以实例方式,给同学一个 STL overview.

#include <vector>
#include <algorithm>
#include <iostream>
#include <functional>
#include <iterator>
using namespace std;
class Dumy
{
friend ostream& operator<<(ostream& os, const Dumy& obj);
};
ostream& operator<<(ostream& os, const Dumy& obj)
{
   os << "this is a dumy object :)" << endl;
   return os;
}
template<typename T>
void print_elem(T i)
{
   cout << i << ' ';
}
int main()
{
   vector<int>  vi;
   vi.push_back(23);
   vi.push_back(14);
   vi.push_back(39);
   vi.push_back(20);
   vi.push_back(9);
   vi.push_back(10);
   vi.push_back(36);
   for(unsigned int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;
   vector<int>::iterator  ite1 = vi.begin();
   vector<int>::iterator  ite2 = vi.end();
   sort(ite1 , ite2);
   for(unsigned int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;
   sort(ite1 , ite2, greater<int>());
   for(unsigned int i=0;  i< vi.size(); ++i)
       cout << vi[i] << ' ';
   cout << endl;
   ostream_iterator<int> oite(cout, " ");
   copy(vi.begin(), vi.end(), oite);
   cout << endl;
   void (*funcptr)(int) = &print_elem;
   for_each(vi.begin(), vi.end(), funcptr);
   cout << endl;
   for_each(vi.begin(), vi.end(), print_elem<int>);  // VC6[o],CB5[o],GCC291[x]
   cout << endl;
   Dumy dumy;
   print_elem(dumy);  // argument deduction by compiler
}


■第3周:03/13  
chap10 : function template

#include <iostream>
using std::cout;
using std::endl;
class Dumy
{
private:
  double d1, d2, d3, d4;
  static int i;
};
int Dumy::i = 5;  // initialization
void func(Dumy& a, Dumy& b)
{
   Dumy c;
   // a.xxxx
   // b.xxx
}
int main()
{
  int i = 3;
  int* pi =  &i;
  int& ri = i;
  double d = 3.5;
  double& rd = d;
  cout << i << endl;
  cout << pi << endl;
  cout << ri << endl;
  cout << sizeof(i) << endl;
  cout << sizeof(pi) << endl;
  cout << sizeof(ri) << endl;   // 4
  cout << sizeof(rd) << endl;   // 8
  Dumy d1;
  cout << sizeof(d1) << endl;   // 32
  func(d1, d1);                 // pass by reference
}

传送日期: 2002年3月14日 AM 11:43
主旨: 元智GP 3-13上课问题

老师您好! 以下是一些问题要麻烦老师了

Q1: 通常Object在离开它的Scope时便会被destroy,那在Java这种(oo)语言中并不提
point及new/delete, 要如何控制Object的life time?

A1: 我後来自己找的答案不知正不正确,请老师指点.. 如果Object在它的Scope中有将
Referance传递出去就不会在离开它的Scope时被destroy

Q2: 课程中提及一个max的Specialization写法如後
typedef const char *PCC
template<> PCC max<PCC>(PCC s1, PCC s2)
有没有可能写成
template<> PCC max<PCC>(PCC s1, PQ s2)?
那max後的角括号内要写PCC或PQ?? (因为一般algorithms应会有许多不同Type的
Param)

A2: 先想一个比较具体的实例,再发问。也请自己上机试试。

Q3: 以Referance传递叁数的好处应包含不会唤起copy construct所以比Pointer更有效率,这样的讲法不知对不对?

A3: 以pointer 传递引数,也不会唤起copy ctor。

Q4: 课堂上所提的Explicit Template Arguments似乎是用以解决不同型别叁数所造成
的引数推导失败这个问题, 对於老师所提的函式返回值型别不在引数型别推导范围这个问题不知Explicit Template Arguments是
用在那 ?
另外即然函式返回值型别不在引数型别推导范围可以Trait解决,那为何还需要
Explicit Template Arguments?

A4: explicit template arguments 用法见书上10.4节。至於 traits,工程比较大。


■第4周:03/20  
chap15 : operator overloading
#include <iostream>
#include <string>
using namespace std;
class INT
{
friend ostream& operator<<(ostream& os, const INT& i);
public:
  INT(int i)
    : m_i(i) // member initialization list
  { }
  INT& operator++()  // prefix, increment and then fetch
  {
     m_i++;
     return *this;
  }
  const INT operator++(int)  // postfix, fetch and then increment
  {
     INT temp = *this;
     ++(*this);              // invoke prefix form
     return temp;
  }
private:
  int m_i;
};
ostream& operator<<(ostream& os, const INT& i)
{
   os << '[' << i.m_i << ']';
   return os;
}
class JFunctor
{
public:
  void operator()(string s)
  {
     cout << s << endl;
  }
};
int main()
{
   {
   INT i(4);
   cout << i++;
   cout << ++i;
   // cout << i++++;
   cout << ++++i;
   }
   {
   int i(4);
   cout << i++;
   cout << ++i;
   // cout << i++++;
   cout << ++++i;
   }
   {
     JFunctor f;
     f("this is my functor");  // like a function call
     JFunctor()("this is my functor");
   }
}

// 本例模拟 STL 架构
#include <iostream>
using namespace std;
template<typename T>
struct JNode    // C++ struct 可取代 C++ class (预设属性为 public)
{
  // something
  T m_data;
};
template<typename T>
class JList_Iterator
{
public:
  JList_Iterator& operator++()    // prefix
  {
     // do something...
     return *this;
  }
  const JList_Iterator operator++(int)   // postfix
  {
     JList_Iterator temp = *this;
     ++(*this);            // invoke prefix form
     return temp;
  }
  T& operator*() const    // dereference operator
  {
     return node->m_data;
  }
  T* operator->()         // member access operator
  {
     return &(this->operator*());
  }
private:
  JNode<T>* node;         // must have a native pointer
};
template<typename T>
class JList
{
public:
  typedef JList_Iterator<T> iterator;
  iterator begin()
  {
     iterator ite;
     // do something...
     return ite;
  }
private:
  // something
};
int main()
{
  JList<int> myList;
  // JList_Iterator<int> ite;
  JList<int>::iterator ite = myList.begin();
  ite++;
  ++ite;
  *ite;
}

■第5周:03/27 
1. 延续上一堂课的程式发展
2. chap12 : generic algorithm
// 以下 find1(), find2(), find3(), find() 示范函式的泛化过程
#include <iostream>
using namespace std;
// step1:
int* find1(int* arrayHead, int arraySize, int value)
{
   int i;
   for (i=0; i<arraySize; i++)
      if (arrayHead[i] == value)
          break;
   return &(arrayHead[i]);
}
// step2:
int* find2(int* begin, int* end, int value)
{
   while (begin != end && *begin != value)
      ++begin;
   return begin;
}
// step3:
template <typename T>
T* find3(T* begin, T* end, const T& value)
{
   while (begin != end && *begin != value)
      ++begin;
   return begin;
}
// final:
// 观念突破:将 T* 视为一种 type,也就是 iterator
// 於是我们可以订制自己的 iterator (behavior like a pointer)
template <typename Iterator, typename T>
Iterator find(Iterator begin, Iterator end, const T& value)
{
   while (begin != end && *begin != value)
      ++begin;
   return begin;
}
//---------------------------------------------------
// 模拟 STL 架构 (container, iterator)
// class JNode
// class JList
// class JList_Iterator
//---------------------------------------------------
template<typename T>
struct JNode    // C++ struct 可取代 C++ class (预设属性为 public)
                // JNode 并不开放给 client,所以无损 encapsulation
{
  void* next;  // 亦可设为 JNode<T>*
  void* prev;
  T m_data;
};
//---------------------------------------------------
template<typename T>
class JList_Iterator
{
public:
  JList_Iterator& operator++()    // prefix
  {
     // do something...           // implementation detail
     return *this;
  }
  const JList_Iterator operator++(int)   // postfix
  {
     JList_Iterator temp = *this;
     ++(*this);            // invoke prefix form
     return temp;
  }
  T& operator*() const    // dereference operator
  {
     return node->m_data;
  }
  T* operator->() const // member access operator
  {
     return &(this->operator*());
  }
  bool operator==(const JList_Iterator<T>& x) const { return node == x.node; }
  bool operator!=(const JList_Iterator<T>& x) const { return node != x.node; }
  JList_Iterator(JNode<T>* x) : node(x) {}
  // one-argument non-explicit ctor. can be used as conversion function
private:
  JNode<T>* node;   // there is always a native (dumb) ptr in a smart ptr
};
//---------------------------------------------------
template<typename T>
class JList
{
public:
  typedef JList_Iterator<T> iterator;  // nested define
  // 以下,由於 node 指向尾节点的下一位置,因此 node 符合 STL 对 end() 的定义。
  iterator end()   { return node; }                      // invoke conversion func.
  iterator begin() { return (JNode<T>*)((*node).next); } // invoke conversion func.
  JList() { empty_initialize(); }  // 产生一个 empty list。
protected:
  // 环状双向串列,只维护一个节点指标,指向最後(尾)节点的下一位置。
  JNode<T>* node; // 永远指向最後节点的下一节点。该节点无元素值,代表空节点。
                  // 其 next 节点永远是头节点。
  //以下用於初始化
  void empty_initialize() {
    node = get_node();  // 配置一个节点空间,令 data member node 指向它。
    node->next = node;  // 令 node 头尾都指向自己,不设元素值。
    node->prev = node;
  }
  JNode<T>* get_node() { return new JNode<T>; }
  // STL use allocator and maybe implement memory pool.
};
//---------------------------------------------------
//
//---------------------------------------------------
int main()
{
   const int arraySize = 7;
   int ia[arraySize] = { 0,1,2,3,4,5,6 };
   int* end = ia + arraySize;
   // test find1()
   int* ip1 = find1(ia, sizeof(ia)/sizeof(int), 4);
   if (ip1 == end)
       cout << "not found" << endl;
   else
       cout << "found. " << *ip1 << endl;
   // test find2()
   int* ip2 = find2(ia, end, 9);
   if (ip2 == end)
       cout << "not found" << endl;
   else
       cout << "found. " << *ip2 << endl;
   // test find3()
   int* ip3 = find3(ia, end, 2);
   if (ip3 == end)
       cout << "not found" << endl;
   else
       cout << "found. " << *ip3 << endl;
   // test find(), final version.
   int* ip = find(ia, end, 5);
   if (ip == end)
       cout << "not found" << endl;
   else
       cout << "found. " << *ip << endl;
   // test Container and Iterator with find()
   JList<int> myList;
   // push_back or pust_front or insert some elements into myList
   // JList_Iterator<int> ite;
   JList<int>::iterator ite = myList.begin();
   ite++;
   ++ite;
   *ite;
   find(myList.begin(), myList.end(), 8);
   // 此例没做任何事情,因为 myList 是个 empty list.
   // 本例只为突显 interface 的设计。
   // 以上无论 find(), JNode, JList, JNode_Iterator,
   // 其介面皆符合 STL 规范,与 SGI STL 几无二致
}


■第6周:04/03  春假

■第7周:04/10 
// file : 42.cpp ([austern98])
// VC6 [x], BCB4 [o], G++ [o]  (VC6 not support partial specialization)
#include <iostream>
#include <cstddef>  // for ptrdiff_t
// 注意,不能开放 namespace std; 否则会和 std 的 iterator_trait 冲突
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
template <class Iterator>
struct iterator_traits {
  typedef typename Iterator::iterator_category  iterator_category;
  typedef typename Iterator::value_type         value_type;
  typedef typename Iterator::difference_type    difference_type;
  typedef typename Iterator::pointer            pointer;
  typedef typename Iterator::reference          reference;
};
// partial specialization for regular pointers
template <class T>
struct iterator_traits<T*> {
  typedef random_access_iterator_tag  iterator_category;
  typedef T                           value_type;
  typedef ptrdiff_t                   difference_type;
  typedef T*                          pointer;
  typedef T&                          reference;
};
// partial specialization for regular const pointers
template <class T>
struct iterator_traits<const T*> {
  typedef random_access_iterator_tag  iterator_category;
  typedef T                           value_type;
  typedef ptrdiff_t                   difference_type;
  typedef const T*                    pointer;
  typedef const T&                    reference;
};
template <class Category,
          class Value,
          class Distance = ptrdiff_t,
          class Pointer = Value*,
          class Reference = Value&>
struct iterator {
  typedef Category  iterator_category;
  typedef Value     iterator_value;
  typedef Distance  iterator_distance;
  typedef Pointer   iterator_pointer;
  typedef Reference iterator_reference;
};
template <class InputIterator>
typename iterator_traits<InputIterator>::value_type
sum_nonempty(InputIterator first, InputIterator last)
{
  typename iterator_traits<InputIterator>::value_type  result = *first++;
  for (; first != last; ++first)
    result += *first;
  return result;
}
template< class InputIterator, class T >
typename iterator_traits<InputIterator>::difference_type
count( InputIterator first, InputIterator last, const T& x )
{
   typename iterator_traits<InputIterator>::difference_type n = 0;
   for ( ;  first != last; ++first)
     if (*first == x)
       ++n;
   return n;
}
template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n, input_iterator_tag)
{
  for ( ; n > 0; --n, ++i );
}
template <class BidirectionalIterator, class Distance>
void advance(BidirectionalIterator& i, Distance n, forward_iterator_tag)
{
  advance(i, n, input_iterator_tag());
}
template <class RandomAccessIterator, class Distance>
void advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag)
{
  i += n;
}
template <class BidiectionalIterator, class Distance>
void advance(BidiectionalIterator& i, Distance n, bidirectional_iterator_tag)
{
  if (i >= 0)
      for ( ; n > 0; --n, ++i ) { }
  else
      for ( ; n < 0; ++n, --i ) { }
}
// top level
template <class Iterator, class Distance>
inline void advance(Iterator& i, Distance n)
{
  advance(i, n, iterator_traits<Iterator>::iterator_category());
}
template <class Category,
          class Value,
          class Distance = ptrdiff_t,
          class Pointer = Value*,
          class Reference = Value&>
class MyIter : public iterator<Category, Value, Distance, Pointer, Reference>
{
};
void main()
{
  int ia[5] = {0, 1, 2, 3, 4};
  int total = sum_nonempty(ia, ia+5);
  int c = count(ia, ia+5, 2);
  std::cout << total << std::endl;  // 10
  std::cout << c << std::endl;      // 1
  std::cout << *ia << std::endl;    // 0
  int* pi = &(ia[0]);
  advance(pi, 3);
  std::cout << *pi << std::endl;    // 3
  MyIter<random_access_iterator_tag, int> myiter;
}


■第8周:04/17  期中考。停课

■第9周:04/24  

从本周开始,每次上课请携带补充讲义(5篇文章)

■第10周:05/01  

同学索求上课用到的 PowerPoint 图档。好的,就在这里

目前分组名单:
注意,不允许四人以下。每组至少四人。未符规定者请重新分组。

组别 (1)
895645 黄新泳
895624 吴文彰
895626 刘宇智
895627 蔡锦明
895644 陈维
895660 黄胜建

组别 (2)
895606 林骏豪
895609 邱华成
895615 李健志
895636 阙乃贤
895658 林裕伟

组别 (3)
892245 林志伟
892216 陈炫廷
892205 林岱庆
892217 林金龙

组别 (4)
895608 萧志坚
895628 郭先正
895649 谢长成
882301 林佳慧
882305 黄建铭
882339 张尧淙

组别 (5)
882209 赖俊升
882210 杨宗羲
882232 王昭雄
882238 何志 (木坚) ←这是一个字
882245 林正甫
854136 江龙飞

组别 (6)
882221高林传
882248张明伟
882307 何汶伟
882309 廖元宏
882310 郭炯彬
882316 李岳寅

组别 (7)
882342 罗彦麟
882315 廖伯璇
882351 谢履成
882359 刘致宣

组别 (8)
882303 陈家隆
882320 谢明治
882322 池柏谚
882335 蔡东泰
882338 杨朝景

组别 (9)
882205 谢松翰
882206 蒋宇程
882208 高裕麟
882246 邱清华
882308 白淳元
882321 陈建安

组别 (10)
资工3A 882240 陈薪圳
资工3A 882251 彭治弘
资工3B 882340 刘成韦
资工3B 882353 陈柏玮
资工3A  882261  邱俊辉

组别 (11)
892251 许百宽
892261 蔡延忠
892266 谢曜任
892255 邱国钧

组别 (12)
875123  黄雅楠
882358  林岳黉
875033  张元睿
881962  李瑷蓉
874014  吴锡钦


组别 (13)
882343游训杰
882344沈明峰
882345谢承恩
882346庞文欣
882354詹俊强

组别 (14)
882360 廖惟中
882336 林洋庆
882350 叶正德
874007 李森茂

组别 (15)
892226 陈习峰
892316 翁哲斌
892320 林仕轩
892324 梁皓云
892339 张志宾
892345 蔡淮洋

组别 (16)
874054 吕友仁
882226 谢秉翰
882231 余键亨
882253 陈冠铭
882254 许家豪
882223 杨泓彬

组别 (17)
874015 程伟伦
874041 陈威任
874042 杨琳贵
874084 黄盈文

■第11周:05/08
allocator。教材:《STL源码剖析》第2章 

■第12周:05/15
STL vector,list,deque内部资料结构与实作手法
教材:《STL源码剖析》第4章
STL 的其他小玩意儿 (about adapters)
design patterns : 
(1) iterator。以 STL iterator为例。
(2) container adapter。以 STL stack, queue为例。 
(3) template method。这是 application framework 最基础最根本
     的一种技法。

■第13周:05/22

1. 续讲 template method...

2. red-black tree, set, map, multiset, multimap, hash table (part1)

from 彭治弘 s882251
老师您好
关於上一次泛型上课的时候曾经有同学向老师反应说
findfirstfile/findnextfile使用的时候会发生错误
学生今晚花一点时间测试发现
若是只要单纯的计算某一个目录下有几个档案等
像是老师上一次demo给我们看的时候那样
那麽使用该函式在vc bcb等都不会有问题
只是在98 2000上跑出来的结果会不一样
也就是说98跑出来的东西会跟vc所compile出来的东西一样
但若是使用BCB编译的话
跑出来的东西会有一点点错误 但是只要稍稍加一些检查
在两个版本的编译器中编译跑出来的结果应该不会错
但唯一的例外是
如果要达成某些特殊的功能时 使用BCB所编译出来的程式再执行时
会跟作业系统independent 也就是我们使用左键框起来点内容时
跑出来的档案大小以及数量会跟程式跑出来的东西不一样
不过那要看程式写到哪一个阶段
如果用到了某些技巧 而又希望能做出跟系统一样的行为时 一定要用vc
用bcb一定出错
以上是学生在自己的电脑上测试所发现的行为
编译环境 98 vc bcb, 2000 vc

谢谢彭治弘同学和大家分享心得

传送日期: 2002年5月9日 PM 01:57
主旨: 上课的问题

老师您好 , 我是□□□ , 感谢老师这些日子来的指导

昨天下课前请教老师的问题描述如下:
以源码剖析第69页图2-7为例 ,若向系统要32bytes的空间 , 而32bytes预留的20个己用完 , 且memory pool又己用尽 , 此时势必需再配置heap空间 , 如此周而复始的malloc动作中 , 每块记忆体应各自有自己的cookie以便未来释放之用 , 在第二级配置器中以free-list将前述各块记忆体串在一起 , 不知後来的释放动作是如何进行的呢?

学生回去叁考SGI source 还是没有头绪 , 在第二级配置器中似乎没看到free( )的踪影 , 书上提到deallocate 若大於__MAX_BYTES 则呼叫malloc_alloc::deallocate 释放 , 反之仅将其收回free-list中 , 不知是在何时释放 ....


传送日期: 2002年5月12日 AM 11:03
主旨: 我知道答案了

老师您好 , 我是□□□

日前请教您的问题 ~在SGI allocator 第二级配置器中 , 记忆体释放是如何进行的?

答案就是在Effective C++的条款10中 (p44 倒数第3行起)

最近才开始看老师所介绍的三本C++小书 , 过程实在是太棒了 , 要不是这次有幸旁听老师的GP , 在看这几本书可能不会有那麽深刻的体验 .
透过C++ Primer及STL课程 , 感觉对OO与GP的精神愈来愈能掌握 , 这阵子在工作及一些书籍的阅读中也都有所验证 , 再次谢谢老师 , 如果能在程式设计这条路上有所成就 , 端赖老师的谆谆教诲 .

Ps : Effective C++条款7 (p31 倒数第8行)所提的混合风格 , 这个例子实在是太棒了 , 要不是这次上课了解到template , 一定会看不懂 .

传送日期: 2002527 PM 05:37
主旨: 侯老师您好

我是今年将毕业的 大六 ( :P ) 学生 因为不想当兵 先选择上班
由於刚开学时慕名跑去加入了老师的课
但期中完後公司这边较忙所以也少去了
其中後也没空去退选 又没有认识的同学在班上
所以对进况不甚了解
想请问一下老师期末的作业定了ㄇ??

毕业生何时之前要缴交呢?
p.s)只是怕成绩单太难看的学生留 && 能否以email方式缴交呢 thx

感恩 :P

●侯捷回覆: ref http://www.jjhou.com/course-generic-yzu-902.htm
如果你没有交作业,没有上台报告,你一定会被当,而且最多 20 分。
20 分是平时成绩。从这封信看,显然你平时没来上课,如果你让我知道你是谁,那麽你的平时成绩只能得 10 分。
这可能就是你的期末总成绩。

我曾经给过期末总成绩17分。所以给10分不会让我手软。你会得到怎样的成绩,就看自己的表现。如果作业交得出来,上台表达也不错(证明你不是抄来的),不上课当然无所谓,还是可以得到不错的成绩(因为你已达到这门课的训练目标)。

传送日期: 2002年5月27日 PM 08:22
主旨: 上霸王课,谢谢侯老师!!

侯老师你好:

我去年於资工所毕业(算是半路出家,大学学的是电机。)目前於桃园工作(Embedded System 软体设计),在研究所时就久仰你的大名,某日工作时突然想到您不是在元智教书,名师的课不能不上,又是免费的,所以鲁莽的闯入上课,未曾知会老师一声,十分抱歉。

到目前为只上了大约半学期的课(我从第九周才去上课),让我收获不少。上你的课之前有先看了一点 STL 的书,但是自己读是十分苦的,又不容易理解,也没体会到多少 STL 的精神,但上了老师你的课三、四周之後,终能体会到 STL 的「神奇」。虽然我有学 C++,但还不知道能够这样用 C++,STL 称为神奇也不为过吧!!之前工作上只是使用 STL,从不会想看 STL source code,因此对它总是半清不楚,但现在从老师那偷得(未付钱就学得)STL的知识後,若有问题也「敢」看 STL 的 SOURCE CODE ,自己找出问题,观念上清淅了许多。

这二周老师有提到 Design Patters 的书,而我也正在读此书,不过和 C++ Primer 比起来,Design Patters 就难以下咽,也许是我程度不够。不过我发现书的翻译风格也有差别,因为这二周老师说到 iterator,adaptor,template method,就文字上的翻译来说,老师你翻译教人一看便明白。另外,Design Patters 的范例不完整,让人有管中窥豹之感,不知老师有没有兴趣写一本 Design Patters 的书,以完整 C++ 的范例贯穿全书,让我们这群人能够满足。

老师您是十分温和的人,由其是老师译/着作非常多,但从不以非正常手段要求学生买你的书,反而将学生可能会叁考到的资料放在网站上,供学生自行下载。元智有您这样的老师,真是学生之福。

学生上霸王课无以回报,便以此封信表感谢之意,望老师您别见怪。祝 身体健康、万事如意

●侯捷回覆:我哪里会见怪,我很开心。欢迎大家来听课,所以我才会刻意把课开在晚上。

拥有很多编程实务经验,才能看懂《Design Patterns》一书。如果有一个(或一些)好例子,的确可以节省大家不少时间。你可以上网搜寻《Design Patterns》的读书会,上面应该有许多实例。已经不少人建议我写本《Design Patterns by Example》之类的书,但是我的时间┅呃┅well┅I dont know┅。《Design Patterns》中译本(叶秉哲译, 培生)其实译得相当不错,难读是因为它本身的层次高。你在课堂上一听就明白是因为 (1) 我选了最简单的patterns来讲,做个入门 (2) 课堂上有3D讲解,当然好过 2D纸页 :)

在我的新作《多型与虚拟 2e》第七章中,以MFC(Lite)为例,讲了十来个Design Patterns,呈现它们在 MFC(Lite)中的实现情形。此书尚未出版,且第七章不在免费公开之列。MFCLite是我模拟的一个 mini application framework(以MFC为模仿对象),可在侯捷网站上下载(「先睹为快」栏)。该章达240页,讲述对的对象MFCLite则达4000~5000行之多,而且十分复杂(毕竟是 application framework),所以阅读它仍然需要相当高的门槛。不过,读懂它可以让人在OO有相当层次的彻悟 ─ 这是完成它之後我的切身感受(不是说它师法的MFC有多好,而是因为这麽一个活生生的例子,又「只有」四五千行)。

传送日期: 2002年5月28日 PM 06:19
主旨: 老师您好 关於泛型

老师您好
关於之前我有寄一封信给老师您
告诉您说BCB在compile含有findfirstfile findnextfile等函式的时候会有问题
当时学生跟您说可能和作业系统有点independent

现在我将我们这组测试的报告以及发现的心得撰写如下
并在附件中将我们这组的程式码附上

以便老师您可以随时debug


(1)两个版本在纯档案版的程式中是没有任何错误的 vc and bcb是相同的
(2)当我们想要写一个读取子目录的档案以及资料夹的功能较强的版本时
发现vc所编译出来的执行档跑出来的结果和我们按右键看到的内容是一样的
在98 and 2000底下都没有错误
(3)但是bcb所产生的档案目录版的执行档会跑出令我们意外的结果
(4)同组的同学在测试的时候,平台是2000 and XP,编译环境都在vc,发现都无错误
(5)所以我们归纳出应该是bcb和系统的哪一部份不相容(有关资料夹的部分就是目录)
(6)或是直接说我们设计的那个演算法和vc的构思相同,但可能bcb不support

老师您可以直接在附加档案中编译执行并看看结果
因为每个人的电脑环境不一样
恐怕在老师您的电脑上可能会跑出错误的答案
不过我们这组已经测试过三到四台的电脑了 发现没问题
唯恐在下星期的demo出槌
所以我们还会继续测试,不过bcb一定会有错就是了,
虽然他所列的档案数量以及资料夹数量是正确的

还有就是老师您一直跟我们说尽量在命令列模式下编译
但是这次学生寄给老师您的却是整合环境的
学生在此项老师说声抱歉
因为我们还是蛮喜欢click的
所以请老师原谅我们吧

●侯捷回覆:命令列只是一个建议。我给学生很大的弹性空间 :) 我说过各位甚至可以自己想题目 :)

■第14周:05/29
1.hash table(part2)
2.heap, heap algorithms. QuickSort, priority-queue. 

3. function adapter

以下摘录自 SGI STL header "stl_functiona.h" (included by header "functional")

// C++ Standard 规定,每一个 Adaptable Unary Function 都必须继承此类别
template <class Arg, class Result>
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};
// C++ Standard 规定,每一个 Adaptable Binary Function 都必须继承此类别
template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};      
// 已知两个 Adaptable Unary Functions f,g,以下配接器用来产生一个 h,
// 使 h(x) = f(g(x))
template <class Operation1, class Operation2>
class unary_compose
  : public unary_function<typename Operation2::argument_type,
                               typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
public:
  unary_compose(const Operation1& x, const Operation2& y) : op1(x), op2(y) {}
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x));
  }
};
// 辅助函式,让我们得以方便使用 unary_compose<Op1,Op2>
template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2> 
compose1(const Operation1& op1, const Operation2& op2) {
  return unary_compose<Operation1, Operation2>(op1, op2);
}
// 已知一个 Adaptable Binary Function f 和两个Adaptable Unary Functions g1,g2,
// 以下配接器用来产生一个 h,使 h(x) = f(g1(x),g2(x))
template <class Operation1, class Operation2, class Operation3>
class binary_compose
  : public unary_function<typename Operation2::argument_type,
                               typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
  Operation3 op3;
public:
  binary_compose(const Operation1& x, const Operation2& y, 
                    const Operation3& z) : op1(x), op2(y), op3(z) { }
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x), op3(x));
  }
};
// 辅助函式,让我们得以方便使用 binary_compose<Op1,Op2,Op3>
template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2, Operation3> 
compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3) {
  return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);
}

■第15周: 06/05 本课程期末作业 分组报告。

注意事项:(1) 缴交书面报告一份,程式(含源码及可执行档)磁片一份。
                    (2) 自行推派主讲同学上台报告。不限一位,可分工。
                    (3) 演练执行你的程式。

本周由 9~17组同学缴交作业并上台报告。

■第16周:06/12  本课程期末作业 分组报告(续上周)。

本周由 1~8组同学缴交作业并上台报告。

■第17周:06/19 期末考周。停课。本学期课程结束。