Still in Love with C++ 中文版(1)

Still in Love with C++ 中文版(1)

| |

| |

---|---|---
| | |

[ 发表日期:2002-4-18 15:44:17 ]


| Still in Love with C++ 中文版

Modern Language Features Enhance the Visual C++ .NET Compiler

作者:Stanley B. Lippman

译者:荣耀

【摘要:使用C++已多年的程序员现正处于迷惑之中:他们的语言如何迎合C#和.NET的来临?本文勾画了C++如何应用于.NET世界的路标。在.NET中,可以两种方式使用C++代码:受管制的(managed)和不受管制的(unmanaged)。不受管制的代码不用CLR,而受管制的代码则致力于使用C++受管制的扩展。本文解释了这两个方式。】

我们这些C++社团里的人第一次感觉自己象是一个刚刚降临了一个新宝贝的家庭中年长的孩子—每个人都狂热地围着这个新来的小家伙,(此刻,)即便能有人轻轻地拍一下我们的脑袋,我们就很幸运了。我们很难不产生被忽视和一丁点儿伤害的感觉。在技术上这实际更糟糕,因为基础不断发生漂移,与之并进,事关生存。

当然,如今微软.NET框架是正在出售的新技术,多么丰盛的大餐啊!每一个人都对那个叫C#的语言啧啧不已。如果你是一名C++程序员,你很难不怀疑你是否应该学习C#,毕竟,在.NET讨论中,要不是拿它跟C#对比,难得提到一次C++。

C++程序员过时了吗?绝对不是!本文中,我将简介Visual Studio .NET当前版本中的新东西,并给你一些微软关于C++将来计划的概念。首先,我将简介Visual Studio.NET中C++的两个方面:标准C++和C++受管制的扩展。

保持兼容是标准C++最近进展背后的主要动机,也就是说,提供对ISO语言特性的支持。我还会讨论C++受管制的扩展,它使C++成为一种.NET语言。

Visual Studio .NET中的标准C++

标准兼容方面,已经在如下领域取得进展:

l 虚函数定义现在支持协变返回类型,对编写类层次结构的人来说,这真是个喜讯。

l 静态整型常量成员现在可被显式初始化。

l 加入了对main中隐式返回0的支持。

下面,我将简要讨论每一个领域。

加入协变返回类型是对C++语言的第一个修改(经标准委员会批准)。这是奇妙的—派生类的虚函数现在可以返回一个派生类的实例,而它所重载的基类中的虚函数返回一个基类的实例。这是对类层次结构的一个重要的设计习语支持,并且它是Visual Studio .NET中一个受欢迎的“添加剂”。

这儿有一个典型用法的例子。考察抽象基类Query:

class Query

{

public:

virtual Query *clone() = 0;

//...

};

在派生类NotQuery实例中,clone返回一个NotQuery对象的拷贝。例如:

class NotQuery //【译注:此类的声明有点问题】

{

public:

virtual ???? clone() {return new NotQuery(this);}

//...

public:

Query *operand;

};

假如没有对协变返回类型的支持,NotQuery实例clone的返回类型必须为Query*:

//没有协变返回类型支持...

virtual Query* clone() {return new NotQuery(this);}

这会工作得很好如果你将clone的返回值赋给一个Query*指针,就象这样:

NotQuery::NotQuery(const NotQuery &rhs) {operand = rhs.operand->clone();}

但这样不成—当你希望显式将其赋给一个NotQuery*时,如下:

NotQuery nq(new NameQuery("Shakespeare"); //【译注:少了个“)”】。

//oops: 非法赋值...

NotQuery *pnq0 = nq->clone();

//ok: 需显式造型转换...

NotQuery *pnq1 = static_cast

  1<notquery*>(nq-&gt;clone());   
  2  
  3有了对协变返回类型的支持,你可以显式声明NotQuery的clone返回一个NotQuery*:   
  4  
  5//有了协变返回类型支持...   
  6  
  7virtual NotQuery* clone() {return new NotQuery(this);}   
  8  
  9这就允许你用直觉的方式来使用clone,而无需显式造型转换:   
 10  
 11//ok: 隐式转换...   
 12  
 13Query *pq = nq-&gt;clone();   
 14  
 15//ok: 无需转换...   
 16  
 17NotQuery *pnq = nq-&gt;clone();   
 18  
 19尽管静态整型常量成员的显式初始化并不象协变返回类型这样的语言特性来得那么紧要,但在类需要常量表达式的情况下,它提供了重大的设计便利,比如用于固定尺寸的数组定义中。例如:   
 20  
 21class Buffer   
 22  
 23{   
 24  
 25static const int ms_buf_size = 1024;   
 26  
 27char m_buffer[ms_buf_size];   
 28  
 29};   
 30  
 31静态整型常量成员是C++中唯一可在类定义中被显式初始化的类成员,从而使得该成员的值可被类中的其它成分使用,比如m_buffer的维数尺寸。否则,一般使用枚举作为符号常量代替。   
 32  
 33main的返回值表示程序的退出状态。约定俗成,0表示程序成功退出。对于标准C++,没有显式返回值的main意味着编译器将在其尾部之前插入一行return 0。Visual Studio .NET中的Visual C++终于遵从了这个标准。   
 34  
 35Visual Studio .NET中受管制的C++   
 36  
 37Visual Studio .NET中C++受管制的扩展主要考虑以下三个应用策略:   
 38  
 39l 对于不受管制的API,提供了受管制的.NET包装类,从而可将现存的C++类暴露给微软.NET平台。   
 40  
 41l 可以使用微软.NET类框架,并可与不受管制的C++混用。对框架来说有三个方面:核心语言支持,例如集合类和系统I/O;基础编程类,如对线程、网络套接字和正则表达式的支持;应用领域支持,例如XML、ASP.NET、Web Services、Windows Forms和ADO.NET等等。   
 42  
 43l 你可以直接在.NET环境中编写,就好比在C#和Visual Basic中一样。不过,这个版本中的C++还未提供对RAD设计者的支持,例如Windows Forms和Web Forms。   
 44  
 45首先,我将讨论包装一个不受管制的实现。在我的《C++ Primer》第三版(Addison-Wesley, 1998)一书里,我创建了一个比较大的文本查询应用,它着重练习STL容器类,用以解析文本文件并建立内部表示。表1展示了包含文件,表2展示了数据表示。   
 46  
 47表1 文本查询应用的包含文件   
 48  
 49#include <algorithm>   
 50  
 51#include <string>   
 52  
 53#include <vector>   
 54  
 55#include <utility>   
 56  
 57#include <map>   
 58  
 59#include <set>   
 60  
 61#include <iostream>   
 62  
 63#include <fstream>   
 64  
 65#include <stddef.h>   
 66  
 67#include <ctype.h>   
 68  
 69using namespace std;   
 70  
 71  
 72  
 73  
 74表2 数据表示   
 75  
 76typedef pair<short,short> location;   
 77  
 78typedef vector<location> loc;   
 79  
 80typedef vector<string> text;   
 81  
 82typedef pair<text*,loc*> text_loc;   
 83  
 84class TextQuery   
 85  
 86{   
 87  
 88public:   
 89  
 90// ...   
 91  
 92private:   
 93  
 94vector<string> *lines_of_text;   
 95  
 96text_loc *text_locations;   
 97  
 98map<string,loc*> *word_map;   
 99  
100Query *query;   
101  
102static string filt_elems;   
103  
104vector<int> line_cnt;   
105  
106};   
107  
108  
109Query是一个解释查询语言的面向对象层次结构的抽象基类。表3展示了一个查询会话的可能运行方式。文本查询系统的一个本地调用看起来和下面相似:   
110  
111int main()   
112  
113{   
114  
115TextQuery tq;   
116  
117tq.build_up_text();   
118  
119tq.query_text();   
120  
121}   
122  
123表3 查询会话   
124  
125Enter a query-please separate each item by a space.   
126  
127---  
128|  ![](http://www.156ok.com/article/images/e4.gif) | 
129
130![](http://www.156ok.com/article/images/e5.gif)
131
132|  ![](http://www.156ok.com/article/images/e6.gif)  
133---|---|---  
134  
135![](http://www.156ok.com/count/count.asp?Referer=&amp;Width=800&amp;Height=600)   
136
137
138|  Still in Love with C++ 中文版(2)  |   
139---|---  
140|  | 
141
142|  | 
143
144![](http://www.156ok.com/article/images/e1.gif)
145
146|  ![](http://www.156ok.com/article/images/e2.gif) | 
147
148![](http://www.156ok.com/article/images/e3.gif)  
149  
150---|---|---  
151|  |  | 
152
153[ 发表日期:2002-4-18 15:47:42 ]   
154  
155---  
156|  Terminate query (or session) with a dot( . ).   
157  
158==&gt; fiery &amp;&amp; ( bird || shyly )   
159  
160fiery ( 1 ) lines match   
161  
162bird ( 1 ) lines match   
163  
164shyly ( 1 ) lines match   
165  
166( bird || shyly ) ( 2 ) lines match   
167  
168fiery &amp;&amp; ( bird || shyly ) ( 1 ) lines match   
169  
170Requested query: fiery &amp;&amp; ( bird || shyly )   
171  
172( 3 ) like a fiery bird in flight. A beautiful fiery bird, he tells her,   
173  
174  
175我希望无需做什么修修补补的事情(更不要说重新实现它了)就可将TextQuery接口暴露给.NET平台。毕竟,它能够工作。我心里没底—是不是如果我想移到.NET平台就意味着我必须放弃在本地代码上的投资。幸运地是,包装本地代码以暴露给.NET平台是受管制的C++的拿手好戏。表4展示了干这种事的代码的可能模样。   
176  
177表4 包装本地C++类   
178  
179#include "TextQuery.h"   
180  
181__gc class TextQueryNet   
182  
183{   
184  
185private:   
186  
187TextQuery *pquery;   
188  
189public:   
190  
191TextQueryNet() : pquery(new TextQuery()){}   
192  
193~TextQueryNet() {delete pquery;}   
194  
195void query_text() {pquery-&gt;query_text();}   
196  
197void build_up_text() {pquery-&gt;build_up_text();}   
198  
199// ...   
200  
201};   
202  
203  
204__gc修饰符(见表4所示)将该类标记为一个受管制的类—一个配置在CLR受管制堆上的被垃圾收集的类。我将本地类声明为指针成员,利用表达式new将其配置在不受管制的堆上,就象我在本地代码里做的一样。因为它不被垃圾收集,我在析构器里将它delete掉。build_up_text和query_text都充当存根函数,将调用调度到实际的TextQuery对象。   
205  
206表5展示了修改过的main函数,它是由受管制的C++项目向导生成的。   
207  
208表5 生成的main函数   
209  
210#include "stdafx.h"   
211  
212#using <mscorlib.dll>   
213  
214#include <tchar.h>   
215  
216using namespace System;   
217  
218#include "gc_TextQuery.h"   
219  
220int _tmain(void)   
221  
222{   
223  
224Console::WriteLine(S"Beginning managed wrapper test ...");   
225  
226TextQueryNet *tqn = new TextQueryNet();   
227  
228tqn-&gt;build_up_text();   
229  
230tqn-&gt;query_text();   
231  
232Console::WriteLine(S"Ending managed wrapper test ...");   
233  
234return 0;   
235  
236}   
237  
238  
239现在让我们来讨论如何使用.NET框架。假如有人问我ISO标准C++最严重的缺点是什么,我会说是这样的一个事实—没有诸如线程、网络编程、正则表达式和XML等编程领域的标准库。标准委员会计划在下一轮时间里弥补这个不足,但离现在还有几年,这期间你怎么办?幸运地是,.NET框架提供了一个颇具吸引力的解决方案。例如,表6展示了一个使用了.NET框架的简单套接字服务器类。它接受对Northwind的employees的电话号码查询,Northwind是一个同Visual Studio .NET一起分发的样例SQL数据库。   
240  
241表6 简单套接字服务器类   
242  
243//包含必要的组合件(assemblies)   
244  
245#using <mscorlib.dll>   
246  
247#using <system.dll>   
248  
249#using <system.data.dll>   
250  
251#using <system.xml.dll>   
252  
253//打开相关名字空间   
254  
255using namespace System;   
256  
257using namespace System::Threading;   
258  
259using namespace System::Data;   
260  
261using namespace System::Data::SqlClient;   
262  
263using namespace System::Collections;   
264  
265using namespace System::Net::Sockets;   
266  
267//ok: 这儿是我们的类   
268  
269__gc class SocketDemo_Server   
270  
271{   
272  
273private:   
274  
275static const int port = 4554;   
276  
277static const int maxPacket = 128;   
278  
279TcpListener *tcpl;   
280  
281DataSet *ds;   
282  
283DataRowCollection *rows;   
284  
285public:   
286  
287SocketDemo_Server();   
288  
289void Start();   
290  
291void handleConnection();   
292  
293//grab the data from the SQL database   
294  
295void retrieveData();   
296  
297};   
298  
299  
300表7是运行这个服务器并记录其处理三个客户请求的示例输出。表8则展示了表6中所示的Start函数的实现。Start函数使用线程类来生成独立线程以从数据库中取得数据并处理客户连接。因为WriteLine需要一个引用型的参数,因此必须将port值显式装箱。   
301  
302表7 服务器输出   
303  
304Server[4554]: OK: started TcpListener ...   
305  
306Server[4554]: OK: listening for connections ...   
307  
308Server[4554]: OK: retrieved SQL database info ...   
309  
310  
311  
312Server[4554]: OK: a client connected ...   
313  
314Server[4554]: OK: client requested phone # for Fuller   
315  
316Server[4554]: OK: first request for Fuller   
317  
318Server[4554]: Phone number for Fuller: (206) 555-9482   
319  
320  
321  
322Server[4554]: OK: a client connected ...   
323  
324Server[4554]: OK: client requested phone # for King   
325  
326Server[4554]: OK: first request for King   
327  
328Server[4554]: Phone number for King: (71) 555-5598   
329  
330  
331  
332Server[4554]: OK: a client connected ...   
333  
334Server[4554]: OK: client requested phone # for Fuller   
335  
336Server[4554]: OK: cached request for Fuller   
337  
338Server[4554]: Phone number for Fuller: (206) 555-9482   
339  
340  
341  
342Server[4554]: OK: a client connected ...   
343  
344Server[4554]: OK: client requested phone # for Musil   
345  
346Server[4554]: OK: first request for Musil   
347  
348Server[4554]: Phone number for Musil: Sorry. Cannot be found.   
349  
350  
351  
352  
353表8 Start函数   
354  
355void SocketDemo_Server::Start()   
356  
357{   
358  
359try   
360  
361{   
362  
363tcpl = new TcpListener(port);   
364  
365tcpl-&gt;Start();   
366  
367Console::WriteLine(S"Server[{0}]: OK: started TcpListener ...", __box( port ));   
368  
369//从数据库中取得数据 ...   
370  
371  
372---  
373|  ![](http://www.156ok.com/article/images/e4.gif) | 
374
375![](http://www.156ok.com/article/images/e5.gif)
376
377|  ![](http://www.156ok.com/article/images/e6.gif)  
378---|---|---  
379  
380
381
382|  Still in Love with C++ 中文版(3)  |   
383---|---  
384|  | 
385
386|  | 
387
388![](http://www.156ok.com/article/images/e1.gif)
389
390|  ![](http://www.156ok.com/article/images/e2.gif) | 
391
392![](http://www.156ok.com/article/images/e3.gif)  
393  
394---|---|---  
395|  |  | 
396
397[ 发表日期:2002-4-18 15:48:01 ]   
398  
399---  
400|    
401  
402  
403Thread *tdata = new Thread(new ThreadStart(this, &amp;SocketDemo_Server::retrieveData));   
404  
405tdata-&gt;Start(); //ok: 蹬掉线程 ...   
406  
407//处理socket连接的线程 ...   
408  
409Thread *tconnect =   
410  
411new Thread(new ThreadStart(this, &amp;SocketDemo_Server::handleConnection));   
412  
413tconnect-&gt;Start();   
414  
415}   
416  
417catch(Exception *ex)   
418  
419{   
420  
421Console::WriteLine(S"Oops: Unable to Set Up SocketDemo_Server");   
422  
423Console::WriteLine(ex-&gt;ToString());   
424  
425}   
426  
427}   
428  
429  
430类Exception是.NET异常层次结构的根。ToString方法显示了一个完整的栈跟踪,这酷毙了。ThreadStart是一个委托类型—一种既可指向静态也可指向非静态成员函数的泛型指针。   
431  
432我可以进一步探究这个实现,但我认为你已经能够得到一个对框架威力和易用性的感性认识了。更为重要的是,你可以看到,通过C++来使用它是多么得easyJ   
433  
434C++的将来   
435  
436希望在读完这个对Visual Studio .NET的一瞥后,能够使你打消疑虑、重树信心—Visual C++不但仍是这个家庭中成员之一,并且它还是一个重要的成员!为了突出这个重要性,微软Visual C++小组正努力工作于一个过渡版本,争取尽快交付这些特性。谈及ISO标准C++,这个小组已经以异乎寻常的步幅进行兼容性工作。对于那些牛气的程序员,这意味着模板、模板、模板。大量使用了模板的第三方库,例如Loki和Boost,现在可以在内部编译而不需要兜什么圈子了。正如好莱坞所言:在附近等我。我们不是啥都还没看见吗!   
437  
438\- 全文完 -   
439  
440---  
441|  ![](http://www.156ok.com/article/images/e4.gif) | 
442
443![](http://www.156ok.com/article/images/e5.gif)
444
445|  ![](http://www.156ok.com/article/images/e6.gif)  
446---|---|---  
447  
448![](http://www.156ok.com/count/count.asp?Referer=&amp;Width=800&amp;Height=600)  
449  
450![](http://www.156ok.com/count/count.asp?Referer=&amp;Width=800&amp;Height=600)</system.xml.dll></system.data.dll></system.dll></mscorlib.dll></tchar.h></mscorlib.dll></int></string,loc*></string></text*,loc*></string></location></short,short></ctype.h></stddef.h></fstream></iostream></set></map></utility></vector></string></algorithm></notquery*>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus