要學(xué)習(xí)數(shù)組,必須先了解跟蹤句柄。
一、跟蹤句柄
跟蹤句柄類似于本地C++指針,但也有很大區(qū)別。跟蹤句柄確實存儲著某個對象的地址,但當(dāng)CLR壓縮堆過程中改變了該對象的地址,則垃圾回收器自動更新句柄所包含的地址。我們不能像本地指針那樣用跟蹤句柄來執(zhí)行地址的算術(shù)運(yùn)算,也不允許對跟蹤句柄進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
在CLR堆中創(chuàng)建的對象必須被跟蹤句柄引用。所有屬于引用類型的對象都存儲在堆中,因此為引用這些對象所創(chuàng)建的變量都必須是跟蹤句柄。例如,String類型是引用類型,因此引用String對象的變量必須是跟蹤句柄。值類型默認(rèn)分配在堆棧上,但也可以用gcnew操作符將其存儲在堆上。此外必須注意,在堆上分配的變量——其中包括所有CLR引用類型——都不能在全局作用域內(nèi)聲明。
通過在類型名稱后加”^”符號,用于聲明一個該類型的句柄。下面的例子聲明了一個String類型的跟蹤句柄proverb。
String^ proverb;
在聲明時句柄時,系統(tǒng)自動為其分配一個空值,該句柄不引用任何對象。也可顯示地將某個句柄置為空值:
proverb = nullptr;
注意不能用0來表示空值。如果用0來初始化某個句柄,則0將自動轉(zhuǎn)換為被引用類型的對象,而句柄則指向該對象。可以在聲明句柄時顯示的將其初始化:
String^ saying = L"I used to think I was indecisive but now I???ê?èm not so sure.";
該語句首先在堆上創(chuàng)建一個包含等號右邊字符串的String對象,然后將該對象的地址存入saying中。注意字符串字面值的類型為const wchar_t*而非String。類String提供了這樣的方法使得const wchar_t*類型的字符串可以用來創(chuàng)建String類型的對象。
下面這條語句創(chuàng)建了值類型的句柄:
int^ value = 99;
該語句在堆上創(chuàng)建一個Int32型的值類型變量,然后將該變量的地址存入句柄value中。由于value是一種指針,因此不能直接參與算術(shù)運(yùn)算,可使用*運(yùn)算符對地址求值(類似于本地C++指針那樣):
int result = 2*(*value)+15;
由于*value表示value所指向地址存儲的數(shù)值,因此result的值為2*99+15=213。注意,當(dāng)value作為運(yùn)算式左值時,不需要*即可對value指向的變量賦值。
int^ result = 0;
result = 2*(*value)+15;
首先創(chuàng)建了一個指向數(shù)值0的句柄result。(該語句會觸發(fā)一條編譯器警告,提示不能利用0來將句柄初始化為空值。)
第2條語句=號右邊為數(shù)值,而左邊為句柄,編譯器將自動將右值賦予句柄所指向的對象,即將其轉(zhuǎn)換為如下語句
*result = 2*(*value)+15;
注意,要采用上面的語句,result句柄必須實際定義過。如果僅僅聲明了result,則會產(chǎn)生運(yùn)行時錯誤
int^ result;
*result = 2*(*value)+15;
這是因為第二句要對地址result求值,即意味著result指向的對象已經(jīng)存在,但實際并非如此,因為聲明該對象時系統(tǒng)默認(rèn)賦予其空值(nullptr)。在這種情況下,采用下面的方法就可以正常工作了
int^ result;
result = 2*(*value)+15;
二、數(shù)組
(一)數(shù)組句柄
CLR數(shù)組是分配在可回收垃圾堆上的。必須用array<typename>指出要創(chuàng)建的數(shù)組,同其它CLR堆上的對象一樣,需要用句柄來訪問它,例子如下:
array<int>^ data;
數(shù)組句柄data可用于存儲對元素類型為int的一維數(shù)組的引用。下面的例子聲明了一個句柄,并新建一個CLR數(shù)組來對此句柄初始化。
array<int>^ data = gcnew array<int>(100);
和本地C++數(shù)組一樣,CLR數(shù)組中元素的索引值也是從0開始的,可以通過[ ]訪問數(shù)組元素。數(shù)組元素都是CLR對象,在上面的例子中數(shù)組元素為Int32型對象,它們在算術(shù)表達(dá)式中就像普通的整數(shù)類型一樣。
Length屬性是數(shù)組的一個重要屬性,記錄著數(shù)組元素的數(shù)量。保存了64位的數(shù)組長度。
for(int i=0; i<data->Length; i++)
data[i] = 2*(i+1);
可以用for each循環(huán)遍歷數(shù)組元素。
array<int>^ value = {3, 5, 6, 8, 6};
for each(int item in value)
{
item = 2*item + 1;
Console::WriteLine("{0, 5}", item);
}
該循環(huán)輸出5字符寬度的字段,以右對齊的方式輸出當(dāng)前元素的計算結(jié)果,輸出如下:
7 11 13 17 13
數(shù)組句柄可以被重新賦值,只要保持?jǐn)?shù)組元素類型和維數(shù)(等級)不變即可,在前面例子中的數(shù)組句柄data指向一個int類型的一維數(shù)組,可以重新給它賦值,使其指向另外的int類型1維數(shù)組:
data = gcnew array<int>(45);
數(shù)組可以在創(chuàng)建時通過元素列表初始化,下例在CLR堆上創(chuàng)建了一個double類型的數(shù)組,并將引用賦值給了數(shù)組句柄:
array<double>^ sample = {3.4, 2.3, 6.8, 1.2, 5.5, 4.9, 7.4, 1.6};
如果在聲明數(shù)組句柄時不進(jìn)行初始化,那么在給句柄賦值時不能采用上面的方法直接用元素列表用作右值,而必須采用顯示創(chuàng)建的方式。即不能
array<double>^ sample;
sample = {3.4, 2.3, 6.8, 1.2, 5.5, 4.9, 7.4, 1.6}
而必須采用如下方式
array<double>^ sample;
sample = gcnew array<double>{3.4, 2.3, 6.8, 1.2, 5.5, 4.9, 7.4, 1.6}
對于字符串?dāng)?shù)組,注意每一個元素也是引用類型,這是因為每一個元素String也是在CLR堆上創(chuàng)建的,因此也要用String^來訪問它。
array<String^>^ names = {"Jack", "John", "Joe", "Jessica", "Jim", "Joanna"};
可以用Array類靜態(tài)函數(shù)Clear()對數(shù)組中的連續(xù)數(shù)組元素清零。
Array::Clear(samples, 0, samples->Length);
Clear()函數(shù)的第一個參數(shù)是被清零的數(shù)組,第二個參數(shù)是要清除地第一個元素的索引,第三個參數(shù)為要清除地元素數(shù)量。因此上述語句將samples數(shù)組的所有元素都置為0。如果Clear()清除的是某個跟蹤句柄,則句柄所對應(yīng)的元素都被應(yīng)用Clear()函數(shù)。如果元素為bool型,則被置為false。
(二)數(shù)組排序
Array類還定義了一個Sort()靜態(tài)函數(shù),可用于對數(shù)組進(jìn)行排序。如果以數(shù)組句柄作為參數(shù),則對整個數(shù)組排序。如果要對數(shù)組部分排序,則還需要增加元素起始索引及數(shù)量,如下例
array<int>^ samples = {27, 3, 54, 11, 18, 2, 16};
Array::Sort(samples, 2, 3);
排序后數(shù)組元素變?yōu)閧27, 3, 11, 18, 54, 2, 16}
Sort函數(shù)還有很多其它版本,下面的例子展示了如何排序兩個相關(guān)的數(shù)組,即第一個數(shù)組中的元素是第二個數(shù)組對應(yīng)元素的鍵。對第一個數(shù)組排序后,可對第二個數(shù)組進(jìn)行相應(yīng)的調(diào)整,使得鍵與值相互對應(yīng)。
Sort()函數(shù)對2個數(shù)組排序時,用第一個數(shù)組參數(shù)來確定兩個數(shù)組的順序,以此保持2個數(shù)組元素對應(yīng)關(guān)系不變。
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex4_13.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_13.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
array<String^>^ names = { "Jill", "Ted", "Mary", "Eve", "Bill", "Al" };
array<int>^ weights = {103, 168, 128, 115, 180, 176};
Array::Sort(names, weights);
for each( String^ name in names )
Console::Write(L"{0, 10}", name);
Console::WriteLine();
for each(int weight in weights)
Console::Write(L"{0, 10}", weight);
Console::WriteLine();
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex4_13.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
輸出為:
Al Bill Eve Jill Mary Ted
176 180 115 103 128 168
(三)數(shù)組搜索
Array類還提供了函數(shù)BinarySearch()以使用對分法搜索算法,對一維數(shù)組或給定范圍內(nèi)搜索特定元素的索引位置。使用該函數(shù)要求數(shù)組必須是順序排列的,因此在搜索之前必須對數(shù)組進(jìn)行排序。
array<int>^ value = { 23, 45, 68, 94, 123, 150, 203, 299 };
int toBeFound = 127;
int position = Array::BinarySearch(value, toBeFound);
if(position<0)
Console::WriteLine(L"{0} was not found.", toBeFound);
else
Console::WriteLine(L"{0} was found at index position {1}", toBeFound, position);
Array::BinarySearch()的第一個參數(shù)是被搜索數(shù)組的句柄,第二個參數(shù)是要查找的內(nèi)容,返回值為int類型的數(shù)值。如果返回值小于0則說明未找到。如果要指定搜索范圍,則需要傳遞4個參數(shù),其中第2參數(shù)為搜索起始索引,第3參數(shù)為搜索的元素數(shù)量,第4個是要搜索的內(nèi)容。下面的代碼從第4個元素開始,一直搜索到結(jié)束位置。
array<int>^ value = { 23, 45, 68, 94, 123, 150, 203, 299 };
int toBeFound = 127;
int position = Array::BinarySearch(value, 3, 6, toBeFound);
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex4_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_14.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
array<String^>^ names = { "Jill", "Ted", "Mary", "Eve", "Bill", "Al", "Ned", "Zoe", "Dan", "Jean" };
array<int>^ weights = {103, 168, 128, 115, 180, 176, 209, 98, 190, 130};
array<String^>^ toBeFound = {"Bill", "Eve", "Al", "Fred"};
int result = 0;
Array::Sort(names, weights);
for each( String^ name in toBeFound )
{
result = Array::BinarySearch(names, name);
if(result<0)
Console::WriteLine(L"{0} was not found.", name);
else
Console::WriteLine(L"{0} weights {1} lbs.", name, weights[result]);
}
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex4_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
當(dāng)搜索不到目標(biāo)時,Array::BinarySearch()函數(shù)輸出的并非任意負(fù)數(shù),而是第一個大于該目標(biāo)的元素索引值的按位補(bǔ)碼。利用該方法,可以不打亂順序在數(shù)組中插入新值。如,我們希望插入”Fred”到names數(shù)組中
array<String^>^ names = { "Jill", "Ted", "Mary", "Eve", "Bill", "Al", "Ned", "Zoe", "Dan", "Jean" }
Array::Sort(names);
String^ name = L"Fred";
int position = Array::BinarySearch(names, name);
if(position<0)
position = ~position;
此時,position保存的是大于Fred的第一個元素的位置,該數(shù)值可用于插入新值。
array<String^>^ newNames = gcnew array<String^>(names->Length+1);
for(int i=0;i<position;i++)
newNames[i] = names[i];
newNames[position] = name;
if(position<name->Length)
for(int i=position; i<names->Length; i++)
newNames[i+1] = names[i];
names = nullptr;
注意:最后一句用于刪除names數(shù)組。
(四)多維數(shù)組
C++/CLI中可以創(chuàng)建多維數(shù)組,最大維數(shù)32維。與ISO/ANSI C++不同的是,C++/CLI中的多維數(shù)組并非數(shù)組的數(shù)組,而是真正的多維數(shù)組,創(chuàng)建整數(shù)多維數(shù)組方法如下:
array<int 2>^ value = gcnew array<int, 2>(4, 5);
上面的代碼創(chuàng)建了一個二維數(shù)組,四行五列,共20個元素。訪問的方法是利用多個用逗號分隔的索引值來訪問每一個元素,而不能用一個索引值訪問一行
int nrows = 4;
int ncols = 5;
array<int, 2>^ value = gcnew array<int, 2>(nrows, ncols);
for(int i=0; i<nrows; i++)
for(int j=0; j<ncols; j++)
value[i, j] = (i+1)*(j+1);
上面的代碼利用循環(huán)給二維數(shù)組value賦值。這里訪問二維數(shù)組元素的符號與本地C++不同:后者實際上是數(shù)組的數(shù)組,而C++/CLI是真正的二維數(shù)組,不能用一個索引值來訪問二維數(shù)組,那樣是沒有意義的。數(shù)組的維數(shù)被稱為等級,上面value數(shù)組的等級為2。而本地C++數(shù)組的等級始終為1。當(dāng)然,在C++/CLI中也可以定義數(shù)組的數(shù)組,方法見下例
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex4_15.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_15.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
const int SIZE = 12;
array<int, 2>^ products = gcnew array<int, 2>(SIZE, SIZE);
for(int i=0; i<SIZE; i++)
for(int j=0; j<SIZE; j++)
products[i, j] = (i+1)*(j+1);
Console::WriteLine(L"Here is the {0} times table:", SIZE);
// Write horizontal divider line
for(int i=0; i<=SIZE; i++)
Console::Write(L"_____");
Console::WriteLine();
// Write top line of table
Console::Write(L" |");
for(int i=1; i<=SIZE; i++)
Console::Write("{0, 3} |", i);
Console::WriteLine();
// Write horizontal divider line with verticals
for(int i=0; i<=SIZE; i++)
Console::Write("____|", i);
Console::WriteLine();
// Write remaining lines
for(int i=0; i<SIZE; i++)
{
Console::Write(L"{0, 3} |", i+1);
for(int j=0; j<SIZE; j++)
Console::Write("{0, 3} |", products[i, j]);
Console::WriteLine();
}
// Write horizontal divider line
for(int i=0; i<=SIZE; i++)
Console::Write("_____", i);
Console::WriteLine();
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex4_15.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
上面的例子創(chuàng)建了一個12x12的乘法表,輸出如下:
Here is the 12 times table:
_________________________________________________________________
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
____|____|____|____|____|____|____|____|____|____|____|____|____|
1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
2 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 |
3 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 |
4 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 |
5 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 |
6 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 | 60 | 66 | 72 |
7 | 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | 77 | 84 |
8 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 |
9 | 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 | 90 | 99 |108 |
10 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 |100 |110 |120 |
11 | 11 | 22 | 33 | 44 | 55 | 66 | 77 | 88 | 99 |110 |121 |132 |
12 | 12 | 24 | 36 | 48 | 60 | 72 | 84 | 96 |108 |120 |132 |144 |
_________________________________________________________________
其中創(chuàng)建二維數(shù)組的代碼如下:
const int SIZE = 12;
array<int, 2>^ products = gcnew array<int, 2>(SIZE, SIZE);
第一行定義了一個整型常量SIZE,用于指定每一維數(shù)組的元素數(shù)量,第二行代碼定義了一個等級2的數(shù)組,為12x12大小,該數(shù)組用于存儲12x12的乘法表乘積。然后在嵌套循環(huán)中給數(shù)組賦值,大部分代碼用于格式化輸出以使其更加美觀,這里就不再說明。
(五)數(shù)組的數(shù)組
如果數(shù)組的元素是引用數(shù)組的跟蹤句柄,那么就可以創(chuàng)建數(shù)組的數(shù)組。同時,每一維數(shù)組的長度可以不同,即所謂的“鋸齒形數(shù)組”。例如,用ABCDE來表示學(xué)生的成績等級,根據(jù)等級分組存儲班內(nèi)學(xué)生的姓名,則可以創(chuàng)建一個包含5個元素的數(shù)組,每個元素為一個姓名數(shù)組(即字符串?dāng)?shù)組)
array<array<String ^>^>^ grades = gcnew array<array<String^>^>(5)
利用上面創(chuàng)建的數(shù)組,然后可以創(chuàng)建5個姓名數(shù)組了
grades[0] = gcnew array<String^>{"Louise", "Jack"};
grades[1] = gcnew array<String^>{"Bill", "Mary", "Ben", "Joan"};
grades[2] = gcnew array<String^>{"Jill", "Will", "Phil"};
grades[3] = gcnew array<String^>{"Ned", "Fred", "Ted", "Jed", "Ed"};
grades[4] = gcnew array<String^>{"Dan", "Ann"};
grades[n]訪問grades數(shù)組的第n個元素,而各元素為指向String^類型數(shù)組的句柄,因此上面的語句用于創(chuàng)建了String對象句柄的數(shù)組,并將創(chuàng)建數(shù)組的地址賦值給了grades數(shù)組元素。同時,這些字符串?dāng)?shù)組的長度是不同的。
上面的語句也可以用一個初始化語句來實現(xiàn)
array<array<String^>^>^ grades = gcnew array<array<String^>^>
{
gcnew array<String^>{"Louise", "Jack"},
gcnew array<String^>{"Bill", "Maray", "Ben", "Joan"},
gcnew array<String^>{"Jill", "Will", "Phil"},
gcnew array<String^>{"Ned", "Fred", "Ted", "Jed", "Ed"},
gcnew array<String^>{"Dan", "Ann"},
};
注意:元素的初值必須寫在花括號里。
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex4_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_16.cpp : main project file.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
array<array<String^>^>^ grades = gcnew array<array<String^>^>
{
gcnew array<String^>{"Louise", "Jack"},
gcnew array<String^>{"Bill", "Maray", "Ben", "Joan"},
gcnew array<String^>{"Jill", "Will", "Phil"},
gcnew array<String^>{"Ned", "Fred", "Ted", "Jed", "Ed"},
gcnew array<String^>{"Dan", "Ann"}
};
wchar_t gradeLetter = 'A';
for each(array<String^>^ grade in grades)
{
Console::WriteLine(L"Students with Grade {0}:", gradeLetter++);
for each(String^ student in grade)
Console::Write("{0, 12}", student);
Console::WriteLine();
}
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex4_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
輸出為
Students with Grade A:
Louise Jack
Students with Grade B:
Bill Maray Ben Joan
Students with Grade C:
Jill Will Phil
Students with Grade D:
Ned Fred Ted Jed Ed
Students with Grade E:
Dan Ann