共用方式為


如何: 執行個體化類別和結構

本文說明如何定義與利用在 C++/CLI中使用者定義的參考型別和實值型別。

內容

物件執行個體化

隱含抽象類別

類型可視性

成員可視性

公用與私用的原生類別

靜態建構函式

這個指標的語意。

依簽名隱藏的函式

複製建構函式

解構函式與完成項

物件執行個體化

參考(ref)型別和實值型別只能在 Managed 堆積上為實例化,既不能在堆疊上也不能在原生堆積上為實例化。

// mcppv2_ref_class2.cpp
// compile with: /clr
ref class MyClass {
public:
   int i;

   // nested class
   ref class MyClass2 {
   public:
      int i;
   };

   // nested interface
   interface struct MyInterface {
      void f();
   };
};

ref class MyClass2 : public MyClass::MyInterface {
public:
   virtual void f() {
      System::Console::WriteLine("test");
   }
};

public value struct MyStruct {
   void f() {
      System::Console::WriteLine("test");
   }   
};

int main() {
   // instantiate ref type on garbage-collected heap
   MyClass ^ p_MyClass = gcnew MyClass;
   p_MyClass -> i = 4;

   // instantiate value type on garbage-collected heap
   MyStruct ^ p_MyStruct = gcnew MyStruct;
   p_MyStruct -> f();

   // instantiate value type on the stack
   MyStruct p_MyStruct2;
   p_MyStruct2.f();

   // instantiate nested ref type on garbage-collected heap
   MyClass::MyClass2 ^ p_MyClass2 = gcnew MyClass::MyClass2;
   p_MyClass2 -> i = 5;
}

隱含抽象類別

隱含抽象類別 不能為具現化。 如果類別的基底型別為介面,且類別不會實作介面所有的成員函式,則類別為隱含抽象類別。

如果您無法建構衍生自介面的類別之物件,原因可能是其類別為隱含抽象類別。 如需抽象類別的詳細資訊,請參閱abstract

下列程式碼範例示範MyClass 類別無法執行具體化,原因是因為函式 MyClass::func2 未實作。 為了編譯此範例,請取消 MyClass::func2的註釋。

// mcppv2_ref_class5.cpp
// compile with: /clr
interface struct MyInterface {
   void func1();
   void func2();
};

ref class MyClass : public MyInterface {
public:
   void func1(){}
   // void func2(){}
};

int main() {
   MyClass ^ h_MyClass = gcnew MyClass;   // C2259 
                                          // To resolve, uncomment MyClass::func2.
}

類型可視性

您可以控制 Common Language Runtime (CLR) 型別的可視性,以便您如果要參考組譯碼,組譯碼中的型別可見於或不可見於組譯碼外。

public 表示型別可見於包含 #using 指示詞且包含型別的組件之所有原始程式檔。 private 表示型別不可見於包含 #using 指示詞且包含型別的組件之原始程式檔。 不過,私用(private)型別可見於相同的組譯碼。 根據預設,類別的可視性則為 private。

根據在 Visual C++ 2005 以前的預設,原生型別在組譯碼外部有公用存取範圍。 可讓編譯器警告 (層級 1) C4692 協助您查看私用原生型別在何處不正確地使用。 在不能被修改的原始程式碼檔案中,請使用 make_public Pragma 來提供公用存取範圍至原生型別。

如需詳細資訊,請參閱#using 指示詞 (C++)

下列範例顯示如何宣告型別以及指定其存取範圍,然後存取組譯碼中的型別。 當然,若組譯碼有因使用#using為參考的私用型別,只有在其組譯碼中的公用型別是可見的。

// type_visibility.cpp
// compile with: /clr
using namespace System;
// public type, visible inside and outside assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// default accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   Private_Class ^ b = gcnew Private_Class;
   b->Test();

   Private_Class_2 ^ c = gcnew Private_Class_2;
   c->Test();
}

Output

  

現在,我們重新撰寫上一個範例,使其建置為 DLL。

// type_visibility_2.cpp
// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside the assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// by default, accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

下一個範例顯示如何存取在組譯碼外的型別。 在此範例中,用戶端會使用前一個範例所內建的元件。

// type_visibility_3.cpp
// compile with: /clr
#using "type_visibility_2.dll"
int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   // private types not accessible outside the assembly
   // Private_Class ^ b = gcnew Private_Class;
   // Private_Class_2 ^ c = gcnew Private_Class_2;
}

Output

  

成員可視性

您可以使至公用類別成員的存取自相同組件組譯碼內,不同於使用存取規範 public、 protected和 private自組譯碼外部存取之。

下表摘要說明各種存取規範的效果:

指定名稱

作用

public

成員存取於組譯碼內部及外部。如需詳細資訊,請參閱public (C++)

private

成員皆無法存取於組譯碼內外。如需詳細資訊,請參閱private (C++)

protected

成員可存取於組譯碼內部和外部,但僅限於自衍生的型別。如需詳細資訊,請參閱protected (C++)

internal

成員在組譯碼內為公用,在組譯碼外為私用。 internal 是內容敏感的關鍵字。如需詳細資訊,請參閱視內容而有所區別的關鍵字 (C++ 元件擴充功能)

public protected
-or-
protected public

成員在組譯碼為公用,但是在組譯碼之外為受保護。

private protected
-or-
protected private

成員在組譯碼為受保護,但是在組譯碼之外為私用。

下列範例顯示具有宣告於不同存取範圍的成員之公用型別,然後顯示這些成員於組譯碼中的存取。

// type_member_visibility.cpp
// compile with: /clr
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();
   a->Protected_Public_Function();
   a->Public_Protected_Function();

   // accessible inside but not outside the assembly
   a->Internal_Function();

   // call protected functions
   b->Test();

   // not accessible inside or outside the assembly
   // a->Private_Function();
}

Output

  

現在我們建置前一個範例為 DLL。

// type_member_visibility_2.cpp
// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

下列範例使用前一個範例所建立的元件,藉此示範如何從組譯碼以外存取成員。

// type_member_visibility_3.cpp
// compile with: /clr
#using "type_member_visibility_2.dll"
using namespace System;
// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Public_Function();
      Public_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();

   // call protected functions
   b->Test();

   // can't be called outside the assembly
   // a->Private_Function();
   // a->Internal_Function();   
   // a->Protected_Private_Function();
   // a->Private_Protected_Function();
}

Output

  

公用(public)與私用(private)的原生類別

原生型別可以從 Managed 型別參考。例如,在 Managed 型別中的函式可以接受型別為原生結構的參數。如果 Managed 型別和函式在組譯碼中為公用,則原生型別也必須為公用。

// mcppv2_ref_class3.h
// native type
public struct N {
   N(){}
   int i;
};

接下來,建立使用原生型別的原始程式碼檔:

// mcppv2_ref_class3.cpp
// compile with: /clr /LD
#include "mcppv2_ref_class3.h"
// public managed type
public ref struct R {
   // public function that takes a native type
   void f(N nn) {}
};

現在,請編譯用戶端:

// mcppv2_ref_class4.cpp
// compile with: /clr
#using "mcppv2_ref_class3.dll"

#include "mcppv2_ref_class3.h"

int main() {
   R ^r = gcnew R;
   N n;
   r->f(n);
}

靜態建構函式

提供 CLR 型別,例如類別或結構,其可以具有可用於初始化靜態資料成員的靜態建構函式。靜態建構函式最多被呼叫一次,且被呼叫於型別中的靜態成員第一次被存取前。

執行個體建構函式總是執行於靜態建構函式之後。

如果類別有靜態建構函式,編譯器無法內嵌建構函式的呼叫。如果類別是實值型別 (Value Type),並具有靜態建構函式,且不具有執行個體建構函式,編譯器無法內嵌任何成員函式的呼叫。CLR 可以內嵌呼叫,但編譯器無法。

因為此僅能由 CLR所呼叫,請定義靜態建構函式為私用成員函式。

如需靜態建構函式的詳細資訊,請參閱如何:定義介面靜態建構函式 (C++/CLI)

// mcppv2_ref_class6.cpp
// compile with: /clr
using namespace System;

ref class MyClass {
private:
   static int i = 0;

   static MyClass() {
      Console::WriteLine("in static constructor");
      i = 9;
   }

public:
   static void Test() {
      i++;
      Console::WriteLine(i);
   }
};

int main() {
   MyClass::Test();
   MyClass::Test();
}

Output

  

這個指標的語意。

當您使用 Visual C++ 來定義型別時,在參考型別上的 this 指標型別為「handle」。 在實值型別上 this 指標型別為「內部指標」。

當預設索引子為呼叫時,這些 this 指標的額外語意可能會造成非預期行為。 下一個範例顯示如何正確存取參考型別和實值型別兩者的預設索引子。

如需詳細資訊,請參閱

// semantics_of_this_pointer.cpp
// compile with: /clr
using namespace System;

ref struct A {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }

   A() {
      // accessing default indexer
      Console::WriteLine("{0}", this[3.3]);
   }
};

value struct B {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }
   void Test() {
      // accessing default indexer
      Console::WriteLine("{0}", this->default[3.3]);
   }
};

int main() {
   A ^ mya = gcnew A();
   B ^ myb = gcnew B();
   myb->Test();
}

Output

  

按簽名隱藏的函式

在 Standard C++中,在基底類別中的函式會被衍生類別中相同名稱的函式隱藏,即使衍生類別函式沒有相同類型或同數目的參數。 這稱為 按名稱隱藏 語意。 在參考型別中,如果名稱和參數清單皆完全相同,則在基底類別中的函式只能被衍生類別中的函式隱藏。 這稱為 按簽章名隱藏 語意。

當其所有函式在中繼資料中被標記為 hidebysig時,類別會視為依簽章隱藏類別。 根據預設,於 /clr 建立的所有類別皆具有 hidebysig 函式。 然而,使用 /clr:oldSyntax 編譯的類別沒有 hidebysig 函式;相反地,它們是依名稱隱藏函式。 如果類別具有 hidebysig 函式時,編譯器不會在任何直接基底類別依名稱來隱藏函式,但是,如果編譯器在繼承鏈結遇到依名稱隱藏類別,則會繼續該依名稱隱藏行為。

在依簽章隱藏語意下,當函式為物件所呼叫時,編譯器會識別大多數包含可以滿足函式呼叫的函式的衍生類別。 如果類別中只有一個可以滿足呼叫的函式,編譯器將呼叫該函式。 如果類別中有多個可以滿足呼叫的函式,編譯器會使用多載解析規則來決定呼叫哪一個函式。 如需多載規則的詳細資訊,請參閱函式多載

對於受指定的函式呼叫,基底類別中的函式可能有簽章,使其與衍生類別中的函式相較下稍微好比對。 不過,如果函式已明確地為衍生類別的物件所呼叫,在衍生類別中的函式為呼叫。

因為傳回值不會被視為函式簽章的一部分,如果其具有相同名稱並且接受相同種類與相同數目的引數為衍生類別函式,基底類別函式則已被隱藏,即使其不同於傳回值的型別。

下列範例顯示在基底類別中的函式不是由衍生類別的函式所隱藏。

// hide_by_signature_1.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test() { 
      Console::WriteLine("Base::Test"); 
   }
};

ref struct Derived : public Base {
   void Test(int i) { 
      Console::WriteLine("Derived::Test"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Test() in the base class will not be hidden
   t->Test();
}

Output

  

下一個範例示範 Visual C++ 編譯器會呼叫衍生類別一致的函式——即使需要轉換來比對一或多個參數——並且不呼叫基底類別中的函式,其為較好的函式呼叫的比對。

// hide_by_signature_2.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test2(Single d) { 
      Console::WriteLine("Base::Test2"); 
   }
};

ref struct Derived : public Base {
   void Test2(Double f) { 
      Console::WriteLine("Derived::Test2"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Base::Test2 is a better match, but the compiler
   // calls a function in the derived class if possible
   t->Test2(3.14f);
}

Output

  

下列範例顯示隱藏函式是可行的,即使基底類別有同於衍生類別的簽章。

// hide_by_signature_3.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   int Test4() { 
      Console::WriteLine("Base::Test4"); 
      return 9; 
   }
};

ref struct Derived : public Base {
   char Test4() { 
      Console::WriteLine("Derived::Test4"); 
      return 'a'; 
   }
};

int main() {
   Derived ^ t = gcnew Derived;

   // Base::Test4 is hidden
   int i = t->Test4();
   Console::WriteLine(i);
}

Output

  

下列範例定義了使用 /clr:oldSyntax所編譯的元件。 使用 Managed Extensions for C++ 所定義的類別具有依名稱隱藏成員函式。

// hide_by_signature_4.cpp
// compile with: /clr:oldSyntax /LD
using namespace System;
public __gc struct Base0 {
   void Test() { 
      Console::WriteLine("in Base0::Test");
   }
};

public __gc struct Base1 : public Base0 {
   void Test(int i) { 
      Console::WriteLine("in Base1::Test");
   }
};

下一個範例是使用前一個範例內建的元件。 請注意依簽章隱藏功能如何不套用至使用 /clr:oldSyntax所編譯基底類別的型別。

// hide_by_signature_5.cpp
// compile with: /clr:oldSyntax /LD
// compile with: /clr
using namespace System;
#using "hide_by_signature_4.dll"

ref struct Derived : public Base1 {
   void Test(int i, int j) { 
      Console::WriteLine("Derived::Test");
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   t->Test(8, 8);   // OK
   t->Test(8);   // OK
   t->Test();   // C2661
}

複製建構函式

C++ 標準表示當物件移動時,建構函式副本為呼叫,這類物件建立和終結皆於相同的位址。

不過, 當**/clr** 用於編譯,且受編譯為MSIL的函式呼叫原生函式至其原生類別——可能不只一個——以傳值方式傳遞,且原生類別具有建構函式副本和(或)解構函式副本的地方時,建構函式副本未受呼叫,而物件終結在不同於所建立地方的位址。 如果類別有指向自己的指標,或如果程式碼以位址追蹤物件,這可能會造成問題。

如需詳細資訊,請參閱/clr (Common Language Runtime 編譯)

下列範例示範當建構函式副本不為產生時。

// breaking_change_no_copy_ctor.cpp
// compile with: /clr
#include<stdio.h>

struct S {
   int i;
   static int n;

   S() : i(n++) { 
      printf_s("S object %d being constructed, this=%p\n", i, this); 
   }

   S(S const& rhs) : i(n++) { 
      printf_s("S object %d being copy constructed from S object "
               "%d, this=%p\n", i, rhs.i, this); 
   }

   ~S() {
      printf_s("S object %d being destroyed, this=%p\n", i, this); 
   }
};

int S::n = 0;

#pragma managed(push,off)
void f(S s1, S s2) {
   printf_s("in function f\n");
}
#pragma managed(pop)

int main() {
   S s;
   S t;
   f(s,t);
}

Output

  

解構函式與完成項

在參考型別的解構函式實作資源判斷清除。 完成項來清除 Unmanaged 資源,而且可以決定性地由解構函式所呼叫,或非決定性由地記憶體回收行程所呼叫。 如需標準 C++ 中解構函式的詳細資訊,請參閱 解構函式 (C++)

class classname {
   ~classname() {}   // destructor
   ! classname() {}   // finalizer
};

在 Managed Visual C++ 類別的解構函式行為不同於其在 Managed Extensions for C++ 。 如需這項變更的詳細資訊,請參閱解構函式語意的變更

當其不再需要時, CLR 記憶體回收行程刪除未使用的 Managed 物件並釋放其記憶體。 不過,型別可以使用記憶體回收行程不會釋放的資源。 這些資源稱為 Unmanaged 資源 (例如原生檔案控制代碼)。 我們建議您釋放在完成項中所有 Unmanaged 資源。 由於記憶體回收行程非判斷性地釋放 Managed 資源,在完成項選用 Managed 資源很不安全,因為記憶體回收行程很有可能已清除 Managed 資源。

Visual C++ 完成項不同於 Finalize 方法。(CLR 文件相同地使用完成項和 Finalize 方法)。 Finalize 方法由記憶體回收行程呼叫,其在類別繼承鏈結叫用每個完成項。 不同於 Visual C++ 解構函式,衍生類別完成項呼叫不會使編譯器叫用所有基底類別的完成項。

由於 Visual C++ 編譯器支援資源的決定版本,請勿嘗試執行 Dispose方法或 Finalize 方法。 不過,如果您熟悉這些方法,這裡有Visual C++ 完成項與其呼叫完成項對應至 Dispose 樣式的解構函式:

// Visual C++ code
ref class T {
   ~T() { this->!T(); }   // destructor calls finalizer
   !T() {}   // finalizer
};

// equivalent to the Dispose pattern
void Dispose(bool disposing) {
   if (disposing) {
      ~T();
   } else {
      !T();
   }
}

在不再需要物件之後,Managed 型別也可以使用您想判斷性地釋出的 Managed 資源,和不讓任何記憶體回收行程非判斷性地釋放。 資源判斷版本可以大幅改善效能。

Visual C++ 編譯器可讓解構函式的定義決定性地清除物件。 使用解構函式來釋放您希望決定性地釋放的任何資源。如果完成項存在,請從解構函式呼叫它以避免程式碼的複本。

// destructors_finalizers_1.cpp
// compile with: /clr /c
ref struct A {
   // destructor cleans up all resources
   ~A() {
      // clean up code to release managed resource
      // ...
      // to avoid code duplication, 
      // call finalizer to release unmanaged resources
      this->!A();
   }

   // finalizer cleans up unmanaged resources
   // destructor or garbage collector will
   // clean up managed resources
   !A() {
      // clean up code to release unmanaged resources
      // ...
   }
};

如果使用您的型別之程式碼不會呼叫解構函式,記憶體回收行程最終釋放所有的 Managed 資源。

解構函式的出現不表示完成項的發生。 不過,完成項的出現提示您必須定義解構函式及呼叫該解構函式的完成項。 這提供 Unmanaged 資源的決定版本。

由使用 SuppressFinalize對解構函式的呼叫隱藏物件的最終版。 如果解構函式未被呼叫,型別的完成項最終將會由記憶體回收行程所呼叫。

透過呼叫解構函式以決定性地清除物件的資源,相較於讓 CLR 非決定性地完成物件,前者可以改善效能。

撰寫於 Visual C++ ,並使用 /clr 執行型別的解構函式編譯的程式碼,如果發生以下:

如果您的型別由另一種語言所撰寫的用戶端所使用,解構函式將如下所示為呼叫:

  • 在對 Dispose的呼叫。

  • 在型別上對 Dispose(void) 的呼叫。

  • 如果型別超出 C# 中的 using 陳述式的範圍。

如果您建立一個參考型別的物件在 Managed 堆積上 (不使用參考型別的堆疊語意),請使用 try-finally 語言以確保例外狀況不會妨礙解構函式執行。

// clr_destructors.cpp
// compile with: /clr
ref struct A {
   ~A() {}
};

int main() {
   A ^ MyA = gcnew A;
   try {
      // use MyA
   }
   finally {
      delete MyA;
   }
}

如果型別具有解構函式,編譯器會產生 Dispose 方法,其實作 IDisposable。 如果撰寫於 Visual C++ 而有從其他語言中使用的解構函式之型別,在其型別上呼叫IDisposable::Dispose 會產生該型別的解構函式之呼叫。 當型別在 Visual C++ 用戶端中使用,您不能直接呼叫 Dispose;不過,您可以使用 delete 運算子呼叫解構函式。

如果您的型別具有完成項,編譯器會產生覆寫 Finalize上的 Finalize(void) 方法。

如果型別具有完成項或解構函式這兩者之一,編譯器會根據設計模式產生 Dispose(bool) 方法。如需詳細資訊,請參閱 Implementing Finalize and Dispose to Clean Up Unmanaged Resources。 您不能明確建立或呼叫在 Visual C++ 的 Dispose(bool)

如果型別具有符合設計模式的基底類別,所有基底類別的解構函式在衍生類別中的解構函式呼叫時皆為呼叫。(如果您的型別以 Visual C++ 撰寫,編譯器會確保您的型別會實作此模式)。換句話說,參考類別中的解構函式依 C++ 標準所指定,鏈結至其基底和成員——首先執行類別的解構函式,然後依其建構順序的反向執行其成員的解構函式,最後依其建構順序的反向執行其基底類別的解構函式。

在實值型別內或介面內不允許使用解構函式與完成項。

完成項只能在參考型別上定義或宣告。 如同建構函式和解構函式,完成項沒有傳回型別。

在物件的完成項執行後,從最少衍生型別開始呼叫在任何基底類別的完成項。 資料成員的完成項不會自動繫結至類別的完成項。

如果完成項刪除 Managed 型別中的原生指標,您必須確定原生指標的參考沒有提前收集;呼叫 Managed 型別的解構函式而不是使用 KeepAlive

在編譯時期,您可以偵測型別是否含有完成項或解構函式。 如需詳細資訊,請參閱類型特性的編譯器支援 (C++ 元件擴充功能)

下一個範例會顯示兩個型別,其一有 Unmanaged 資源而另一有 Managed 資源,此二為決定性地釋出。

// destructors_finalizers_2.cpp
// compile with: /clr
#include <vcclr.h>
#include <stdio.h>
using namespace System;
using namespace System::IO;

ref class SystemFileWriter {
   FileStream ^ file;
   array<Byte> ^ arr;
   int bufLen;

public:
   SystemFileWriter(String ^ name) : file(File::Open(name, FileMode::Append)), 
                                     arr(gcnew array<Byte>(1024)) {}

   void Flush() {
      file->Write(arr, 0, bufLen);
      bufLen = 0;
   }

   ~SystemFileWriter() {
      Flush();
      delete file;
   }
};

ref class CRTFileWriter {
   FILE * file;
   array<Byte> ^ arr;
   int bufLen;

   static FILE * getFile(String ^ n) {
      pin_ptr<const wchar_t> name = PtrToStringChars(n);
      FILE * ret = 0;
      _wfopen_s(&ret, name, L"ab");
      return ret;
   }

public:
   CRTFileWriter(String ^ name) : file(getFile(name)), arr(gcnew array<Byte>(1024) ) {}

   void Flush() {
      pin_ptr<Byte> buf = &arr[0];
      fwrite(buf, 1, bufLen, file);
      bufLen = 0;
   }

   ~CRTFileWriter() {
      this->!CRTFileWriter();
   }

   !CRTFileWriter() {
      Flush();
      fclose(file);
   }
};

int main() {
   SystemFileWriter w("systest.txt");
   CRTFileWriter ^ w2 = gcnew CRTFileWriter("crttest.txt");
}

請參閱

參考

類別和結構 (C++ 元件擴充功能)

類別和結構 (C++ 元件擴充功能)