從今日開(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類型。