Postupy: Definování a využívání tříd a struktur (C++/CLI)
Tento článek ukazuje, jak definovat a využívat uživatelem definované odkazové typy a typy hodnot v jazyce C++/CLI.
Vytvoření instance objektu
Odkazové typy (ref) lze vytvořit pouze na spravované haldě, nikoli v zásobníku nebo v nativní haldě. Typy hodnot se dají vytvořit v zásobníku nebo spravované haldě.
// 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;
}
Implicitně abstraktní třídy
Implicitně abstraktní třídu nelze vytvořit instanci. Třída je implicitně abstraktní v případech:
- základní typ třídy je rozhraní a
- třída neimplementuje všechny členské funkce rozhraní.
Je možné, že nebudete moct vytvářet objekty z třídy odvozené z rozhraní. Důvodem může být, že třída je implicitně abstraktní. Další informace o abstraktních třídách najdete v abstrakci.
Následující příklad kódu ukazuje, že MyClass
třídu nelze vytvořit instanci, protože funkce MyClass::func2
není implementována. Chcete-li povolit kompilaci příkladu, zrušte komentář 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.
}
Viditelnost typů
Můžete řídit viditelnost typů CLR (Common Language Runtime). Při odkazování na sestavení určujete, zda jsou typy v sestavení viditelné nebo nejsou viditelné mimo sestavení.
public
označuje, že typ je viditelný pro jakýkoli zdrojový soubor, který obsahuje #using
direktivu pro sestavení obsahující typ. private
označuje, že typ není viditelný pro zdrojové soubory, které obsahují #using
direktivu pro sestavení obsahující typ. Soukromé typy jsou však viditelné ve stejném sestavení. Ve výchozím nastavení je private
viditelnost třídy .
Ve výchozím nastavení před sadou Visual Studio 2005 měly nativní typy veřejné přístupnosti mimo sestavení. Povolte upozornění kompilátoru (úroveň 1) C4692 , abyste zjistili, kde se nesprávně používají soukromé nativní typy. Pomocí direktivy pragma make_public udělte veřejné přístupnosti nativnímu typu v souboru zdrojového kódu, který nemůžete upravit.
Další informace naleznete v tématu #using směrnice.
Následující ukázka ukazuje, jak deklarovat typy a specifikovat jejich přístupnost a pak přistupovat k těmto typům uvnitř sestavení. Pokud je sestavení s privátními typy odkazováno pomocí #using
, jsou viditelné pouze veřejné typy v sestavení.
// 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();
}
Výstup
in Public_Class
in Private_Class
in Private_Class_2
Teď přepíšeme předchozí ukázku tak, aby byla sestavena jako knihovna 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");}
};
Další ukázka ukazuje, jak přistupovat k typům mimo sestavení. V této ukázce klient využívá součást, která je integrovaná v předchozí ukázce.
// 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;
}
Výstup
in Public_Class
Viditelnost člena
Přístup k členu veřejné třídy můžete zpřístupnit v rámci stejného sestavení, který se liší od přístupu mimo sestavení pomocí párů specifikátorů public
přístupu , protected
a private
Tato tabulka shrnuje účinek různých specifikátorů přístupu:
Specifikátor | Účinnost |
---|---|
public |
Člen je přístupný uvnitř a mimo sestavení. Další informace najdete na webu public . |
private |
Člen je nepřístupný, uvnitř i mimo sestavení. Další informace najdete na webu private . |
protected |
Člen je přístupný uvnitř i mimo sestavení, ale pouze pro odvozené typy. Další informace najdete na webu protected . |
internal |
Člen je veřejný uvnitř sestavení, ale soukromý mimo sestavení. internal je kontextově citlivé klíčové slovo. Další informace najdete v tématu Klíčová slova citlivá na kontext. |
public protected -nebo- protected public |
Člen je veřejný uvnitř sestavení, ale chráněn mimo sestavení. |
private protected -nebo- protected private |
Člen je chráněn uvnitř sestavení, ale soukromý mimo sestavení. |
Následující ukázka ukazuje veřejný typ, který má členy deklarované pomocí různých specifikátorů přístupu. Pak se zobrazí přístup k těmto členům ze sestavení.
// 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();
}
Výstup
in Public_Function
in Protected_Public_Function
in Public_Protected_Function
in Internal_Function
=======================
in function of derived class
in Protected_Function
in Protected_Private_Function
in Private_Protected_Function
exiting function of derived class
=======================
Teď vytvoříme předchozí ukázku jako knihovnu DLL.
// 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("=======================");
}
};
Následující ukázka využívá komponentu vytvořenou v předchozí ukázce. Ukazuje, jak přistupovat k členům mimo sestavení.
// 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();
}
Výstup
in Public_Function
=======================
in function of derived class
in Protected_Function
in Protected_Public_Function
in Public_Protected_Function
exiting function of derived class
=======================
Veřejné a privátní nativní třídy
Na nativní typ lze odkazovat ze spravovaného typu. Například funkce ve spravovaném typu může převzít parametr, jehož typ je nativní strukturou. Pokud je spravovaný typ a funkce veřejné v sestavení, musí být také veřejný nativní typ.
// native type
public struct N {
N(){}
int i;
};
Dále vytvořte soubor zdrojového kódu, který využívá nativní typ:
// 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) {}
};
Teď zkompilujte klienta:
// compile with: /clr
#using "mcppv2_ref_class3.dll"
#include "mcppv2_ref_class3.h"
int main() {
R ^r = gcnew R;
N n;
r->f(n);
}
Statické konstruktory
Typ CLR, například třída nebo struktura, může mít statický konstruktor, který lze použít k inicializaci statických datových členů. Statický konstruktor se volá nejvýše jednou a volá se před prvním přístupem ke statickému členu typu.
Konstruktor instance se vždy spouští po statickém konstruktoru.
Kompilátor nemůže vložit volání konstruktoru, pokud třída má statický konstruktor. Kompilátor nemůže vložit volání žádné členské funkce, pokud je třída typ hodnoty, má statický konstruktor a nemá konstruktor instance. CLR může vkládat volání, ale kompilátor nemůže.
Definujte statický konstruktor jako privátní členskou funkci, protože má být volána pouze clr.
Další informace o statických konstruktorech naleznete v tématu Postupy: Definování statického konstruktoru rozhraní (C++/CLI) .
// 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();
}
Výstup
in static constructor
10
11
Sémantika this
ukazatele
Pokud k definování typů používáte C++\CLI, this
ukazatel v typu odkazu je popisovač typu. Ukazatel this
v typu hodnoty je typu vnitřní ukazatel.
Tyto různé sémantiky this
ukazatele můžou způsobit neočekávané chování při zavolání výchozího indexeru. Následující příklad ukazuje správný způsob přístupu k výchozímu indexeru v ref typu i typu hodnoty.
Další informace najdete v tématu Popisovač objektu (^) a interior_ptr (C++/CLI)
// 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();
}
Výstup
10.89
10.89
Skrýt funkce podle podpisu
Ve standardním jazyce C++ se funkce v základní třídě skryje funkcí, která má stejný název v odvozené třídě, i když funkce odvozené třídy nemá stejný druh nebo počet parametrů. Označuje se jako sémantika skrytí podle názvu . V typu odkazu se funkce v základní třídě skryje pouze funkcí v odvozené třídě, pokud je název i seznam parametrů stejný. Označuje se jako sémantika skrytí podle podpisu .
Třída je považována za třídu hide-by-signature, pokud jsou všechny jeho funkce označeny v metadatech jako hidebysig
. Ve výchozím nastavení mají hidebysig
všechny třídy vytvořené v rámci /clr
funkcí. Pokud třída obsahuje hidebysig
funkce, kompilátor neskryje funkce podle názvu v žádné přímé základní třídy, ale pokud kompilátor v řetězu dědičnosti narazí na třídu hide-by-name, pokračuje v chování skrytí podle názvu.
Pod sémantikou hide-by-signature, když je funkce volána na objektu, kompilátor identifikuje nejvíce odvozenou třídu, která obsahuje funkci, která by mohla vyhovět volání funkce. Pokud je ve třídě pouze jedna funkce, která splňuje volání, kompilátor tuto funkci zavolá. Pokud je ve třídě více než jedna funkce, která by mohla vyhovět volání, kompilátor pomocí pravidel překladu přetížení určí, která funkce se má volat. Další informace o pravidlech přetížení naleznete v tématu Přetížení funkce.
U daného volání funkce může mít funkce v základní třídě podpis, který z ní dělá trochu lepší shodu než funkce v odvozené třídě. Pokud však byla funkce explicitně volána na objekt odvozené třídy, je volána funkce v odvozené třídě.
Vzhledem k tomu, že návratová hodnota není považována za součást podpisu funkce, funkce základní třídy se skryje, pokud má stejný název a přebírá stejný druh a počet argumentů jako funkci odvozené třídy, i když se liší v typu návratové hodnoty.
Následující ukázka ukazuje, že funkce v základní třídě není skryta funkcí v odvozené třídě.
// 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();
}
Výstup
Base::Test
Další ukázka ukazuje, že kompilátor jazyka Microsoft C++ volá funkci ve většině odvozených tříd , a to i v případě, že převod musí odpovídat jednomu nebo více parametrům, a ne volání funkce v základní třídě, která je lepší shodou pro volání funkce.
// 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);
}
Výstup
Derived::Test2
Následující ukázka ukazuje, že funkci je možné skrýt i v případě, že základní třída má stejný podpis jako odvozená třída.
// 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);
}
Výstup
Derived::Test4
97
Konstruktory kopírování
Standard jazyka C++ říká, že při přesunutí objektu je volána konstruktor kopírování, aby byl objekt vytvořen a zničen na stejné adrese.
Pokud však funkce kompilovaná do jazyka MSIL volá nativní funkci, ve které je nativní třída (nebo více než jedna) předána hodnotou a kde nativní třída má konstruktor kopírování nebo destruktor, není volána žádná konstruktor kopírování a objekt je zničen na jiné adrese, než kde byla vytvořena. Toto chování může způsobit problémy, pokud má třída ukazatel na sebe nebo pokud kód sleduje objekty podle adresy.
Další informace naleznete v tématu /clr (Common Language Runtime Compilation).
Následující ukázka ukazuje, kdy není vygenerován konstruktor kopírování.
// 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);
}
Výstup
S object 0 being constructed, this=0018F378
S object 1 being constructed, this=0018F37C
S object 2 being copy constructed from S object 1, this=0018F380
S object 3 being copy constructed from S object 0, this=0018F384
S object 4 being copy constructed from S object 2, this=0018F2E4
S object 2 being destroyed, this=0018F380
S object 5 being copy constructed from S object 3, this=0018F2E0
S object 3 being destroyed, this=0018F384
in function f
S object 5 being destroyed, this=0018F2E0
S object 4 being destroyed, this=0018F2E4
S object 1 being destroyed, this=0018F37C
S object 0 being destroyed, this=0018F378
Destruktory a finalizátory
Destruktory v referenčním typu dělají deterministické vyčištění prostředků. Finalizátory vyčistí nespravované prostředky a lze je volat buď deterministicky destruktorem, nebo nedeterministicky uvolňováním paměti. Informace o destruktoru ve standardním jazyce C++ naleznete v tématu Destruktory.
class classname {
~classname() {} // destructor
! classname() {} // finalizer
};
Uvolňování paměti CLR odstraní nepoužívané spravované objekty a uvolní jejich paměť, když už nejsou potřeba. Typ ale může používat prostředky, které systém uvolňování paměti nezná. Tyto prostředky se označují jako nespravované prostředky (například nativní popisovače souborů). Doporučujeme uvolnit všechny nespravované prostředky v finalizátoru. Systém uvolňování paměti uvolní spravované prostředky nedeterministicky, takže není bezpečné odkazovat na spravované prostředky v finalizátoru. Je to proto, že je možné, že je systém uvolňování paměti už vyčistil.
Finalizační metoda Visual C++ není stejná jako Finalize metoda. (Dokumentace CLR používá finalizátor a metodu Finalize synonyma). Metoda Finalize je volána uvolňováním paměti, která volá každou finalizační metodu v řetězu dědičnosti tříd. Na rozdíl od destruktorů jazyka Visual C++ volání finalizátoru odvozené třídy nezpůsobí, že kompilátor vyvolá finalizátor ve všech základních třídách.
Vzhledem k tomu, že kompilátor Microsoft C++ podporuje deterministické vydání prostředků, nepokoušejte se implementovat Dispose ani Finalize metody. Pokud ale znáte tyto metody, tady je postup finalizátoru Visual C++ a destruktoru, který volá mapování finalizátoru na Dispose vzor:
// 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();
}
}
Spravovaný typ může také používat spravované prostředky, které byste chtěli vydat deterministicky. Možná nebudete chtít, aby systém uvolňování paměti vystavil objekt nedeterministicky v určitém okamžiku, jakmile už objekt není vyžadován. Deterministické uvolnění prostředků může výrazně zlepšit výkon.
Kompilátor jazyka Microsoft C++ umožňuje definici destruktoru deterministicky vyčistit objekty. Destruktor použijte k uvolnění všech prostředků, které chcete deterministicky uvolnit. Pokud je k dispozici finalizátor, zavolejte ho z destruktoru, abyste se vyhnuli duplikaci kódu.
// 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
// ...
}
};
Pokud kód, který využívá váš typ, nevolá destruktor, uvolňování paměti nakonec uvolní všechny spravované prostředky.
Přítomnost destruktoru neznamená přítomnost finalizátoru. Přítomnost finalizátoru však znamená, že je nutné definovat destruktor a volat finalizační metodu z tohoto destruktoru. Toto volání poskytuje deterministické vydání nespravovaných prostředků.
Volání destruktoru potlačuje finalizaci objektu ( pomocí SuppressFinalize). Pokud není destruktor volána, finalizátor vašeho typu bude nakonec volána uvolňováním paměti.
Výkon můžete zlepšit voláním destruktoru, který deterministicky vyčistí prostředky objektu, namísto toho, aby clR nedeterministicky finalizoval objekt.
Kód napsaný v jazyce Visual C++ a zkompilovaný pomocí spuštění /clr
destruktoru typu, pokud:
Objekt vytvořený pomocí sémantiky zásobníku je mimo rozsah. Další informace naleznete v tématu Sémantika zásobníku C++ pro odkazové typy.
Při konstrukci objektu se vyvolá výjimka.
Objekt je členem objektu, jehož destruktor je spuštěn.
Volání operátoru delete na popisovači (popisovač objektu – operátor ^)).
Explicitně voláte destruktor.
Pokud klient napsaný v jiném jazyce váš typ využívá, zavolá se destruktor následujícím způsobem:
Na volání Dispose.
Při volání
Dispose(void)
typu.Pokud typ v příkazu jazyka C#
using
přestane být oborem.
Pokud nepoužíváte sémantiku zásobníku pro odkazové typy a vytváříte objekt typu odkazu ve spravované haldě, použijte syntaxi try-finally , abyste zajistili, že výjimka nezabrání spuštění destruktoru.
// compile with: /clr
ref struct A {
~A() {}
};
int main() {
A ^ MyA = gcnew A;
try {
// use MyA
}
finally {
delete MyA;
}
}
Pokud váš typ má destruktor, kompilátor vygeneruje metodu Dispose
, která implementuje IDisposable. Pokud je typ napsaný v jazyce Visual C++ a má destruktor, který je spotřebován z jiného jazyka, volání IDisposable::Dispose
na tento typ způsobí volání destruktoru typu. Pokud je typ spotřebován z klienta Visual C++, nemůžete přímo volat Dispose
; místo toho zavolejte destruktor pomocí operátoru delete
.
Pokud váš typ má finalizátor, kompilátor vygeneruje metodu Finalize(void)
, která přepíše Finalize.
Pokud má typ finalizátor nebo destruktor, kompilátor vygeneruje metodu Dispose(bool)
podle vzoru návrhu. (Informace najdete v tématu Dispose Pattern). V jazyce Visual C++ nemůžete explicitně vytvářet ani volat Dispose(bool)
.
Pokud typ má základní třídu, která odpovídá vzoru návrhu, destruktory pro všechny základní třídy jsou volány při zavolání destruktoru pro odvozenou třídu. (Pokud je váš typ napsaný v jazyce Visual C++, kompilátor zajistí, aby vaše typy implementovaly tento vzor.) Jinými slovy, destruktor referenční třídy řetězí na své základny a členy, jak je specifikováno standardem C++. Nejprve se spustí destruktor třídy. Potom se destruktory členů spustí v obráceném pořadí, ve kterém byly sestaveny. Nakonec destruktory pro své základní třídy se spustí v obráceném pořadí, ve kterém byly sestaveny.
Destruktory a finalizátory nejsou povoleny uvnitř typů hodnot nebo rozhraní.
Finalizátor lze definovat nebo deklarovat pouze v referenčním typu. Stejně jako konstruktor a destruktor nemá finalizátor žádný návratový typ.
Po spuštění finalizátoru objektu se finalizační metody v jakékoli základní třídě také nazývají, počínaje nejméně odvozeným typem. Finalizační metody datových členů nejsou automaticky zřetězenými finalizátorem třídy.
Pokud finalizátor odstraní nativní ukazatel ve spravovaném typu, je nutné předčasně shromáždit odkazy na nebo přes nativní ukazatel. Místo použití KeepAlivevolejte destruktor u spravovaného typu .
V době kompilace můžete zjistit, jestli má typ finalizátor nebo destruktor. Další informace naleznete v tématu Podpora kompilátoru pro vlastnosti typu.
Následující ukázka ukazuje dva typy: jeden, který má nespravované prostředky, a jeden, který má spravované prostředky, které se vydávají deterministicky.
// 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");
}