情有獨鐘C++:Visual C++ .NET編譯器的新特性
發表于:2007-07-01來源:作者:點擊數:
標簽:
廣州市天河村迎福七巷9號 王凌峰 本文假定您已熟悉 C++ 。 摘要 老資格的 C++ 程序員 們都想知道:他們賴以生存的 C++ 語言在 C# 和微軟的 .NET 的沖擊下何去何從?本文將對 .NET 世界中的 C++ 進行簡要描述。在 .NET 中,C++ 分裂為兩大陣營:受管代碼 (Man
廣州市天河村迎福七巷9號 王凌峰
老資格的 C++ 程序員們都想知道:他們賴以生存的 C++ 語言在 C# 和微軟的 .NET 的沖擊下何去何從?本文將對 .NET 世界中的 C++ 進行簡要描述。在 .NET 中,C++ 分裂為兩大陣營:受管代碼 (Managed Code) 和非受管代碼 (Unmanaged Code)。非受管代碼不使用通用語言運行時環境 (CLR) ,而受管代碼則用到了 Managed Extensions for C++ 。本文將對兩者分別討論。 |
C++ 社區給人的感覺就象一個大家庭:初生的小寶寶總是倍受呵護,而年長的孩子卻是無人關心。如此被忽視,又怎能教人不心痛?事實上,技術領域的情況比這更糟:技術的革新實在太快,人們疲于奔命卻無可奈何,否則連飯碗都保不住了。 |
如今,微軟公司的新產品 .NET 框架被炒得沸沸揚揚——的確很不錯;眾人還對所謂的 C# 語言連聲喝彩。作為 C++ 的程序員,心里卻很不是滋味:我是不是也要改行學 C# 了?因為人們只有在比較 C# 與 C++ 才會偶爾提及 C++ 了。 |
C++ 程序員要過時了嗎?絕不!本文先簡要描述 Visual Studio .NET 為 C++ 引入的新特性,然后再介紹微軟公司對新版 C++ 的計劃。首先從兩個方面來談談 Visual Studio .NET 里的 C++ :標準的 C++ 和 Managed Extensions for C++ 。 |
對標準 C++ 的擴充主要是為了保持兼容性;也就是說,為了保持國際標準化組織 (ISO) 所規定的 C++ 語言特性。而 Managed Extensions for C++ 則是為了把 C++ 納入 .NET 框架之中。 |
Visual Studio .NET 下的標準 C++ |
為了保持對 ISO 標準的兼容性,標準 C++ 作了如下改進: |
(1) 虛擬函數的返回值現在支持共變 (covariant) 類型了,這是類層次體系上的一個重大改進; |
(2) 支持對靜態整數類型常量( static const integer )成員的顯式初始化; |
第一項是增加 covariant 返回類型,它已經由標準委員會核準。也就是說,如果基類的某個虛擬函數返回值是該類本身的實例,那么它在派生類中被重載后也能返回此派生類本身的實例。在類層次體系中,這是一種很重要的設計模式;在 Visual Studio .NET 中,它也大受歡迎。 |
virtual Query *clone() = 0; |
如果想要其派生類實例 NotQuery 的 clone 函數返回一個 NotQuery 對象,即: |
{ return new NotQuery( this ); } |
當沒有 covariant 返回類型支持時,clone 的返回類型必然是 Query* : |
// without covariant return type support |
此時如果把 clone 的返回值分配給 Query* 類型的指針,那么一切工作正常: |
NotQuery::NotQuery( const NotQuery &rhs ) |
{ operand = rhs.operand->clone(); } |
但是如果要分配給 NotQuery* 類型的指針呢? |
NotQuery nq( new NameQuery( "Shakespeare" ); |
// oops: illegal assignment ... |
NotQuery *pnq0 = nq->clone(); |
// ok: required explicit cast ... |
static_cast<NotQuery*>(nq->clone()); |
現在有了 covariant 返回類型,你就可以顯式返回 NotQuery* 類型了: |
// with covariant return type support |
virtual NotQuery* clone() |
{ return new NotQuery( this ); } |
于是,clone 的返回類型變得更直觀,不再需要強制類型轉換了: |
// ok: implicit conversion ... ; |
// ok: no conversion necessary ... ; |
NotQuery *pnq = nq->clone(); |
第二項是靜態整數常量類型成員的顯式初始化,它對 C++ 的影響就沒有 covariant 返回類型那么大了。它僅僅給使用常量表達式(如:定長數組)的類的設計帶來了方便。例如: |
static const int ms_buf_size = 1024; |
char m_buffer[ ms_buf_size ]; |
static const int 類型成員被初始化以后,就可以為本類的其它成員所用,例如設置 m_buffer 的大小,而不用象以前那樣,使用枚舉變量了。 |
第三項是主函數的默認返回值為 0 。主函數的返回值代表程序的退出狀態。習慣上,返回 0 代表程序正常結束。在標準 C++ 里,如果沒有顯性指定返回值,編譯器就會自動地在主函數末尾插入一行: |
進入 Visual Studio .NET 時代,Visual C++ 終于支持它了! |
Visual Studio .NET 下的受管 C++ |
Visual Studio .NET 的 Managed Extensions for C++ 提供了三種基本的應用策略: |
(1) 為原有的 API 提供 .NET“包裝”,把 C++ 類統統移植到 .NET 平臺中。 |
(2) 混用 C++ 類與微軟 .NET 的三種框架類: |
基礎程序類, 例如:線程支持,網絡套接字和正規表達式; |
應用領域支持,例如:XML、ASP.NET、Web 服務、Windows 窗體、ADO.NET,等等。 |
(3) 象 C# 和 Visual Basic 那樣直接操作 .NET 環境。然而,目前尚無相應的快速開發工具(RAD)用于 Windows 窗體和 Web 表單的可視化編程。 |
先來看看第一種方案:用 .NET 包裝原有的代碼。在我的 C++ Primer 一書 (Addison-Wesley,1998) 中,我示范了一個相當大的文本查詢系統,其中大量使用了 STL 容器類用于解析文本文件和建立數據結構。它的引用文件內容如下: |
typedef pair<short,short> location; |
typedef vector<location> loc; |
typedef vector<string> text; |
typedef pair<text*,loc*> text_loc; |
vector<string> *lines_of_text; |
text_loc *text_locations; |
map<string,loc*> *word_map; |
static string filt_elems; |
Query 類是用于解釋查詢語言的抽象基類,下面演示了它的功能: |
Enter a query-please separate each item by a space. |
Terminate query (or session) with a dot( . ). |
==> fiery && ( bird || shyly ) |
( 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 平臺必須完全重寫代碼的話,我可能就不會那么做了。幸好受管 C++ 提供了包裝移植的方式。下面是包裝的一種方式: |
TextQueryNet() : pquery( new TextQuery()){} |
~TextQueryNet(){ delete pquery; } |
void query_text() { pquery->query_text(); } |
void build_up_text() { pquery->build_up_text();} |
關鍵字 __gc 用于指定受管類,它們接受垃圾回收器管理,占用通用語言運行時 (CLR) 受管堆。與原先的代碼一樣,我把原生類 (native class) 宣告為指針類型成員,使用關鍵字 new 在非受管堆分配空間,由于它不支持垃圾回收管理,故在析構函數中使用 delete 回收空間。build_up_text 和 query_text 都是殘余 (stub) 函數,用于呼叫真正的 TextQuery 對象。 |
作為對照,我們來看看由 Managed C++ Project Wizard 自動生成的主函數: |
#include "gc_TextQuery.h" |
S"Beginning managed wrapper test ..." ); |
TextQueryNet *tqn = new TextQueryNet(); |
S"Ending managed wrapper test ..." ); |
現在,我們來討論 .NET 框架的利用。如果有人問我,ISO 標準 C++ 最大的缺點是什么?我的答案是:它沒有提供支持諸如線程、網絡編程、正規表達式以及XML 等應用領域的標準庫。標準委員會已將其列入下一輪工作計劃,但那是一年以后的事了。目前我們怎么辦?其實,.NET 框架已經提供了引人入勝的解決方案。下面演示一個簡單的 Socket 服務器類,它利用了 .NET 框架: |
// include the necessary assemblies |
// open up the associated namespaces |
using namespace System::Threading; |
using namespace System::Data; |
using namespace System::Data::SqlClient; |
using namespace System::Collections; |
using namespace System::Net::Sockets; |
__gc class SocketDemo_Server |
static const int port = 4554; |
static const int maxPacket = 128; |
// grab the data from the SQL database |
它的功能是查詢 Northwind 公司職員的電話號碼;其中的 SQL 數據庫可以在 Visual Studio .NET 發行版本中找到。 |
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. |
tcpl = new TcpListener( port ); |
S"Server[{0}]: OK: started TcpListener ... ", |
// retrieve the data from the data base ... |
new Thread( new ThreadStart( this, |
&SocketDemo_Server::retrieveData )); |
tdata->Start(); // ok: kick off the thread ... |
// thread to handle a socket connection ... |
new Thread( new ThreadStart( this, |
&SocketDemo_Server::handleConnection )); |
S"Oops: Unable to Set Up SocketDemo_Server" ); |
Console::WriteLine( ex->ToString() ); |
我們看到,Start 函數利用 Thread 類生成許多線程,分別響應每位客戶的請求。此外,由于 WriteLine 函數需要引用參數,故我們使用了 __box( port ) 。 |
再有,Exception 類是一切 .NET 的異常處理類的基類;ToString 方法用于顯示整個堆棧 (酷極了); ThreadStart 是 代表 (delegate) 類型 —— 既能指向靜態成員函數,也能指向動態成員函數的通用指針類型。 |
當然,它的實現代碼還可以進一步完善,但是我想您現在已經體會到 .NET 框架的強大和易用性了。更重要的是,您已經看到,在 C++ 里面使用它是多么的簡單! |
本文是對 Visual Studio .NET 下的 C++ 的簡單介紹,希望在讀過之后,您會對 Visual C++ 充滿信心,因為它不僅僅是 Visual Studio .NET 中的一份子,而且是其重要的一員。為了體現其重要性,微軟公司 Visual C++ 工作組正在努力工作以爭取盡早完成新一代 Visual C++ 的過渡版本,它將具有本文所述的全部新特性。在 ISO 標準 C++ 的兼容性方面,工作組已經邁出了一大步。對于優秀的程序員來說,它意味著模板,模板,還是模板!諸如 Loki 和 Boost 等大量使用模板的第三方程序庫,正在緊張工作中。正如人們在電影中說的那樣:“瞧著吧,好戲還在后頭呢!” |
原文轉自:http://www.anti-gravitydesign.com