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


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

e1.gif

e2.gif

e3.gif

[  發表日期: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<NotQuery*>(nq->clone());

有了對協變返回類型的支持,你可以顯式聲明NotQuery的clone返回一個NotQuery*:

//有了協變返回類型支持...

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

這就允許你用直覺的方式來使用clone,而無需顯式造型轉換:

//ok: 隱式轉換...

Query *pq = nq->clone();

//ok: 無需轉換...

NotQuery *pnq = nq->clone();

儘管靜態整型常量成員的顯式初始化並不象協變返回類型這樣的語言特性來得那麼緊要,但在類需要常量表達式的情況下,它提供了重大的設計便利,比如用於固定尺寸的數組定義中。例如:

class Buffer

{

   static const int ms_buf_size = 1024;

   char m_buffer[ms_buf_size];

};

靜態整型常量成員是C++中唯一可在類定義中被顯式初始化的類成員,從而使得該成員的值可被類中的其它成分使用,比如m_buffer的維數尺寸。否則,一般使用枚舉作爲符號常量代替。

main的返回值表示程序的退出狀態。約定俗成,0表示程序成功退出。對於標準C++,沒有顯式返回值的main意味着編譯器將在其尾部之前插入一行return 0。Visual Studio .NET中的Visual C++終於遵從了這個標準。

Visual Studio .NET中受管制的C++

Visual Studio .NET中C++受管制的擴展主要考慮以下三個應用策略:

l          對於不受管制的API,提供了受管制的.NET包裝類,從而可將現存的C++類暴露給微軟.NET平臺。

l          可以使用微軟.NET類框架,並可與不受管制的C++混用。對框架來說有三個方面:核心語言支持,例如集合類和系統I/O;基礎編程類,如對線程、網絡套接字和正則表達式的支持;應用領域支持,例如XML、ASP.NET、Web Services、Windows Forms和ADO.NET等等。

l          你可以直接在.NET環境中編寫,就好比在C#和Visual Basic中一樣。不過,這個版本中的C++還未提供對RAD設計者的支持,例如Windows Forms和Web Forms。

首先,我將討論包裝一個不受管制的實現。在我的《C++ Primer》第三版(Addison-Wesley, 1998)一書裏,我創建了一個比較大的文本查詢應用,它着重練習STL容器類,用以解析文本文件並建立內部表示。表1展示了包含文件,表2展示了數據表示。

表1 文本查詢應用的包含文件

#include <algorithm>

#include <string>

#include <vector>

#include <utility>

#include <map>

#include <set>

#include <iostream>

#include <fstream>

#include <stddef.h>

#include <ctype.h>

using namespace std;




表2 數據表示

typedef pair<short,short> location;

typedef vector<location>  loc;

typedef vector<string>    text;

typedef pair<text*,loc*>  text_loc;

class TextQuery 

{

public:

   // ...

private:

   vector<string>     *lines_of_text;

   text_loc           *text_locations;

   map<string,loc*>   *word_map;

   Query              *query;

   static string      filt_elems;

   vector<int>        line_cnt;

};


Query是一個解釋查詢語言的面向對象層次結構的抽象基類。表3展示了一個查詢會話的可能運行方式。文本查詢系統的一個本地調用看起來和下面相似:

int main()

{

TextQuery tq;

tq.build_up_text();

tq.query_text();

}

表3 查詢會話

Enter a query-please separate each item by a space. 
e4.gif

e5.gif

e6.gif
<script language=JavaScript> document.write(""); document.write("count.asp?Referer=&Width="+escape(screen.width)+"&Height="+escape(screen.height)+""); document.write(""); </script> count.asp?Referer=&Width=800&Height=600
  Still in Love with C++ 中文版(2)

e1.gif

e2.gif

e3.gif

[  發表日期:2002-4-18 15:47:42    ]    

Terminate query (or session) with a dot( . ).

==> fiery && ( bird || shyly )

       fiery ( 1 ) lines match

       bird ( 1 ) lines match

       shyly ( 1 ) lines match

        ( bird || shyly )  ( 2 ) lines match

       fiery &&  ( bird || shyly )  ( 1 ) lines match

Requested query: fiery &&  ( bird || shyly ) 

( 3 ) like a fiery bird in flight. A beautiful fiery bird, he tells her,


我希望無需做什麼修修補補的事情(更不要說重新實現它了)就可將TextQuery接口暴露給.NET平臺。畢竟,它能夠工作。我心裏沒底—是不是如果我想移到.NET平臺就意味着我必須放棄在本地代碼上的投資。幸運地是,包裝本地代碼以暴露給.NET平臺是受管制的C++的拿手好戲。表4展示了幹這種事的代碼的可能模樣。

表4 包裝本地C++類

#include "TextQuery.h"

__gc class TextQueryNet 

{

private:

   TextQuery *pquery;    

public:

   TextQueryNet() : pquery(new TextQuery()){}

  ~TextQueryNet() {delete pquery;}

   void query_text() {pquery->query_text();}

   void build_up_text() {pquery->build_up_text();}

  // ...

};


__gc修飾符(見表4所示)將該類標記爲一個受管制的類—一個配置在CLR受管制堆上的被垃圾收集的類。我將本地類聲明爲指針成員,利用表達式new將其配置在不受管制的堆上,就象我在本地代碼裏做的一樣。因爲它不被垃圾收集,我在析構器裏將它delete掉。build_up_text和query_text都充當存根函數,將調用調度到實際的TextQuery對象。

表5展示了修改過的main函數,它是由受管制的C++項目嚮導生成的。

表5 生成的main函數

#include "stdafx.h"

#using <mscorlib.dll>

#include <tchar.h>

using namespace System;

#include "gc_TextQuery.h"

int _tmain(void) 

{

    Console::WriteLine(S"Beginning managed wrapper test ...");

    TextQueryNet *tqn = new TextQueryNet();

    tqn->build_up_text();

    tqn->query_text();

Console::WriteLine(S"Ending managed wrapper test ...");

return 0;

}


現在讓我們來討論如何使用.NET框架。假如有人問我ISO標準C++最嚴重的缺點是什麼,我會說是這樣的一個事實—沒有諸如線程、網絡編程、正則表達式和XML等編程領域的標準庫。標準委員會計劃在下一輪時間裏彌補這個不足,但離現在還有幾年,這期間你怎麼辦?幸運地是,.NET框架提供了一個頗具吸引力的解決方案。例如,表6展示了一個使用了.NET框架的簡單套接字服務器類。它接受對Northwind的employees的電話號碼查詢,Northwind是一個同Visual Studio .NET一起分發的樣例SQL數據庫。

表6 簡單套接字服務器類

//包含必要的組合件(assemblies)

#using <mscorlib.dll>

#using <System.dll>

#using <System.Data.dll>

#using <System.Xml.dll>

//打開相關名字空間

using namespace System;

using namespace System::Threading;

using namespace System::Data;

using namespace System::Data::SqlClient;

using namespace System::Collections;

using namespace System::Net::Sockets;    

//ok: 這兒是我們的類

__gc class SocketDemo_Server 

{

private:

   static const int port = 4554;

   static const int maxPacket = 128;

   TcpListener       *tcpl;

   DataSet           *ds;

   DataRowCollection *rows;

public:

   SocketDemo_Server();

   void Start();

void handleConnection();

  //grab the data from the SQL database

   void retrieveData();

};


表7是運行這個服務器並記錄其處理三個客戶請求的示例輸出。表8則展示了表6中所示的Start函數的實現。Start函數使用線程類來生成獨立線程以從數據庫中取得數據並處理客戶連接。因爲WriteLine需要一個引用型的參數,因此必須將port值顯式裝箱。

表7 服務器輸出

Server[4554]: OK: started TcpListener ...

Server[4554]: OK: listening for connections ...

Server[4554]: OK: retrieved SQL database info ...



Server[4554]: OK: a client connected ...

Server[4554]: OK: client requested phone # for Fuller

Server[4554]: OK: first request for Fuller

Server[4554]: Phone number for Fuller: (206) 555-9482



Server[4554]: OK: a client connected ...

Server[4554]: OK: client requested phone # for King

Server[4554]: OK: first request for King

Server[4554]: Phone number for King: (71) 555-5598



Server[4554]: OK: a client connected ...

Server[4554]: OK: client requested phone # for Fuller

Server[4554]: OK: cached request for Fuller

Server[4554]: Phone number for Fuller: (206) 555-9482



Server[4554]: OK: a client connected ...

Server[4554]: OK: client requested phone # for Musil

Server[4554]: OK: first request for Musil

Server[4554]: Phone number for Musil:  Sorry. Cannot be found.




表8 Start函數

void SocketDemo_Server::Start()

{

   try 

   {

       tcpl = new TcpListener(port);

       tcpl->Start();

       Console::WriteLine(S"Server[{0}]: OK: started TcpListener ...", __box( port ));

       //從數據庫中取得數據 ... 

e4.gif

e5.gif

e6.gif

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

e1.gif

e2.gif

e3.gif

[  發表日期:2002-4-18 15:48:01    ]    




       Thread *tdata = new Thread(new ThreadStart(this, &SocketDemo_Server::retrieveData));

       tdata->Start(); //ok: 蹬掉線程 ...                

       //處理socket連接的線程 ...

       Thread *tconnect = 

new Thread(new ThreadStart(this, &SocketDemo_Server::handleConnection));

       tconnect->Start();

   }

   catch(Exception *ex)

   {

       Console::WriteLine(S"Oops: Unable to Set Up SocketDemo_Server");

       Console::WriteLine(ex->ToString());

   }

}


類Exception是.NET異常層次結構的根。ToString方法顯示了一個完整的棧跟蹤,這酷斃了。ThreadStart是一個委託類型—一種既可指向靜態也可指向非靜態成員函數的泛型指針。

我可以進一步探究這個實現,但我認爲你已經能夠得到一個對框架威力和易用性的感性認識了。更爲重要的是,你可以看到,通過C++來使用它是多麼得easyJ

C++的將來

希望在讀完這個對Visual Studio .NET的一瞥後,能夠使你打消疑慮、重樹信心—Visual C++不但仍是這個家庭中成員之一,並且它還是一個重要的成員!爲了突出這個重要性,微軟Visual C++小組正努力工作於一個過渡版本,爭取儘快交付這些特性。談及ISO標準C++,這個小組已經以異乎尋常的步幅進行兼容性工作。對於那些牛氣的程序員,這意味着模板、模板、模板。大量使用了模板的第三方庫,例如Loki和Boost,現在可以在內部編譯而不需要兜什麼圈子了。正如好萊塢所言:在附近等我。我們不是啥都還沒看見嗎! 

- 全文完 - 
e4.gif

e5.gif

e6.gif
<script language=JavaScript> document.write(""); document.write("count.asp?Referer=&Width="+escape(screen.width)+"&Height="+escape(screen.height)+""); document.write(""); </script> count.asp?Referer=&Width=800&Height=600
<script language=JavaScript> document.write(""); document.write("count.asp?Referer=&Width="+escape(screen.width)+"&Height="+escape(screen.height)+""); document.write(""); </script> count.asp?Referer=&Width=800&Height=600
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章