2005年07月03日

지금 밑에서 1학년생들이 군훈하느라구 난시다,,1년전 우리 그때 영원히 잊을수 없는 그 40일,,대학 필업하면

남는것이 이것뿐이라는데,,

時間がとても早いですね。。!もう、、(放假)しました。  치,,근 10년 배워가는 일본어를 ..

벌써 방학이다..시험두 ,,60점이 만세라구 ,,난 이미 모두 만세한 상태……ㅋㅋ,,,전번날

시험 다치구 조선족끼리 멀리 놀러갔는데,,정말 참시원했다,,한학기 긴장한 상태속에서

벗어나 멀리가 놀고 먹고,,하루 지내구 오니 ,,정말 마음이 확 풀리는 모양이다..

2005年06月23日

CSS语法手册

  来源:网络收集  整理日期:07月05日 05:14  www.dhxy.com


字体属性

1、font-family

功能:用于改变HTML标志或元素的字体,你可设置一个可用字体清单。浏览器由前向后选用字体。
语法:{font-family:字体1,字体2, … ,字体n}
例子

字体

2、font-style

功能:使文本显示为扁斜体或斜体等表示强调 。
数值
normal – 正常
italic – 斜体
oblique – 偏斜体
例子

Normal

Italic

Oblique

3、font-variant

功能:用于在正常与小型大写字母字体间切换。
数值:normal – 如果该标志继承父元素的 small-caps 设置,则关键字 normal 将 font-variant 设置为正常字体。
small-caps – 把小写字母显示为字体较小的大写字母。
例子

FONT-VARIANT

4、font-weight

功能:用于设置字体灰度,生成字体的深,浅版本。
数值
正常灰度 – normal
相对灰度 – bold, bolder, light, lighter
梯度灰度 – 取值如下:100, 200, 300, 400(相当于normal), 500, 600, 700(相当于bold), 800, 900。
例子

字体灰度

5、font-size

功能:用各种度量单位控制文本字体大小。
数值:有四种数值方式
绝对尺寸- 用具体字号表定义字体大小,可以取下列数值之一:xx – small, x-small, small, medium, large, x-large, xx-large。不同字体有不同的数值。
相对尺寸 – larger, smaller,产生的尺寸是相对于父容器字号而言的。
长度 – 用毫米(mm),厘米(cm),英寸(in),点数(pt),象素(px),pica(pc),ex(小写字母x的高度)或em(字体高度)作为度量单位。
百分比 – 相对于其父元素字体大小的百分比。
例子

6、font

功能:简写属性,提供了对字体所有属性进行设置的快捷方法。
语法:{font:字体属性1 字体属性2 … 字体属性n}
数值:字体属性值为前面已列出的值,此外还可以设置行间距属性 line-height(见文本属性),内容的顺序为:font-style; font-variant; font-weight; font-size; line-height; font-family。可省略部分属性,属性值间用空格分开。
例子

FONT

HTML语法大全


   卷标 , 属性名称 , 简介 

 <! - - … - -> 批注 
 <!> 跑马灯 
 <marquee>…</marquee>普通卷动 
 <marquee behavior=slide>…</marquee>滑动 
 <marquee behavior=scroll>…</marquee>预设卷动 
 <marquee behavior=alternate>…</marquee>来回卷动 
 <marquee direction=down>…</marquee>向下卷动 
 <marquee direction=up>…</marquee>向上卷动 
 <marquee direction=right></marquee>向右卷动 
 <marquee direction=left></marquee>向左卷动 
 <marquee loop=2>…</marquee>卷动次数 
 <marquee width=180>…</marquee>设定宽度 
 <marquee height=30>…</marquee>设定高度 
 <marquee bgcolor=FF0000>…</marquee>设定背景颜色 
 <marquee scrollamount=30>…</marquee>设定卷动距离 
 <marquee scrolldelay=300>…</marquee>设定卷动时间 
 <!>字体效果 
 <h1>…</h1>标题字(最大) 
 <h6>…</h6>标题字(最小) 
 <b>…</b>粗体字 
 <strong>…</strong>粗体字(强调) 
 <i>…</i>斜体字 
 <em>…</em>斜体字(强调) 
 <dfn>…</dfn>斜体字(表示定义) 
 <u>…</u>底线 
 <ins>…</ins>底线(表示插入文字) 
 <strike>…</strike>横线 
 <s>…</s>删除线 
 <del>…</del>删除线(表示删除) 
 <kbd>…</kbd>键盘文字 
 <tt>…</tt> 打字体 
 <xmp>…</xmp>固定宽度字体(在文件中空白、换行、定位功能有效) 
 <plaintext>…</plaintext>固定宽度字体(不执行标记符号) 
 <listing>…</listing> 固定宽度小字体 
 <font color=00ff00>…</font>字体颜色 
 <font size=1>…</font>最小字体 
 <font style =font-size:100 px>…</font>无限增大 
 <!>区断标记 
 <hr>水平线 
 <hr size=9>水平线(设定大小) 
 <hr width=80%>水平线(设定宽度) 
 <hr color=ff0000>水平线(设定颜色) 
 <br>(换行) 
 <nobr>…</nobr>水域(不换行) 
 <p>…</p>水域(段落) 
 <center>…</center>置中 
 <!>连结格式 
 <base href=地址>(预设好连结路径) 
 <a href=地址></a>外部连结 
 <a href=地址 target=_blank></a>外部连结(另开新窗口) 
 <a href=地址 target=_top></a>外部连结(全窗口连结) 
 <a href=地址 target=页框名></a>外部连结(在指定页框连结) 
 <!>贴图/音乐 
 <img src=图片地址>贴图 
 <img src=图片地址 width=180>设定图片宽度 
 <img src=图片地址 height=30>设定图片高度 
 <img src=图片地址 alt=提示文字>设定图片提示文字 
 <img src=图片地址 border=1>设定图片边框 
 <bgsound src=MID音乐文件地址>背景音乐设定 
 <!>表格语法 
 <table aling=left>…</table>表格位置,置左 
 <table aling=center>…</table>表格位置,置中 
 <table background=图片路径>…</table>背景图片的URL=就是路径网址 
 <table border=边框大小>…</table>设定表格边框大小(使用数字) 
 <table bgcolor=颜色码>…</table>设定表格的背景颜色 
 <table borderclor=颜色码>…</table>设定表格边框的颜色 
 <table borderclordark=颜色码>…</table>设定表格暗边框的颜色 
 <table borderclorlight=颜色码>…</table>设定表格亮边框的颜色 
 <table cellpadding=参数>…</table>指定内容与网格线之间的间距(使用数字) 
 <table cellspacing=参数>…</table>指定网格线与网格线之间的距离(使用数字) 
 <table cols=参数>…</table>指定表格的栏数 
 <table frame=参数>…</table>设定表格外框线的显示方式 
 <table width=宽度>…</table>指定表格的宽度大小(使用数字) 
 <table height=高度>…</table>指定表格的高度大小(使用数字) 
 <td colspan=参数>…</td>指定储存格合并栏的栏数(使用数字) 
 <td rowspan=参数>…</td>指定储存格合并列的列数(使用数字) 
 <!>分割窗口 
 <frameset cols="20%,*">左右分割,将左边框架分割大小为20%右边框架的大小浏览器会自动调整 
 <frameset rows="20%,*">上下分割,将上面框架分割大小为20%下面框架的大小浏览器会自动调整 
 <frameset cols="20%,*">分割左右两个框架 
 <frameset cols="20%,*,20%">分割左中右三个框架 
 <分割上下两个框架 
 <frameset rows="20%,*,20%">分割上中下三个框架 
 <! - - … - -> 批注 
 <A HREF TARGET> 指定超级链接的分割窗口 
 <A HREF=#锚的名称> 指定锚名称的超级链接 
 <A HREF> 指定超级链接 
 <A NAME=锚的名称> 被连结点的名称 
 <ADDRESS>….</ADDRESS> 用来显示电子邮箱地址 
 <B> 粗体字 
 <BASE TARGET> 指定超级链接的分割窗口 
 <BASEFONT SIZE> 更改预设字形大小 
 <BGSOUND SRC> 加入背景音乐 
 <BIG> 显示大字体 
 <BLINK> 闪烁的文字 
 <BODY TEXT LINK VLINK> 设定文字颜色 
 <BODY> 显示本文 
 <BR> 换行 
 <CAPTION ALIGN> 设定表格标题位置 
 <CAPTION>…</CAPTION> 为表格加上标题 
 <CENTER> 向中对齐 
 <CITE>…<CITE> 用于引经据典的文字 
 <CODE>…</CODE> 用于列出一段程序代码 
 <COMMENT>…</COMMENT> 加上批注 
 <DD> 设定定义列表的项目解说 
 <DFN>…</DFN> 显示"定义"文字 
 <DIR>…</DIR> 列表文字卷标 
 <DL>…</DL> 设定定义列表的卷标 
 <DT> 设定定义列表的项目 
 <EM> 强调之用 
 <FONT FACE> 任意指定所用的字形 
 <FONT SIZE> 设定字体大小 
 <FORM ACTION> 设定户动式窗体的处理方式 
 <FORM METHOD> 设定户动式窗体之资料传送方式 
 <FRAME MARGINHEIGHT> 设定窗口的上下边界 
 <FRAME MARGINWIDTH> 设定窗口的左右边界 
 <FRAME NAME> 为分割窗口命名 
 <FRAME NORESIZE> 锁住分割窗口的大小 
 <FRAME SCROLLING> 设定分割窗口的滚动条 
 <FRAME SRC> 将HTML文件加入窗口 
 <FRAMESET COLS> 将窗口分割成左右的子窗口 
 <FRAMESET ROWS> 将窗口分割成上下的子窗口 
 <FRAMESET>…</FRAMESET> 划分分割窗口 
 <H1>~<H6> 设定文字大小 
 <HEAD> 标示文件信息 
 <HR> 加上分网格线 
 <HTML> 文件的开始与结束 
 <I> 斜体字 
 <IMG ALIGN> 调整图形影像的位置 
 <IMG ALT> 为你的图形影像加注 
 <IMG DYNSRC LOOP> 加入影片 
 <IMG HEIGHT WIDTH> 插入图片并预设图形大小 
 <IMG HSPACE> 插入图片并预设图形的左右边界 
 <IMG LOWSRC> 预载图片功能 
 <IMG SRC BORDER> 设定图片边界 
 <IMG SRC> 插入图片 
 <IMG VSPACE> 插入图片并预设图形的上下边界 
 <INPUT TYPE NAME value> 在窗体中加入输入字段 
 <ISINDEX> 定义查询用窗体 
 <KBD>…</KBD> 表示使用者输入文字 
 <LI TYPE>…</LI> 列表的项目 ( 可指定符号 ) 
 <MARQUEE> 跑马灯效果 
 <MENU>…</MENU> 条列文字卷标 
 <META NAME="REFRESH" CONTENT URL> 自动更新文件内容 
 <MULTIPLE> 可同时选择多项的列表栏 
 <NOFRAME> 定义不出现分割窗口的文字 
 <OL>…</OL> 有序号的列表 
 <OPTION> 定义窗体中列表栏的项目 
 <P ALIGN> 设定对齐方向 
 <P> 分段 
 <PERSON>…</PERSON> 显示人名 
 <PRE> 使用原有排列 
 <SAMP>…</SAMP> 用于引用字 
 <SELECT>…</SELECT> 在窗体中定义列表栏 
 <SMALL> 显示小字体 
 <STRIKE> 文字加横线 
 <STRONG> 用于加强语气 
 <SUB> 下标字 
 <SUP> 上标字 
 <TABLE BORDER=n> 调整表格的宽线高度 
 <TABLE CELLPADDING> 调整数据域位之边界 
 <TABLE CELLSPACING> 调整表格线的宽度 
 <TABLE HEIGHT> 调整表格的高度 
 <TABLE WIDTH> 调整表格的宽度 
 <TABLE>…</TABLE> 产生表格的卷标 
 <TD ALIGN> 调整表格字段之左右对齐 
 <TD BGCOLOR> 设定表格字段之背景颜色 
 <TD COLSPAN ROWSPAN> 表格字段的合并 
 <TD NOWRAP> 设定表格字段不换行 
 <TD VALIGN> 调整表格字段之上下对齐 
 <TD WIDTH> 调整表格字段宽度 
 <TD>…</TD> 定义表格的数据域位 
 <TEXTAREA NAME ROWS COLS> 窗体中加入多少列的文字输入栏 
 <TEXTAREA WRAP> 决定文字输入栏是自动否换行 
 <TH>…</TH> 定义表格的标头字段 
 <TITLE> 文件标题 
 <TR>…</TR> 定义表格美一行 
 <TT> 打字机字体 
 <U> 文字加底线 
 <UL TYPE>…</UL> 无序号的列表 ( 可指定符号 ) 
 <VAR>…</VAR> 用于显示变量

<span>在CSS定义中属于一个行内元素,而<div>是块级元素。
对于学过CSS的人来说一听就能明白。可对于新手来说不易理解,我主要对新手说通熟点吧!用容器这一词会更容易形象理解它们的存在与用途,行内元素相当一个小容器,而<div>相当于一个大容器,大容器当然可以放一个小容器了,<span>就是小容器,这样一说你也许会在脑海中有一个初步的印象了吧,如果我们想在大容器中装一些清水.但我也想在里装一些墨水怎么办?很简单,我们把小容器拿出来装上墨水然后放入大容器里的清水中不就成了吗.
我在举个简单的一个实际例子吧比如<div>abcdefg</div>我想用CSS定义c的样式因此我们就可以用到<span>了.<div>ab<span>c</span>defg</div>(此处省略样式代码)

2005年05月16日

Windows系列操作系统对线程间通信的支持           ——管道与邮路

Windows应用程序间数据通讯的基本方式有四种。最简单的是利用剪切板;另一种是DDEDynamic Data Exchange动态数据交换),它利用一种公共的协议实现两个或多个应用程序之间的通讯;再者是通过内存映射文件,内存映射可以将一个进程的一段虚拟地址映射为一个文件,然后其它的进程可以共享该段虚拟地址;最后就是通过管道与邮路实现进程间数据通信。

要讨论管道与邮路之前先让我们来复习一下这几个概念:进程、线程。微软官方对进程和线程的定义如下

进程:用最简洁的话来说,进程就是一个正在执行的程序,一个或多个线程在进程中运行,线程是操作系统分配CPU运算时间的最小单位。每一个进程都提供了运行一个程序所必需的资源,一个进程具有4GB的虚拟地址空间(Windows NT Server Enterprise EditionWindows 2000 Advanced Server中低3GB虚拟地址空间供进程使用,高1GB供操作系统的内核代码使用。Windows NT/2000中低2GB供进程使用,高2GB供操作系统内核代码使用。Windows9X0——64K只读空间用来装入Microsoft DOS信息,64K——4M装入DOS的兼容代码,4M——2GB的私有空间供进程使用,2GB——3GB的共享空间装入各种DLL代码,3GB——4GB为共享的系统内核代码空间,其中 共享的2GB——4GB的空间是99%的“内存无效页错误”、“General Protect Error(GPE)”及蓝屏的罪魁祸首。),可执行代码,数据,对象句柄,环境变量,优先权以及设置最大化最小化的功能。每一个进程都从一个主线程开始执行,但可以在它所拥有的线程中创建额外的线程。一个进程的所有线程共享进程的虚拟地址空间和系统资源,一个线程的资源包括线程的机器寄存器设置,内核堆栈,线程环境变量和进程虚拟地址中的用户堆栈。

让我们再来看看微软官方对管道和邮路是怎么解释的。

管道(pipe)是进程用来通讯的共享内存区域。一个进程往管道中写入信息,而其它的进程可以从管道中读出信息。如其名,管道是进程间数据交流的通道。邮路(Mailslots)的功能与管道类似,也是进程间通讯(interprocess communicationsIPC)的媒介,只不过其具体实现方式与管道有些差别。一个基于Win32的应用程序可以在邮路中储存消息,这些消息通常通过网络发往一个指定的计算机或某域名(域是共享一个组名的一组工作站或服务器。)下的所有计算机。你也可以使用命名管道代替邮路来进行进程间通信。命名管道最适合用来两个进程间的消息传递,邮路则更适合一个进程向多个进程广播消息。邮路具有一个重要的特点,它使用数据包广播消息。广播(broadcast)是网络传输中使用的术语,它意味着接收方收到数据后不发送确认消息通知发送方。而管道(这里的管道指命名管道,有关命名管道以下详解。)则不同,它更类似于打电话,你只对一个当事人说话,但是你却非常清楚你的话都被对方听到。邮路和管道一样,也是一个虚拟文件,它保存在内存中,但是你却必须使用普通的Win32文件函数访问它,比如CreateFileReadFileWriteFile等。邮路中储存的数据可以是任何形式的,唯一的要求是不得超过64K。与磁盘文件不同的是,邮路是一个临时的对象,当某个邮路所有的句柄都关闭的时候,该邮路及其中的数据就被删除。

管道的类型有两种:匿名管道和命名管道。匿名管道是不命名的,它最初用于在本地系统中父进程与它启动的子进程之间的通信。命名管道更高级,它由一个名字来标识,以使客户端和服务端应用程序可以通过它进行彼此通信。而且,Win32命名管道甚至可以在不同系统的进程间使用,这使它成为许多客户/服务器应用程序的理想之选。

就像水管连接两个地方并输送水一样,软件的管道连接两个进程并输送数据。一个一个管道一旦被建立,它就可以象文件一样被访问,并且可以使用许多与文件操作同样的函数。可以使用CreateFile函数获取一个已打开的管道的句柄,或者由另一个进程提供一个句柄。使用WriteFile函数向管道写入数据,之后这些数据可以被另外的进程用ReadFile函数读取。管道是系统对象,因此管道的句柄在不需要时必须使用CloseHandle函数关闭。

匿名管道只能单向传送数据,而命名管道可以双向传送。管道可以以比特流形式传送任意数量的数据。命名管道还可以将数据集合到称为消息的数据块中。命名管道甚至具有通过网络连接多进程的能力。但遗憾的是Windows9X不支持创建命名管道,它只能在WindowsNT系列(如Windows NTWindows 2000Windows XP)的操作系统上创建。

当讨论管道时,通常涉及到两个进程:客户进程和服务进程。服务进程负责创建管道。客户进程连接到管道。服务进程可以创建一个管道的多个实例,以此支持多个客户进程。

匿名管道用以下函数创建:

BOOL CreatePipe(


      PHANDLE hReadPipe,                       // 用于读操作的句柄
      PHANDLE hWritePipe,                      // 用于写操作的句柄
      LPSECURITY_ATTRIBUTES lpPipeAttributes,  // 描述安全信息的一个结构
      DWORD nSize                              // 管道大小

);

命名管道用以下函数创建:

HANDLE CreateNamedPipe(


  LPCTSTR lpName,                             // 管道名
      DWORD dwOpenMode,                           // 管道打开方式
      DWORD dwPipeMode,                           // 管道模式
  DWORD nMaxInstances,                        // 该管道最大的实例数量
  DWORD nOutBufferSize,                       // 输出缓冲区大小
  DWORD nInBufferSize,                        // 输入缓冲区大小
  DWORD nDefaultTimeOut,                      // 指定默认的超时时间
  LPSECURITY_ATTRIBUTES lpSecurityAttributes  // 描述安全信息的一个结构

);

管道由以下函数删除:

BOOL CloseHandle(


  HANDLE hObject   // 管道句柄

);

其它的管道函数简介如下:

CallNamedPipe:连接到一个命名管道,读取或写入数据之后关闭它。

ConnectNamedPipe:服务进程准备好一个连接到客户进程的管道,并等待一个客户进程连接上为止。

DisconnectNamedPipe:服务端用来断开与客户端的连接。

GetNamedPipeHandleState:获取一个命名管道的状态信息。

GetNamedPipeInfo:获取一个命名管道的信息。

PeekNamedPipe:从一个匿名或命名管道中拷贝数据到一个缓冲区。

SetNamedPipeHandleState:设置管道的类型及其它状态信息,比如说是比特流还是消息流管道。

TransactNamedPipe:从一个消息管道读消息或向其写入消息。

WaitNamedPipe:客户进程用来连接到一个命名管道。

邮路是由邮路服务进程创建。当邮路服务进程创建了一个邮路后,便返回该邮路句柄。当某个进程需要从该邮路中读取消息时,它必须提供该句柄。只有创建该邮路的进程,或以某种机制(比如继承)获得该邮路句柄的进程能够从邮路中读取消息。与管道不同,所有的邮路都是从属于创建它的本地进程的,你无法创建一个远程的邮路。邮路的客户进程具有向邮路写入消息的权限。任何进程只要获得了邮路的名字,就可以往里面写入消息,新的消息将放在邮路的消息队列后面。

邮路能在一个域中广播消息。如果域中几个进程每个都用相同的名字创建了一个邮路,则它们都会收到送往该邮路的消息。

在邮路中传递的消息如果小于425字节,那么它们会以数据包的形式传递。大于425字节的消息会以其它的方式传输,在这种情况下,你只能从一个客户进程传递给一个服务进程,而不能从一个客户进程传递给多个服务进程。且Windows NT不支持大于等于425个字节的消息传递。

邮路由以下函数创建:

HANDLE CreateMailslot(


  LPCTSTR lpName,                            // 邮路名
  DWORD nMaxMessageSize,                     // 消息最大的大小
  DWORD lReadTimeout,                        // 读取操作的超时时间
  LPSECURITY_ATTRIBUTES lpSecurityAttributes // 描述安全信息的一个结构

);

邮路由以下函数删除:

BOOL CloseHandle(


  HANDLE hObject   // 邮路句柄

);

其它邮路函数简介如下:

GetMailslotInfo:获取指定邮路的相关信息。

SetMailslotInfo:设置指定邮路的相关信息。

Windows系列操作系统对管道和邮路的支持就是这样,除此之外,内存映射文件也是进程间数据交流的重要方式,但限于篇幅,本文不再具体介绍。

2005年05月15日

考了线代,,终于考完了,轻松呀~~昨天晚上整了欢送会,,只可惜,因为今天有考试了,先回来了,

2005年05月10日

转了一圈,,试用了一下,,还不错,很爽,真像是一个社区的感觉,,里面可以共享音乐什么特方便,我还有几个邀请名额 ,,谁想要,我也可以邀请,,

2005年05月05日

我搞到几个邀请,想要的我可以给发

C++中的模板(template)

  作者:张笑猛   来源:http://objects.nease.net/

网上我最喜欢的技术文章是类似某何君所著“CVS快速入门”或者“UML reference card”之类,简短扼要,可以非常快的领着你进入一个新天地。而对于比较长的文章我通常是将其保存到硬盘上,然后准备着“以后有时间”的时候再看,但它们通常的命运都是“闲坐说玄宗”,直到某一天在整理硬盘时将它们以“不知所云”入罪,一并删除。
 这篇小文主要是针对刚刚接触模板概念的读者,希望能帮助读者学习模板的使用。为了避免本文也在诸公的硬盘上遭逢厄运,我决定写的短些。“以后有时间”的时候再补充些内容。
TOC
1. 简介
2. 语法
3. 使用技巧
  3.1 语法检查
  3.2 继承
  3.3 静态成员
3.4 模板类的运用
4. 参考资料
 
1. 简介
模板是C++在90年代引进的一个新概念,原本是为了对容器类(container classes)的支持[1],但是现在模板产生的效果已经远非当初所能想象。
简单的讲,模板就是一种参数化(parameterized)的类或函数,也就是类的形态(成员、方法、布局等)或者函数的形态(参数、返回值等)可以被参数改变。更加神奇的是这里所说的参数,不光是我们传统函数中所说的数值形式的参数,还可以是一种类型(实际上稍微有一些了解的人,更多的会注意到使用类型作为参数,而往往忽略使用数值作为参数的情况)。
举个常用的例子来解释也许模板就从你脑袋里的一个模糊的概念变成活生生的代码了:
在C语言中,如果我们要比较两个数的大小,常常会定义两个宏:
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)>(b)?(a):(b))
这样你就可以在代码中:
return min(10, 4);
或者:
return min(5.3, 18.6);
这两个宏非常好用,但是在C++中,它们并不像在C中那样受欢迎。宏因为没有类型检查以及天生的不安全(例如如果代码写为min(a++, b–);则显然结果非你所愿),在C++中被inline函数替代。但是随着你将min/max改为函数,你立刻就会发现这个函数的局限性 —— 它不能处理你指定的类型以外的其它类型。例如你的min()声明为:
int min(int a, int b);
则它显然不能处理float类型的参数,但是原来的宏却可以很好的工作!你随后大概会想到函数重载,通过重载不同类型的min()函数,你仍然可以使大部分代码正常工作。实际上,C++对于这类可以抽象的算法,提供了更好的办法,就是模板:
template <class T> const T & min(const T & t1, const T & t2) {
    return t1>t2?t2:t1;
}
这是一个模板函数的例子。在有了模板之后,你就又自由了,可以像原来在C语言中使用你的min宏一样来使用这个模板,例如:
return min(10,4);
也可以:
return min(5.3, 18.6)
你发现了么?你获得了一个类型安全的、而又可以支持任意类型的min函数,它是否比min宏好呢?
当然上面这个例子只涉及了模板的一个方面,模板的作用远不只是用来替代宏。实际上,模板是泛化编程(Generic Programming)的基础。所谓的泛化编程,就是对抽象的算法的编程,泛化是指可以广泛的适用于不同的数据类型。例如我们上面提到的min算法。
2. 语法
你千万不要以为我真的要讲模板的语法,那太难为我了,我只是要说一下如何声明一个模板,如何定义一个模板以及常见的语法方面的问题。
template<> 是模板的标志,在<>中,是模板的参数部分。参数可以是类型,也可以是数值。例如:
template<class T, T t>
class Temp{
public:
    …
    void print() { cout << t << endl; }
private:
    T t_;
};
在这个声明中,第一个参数是一个类型,第二个参数是一个数值。这里的数值,必须是一个常量。例如针对上面的声明:
Temp<int, 10> temp; // 合法
int i = 10;
Temp<int, i> temp; // 不合法
const int j = 10;
Temp<int, j> temp; // 合法
参数也可以有默认值:
template<class T, class C=char> …
默认值的规则与函数的默认值一样,如果一个参数有默认值,则其后的每个参数都必须有默认值。
参数的名字在整个模板的作用域内有效,类型参数可以作为作用域内变量的类型(例如上例中的T t_),数值型参数可以参与计算,就象使用一个普通常数一样(例如上例中的cout << t << endl)。
模板有个值得注意的地方,就是它的声明方式。以前我一直认为模板的方法全部都是隐含为inline的,即使你没有将其声明为inline并将函数体放到了类声明以外。这是模板的声明方式给我的错觉,实际上并非如此。我们先来看看它的声明,一个作为接口出现在头文件中的模板类,其所有方法也都必须与类声明出现在一起。用通俗的话来说,就是模板类的函数体也必须出现在头文件中(当然如果这个模板只被一个C++程序文件使用,它当然也可以放在.cc中,但同样要求类声明与函数体必须出现在一起)。这种要求与inline的要求一样,因此我一度认为它们隐含都是inline的。但是在Thinking In C++[2]中,明确的提到了模板的non-inline function,就让我不得不改变自己的想法了。看来正确的理解应该是:与普通类一样,声明为inline的,或者虽然没有声明为inline但是函数体在类声明中的才是inline函数。
澄清了inline的问题候,我们再回头来看那些我们写的包含了模板类的丑陋的头文件,由于上面提到的语法要求,头文件中除了类接口之外,到处充斥着实现代码,对用户来说,十分的不可读。为了能像传统头文件一样,让用户尽量只看到接口,而不用看到实现方法,一般会将所有的方法实现部分,放在一个后缀为.i或者.inl的文件中,然后在模板类的头文件中包含这个.i或者.inl文件。例如:
// start of temp.h
template<class T> class Temp{
public:
    void print();
};
 #include "temp.inl"
// end of temp.h
// start of temp.inl
template<class T> void Temp<T>::print() {
    …
}
// end of temp.inl
通过这样的变通,即满足了语法的要求,也让头文件更加易读。模板函数也是一样。
普通的类中,也可以有模板方法,例如:
class A{
public:
    template<class T> void print(const T& t) { …}
    void dummy();
};
对于模板方法的要求与模板类的方法一样,也需要与类声明出现在一起。而这个类的其它方法,例如dummy(),则没有这样的要求。
3. 使用技巧
知道了上面所说的简单语法后,基本上就可以写出自己的模板了。但是在使用的时候还是有些技巧。
3.1 语法检查
对模板的语法检查有一部分被延迟到使用时刻(类被定义[3],或者函数被调用),而不是像普通的类或者函数在被编译器读到的时候就会进行语法检查。因此,如果一个模板没有被使用,则即使它包含了语法的错误,也会被编译器忽略,这是语法检查问题的第一个方面,这不常遇到,因为你写了一个模板就是为了使用它的,一般不会放在那里不用。与语法检查相关的另一个问题是你可以在模板中做一些假设。例如:
template<class T> class Temp{
public:
    Temp(const T & t): t_(t) {}
    void print() { t.print();}
private:
    T t_;
};
在这个模板中,我假设了T这个类型是一个类,并且有一个print()方法(t.print())。我们在简介中的min模板中其实也作了同样的假设,即假设T重载了’>’操作符。
因为语法检查被延迟,编译器看到这个模板的时候,并不去关心T这个类型是否有print()方法,这些假设在模板被使用的时候才被编译器检查。只要定义中给出的类型满足假设,就可以通过编译。
之所以说“有一部分”语法检查被延迟,是因为有些基本的语法还是被编译器立即检查的。只有那些与模板参数相关的检查才会被推迟。如果你没有写class结束后的分号,编译器不会放过你的。
3.2 继承
模板类可以与普通的类一样有基类,也同样可以有派生类。它的基类和派生类既可以是模板类,也可以不是模板类。所有与继承相关的特点模板类也都具备。但仍然有一些值得注意的地方。
假设有如下类关系:
template<class T> class A{ … };
 |
+– A<int> aint;
 |
+– A<double> adouble;
则aint和adouble并非A的派生类,甚至可以说根本不存在A这个类,只有A<int>和A<doubl>这两个类。这两个类没有共同的基类,因此不能通过类A来实现多态。如果希望对这两个类实现多态,正确的类层次应该是:
class Abase {…};
template<class T> class A: public Abase {…};
 |
+– A<int> aint;
 |
+– A<double> adouble;
也就是说,在模板类之上增加一个抽象的基类,注意,这个抽象基类是一个普通类,而非模板。
再来看下面的类关系:
template<int i> class A{…};
 |
+– A<10> a10;
 |
+– A<5> a5;
在这个情况下,模板参数是一个数值,而不是一个类型。尽管如此,a10和a5仍然没有共同基类。这与用类型作模板参数是一样的。
3.3 静态成员
与上面例子类似:
template<class T> class A{ static char a_; };
 |
+– A<int> aint1, aint2;
 |
+– A<double> adouble1, adouble2;
这里模板A中增加了一个静态成员,那么要注意的是,对于aint1和adouble1,它们并没有一个共同的静态成员。而aint1与aint2有一个共同的静态成员(对adouble1和adouble2也一样)。
这个问题实际上与继承里面讲到的问题是一回事,关键要认识到aint与adouble分别是两个不同类的实例,而不是一个类的两个实例。认识到这一点后,很多类似问题都可以想通了。
3.4 模板类的运用
模板与类继承都可以让代码重用,都是对具体问题的抽象过程。但是它们抽象的侧重点不同,模板侧重于对于算法的抽象,也就是说如果你在解决一个问题的时候,需要固定的step1 step2…,那么大概就可以抽象为模板。而如果一个问题域中有很多相同的操作,但是这些操作并不能组成一个固定的序列,大概就可以用类继承来解决问题。以我的水平还不足以在这么高的层次来清楚的解释它们的不同,这段话仅供参考吧。
模板类的运用方式,更多情况是直接使用,而不是作为基类。例如人们在使用STL提供的模板时,通常直接使用,而不需要从模板库中提供的模板再派生自己的类。这不是绝对的,我觉得这也是模板与类继承之间的以点儿区别,模板虽然也是抽象的东西,但是它往往不需要通过派生来具体化。
在设计模式[4]中,提到了一个模板方法模式,这个模式的核心就是对算法的抽象,也就是对固定操作序列的抽象。虽然不一定要用C++的模板来实现,但是它反映的思想是与C++模板一致的。
4. 参考资料
[1] 深度C++对象模型,Stanley B.Lippman, 侯捷译
[2] Thinking In C++ 2nd Edition Volumn 1, Bruce Eckel
[3] 定义– 英文为definition,意思是"Make this variable here",参见[2] p93
[4] Design Patterns – Elements of Reusable Object-Oriented Software GOF