從今日開(kāi)始,將前期學(xué)習(xí)《Visual C++ 2005入門(mén)經(jīng)典》(Ivor Horton著 清華大學(xué)出版社出版)的相關(guān)筆記整理到隨筆中,希望能和C++/CLI愛(ài)好者分享學(xué)習(xí)過(guò)程中的心得。文中主要內(nèi)容和例子摘自原書(shū)相關(guān)章節(jié),如有侵權(quán),請(qǐng)留言或來(lái)信告知。
相比于ISO/ANSI C++而言,C++/CLI進(jìn)行了大量的擴(kuò)充,并且提供了大量的附加功能。主要包括:
-
在C++/CLI程序中,所有ISO/ANSI基本數(shù)據(jù)類型都可以使用,但在一些特殊的上下文環(huán)境中,它們具有一些額外屬性;
-
在控制臺(tái)程序中,C++/CLI對(duì)鍵盤(pán)和命令行輸出提供了自己的機(jī)制;
-
C++/CLI中引入了safe_cast運(yùn)算符,確保強(qiáng)制類型轉(zhuǎn)換操作能夠生成可檢驗(yàn)的代碼;
-
C++/CLI提供了另外一種基于類的枚舉功能,其靈活性超過(guò)了ISO/ANSI C++中的enum聲明。
一、基本數(shù)據(jù)類型
C++/CLI中包括了所有ISO/ASNI C++中的基本數(shù)據(jù)類型,算術(shù)運(yùn)算也和本地C++完全一樣。除此之外,C++/CLI中還定義了2種整數(shù)類型,如表1所示:
表1:C++/CLI新增基本數(shù)據(jù)類型
類型 | 字節(jié) | 值域 |
long long | 8 | 從-9223372036854775808到9223372036854775807 |
Unsigned long long | 8 | 從0到18446744073709551615 |
指定long long數(shù)據(jù)類型時(shí),需要在整數(shù)數(shù)值后面加LL或小寫(xiě)字母ll,如
longlong big = 123456789LL;
指定unsinged long long類型時(shí),需要在整數(shù)數(shù)值后面加ULL或小寫(xiě)字母ull,如
unsigned long long huge = 123456789LL;
在C++/CLI中,每一個(gè)ISO/ANSI C++基本類型名稱都映射到System命名空間中定義的值類類型。在C++/CLI程序中,ISO/ANSI C++基本類型名稱都是CLI中對(duì)應(yīng)值類類型的簡(jiǎn)略形式。表2給出了基本類型、占用內(nèi)存以及對(duì)應(yīng)的值類類型。
表2:基本類型與CLI值類型
基本類型 |
字節(jié) |
CLI值類類型 |
bool char singed char unsigned char short unsigned short int unsigned int long unsigned long long long unsigned long long float double long double wchar_t |
1 1 1 1 2 2 4 4 4 4 8 8 4 8 8 2 |
System::Boolean System::SByte System::SByte System::Byte System::Int16 System::UInt16 System::Int32 System::UInt32 System::Int32 System::UInt32 System::Int64 System::UInt64 System::Single System::Double System::Double System::Char |
默認(rèn)情況下,char類型被視為singed char,因此其關(guān)聯(lián)的值類類型為System::SByte。如果編譯選項(xiàng)/J,則char 默認(rèn)為unsigned char,此時(shí)關(guān)聯(lián)為System::Byte。System為根命名空間名,C++/CLI的值類類型在這個(gè)空間中定義。此外System空間中還定義了許多其他類型,如表示字符串的String類型、精確存儲(chǔ)的十進(jìn)制小數(shù)類型Decimal等等。
在C++/CLI中,關(guān)聯(lián)的值類類型為基本類型添加了重要的附加功能。編譯器在需要時(shí),將安排原值與關(guān)聯(lián)類型之間的自動(dòng)轉(zhuǎn)換,其中從原值轉(zhuǎn)換為關(guān)聯(lián)類型成為裝箱(boxing),反之稱為拆箱(unboxing)。根據(jù)上下文環(huán)境,這些變量將表現(xiàn)為簡(jiǎn)單的值或者對(duì)象。
由于ISO/ANSI C++基本類型的名稱是C++/CLI程序中值類類型名稱的別名,所以原則上C++/CLI代碼中可用任何一種名稱。
int count = 10;
double value = 2.5;
與下面的代碼是等價(jià)的
System::Int32 count = 10;
System::Double value = 2.5;
上面2種代碼是完全合法的,但應(yīng)盡量使用基本類型名稱,如int和double,而不是System::Int32和System::Double。這是因?yàn)樯厦婷枋龅倪@種映射關(guān)系僅適用于Visual C++ 2005及以上版本的編譯器,其他版本編譯器未必實(shí)現(xiàn)這種映射關(guān)系。
將基本類型轉(zhuǎn)換為值類類型是C++/CLI的一個(gè)重要特征。在ISO/ANSI C++中基本類型與類類型完全不同,而在C++/CLI中,所有數(shù)據(jù)都以類類型的形式存儲(chǔ),包括值類型(存儲(chǔ)在堆棧上)和引用類型(存儲(chǔ)在堆上)2種。
例子:Fruit CLR控制臺(tái)項(xiàng)目
在Visual Studio 2005中創(chuàng)建CLR Console Application項(xiàng)目,輸入名稱Ex2_12,將生成如下文件
// Ex2_12.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
return 0;
}
main函數(shù)后的參數(shù)為命令行參數(shù)。然后按如下方式改寫(xiě)代碼
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開(kāi)始==>> [Ex_12.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex2_12.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
int apples, oranges;
int fruit;
apples = 5;
oranges = 6;
fruit = apples + oranges;
Console::WriteLine(L"\nOranges are not the only fruit ...");
Console::Write(L"- and we have ");
Console::Write(fruit);
Console::Write(L" fruit in all.\n");
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex_12.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
編譯后執(zhí)行得到如下輸出:
Oranges are not the only fruit?-
- and we have 11 fuit in all.
與ISO/ANSI C++版本比較,變量的類型int將成為C++/CLI類型System::Int32。如果用System::Int32替換代碼中的int,然后重新編譯運(yùn)行,結(jié)果將沒(méi)有變化。
WriteLine()函數(shù)為C++/CLI函數(shù),定義在System命名空間的Console類中。Console表示標(biāo)準(zhǔn)輸入輸出流。Write()函數(shù)為該類的另一個(gè)輸出函數(shù),不會(huì)自動(dòng)換行。下面專門(mén)討論C++/CLI的控制臺(tái)輸入輸出。
二、控制臺(tái)輸出
C++/CLI中特有控制臺(tái)格式化輸出功能,例如可用下面的代碼來(lái)輸出字符串與變量混合文本。
Console::WriteLine(L"There are {0} fruit.", fruit);
Console::WriteLine()的第一個(gè)參數(shù)是L”There are {0} fruit.”,其中{0}為格式化占位符,表示在此處插入第二個(gè)參數(shù)的值,如果有更多需要插入的參數(shù),則該參數(shù)對(duì)應(yīng)的占位符編號(hào)繼續(xù)增加:{1}、{2}、{3}…。在第一個(gè)字符串參數(shù)中,編號(hào)的順序可以顛倒,如
Console::WriteLine(L"There are {1} packages weighting {0} pounds", packageWeight, packageCount);
格式化占位符還可以控制顯示的格式,如{1:F2}表示第2個(gè)參數(shù)顯示成有2位小數(shù)的浮點(diǎn)數(shù),冒號(hào)后的為格式規(guī)范
Console::WriteLine(L"There are {0} packages weighting {1:F2} pounds ", packageCount, packageWeight);
輸出為
There are 25 packages weighting 7.50 pounds.
一般說(shuō)來(lái),可以編寫(xiě)格式為{n,w:Axx}的格式規(guī)范,其中n為索引值,用于選擇逗號(hào)后的第幾個(gè)參數(shù); A為單個(gè)字母,表示如何對(duì)變量格式化;xx為1個(gè)或2個(gè)數(shù)字,指定參數(shù)的精度;w為有符號(hào)整數(shù),表示可選的字段寬度范圍,如果w為+,則字段右對(duì)齊,如果w為-,則左對(duì)齊。如果數(shù)值的位置數(shù)小于w指定的位置數(shù),則多出來(lái)的用空格填充,如果數(shù)值的位置數(shù)大于w指定的位置數(shù),則忽略w的限定:
Console::WriteLine(L"Packages: {0,3} Weight: {1,5£oF2} pounds ", packageCount, packageWeight);
輸出為(下劃線表示空格填充位):
Packages: _25 Weight: __7.50 pounds
可選的格式說(shuō)明符如表3所示
表3:格式說(shuō)明符
格式說(shuō)明符 |
說(shuō)明 |
C或c |
把值作為貨幣量輸出 |
D或d |
把整數(shù)作為十進(jìn)制值輸出。如果指定的精度大于位數(shù),則在數(shù)值的坐標(biāo)填充0 |
E或e |
按照科學(xué)技術(shù)法輸出浮點(diǎn)值 |
F或f |
把浮點(diǎn)數(shù)作為±####.##的定點(diǎn)數(shù)輸出 |
G或g |
以最緊湊的形式輸出,取決于是否指定了精度值,如果沒(méi)有則適用默認(rèn)精度 |
N或n |
把值作為定點(diǎn)十進(jìn)制值輸出,必要時(shí)以3位一組用逗號(hào)分隔 |
X或x |
把整數(shù)作為十六進(jìn)制值輸出。根據(jù)X或x,以大寫(xiě)或小寫(xiě)輸出。 |
例子:計(jì)算地毯價(jià)格,演示CLR控制臺(tái)程序的格式化輸出
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開(kāi)始==>> [Ex_13.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex2_13.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
double carpetPriceSqYd = 27.95;
double roomWidth = 13.5; // in feet
double roomLength = 24.75; // in feet
const int feetPerYard = 3;
double roomWidthYard = roomWidth/feetPerYard;
double roomLengthYard = roomLength/feetPerYard;
double carpetPrice = roomWidthYard*roomLengthYard*carpetPriceSqYd;
Console::WriteLine(L"Room is {0:F2} yards by {1:F2} yards",
roomWidthYard, roomLengthYard);
Console::WriteLine(L"Room area is {0:F2} square yards",
roomWidthYard*roomLengthYard);
Console::WriteLine(L"Carpet price is ${0:F2}", carpetPrice);
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex_13.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
三、控制臺(tái)輸入
.Net Framework的控制臺(tái)鍵盤(pán)輸入功能有限,可以適用Console::ReadLine()函數(shù)把整行輸入作為字符串讀取,或者使用Console::Read()讀取單個(gè)字符,還可以適用Console::ReadKey()讀取按鍵。ReadLine()的例子如下:
ReadLine()用于將整行文本存入字符串中,按下Enter鍵時(shí),文本結(jié)束。變量line為String^型,表示String數(shù)據(jù)類型的引用,line為Console::ReadLine()函數(shù)讀入字符串的引用。
String^ line = Console::ReadLine();
Read()用于逐字符的讀入輸入數(shù)據(jù),并將其轉(zhuǎn)換成對(duì)應(yīng)的數(shù)字值。ReadKey的例子如下:
char ch = Console::Read();
ReadKey()用于讀取按鍵值,并返回ConsoleKeyInfo對(duì)象,該為對(duì)象定義在System命名空間中的值類型。參數(shù)true表示按鍵不在命令行上顯示出來(lái),false則表示顯示按鍵回顯。按鍵對(duì)應(yīng)的字符可用ConsoleKeyInfo對(duì)象的KeyChar得到。
ConsoleKeyInfo keyPressed = Console::ReadKey(true);
Console::WriteLine(L"The key press corresponds to the character: {0}", keyPress.KeyChar);
盡管C++/CLI控制臺(tái)程序中不能格式化輸入,但輸入一般都通過(guò)窗口組件得到,因此這僅僅是一個(gè)小缺陷。
四、強(qiáng)制類型轉(zhuǎn)換safe_cast
在CLR環(huán)境中safe_cast用于顯示的強(qiáng)制類型轉(zhuǎn)換。safe_cast用于將一種類型轉(zhuǎn)換為另一種類型,在不成功時(shí)能夠拋出異常,因此在C++/CLI中使用safe_cast是比較好的選擇。其用法和static_cast一樣:
double value1 = 10.5;
double value2 = 15.5;
int whole_number = safe_cast<int>(value1) + safe_cast<int>(value2);
五、枚舉
C++/CLI的枚舉與ISO/ANSI C++有較大的區(qū)別。下例為C++/CLI中的一個(gè)枚舉類型:該語(yǔ)句定義了一個(gè)枚舉類型Suit,該類型的變量只能被賦值枚舉定義中的值,且必須用枚舉類型名稱限定枚舉常數(shù)。
enum class Suit {Clubs, Diamonds, Hearts, Spades};
Suit suit = Suit::Diamonds;
注意class關(guān)鍵字跟在enum之后。說(shuō)明該枚舉類型為C++/CLI,該關(guān)鍵字還表明在定義中規(guī)定的常量: Clubs\Diamonds\Hearts\Spades都是類對(duì)象而非ISO/ANSI C++中的基本類型(整型)值。實(shí)際上,默認(rèn)情況下這些都是Int32類型的對(duì)象。
由于C++/CLI枚舉定義中的變量都是類對(duì)象,因此不能在函數(shù)內(nèi)部定義。
(一)指定枚舉常量的類型
枚舉中常量的類型可以是下表中任一基本類型:
short |
int |
long |
long long |
signed char |
char |
unsigned short |
unsigned int |
unsigned long |
unsigned long long |
unsigned char |
bool |
要指定一個(gè)枚舉常量的類型,可以在枚舉類型名稱之后寫(xiě)入常量類型名稱(要用冒號(hào)隔開(kāi)),下例枚舉類型中的常量為Char類型,對(duì)應(yīng)的基本類型為char。其中第一個(gè)常量默認(rèn)情況下對(duì)應(yīng)于代碼值0,后面的依次遞增。
enum class Face:char { Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King};
(二)指定枚舉常量的值
可以賦予枚舉類型定義中的一個(gè)或全部常數(shù)對(duì)應(yīng)的值,下例使得Ace獲得1,Two獲得2,其余依此類推,直到King=13。
enum class Face:char { Ace=1,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King};
如果想讓Ace獲得最大值,則可以如下定義:
enum class Face:Char { Ace=14,Two=2,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King};
例子:使用枚舉類型
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開(kāi)始==>> [Ex_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex2_14.cpp : main project file.
#include "stdafx.h"
using namespace System;
enum class Suit {Clubs, Diamonds, Hearts, Spades};
int main(array<System::String ^> ^args)
{
Suit suit = Suit::Clubs;
int value = safe_cast<int>(suit);
Console::WriteLine(L"Suit is {0} and the value is {1}", suit, value);
suit = Suit::Diamonds;
value = safe_cast<int>(suit);
Console::WriteLine(L"Suit is {0} and the value is {1}", suit, value);
suit = Suit::Hearts;
value = safe_cast<int>(suit);
Console::WriteLine(L"Suit is {0} and the value is {1}", suit, value);
suit = Suit::Spades;
value = safe_cast<int>(suit);
Console::WriteLine(L"Suit is {0} and the value is {1}", suit, value);
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
該例子的輸出為
Suit is Clubs and the value is 0
Suit is Diamonds and the value is 1
Suit is Hearts and the value is 2
Suit is Spades and the value is 3
例子說(shuō)明
- Suit為枚舉類型,不能在函數(shù)main()內(nèi)部定義,因此只能定義為全局作用域內(nèi)。
- 必須用類型名稱Suit限定枚舉常量,如Suit::Clubs,否則編譯器將無(wú)法識(shí)別。
- 變量suit的值為類對(duì)象,要獲取其值必須顯示的將其轉(zhuǎn)換成int類型。