1 / 88

Chapter 9 Visual C++ 語法升級

Chapter 9 Visual C++ 語法升級. 9.1 CLR 主控台應用程式專案. CLR = Common Language Runtime

Download Presentation

Chapter 9 Visual C++ 語法升級

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 9 Visual C++語法升級

  2. 9.1 CLR主控台應用程式專案 • CLR = Common Language Runtime • The Common Language Runtime (CLR) is a core component of Microsoft's .NET initiative. It is Microsoft's implementation of the Common Language Infrastructure (CLI) standard, which defines an execution environment for program code. In the CLR, code is expressed in a form of bytecode called the Common Intermediate Language (CIL, previously known as MSIL—Microsoft Intermediate Language).

  3. 9.1 CLR主控台應用程式專案 • Developers using the CLR write code in a language such asVisual C++ or VB.NET. At compile time, a .NET compiler converts such code into CIL code. At runtime, the CLR's just-in-time compiler converts the CIL code into code native to the operating system. Alternatively, the CIL code can be compiled to native code in a separate step prior to runtime by using the Native Image Generator (NGEN). This speeds up all later runs of the software as the CIL-to-native compilation is no longer necessary.

  4. http://prabirchoudhury.wordpress.com/2008/12/15/common-language-runtime-clr/clr_image21/http://prabirchoudhury.wordpress.com/2008/12/15/common-language-runtime-clr/clr_image21/

  5. 9.1 CLR主控台應用程式專案 • CLR = Common Language Runtime • 前面章節使用Win32主控台應用程式學習標準ANSIC++是屬於 unmanaged 程式,適用於其他C++整合環境,如:DEV C++、Turbo C++等 • 本章介紹 .NET應用程式,如何由標準的 C++升級至VC++ • VC++ .NET 稱為 Managed Extensions for C++ • 只介紹常用 VC++ 或視窗應用程式會使用的語法 • 學習 VC++ 語法最好途徑之一是使用CLR主控台應用程式,因不需考慮使用者介面 • 可專注在語法改變

  6. 9.1 CLR主控台應用程式專案 Step1 進入 Visual C++ 2005 的整合開發環境(IDE) Step2 • 在 VC++ 2005 IDE執行功能表【檔案(F)/新增(N)/專案(P)】開啟「新增專案」對話方塊 • 依下圖步驟選取「CLR主控台應用程式」專案,將專案名稱設為「FirstCLRVC」 • 最後按 鈕 • 此專案是屬於 VC++ 2005主控台專案,也就是.NET應用程式

  7. 9.1 CLR主控台應用程式專案

  8. 9.1 CLR主控台應用程式專案 Step3 • 進入VC++ CLR主控台應用程式專案的編輯環境 • 右窗格為 VC++ 2005的程式碼編輯窗格;左邊出現「方案總管」視窗與Win32主控台應用程式有差異 • 在「return 0;」敘述上方加入「Console::Read();」敘述,透過 Console.Read()方法等待由鍵盤輸入資料 • 執行此敘述時會暫停,等待由鍵盤輸入資料,一直到按 <Enter>鍵才結束程式 • 加入此行敘述的目的是為了等待並讓使用者觀看主控台應用程式的輸出訊息

  9. 9.1 CLR主控台應用程式專案

  10. 【說明】 • using namespace System; Console 類別主要對主控台做讀取和寫入資料使用 Console 類別,必須在程式開始撰寫「using namespace System;」敘述含入 System 命名空間 • Console::WriteLine(L“Hello World”); 在 CLR 主控台環境下的目前游標處印出小括號內雙引號括住的字串訊息後,並將游標移到下一行;此敘述印出「Hello World」,並將游標移下一行 • Console::Read();等待由鍵盤輸入資料。執行此敘述時,程式會暫停,等待由鍵盤輸入資料,一直到按 <Enter> 鍵才結束程式的執行。加入此敘述目的是為讓使用者觀看主控台應用程式的輸出訊息

  11. 9.1 CLR主控台應用程式專案 Step 4 • 完成程式撰寫,執行功能表的【偵錯(D)/開始偵錯(S)】編譯並執行程式 • 出現黑色畫面主控台視窗,顯示本專案程式執行結果 Step 5 • 若想回VC++環境繼續編寫程式,執行功能表的【偵錯D)/停止偵錯(E)】

  12. 9.2 主控台專案格式化輸出入 9.2.1 Write/WriteLine方法 • Write()和WriteLine()都是 System::Console 類別提供的方法,將輸出的串流由指定輸出裝置(預設為螢幕)顯示出來 • 兩者差異在 WrtieLine() 方法是將要輸出的字串包含換行控制字元(Carriage return)一起輸出。當此敘述執行完時,游標移到目前輸出字串下一行的最前面 • Write() 方法游標會停在所輸出字串最後一個字元的後面,不會自動移下一行最前面

  13. 9.2.2 Read/ReadLine方法 • Read() 和 ReadLine() 是 System::Console 類別所提供的方法,用來由指定輸入裝置(預設為鍵盤) 把鍵入的資料讀進來形成一個輸入串流放入指定的變數 • ReadLine() 方法允許接受一連串的輸入串流(一行字元)一直到按下 <Enter> 鍵為止 • Read() 只能由指定輸入裝置輸入的串流中接受一個字元

  14. 【程式碼】 FileName:ConsoleEx.sln 01 #include "stdafx.h" 02 using namespace System; 03 04 int main(array<System::String ^> ^args) 05 { 06 Console::Write("請輸入你的名字:"); 07 String^ yourName; //.NET 的String類別,不是標準C++的string類別 08 yourName = Console::ReadLine(); 09 Console::WriteLine("Hello , {0}", yourName); 10 Console::Write("請輸入你的年齡:"); 11 int age; 12 //使用int::Parse()方法將輸入的字串轉成整數 13 age = int::Parse(Console::ReadLine()); 14 Console::WriteLine("{0} 您好! , 您是 {1} 歲 ", yourName, age); 15 Console::Read (); 16 return 0; 17 }

  15. 9.2.3 Format 輸出入如何格式化 • Write()和WriteLine()連續輸出資料時,常無法控制上下行的資料能對齊。Visual C++提供格式化符號字元,譬如:將原來的 {0} 改為 {0:D9},表示將第一個資料以十進制顯示且長度設為9,空白處補0,便可讓上下文資料對齊 • 格式化的數值若有小數,且格式化字元後面未接數字,則預設小數位數佔兩位;若格式化字元後面有接數字,該數字表示小數佔用的位數:

  16. 【程式碼】 FileName:ConsoleFormat.sln 01 #include "stdafx.h" 02 using namespace System; 03 04 int main(array<System::String ^> ^args) 05 { 06 int num1, num2; 07 double num3, num4; 08 num3 = 1234.567; 09 num4 = -1234.567; 10 Console::WriteLine 11 ("num3=1234.567 \t\t num4=-1234.567 的 C/c3 格式分別為:"); 12 Console::WriteLine("num3={0:C} \t\t num4={1:c3} ", num3, num4); 13 Console::WriteLine(); 14

  17. 15 num1 = 123456; 16 num2 = -123456; 17 Console::WriteLine(); 18 Console::WriteLine 19 ("num1=123456 \t\t num2=-123456 的 D9/d3/D 格式分別為 :"); 20 Console::WriteLine 21 ("num1={0:D9} \t num2={1:d3} \t num2={2:D}", num1, num2, num2); 22 23 Console::WriteLine(); 24 Console::WriteLine 25 ("num3=1234.567 \t\t num4=-1234.567 的 E/e2 格式分別為 :"); 26 Console::WriteLine("num3={0:E} \t num4={1:e2}", num3, num4); 27 Console::WriteLine();

  18. 29 num3 = 1234.567; 30 num4 = -1234.567; 31 Console::WriteLine 32 ("num3=1234.567 \t\t num4=-1234.567 的 F1/f 格式分別為:"); 33 Console::WriteLine("num3={0:F1} \t\t num4={1:f} ", num3, num4); 34 Console::WriteLine(); 35 36 num3 = 1234.567; 37 num4 = -1234.567; 38 Console::WriteLine 39 ("num3=1234.567 \t\t num4=-1234.567 的 G3 和 g 格式分別為 :"); 40 Console::WriteLine("num3={0:G3} \t\t num4={1:g} ", num3, num4); 41 Console::WriteLine();

  19. 43 num3 = 1234.567; 44 num4 = -1234.567; 45 Console::WriteLine 46 ("num3=1234.567 \t\t num4=-1234.567 的 G/g4 格式分別為:"); 47 Console::WriteLine("num3={0:G} \t\t num4={1:g4} ", num3, num4); 48 Console::WriteLine(); 49 50 num3 = 1234.567; 51 num4 = -1234.567; 52 Console::WriteLine 53 ("num3=1234.567 \t\t num4=-1234.567 的 N3 和 n 格式分別為 :"); 54 Console::WriteLine("num3={0:N3} \t num4={1:n} ", num3, num4); 55 Console::WriteLine(); 57 num1 = 123; 58 num2 = -123; 59 Console::WriteLine("num1=123 \t\t num2=-123 的 X 格式分別為:"); 60 Console::WriteLine("num1={0:X} \t\t num2={1:x} ", num1, num2); 61 Console::Read(); 62 return 0; 63 }

  20. 9.2.4 自訂數值格式輸出字串

  21. 【程式碼】 FileName:ConsoleToString.sln 01 #include "stdafx.h" 02 using namespace System; 03 04 int main(array<System::String ^> ^args) 05 { 06 int myvar1 = 801234567; //輸出結果:(080)123-4567 07 Console::WriteLine("1. " + myvar1.ToString("(0##) ###-####")); 08 09 int myvar2 = -12345; //輸出結果:-12345 10 Console::WriteLine("2. " + myvar2.ToString("######")); 11 12 int myvar3 = -12345; //輸出結果:-012345 13 Console::WriteLine("3. " + myvar3.ToString("000000")); 14 double myvar4 = -2.455; //輸出結果:-2.46 15 Console::WriteLine("4. " + myvar4.ToString("#.##"));

  22. 17 double myvar5 = -2.4; //輸出結果:-2.40 18 Console::WriteLine("5. " + myvar5.ToString("0.00")); 19 20 double myvar6 = -2.455; //輸出結果:-02.46 21 Console::WriteLine("6. " + myvar6.ToString("00.00")); 22 23 double myvar7 = 1234567890; //輸出結果:1,234,567,890 24 Console::WriteLine("7. " + myvar7.ToString("#,#")); 25 26 double myvar8 = 1234567890; //輸出結果:1234568 27 Console::WriteLine("8. " + myvar8.ToString("#,")); 28 29 double myvar9 = 1234567890; //輸出結果: 1235 30 Console::WriteLine("9. " + myvar9.ToString("#,,")); 31 32 double myvar10 = 1234567890; //輸出結果:1 33 Console::WriteLine("10. " + myvar10.ToString("#,,,"));

  23. 35 double myvar11 = 1234567890; //輸出結果:1,235 36 Console::WriteLine("11. " + myvar11.ToString("#,##0,,")); 37 38 double myvar12 = 0.086; //輸出結果:8.6% 39 Console::WriteLine("12. " + myvar12.ToString("#0.##%")); 40 41 double myvar13 = 0.08647; //輸出結果:8.65% 42 Console::WriteLine("13. " + myvar13.ToString("#0.##%")); 43 double myvar14 = 16800; //輸出結果:1.68E+4 44 Console::WriteLine("14. " + myvar14.ToString("0.###E+0")); 45 46 double myvar15 = 16800; //輸出結果:1.68E+004 47 Console::WriteLine("15. " + myvar15.ToString("0.###E+000")); 48

  24. 49 double myvar16 = 16800; //輸出結果:1.68E004 50 Console::WriteLine("16. " + myvar16.ToString("0.###E-000")); 51 52 double myvar17 = 123456; //輸出結果:[12-34-56] 53 Console::WriteLine("17. " + myvar17.ToString("[##-##-##]")); 54 55 int myvar18 = 1234; //輸出結果:1234 56 Console::WriteLine("18. " + myvar18.ToString("##;(##)")); 57 58 int myvar19 = 1234; //輸出結果:(1234) 59 Console::WriteLine("19. " + myvar19.ToString("(##);##")); 60 61 int myvar20 = -1234; //輸出結果:(1234) 62 Console::WriteLine("20. " + myvar20.ToString("##;(##)")); 63 Console::ReadLine(); 64 return 0; 65 }

  25. 9.3 Visual C++ 2005語法升級 • 底下介紹標準 C++ 升級至常用視窗應用程式的VC++語法 1. 在VC++ .NET 2003 的參考類別型別是使用__gc 關鍵字來宣告類別,在 VC++ 2005 是使用 ref來取代 __gc,例如 ref Car { //敘述區塊 }; 2. 在VC++若要動態配置建立 ref 參考類別型別 的物件,必須使用 gcnew來取代 new 運算子,使用 ^符號來取代 * 指標運算子。寫法: ref Car{ //敘述區塊 }; …… Car^ c = gcnew Car(); //動態配置記憶體

  26. 3. 標準 C++ 可將指標變數設為0或null,表示指標不指向任何記憶體位址;在VC++則是使用nullptr來取代0或null。寫法: C = nullptr; 4. 標準 C++ 類別的解構式是在類別名稱之前加 ~ 符號;在VC++的解構式則是使用 ! 取代 ~ 符號。寫法: !Car(){ //為解構式,若將指標變數設為 c = nullptr; ,此時會執行解構式 } 5. 標準 C++ 一個子類別一次可同時繼承兩個以上的父類別;但在VC++ 2005一個類別一次只能繼承一個父類別

  27. 6. 在Visual C++如果要覆寫父類別的方法,須在父類別及子類別的方法宣告為virtaul,接著在子類別欲覆寫的方法()之後加上override關鍵字。寫法如下: ref Car(){ public: //方法之前加入virtual表示此方法可以被複寫 virtual void Move(){……} } ; ref SubCar : Car(){ public://若要複寫父類別的方法,除將方法宣告為virtual //還必須在()之後加上override virtual void Move()override{……} } ;

  28. 【程式碼】 FileName:refClass.sln 01 #include "stdafx.h" 02 03 using namespace System; 04 ref class Car{ 05 private: 06 int m_speed ; 07 08 public: 09 // 物件的建構式 #1 10 Car() { 11 m_speed = 0; 12 Console::WriteLine("初始化後速度 = {0}", m_speed); 13 } 14 // 物件的建構式 #2 15 Car(int vSpeed) { 16 m_speed = vSpeed; 17 Console::WriteLine("初始化後速度 = {0}", m_speed); 18 }

  29. 19 protected: 20 // 物件的解構式 21 !Car(){ 22 Console::WriteLine("車子物件消滅了 ..."); 23 } 24 }; 26 void DoSomething() 27 { 28 Console::WriteLine("進入程序,並宣告 BMW 物件 ..."); 29 Car^ BMW = gcnew Car(10); //建立物件 30 Console::WriteLine("BMW 物件宣告完成,準備離開方法 ..."); 31 } 33 int main(array<System::String ^> ^args) 34 { 35 DoSomething(); 36 Console::WriteLine("宣告 Benz 物件 .."); 37 Car^ Benz = gcnew Car(); //建立物件 38 Console::WriteLine("Benz 物件宣告完成 .."); 39 Console::WriteLine("準備執行 Benz = nullptr"); 40 Benz = nullptr; //物件指標指向空值 41 Console::WriteLine("Benz=nullptr 執行完成 !!"); 42 Console::Read(); 43 return 0; 44 }

  30. 上述程式之BMW物件是在DoSomething方法中宣告的區域物件,而Benz物件則是在main函式中宣告的。結果顯示程式似乎根本就沒有執行到物件解構式 !Car • 在 .NET程式中,解構式在物件消滅時是一定會執行的,但是無法預期它什麼時候執行,唯一能確定的是,.NET Framework一定會在之後的某一個時間執行它 • 為何 .NET Framework對解構式的執行要設計成如此?這與 .NET Framework內部的垃圾收集 (Garbage Collection) 機制有關,為了程式執行效率,.NET Framework中的Garbage Collection Process會在記憶體不足或程式結束時才會回收物件的記憶體,Benz = nullptr;敘述只是將Benz這個物件的參考(reference)釋放掉,並不見得會回收Benz原本所指的物件。所以當結束執行本範例時,如下圖主控台視窗會顯示兩行「車子物件消滅了…」的訊息,接著再關掉主控台視窗。

  31. 9.4 Visual C++ 2005屬性的建立 9.4.1 property 屬性的建立 • 第七章 使用方法(成員函式)的方式來設定屬性值,這種方式有個小缺點,在設定屬性時必須用呼叫方法加參數的方式 • 例: 物件->方法(500); 不能直接使用指定屬性值方式:物件->屬性 = 500,不很方便 • 以往 C++ 中是使用這種方式設定屬性值。現在VC++ 可用 property 建立屬性,語法: property 資料型別 屬性名稱 { //屬性區塊 void set(資料型別); // set為設定屬性區塊 資料型別 get(); // get為取得屬性區塊 }

  32. 【程式碼】 FileName:PropertyUsing_get_set.sln 01 # include "stdafx.h" 03 using namespace System; 04 ref class Car { 05 private: 06 int m_speed; 07 08 public: 09 // 設定Speed屬性 10 property int Speed { 11 int get() { 12 return m_speed; // 傳回屬性值 13 } 14 void set(int value){ 15 if (value < 0) { // 速度不得低於 0 16 value = 0; 17 }

  33. 18 if (value > 200) {// 速度不得高於 200 19 value = 200; 20 } 21 m_speed = value; // 設定屬性值 22 } 23 } 24 }; 26 int main (array<System::String ^> ^args) 27 { 28 Car^ Benz = gcnew Car(); 29 Benz->Speed = 500; // 速度值超過 200 30 Console::WriteLine(Benz->Speed); 31 Console::Read(); 32 return 0; 33 }

  34. 9.4.2 如何建立唯讀(ReadOnly)屬性 • 方法之一是使用上述的 Speed 屬性中,在set {…} 區段中不加入任何程式碼或只加入顯示錯誤訊息程式 • 可做出唯讀屬性,但在程式編譯階段,C++編譯器並不會對 Benz->Angle = 180; 之類的設定屬性敘述,出現任何錯誤或警告訊息 • 因語法並無任何錯誤,標準作法是在屬性定義中只能出現 get {…} 區段,絕不能加入 set {…} 區段

  35. 【程式碼】 FileName:ReadOnlyProperty.sln 01 #include "stdafx.h" 03 using namespace System; 04 ref class Car { 05 private: 06 static int m_angle = 10; 07 public: 08 property int Angle { 09 int get() { 10 return m_angle; 11 } 12 } 13 }; 15 int main (array<System::String ^> ^args) 16 { 17 Car^ Benz = gcnew Car(); 18 Console::WriteLine(Benz->Angle); 19 Console::Read(); 20 return 0; 21 }

  36. 9.4.3 如何建立唯寫(WriteOnly)屬性 【程式碼】 FileName:WriteOnlyProperty.sln 01 #include "stdafx.h" 03 using namespace System; 04 ref class Car 05 { 06 private: 07 static bool m_turbo = false; 09 public: 10 property bool Turbo { 11 void set(bool value) { 12 m_turbo = value; 13 } 14 } 15 }; 17 int main(array<System::String ^> ^args) 18 { 19 Car^ Benz = gcnew Car(); 20 Benz->Turbo = true; 21 return 0; 22 }

  37. 9.5 Visual C++ 2005事件的建立 • 沿用之前的Car類別,假設希望當物件的Speed屬性值超過 200時,物件能夠透過事件通知我們,並將目前速度當成事件參數,以便在事件中可知目前的速度

  38. 【程式碼】 FileName:Event-1.sln 01 #include "stdafx.h" 03 using namespace System; 04 delegate void DangerEvent(int vSpeed); // 事件 delegate 型別 06 ref class Car { 07 private: 08 int m_speed; 10 public: 11 event DangerEvent^ Danger; // 宣告事件 12 property int Speed { // 定義 Speed 屬性 13 int get() { 14 return m_speed; 15 } 16 void set(int value){ 17 if (value > 200) { 18 Danger(value); // 啟動事件 19 } 20 m_speed = value; 21 } 22 } 23 };

  39. 25 void TooFast(int vSpeed) { 26 Console::WriteLine 27 ("你目前的速度是 {0},超過 200,請減速 !!!", vSpeed); 28 } 29 30 int main (array<System::String ^> ^args) 31 { 32 Car^ Benz = gcnew Car(); 33 34 // 指定 Danger 事件由 TooFast 方法來處理 35 Benz->Danger += gcnew DangerEvent(TooFast); 36 Benz->Speed = 300; 37 Console::Read(); 38 return 0; 39 }

  40. 一. 建立delegate型別 • delegate (委派)型別它可以指向方法的參考指標,也就是透過 delegate型別可以呼叫物件(執行個體)的方法,以傳回特定的資訊 • 在Visual C++中delegate型別最常應用在事件處理上。本例使用下面敘述定義事件delegate型別,其名稱為DangerEvent,事件傳入的參數為一個int整數型別。(參考第4行程式) • delegate void DangerEvent(int vSpeed); 二. 使用event敘述宣告事件 • 接著可在類別中宣告一個事件如下:(參考第11行程式) event DangerEvent^ Danger;// 宣告事件 • 此事件名稱叫做Danger,屬於DangerEvent delegate型別,且有一個整數參數vSpeed,將事件的定義單純化

  41. 三. 觸動事件 • 當物件發覺Speed屬性值超過200時,要怎樣才能觸動這個事件呢?只要在Speed屬性set {…} 區段 (設定屬性值時會執行到) 中,直接呼叫事件即可。 • 本例使用下面敘述來馬上呼叫事件。(參考第18行程式) property int Speed { // 定義 Speed 屬性 int get() { return m_speed; } void set(int value){ if (value > 200) { Danger(value); // 啟動事件 } m_speed = value; } }

  42. 四. 定義事件 • 回憶一下第六章所介紹的控制項事件,當我們在button1按鈕上按一下時,不是會觸動button1_Click事件嗎?我們須將所要執行的程式寫在button1_Click事件中 • 在自訂的物件中也是類似;我們在程式中設計了一個全域的TooFast方法,用來當做事件。既然是事件,當然參數就要和Car類別中的Danger事件定義一樣才可以,也就是都有一個vSpeed的整數參數,用來表示目前的速度。(參考第25~28行程式)

  43. 五. 指定物件發生事件所要處理的方法 • 當Speed屬性設定超過200時,物件會呼叫Danger事件,然我們的事件處理方法(函式)是TooFast …,二者何干? • 類別中所執行的是Danger事件,跟我們後來定義的TooFast方法原本並無關係,但我們須清楚定義「呼叫Danger事件其實就是呼叫TooFast方法」這項重要的事,在宣告Benz這個Car物件後必須再使用gcnew來建立屬於DangerEvent delegate型別的實體,然後將Benz物件中的Danger事件對應到TooFast方法:(第35行) Benz->Danger += gcnew DangerEvent(TooFast);

  44. 9.6 VC++ 2005 陣列的建立 • 在VC++ 2005 中 CLR 陣列為參考型別,須使用array <資料型別, 陣列維度>宣告陣列,再配合 gcnew來配置陣列記憶體空間 一. 一維陣列的宣告與初值設定 寫法1: array<int>^ a = {1,2,3,4}; 宣告整數型別a陣列 並給初值a[0]=1 ; a[1]=2 ; a[2]=3 ; a[3]=4;

  45. 9.6 VC++ 2005 陣列的建立 寫法2: 宣告整數型別 a陣列, 共 a[0]~a[3] 四個陣列元素: array<int>^ a = gcnew array<int>(4); 為 a[0]~a[3] 設定陣列元素初值: a[0]=1 ; a[1]=2 ; a[2]=3 ; a[3]=4 ;

  46. 9.6 VC++ 2005 陣列的建立 二. 二維陣列的宣告與初值設定 array<int, 2>^ a = gcnew array<int, 2>(2, 3); 宣告整數型別a陣列為2*3的二維陣列 設定二維陣列的初值: a[0,0]=1 ; a[0,1]=2 ; a[0,2]=3 ; a[1,0]=4 ; a[1,1]=5 ; a[1,2]=6;

More Related