官术网_书友最值得收藏!

1.5 C++/CLI數(shù)組

與標(biāo)準(zhǔn)C++可以自己維護堆不同,C++/CLI中動態(tài)分配的內(nèi)存是由CLR來維護的。當(dāng)不需要堆時,CLR自動將其刪除并回收,同時CLR還能自動壓縮內(nèi)存堆以避免產(chǎn)生不必要的內(nèi)存碎片。C++/CLI的這種機制能夠避免內(nèi)存泄露和產(chǎn)生內(nèi)存碎片,被稱為垃圾回收。而由CLR管理的這種堆被稱為CLR堆,它是由gcnew操作符創(chuàng)建的。

由于垃圾回收機制會改變堆中對象的地址,如果使用指針,則指針將不再有效,因此不能在CLR堆中使用普通C++指針。為此,CLR提供了跟蹤句柄和跟蹤引用來安全地訪問堆中的對象。

1.5.1 跟蹤句柄

跟蹤句柄類似于本地標(biāo)準(zhǔn)C++中的指針,其中存儲著某個對象的地址。但是與C++指針不同的是,當(dāng)CLR壓縮堆過程中時將改變該對象的實際地址,由垃圾回收器自動更新跟蹤句柄所包含對象的地址。所以,在程序中不能夠像本地C++指針那樣執(zhí)行地址的算術(shù)運算,也不允許對跟蹤句柄進(jìn)行強制類型轉(zhuǎn)換。

跟蹤句柄所引用的對象是在CLR堆中被創(chuàng)建的,并且所有屬于引用類型的對象都存儲在堆中,因此為了能夠引用這些對象而創(chuàng)建的變量都必須是該類型對象的跟蹤句柄。例如,為了能夠引用一個String類型的對象,必須創(chuàng)建一個String類型的跟蹤句柄。

在聲明一個跟蹤句柄時,可以將符號“^”添加到類型名稱的后面用于指定該類型的句柄變量。當(dāng)聲明某個句柄時,系統(tǒng)會自動將該句柄初始化為空值,以表示該句柄未引用任何對象。同時可以通過關(guān)鍵字nullptr顯式地將跟蹤句柄初始化為空值,還可以將跟蹤句柄初始化為指定的值。例如:

        String^ name;               // 聲明名稱為name的String類型的句柄,初始值為空
        String^ words = nullptr;   // 聲明名稱為words的句柄, 并顯示初始化為空
        String^ saying = L"Hello World!";  // 聲明名稱為saying的句柄, 并指定初始化值

如果在基本數(shù)據(jù)類型后添加“^”符號,那么將創(chuàng)建一個值類型的跟蹤句柄,并且將在CLR堆上創(chuàng)建該類型的變量。由于此時定義的變量相當(dāng)于本地C++中的指針,所以此變量不能夠參與算術(shù)運算,而需要通過“*”運算符對地址求值。例如:

        int^ value = 99;
        int result = 2 * (*value) + 15;   // result = 2*99+15=213

同時,如果當(dāng)value作為左值時,可以直接對value指向的變量賦值而不需要通過“*”運算符對地址求值。但是在這種情況下,作為左值的跟蹤句柄必須已經(jīng)實際定義過,如果僅僅是一個聲明,則會在運行時產(chǎn)生錯誤。例如:

        int^ result1 = 0;               // 警告: 不能用0來初始化句柄
        result1 = 2 * (*value) + 15;   // 或者: *result1 = 2 * (*value) + 15
        int^ result2;
        *result2 = 2 * (*value) + 15;  // 錯誤, resuilt2并未引用實際的對象

因此,凡是在CLR堆上創(chuàng)建的對象都必須被跟蹤句柄所引用,其中,這些對象中包括了用gcnew操作符顯式地創(chuàng)建在堆上的對象和所引用的數(shù)據(jù)類型。然而需要注意的是,所有分配在堆上的對象都不能在全局范圍內(nèi)被創(chuàng)建。例如,在程序中不能夠創(chuàng)建一個String類型的全局變量。

跟蹤引用類似于本地C++引用,用于表示某對象的別名。可以給堆棧上的值對象、CLR堆上的跟蹤句柄創(chuàng)建跟蹤引用。與本地C++定義引用的方式不同,C++/CLI通過“%”符號定義句柄的跟蹤引用。例如:

        int value = 10;
        int% trackValue = value;
        trackValue *= 5;
        Console::WriteLine(value);     // value = 50

跟蹤引用本身永遠(yuǎn)是在堆棧上創(chuàng)建的,如果垃圾回收移動了被引用的對象,則跟蹤引用將被自動更新。

1.5.2 CLR數(shù)組

CLR數(shù)組與標(biāo)準(zhǔn)C++數(shù)組不同,CLR數(shù)組所占用的內(nèi)存是在可回收垃圾堆上分配的,并且CLR數(shù)組提供了一些內(nèi)置功能用于取得數(shù)組的相關(guān)信息和操作數(shù)組等。

在創(chuàng)建CLR數(shù)組時,需要通過關(guān)鍵字array定義數(shù)組對象的跟蹤句柄,并且需要在array關(guān)鍵字后通過尖括號(“< >”)為數(shù)組中的元素指定數(shù)據(jù)類型。例如:

        array<int>^ value;               // 聲明一個數(shù)組元素為int類型的數(shù)組跟蹤句柄
        array<String^>^ lines = nullptr;// 聲明一個數(shù)組元素為字符串的數(shù)組,并初始化為空

在聲明數(shù)組變量的同時,可以通過gcnew運算符在堆上創(chuàng)建CLR數(shù)組,并且還可以通過圓括號(“( )”)指定新創(chuàng)建的數(shù)組所能夠包含元素的數(shù)目。并且,在CLR數(shù)組中包含Length屬性,該屬性為32位的整數(shù)值,用于記錄數(shù)組中所能包含元素的數(shù)量(即數(shù)值的長度),而CLR數(shù)組中的LongLength屬性將以64位表示數(shù)組長度。可以通過“->”運算符訪問數(shù)組對象中的屬性。例如:

        array<int>^ data = gcnew array<int>(100);   // 創(chuàng)建一個長度為100的整形數(shù)組
        Console::WriteLine(L"數(shù)組的長度為:{0}",data->Length);// 輸出數(shù)組的長度為100

數(shù)組可以在創(chuàng)建時通過元素列表初始化,也可以通過gcnew顯式地創(chuàng)建數(shù)組對象時初始化數(shù)組。例如:

        array<double>^ sample1 = {3.4, 2.3, 6.8, 1.2, 5.5, 4.9, 7.4, 1.6};
        array<double>^ sample2 = gcnew array<double>{3.4, 2.3, 6.8, 1.2, 5.5, 4.9, 7.4, 1.6}
        array<String^>^names={L"Jack",L"John",L"Joe",L"Jessica",L"Jim",L"Joanna"};

與標(biāo)準(zhǔn)C++數(shù)組相同,CLR數(shù)組中元素的索引值同樣以0開始,可以通過“[ ]”運算符訪問數(shù)組指定位置的元素,也可以通過for each循環(huán)遍歷數(shù)組中的所有元素。例如:

        array<int>^ value = {3, 5, 6, 8, 6};
        for each(int item in value)        // 遍歷數(shù)組
            Console::Write(L"{0, 5}, ", item);
        Console::WriteLine();

C++/CLI提供的for each循環(huán)語句用于重復(fù)處理一組特定類型對象中的所有對象。在for each循環(huán)中,首先需要定義一個與集合中元素類型相同的迭代變量,該變量作為一個只讀的局部變量,用來依次獲得集合中的每個元素。然后,通過in關(guān)鍵字指定需要被遍歷的集合對象。

【例1.8】 一維數(shù)組的使用示例。創(chuàng)建一個元素類型為double類型的一維數(shù)組,并將其中的元素初始化為隨機數(shù),然后從數(shù)組中找出其中的最大值。代碼如下:

        // Ex1_8.cpp : 主項目文件
        #include "stdafx.h"
        using namespace System;
        int main(array<System::String ^> ^args)
        {
            Random^ random = gcnew Random();        // Random對象,用于產(chǎn)生隨機數(shù)
            array<double>^ numbers = gcnew array<double>(50);
            // 產(chǎn)生一個隨機數(shù)
            Console::WriteLine(L"產(chǎn)生一個隨機數(shù).");
            for ( int i = 0; i<numbers->Length; i++)
              numbers[i] = random->NextDouble()*100.0;//產(chǎn)生一個0~100.0之間的隨機數(shù)
            // 輸出數(shù)組中的元素
          Console::WriteLine(L"數(shù)組中的元素為: ");
          for (int i = 0; i<numbers->Length; i++)
          {
            Console::Write(L"{0, 10:F2}", numbers[i]);//右對齊,保留兩位有效小數(shù)位輸出
              if ((i+1) % 5 == 0) Console::WriteLine();
          }
          // 找出數(shù)組中的最大值
          double max = 0.0;
          for each (double value in numbers)
              max = value > max ? value : max;
          Console::WriteLine(L"數(shù)組中的最大值為: {0:F2}", max);
          return 0;
      }

在Ex1_8項目中輸出數(shù)組的元素和最大值時,將在每行輸出5個數(shù)組元素,其中每個元素占10個字符的寬度,并保留兩位有效小數(shù)位。Ex1_8項目的運行結(jié)果如圖1.12所示。

圖1.12 Ex1_8項目的運行結(jié)果

1.5.3 數(shù)組的排序及查找

事實上,所有CLR數(shù)組都隱式地繼承于System::Array類。該類提供了創(chuàng)建、操作、搜索和排序數(shù)組的基本方法,因而作為所有CLR數(shù)組的基類。Array類的常用方法及說明如表1.6所示。

表1.6 Array類的常用方法及說明

1. 數(shù)組排序

Array類提供了一組Sort靜態(tài)方法,分別用于對數(shù)組中的部分元素或整個數(shù)組進(jìn)行排序。Sort方法使用快速排序(Quick Sort)算法對數(shù)組中的元素進(jìn)行排序,因此該方法執(zhí)行的是不穩(wěn)定排序。也就是說,如果數(shù)組中兩個元素相等,則其所處的順序可能會與排序前的順序不同。Array類的Sort方法及重載方法的聲明如下:

      static void Sort(array<T>^array);
      static void Sort(array<T>^array,int index,int length);
      static void Sort(array<T1>^keys,array<T2>^items);
      static void Sort(array<T1>^keys,array<T2>^items,int index,int length);

其中,array、keys和items參數(shù)表示需要排序的數(shù)組,并且T、T1和T2分別表示數(shù)組中元素的類型;index參數(shù)指定了數(shù)組中排序范圍的起始索引,而需要排序的元素個數(shù)由length參數(shù)指定。

另外,Sort方法還可以對兩個相關(guān)的數(shù)組進(jìn)行排序。其中,第一個數(shù)組中的元素作為第二個數(shù)組對應(yīng)元素的鍵,當(dāng)對第一個數(shù)組中的元素排序后,將對第二個數(shù)組中元素的順序進(jìn)行相應(yīng)的調(diào)整,以使其與第一個數(shù)組中的元素相對應(yīng)。例如:

        array<double>^ grades = { … };// 若grades中值為: 3.0, 4.0, 2.0, 1.0
        array<String^>^ names = { … };// 若names中值為: "A", "B", "C", "D"
        Array::Sort(grades, names);  // 排序后, grade中元素順序為: 1.0, 2.0, 3.0, 4.0
                                      // 排序后, names中元素順序為: "D", "C", "A", "B"

由于Sort方法采用快速排序算法對數(shù)組進(jìn)行排序,所以,在一般情況下該方法執(zhí)行的時間復(fù)雜度為O(nlogn)(其中n為數(shù)組的長度);而在最壞的情況下,Sort方法執(zhí)行的時間復(fù)雜度為O(n2)。

【例1.9】 一維數(shù)組的排序示例。首先對包含學(xué)生姓名的數(shù)組進(jìn)行排序,并為這些學(xué)生分別產(chǎn)生一個隨機的成績。然后,將包含成績信息和學(xué)生姓名的數(shù)組作為兩個相關(guān)數(shù)組,并按照成績的高低對這兩個相關(guān)數(shù)組進(jìn)行排序,并輸出排序的結(jié)果。代碼如下:

        // Ex1_9.cpp : 主項目文件
        #include "stdafx.h"
        using namespace System;
        int main(array<System::String ^> ^args)
        {
            array<String^>^ names = { L"林一帆", L"張薇", L"趙琳", L"孫研", L"馬琳琳" }
            Console::WriteLine(L"\n排序前的學(xué)生姓名為: ");
            for each (String^ name in names)
              Console::Write(L"\t{0}", name);
            Array::Sort(names);
            Console::WriteLine(L"\n排序后的學(xué)生姓名為: ");
            for each (String^ name in names)
              Console::Write(L"\t{0}", name);
          Random^ random = gcnew Random();
          array<double>^ grades = gcnew array<double>(5);
          for (int i = 0; i<grades->Length; i++)
              grades[i] = random->NextDouble() * 100.0;
          Console::WriteLine(L"\n學(xué)生對應(yīng)的成績?yōu)? ");
          for each (double grade in grades)
              Console::Write(L"\t{0:F2}", grade);
          Array::Sort(grades, names);
          Console::WriteLine(L"\n按成績排序后的成績及學(xué)生姓名:");
          for each (double grade in grades)
              Console::Write(L"\t{0:F2}", grade);
          Console::WriteLine();
          for each (String^ name in names)
              Console::Write(L"\t{0}", name);
          Console::WriteLine();
          return 0;
      }

Ex1_9項目運行后,當(dāng)對包含成績信息和學(xué)生姓名的兩個相關(guān)數(shù)組進(jìn)行排序時,學(xué)生姓名數(shù)組中元素的順序?qū)凑粘煽償?shù)組的排序而排列。Ex1_9項目的運行結(jié)果如圖1.13所示。

圖1.13 Ex1_9項目的運行結(jié)果

2. 查找元素

Array類還提供了一組BinarySearch靜態(tài)方法,可以通過二分查找法從一維數(shù)組的所有或部分元素中搜索特定的元素,并返回該元素在數(shù)組中的索引。BinarySearch方法的聲明如下:

      static int BinarySearch(array<T>^array,T value);
      static int BinarySearch(array<T>^array,int index,int length,T value);

其中,array參數(shù)是被搜索數(shù)組的句柄,需要查找的元素由value參數(shù)指定。如果需要指定搜索范圍,那么指定index參數(shù)為搜索的起始索引,并指定length參數(shù)為搜索的元素數(shù)量。

BinarySearch方法返回一個int類型的整數(shù)值,如果其返回值小于0,則說明該元素未包含在數(shù)組中。然而需要說明的是,在通過BinarySearch方法查找元素時,其查找的數(shù)組中的元素必須是順序排列的,因此在搜索之前必須對數(shù)組進(jìn)行排序。例如:

        Array::Sort(data);
        int pos = Array::BinarySearch(data, value);

另外,當(dāng)通過BinarySearch方法未查找到目標(biāo)元素時,該方法并非返回一個任意的負(fù)數(shù),而是返回第一個大于目標(biāo)元素的數(shù)組元素的索引值的補碼;如果數(shù)組中不包含目標(biāo)元素且不含有任何大于目標(biāo)元素的數(shù)組元素,那么該方法將返回數(shù)組中最后一個數(shù)組元素的索引值加1的補碼。

【例1.10】 從數(shù)組中查找指定的元素。在main函數(shù)中創(chuàng)建兩個數(shù)組,其中一個數(shù)組用于保存學(xué)生的姓名,而另一個數(shù)組保存對應(yīng)的成績。然后通過Array類的BinarySearch方法從姓名數(shù)組中查找指定的學(xué)生姓名,并輸出該學(xué)生的成績。代碼如下:

          // Ex1_10.cpp : 主項目文件
          #include "stdafx.h"
          using namespace System;
          int main(array<System::String ^> ^args)
          {
              array<String^>^ names = { L"林一帆", L"張薇", L"趙琳", L"孫研", L"馬琳琳" };
              array<String^>^ found = { L"張薇", L"夢帆", L"孫研", L"趙琳" };
              Random^ random = gcnew Random();
              array<double>^ grades = gcnew array<double>(5);
              for (int i = 0; i<grades->Length; i++)
                grades[i] = random->NextDouble() * 100.0;
              Array::Sort(names, grades);
              Console::WriteLine(L"學(xué)生的姓名和成績?yōu)? ");
              for each (String^ name in names)
                Console::Write(L"\t{0}", name);
              Console::WriteLine();
              for each (double grade in grades)
                Console::Write(L"\t{0:F2}", grade);
              Console::WriteLine();
              int i = -1;
              for each (String^ name in found)
              {
                i = Array::BinarySearch(names, name);
                if (i < 0) Console::WriteLine(L"未查找到學(xué)生: {0}", name);
                else Console::WriteLine(L"學(xué)生{0} 的成績?yōu)? {1:F2}",
                                          name, grades[i]);
              }
              return 0;
          }

Ex1_10項目運行后,在進(jìn)行從學(xué)生姓名數(shù)組中查找指定姓名的操作前,需要對該數(shù)組中的元素進(jìn)行排序。如果Array::BinarySearch方法返回的值小于0,則說明數(shù)組中不包含該學(xué)生的姓名。Ex1_10項目運行結(jié)果如圖1.14所示。

圖1.14 Ex1_10項目的運行結(jié)果

1.5.4 多維數(shù)組

在C++/CLI中可以創(chuàng)建二維或多維數(shù)組,其中數(shù)組的最大維數(shù)為32。然而與標(biāo)準(zhǔn)C++多維數(shù)組不同的是,CLR數(shù)組是真正意義上的多維數(shù)組,而非數(shù)組的數(shù)組。

在創(chuàng)建CLR數(shù)組時,如果在尖括號(“< >”)中未指定數(shù)組的維數(shù),那么默認(rèn)創(chuàng)建一維數(shù)組。而在創(chuàng)建CLR多維數(shù)組時,需要在尖括號內(nèi)元素類型后面指定數(shù)組的維數(shù),維數(shù)中間用逗號(“,”)隔開。例如:

        array<int, 2>^ data = gcnew array<int, 2>(4, 5);//創(chuàng)建一個二維數(shù)組,且為4行5列

訪問CLR多維數(shù)組中元素的方法是分別通過多個用逗號分隔的索引值訪問每一個元素,而不能用一個索引值訪問整個一行。例如,如果需要訪問CLR二維數(shù)組中第i行、第j列的元素,那么需要通過[i, j]的方式來訪問。例如:

        for (int i = 0; i<rows; i++) {
            for (int j = 0; j<cols; j++)
              Console::Write(L"{0,5}, ", data[i,j]);  // 輸出第i行、第j列的元素
            Console::WriteLine();
        }

【例1.11】 多維數(shù)組的使用示例。在main函數(shù)中創(chuàng)建一個二維數(shù)組,并將9×9乘法表保存到該數(shù)組中,然后輸出該二維數(shù)組。代碼如下:

        // Ex1_11.cpp : 主項目文件
        #include "stdafx.h"
        using namespace System;
        int main(array<System::String ^> ^args)
        {
            array<int, 2>^ table = gcnew array<int, 2>(9, 9);
            for (int i = 0; i<9; i++)
              for (int j = 0; j<9; j++)
                  table[i, j] = (i+1) * (j+1);
            Console::WriteLine(L"二維數(shù)組中的9×9乘法表:");
            for (int i = 0; i<9; i++)
          {
              for (int j = 0; j<=i; j++)
                Console::Write(L"{0,3}  |", table[i, j]);
              Console::WriteLine();
          }
          return 0;
      }

Ex1_11項目運行后,其執(zhí)行結(jié)果如圖1.15所示。

圖1.15 Ex1_11項目的運行結(jié)果

1.5.5 數(shù)組的數(shù)組

由于在創(chuàng)建CLR數(shù)組時,可以將數(shù)組元素指定為任意的對象類型,所以如果將數(shù)組元素指定為一個數(shù)組的跟蹤句柄類型,那么也就創(chuàng)建了數(shù)組的數(shù)組,即所謂的鋸齒形數(shù)組。例如:

        array< array< String^ >^ >^ grades = gcnew array< array<String^>^ >(5);

從上面的代碼中可以看出,跟蹤句柄grades引用了一個包含5個元素的數(shù)組。其中每個元素實際上也是數(shù)組的跟蹤句柄,并且它們都引用一個元素為String^類型的數(shù)組。那么可以通過以下方式來初始化grades所引用的數(shù)組。例如:

        grades[0] = gcnew array<String^>{L"王林",L"程明"};
        grades[1] = gcnew array<String^>{L"韋平平",L"李方",L"林一凡"};
        grades[2] = gcnew array<String^>{L"趙琳",L"張強民",L"王敏",L"張薇"};
        grades[3] = gcnew array<String^>{L"李計",L"馬琳琳",L"孫研",L"孫祥慶",L"羅林琳"};
        grades[4] = gcnew array<String^>{L"李紅慶", L"吳薇華"};

其中,grades[i]將訪問grades所引用的數(shù)組中的第i個元素,而各元素為指向String^類型數(shù)組的句柄。因此上面的語句將創(chuàng)建一個String對象句柄的數(shù)組,并將被創(chuàng)建數(shù)組的地址賦值給了grades數(shù)組中的每個元素。

【例1.12】 數(shù)組的數(shù)組的使用示例。創(chuàng)建一個元素為array<String^>^類型的數(shù)組,在main函數(shù)中遍歷整個數(shù)組,并輸出每個數(shù)組元素中的學(xué)生姓名。代碼如下:

        // Ex1_12.cpp: 主項目文件
        #include "stdafx.h"
        using namespace System;
        int main(array<System::String ^> ^args)
        {
            array<array<String^>^>^ grades =
            {
              gcnew array<String^>{L"王林", L"程明"},
              gcnew array<String^>{L"韋平平", L"李方", L"林一凡"},
              gcnew array<String^>{L"趙琳", L"張強民", L"王敏", L"張薇"},
              gcnew array<String^>{L"李計", L"馬琳琳", L"孫研", L"孫祥慶",L"羅林琳"},
              gcnew array<String^>{L"李紅慶", L"吳薇華"}
            };
            wchar_t gradeLetter = 'A';
            for each(array<String^>^ grade in grades)
            {
              Console::WriteLine(L"成績等級為{0} 學(xué)生:", gradeLetter++);
              for each(String^ student in grade)
                  Console::Write("\t{0}", student);
              Console::WriteLine();
            }
            return 0;
        }

Ex1_12項目運行后,其執(zhí)行結(jié)果如圖1.16所示。

圖1.16 Ex1_12項目的運行結(jié)果

主站蜘蛛池模板: 海晏县| 拜泉县| 迭部县| 黄梅县| 通辽市| 竹山县| 新密市| 彰化县| 清原| 顺昌县| 灌云县| 古蔺县| 镇雄县| 美姑县| 礼泉县| 滨州市| 海门市| 双江| 任丘市| 华容县| 彭山县| 大足县| 濮阳县| 靖江市| 烟台市| 昆山市| 西乌珠穆沁旗| 吴旗县| 抚宁县| 逊克县| 莲花县| 云霄县| 郎溪县| 云安县| 汉中市| 永安市| 依兰县| 买车| 郓城县| 绵竹市| 哈尔滨市|