Практическое руководство. Определение и использование классов и структур (C++/CLI)
В данной статье показано, как определять и использовать определяемые пользователем типы значений и ссылочные типы в C++/CLI.
Создание объектов
Типы и типы значений (ref) ссылки можно создавать только в управляемой куче, не в стеке или в собственной куче.
// 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.
}
Видимость типа
Элемент управления можно видимость (CLR) типов среды CLR, что, если сборку, типы в сборке могут быть видны или не видны вне сборки.
public указывает, что тип является видимым в любой файл источника, который содержит директиву #using для сборки, содержащей тип.private указывает, что тип не отображается в файлы источника, которые содержат директиву #using для сборки, содержащей тип.Однако закрытым типам видны в пределах одной сборки.По умолчанию видимость для класса private.
По умолчанию в Visual C-++ 2005 C, собственные типы имеют общую специальных возможностей за пределами сборки.Предупреждение компилятора (уровень 1) C4692 может помочь понять, где закрытые собственные типы используются неправильно.Используйте директиву pragma make_public, чтобы получить открытую специальных возможностей в собственный тип в файле исходного кода, нельзя изменять.
Дополнительные сведения см. в разделе Директивы #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 (C++). |
|
Участник недоступен, а внутри и вне сборки.Дополнительные сведения см. в разделе private (C++). |
|
Член доступные внутри и вне сборки, но только к производным типам.Дополнительные сведения см. в разделе protected (C++). |
|
Член является общим внутри сборки, но в этом случае за пределами сборки.контекстно-зависимое ключевое слово internal.Для получения дополнительной информации см. Контекстные ключевые слова (расширения компонентов C++). |
|
Член является общим внутри сборки, но защищен за пределами сборки. |
|
Член защищен закрытый внутри сборки, но за пределами сборки. |
В следующем примере показано открытый тип, который имеет члены, объявленные с различными специальными возможностями и затем отображает получения этих членов внутри сборки.
// 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
Открытые и закрытые собственные классы
Собственный тип можно ссылаться из управляемого типа.Например, функция в управляемом типе может принимать параметр, тип которого собственная структура.Если управляемый тип и функция открытым в сборке, собственный тип также должен быть открытым.
// 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 тип- для примера, класса или структуры являются может иметь статический конструктор, который можно использовать для инициализации статические члены данных.Вызывается не более одного раза, и вызывает статический конструктор до любого статический член типа получен в первый раз.
Конструктор экземпляра всегда выполняется после статического конструктора.
Компилятор не может последовательно вызов конструктора, если класс имеет статический конструктор.Компилятор не может последовательно вызов любой функцию-член, если класс тип значения, имеет статический конструктор, и нет конструктора экземпляра.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 C-++ для определения типов, указатель this в ссылочный тип дескриптора типа "".Указатель this в типе значения типа "внутреннего указателя".
Семантика этого является указателем this может вызвать непредвиденное расширение функциональности при вызове индексатора по умолчанию.В следующем примере показано, как правильно подключитесь к нему по умолчанию как в ссылочного типа, так и в списке значений.
Дополнительные сведения см. в следующем разделе.
Оператор дескриптора объекта (^) (расширения компонентов C++)
Практическое руководство. Использование индексированных свойств
// 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
Функции Мостовь--сигнатуры
В стандартном языке C, C-++, функция в базовом классе скрыта функцией с тем же именем в производном классе, даже если функция производного класса не имеют одинаковое число или тип параметров.Это называется семантика мостовь -- имени.В ссылочного типа, функция в базовом классе может быть скрыта функцией в производном классе, и имя и список параметров совпадают.Это как семантика мостовь-- сигнатуры.
Класс считается классом мостовь -- сигнатуры, когда все свои функций помечены в метаданных как hidebysig.По умолчанию все классы, созданные в /clr имеют функции hidebysig.Однако класс, компилироваться с помощью /clr:oldSyntax не имеет функции hidebysig; вместо этого они функции мостовь -- имени.Если класс имеет функции hidebysig, компилятор не скрывает функцию по имени во всех прямых базовым классом, но если компилятор обнаруживает класс мостовь -- name в цепочке наследования, продолжается это расширение функциональности мостовь -- имени.
В мостовь семантикой -- сигнатуры, когда функция вызывается в объекте, компилятор определяет наиболее производный класс, который содержит функцию, которая может быть вызов функции.Если только одной функции в классе, который может быть вызов, компилятор вызывает эту функцию.Если несколько функций в классе, который может быть вызов, компилятор использует правила разрешения перегруженных версий, определить функцию вызвать.Дополнительные сведения о правилах перегруженной см. в разделе Перегружать функции.
Для вызова указанной функции функции в базовом классе может иметь подпись, которая позволяет использовать несколько является более подходящим, чем функция в производном классе.Однако если функция явно называется объекта производного класса, то вызывается функция в производном классе.
Так как возвращаемое значение не является частью сигнатуры функции, функция базового класса скрыта, если она имеет то же имя и принимает те же количество и тип аргументов, как функция производного класса, даже если она отличается в типе возвращаемого значения.
В следующем примере показывается, что функция в базовом классе не скрыта функцией в производном классе.
// 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-++ 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.Классы, которые определяются с помощью управляемых расширений для C-++ имеют 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 C-++ говорится, что конструктор копии при перемещении объекта, такие как объект создан и разрушан на этих же адрес.
Однако если /clr используется компилироваться и функцию, скомпилирована в MSIL вызывает собственная функция, собственный класса или более чем от передает значение и где собственный класс содержит конструктор копии и/или деструктор, не вызывают отсутствует конструктор копий и объект уничтожается в другой адреса, где он был создан.Это может вызвать проблемы, если класс содержит указатель на себя, или, если код отслеживает объекты адреса.
Дополнительные сведения см. в разделе /clr (компиляция CLR).
В следующем примере показано, когда не создают конструктор копий.
// 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
Деструкторы и завершения
Деструкторы в ссылочном типе выполняют детерминистскую уборку ресурсов.Метод завершения очищает неуправляемых ресурсов и может вызываться являются детерминированными деструктором или недетерминированно сборщиком мусора.Дополнительные сведения о деструкторах в стандарте Языка C-++ - см. в разделе Деструкторы (C++).
class classname {
~classname() {} // destructor
! classname() {} // finalizer
};
Расширение функциональности деструкторов в управляемом классе C Visual C-++ отличается от управляемых расширений для C-++ C.Дополнительные сведения об этом изменении см. в разделе Изменения в семантике деструктора.
Сборщик мусора среды CLR удаляет неиспользуемые управляемые объекты и выпуски их память, если они больше не требуются.Однако тип может использовать ресурсы, сборщик мусора не знает, как выпуска.Эти ресурсы в неуправляемых ресурсов (собственные дескрипторы файлов, например).Рекомендуется выпуске все неуправляемые ресурсы в методе завершения.Поскольку управляемые ресурсы освобождаются недетерминированно сборщиком мусора, небезопасно ссылаться в управляемые ресурсы в методе завершения, поскольку возможно, что сборщик мусора уже очищал вверх, управляемый ресурс.
Завершение C Visual C-++ - не то же, что и метод Finalize.(Документация CLR завершения и метод Finalize синонимно).Метод Finalize вызывается сборщиком мусора, которая вызывает каждый метод в цепочке наследования классов.В отличие от деструкторы Visual C, C-++ метода завершения производного класса не вызывает завершение вызова компилятора во всех базовых классах.
Поскольку выпуск технология поддерживает компилятора Visual C-++ C детерминированный ресурсов не пытается реализовать методы Dispose или Finalize.Однако если вы знакомы с этими методами, описание завершение Visual C и 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();
}
}
Управляемый тип также может использовать управляемые ресурсы, которые предпочитали бы освобождение являются детерминированными и не оставляя к сборщику мусора получения освобождение недетерминированно в некоторой точке после того, как объект больше не нужен.Детерминированный выпуск ресурсов может значительно повысить производительность.
Компилятор Visual C-++ позволяет 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
// ...
}
};
Если код, использующий данный тип не вызывает деструктор, сборщик мусора выпуски концов все управляемые ресурсы.
Наличие деструктора не подразумевает наличие завершения.Однако наличие завершения означает, что необходимо указать деструктор и вызвать метод завершения из деструктора.Это обеспечивает для детерминистского выпуска неуправляемых ресурсов.
Вызов деструктора подавлять- вызов SuppressFinalize— метода завершения объекта.Если деструктор не вызывается, завершение пользовательского типа в конечном счете будет вызвано сборщиком мусора.
Являются детерминированными очистку ресурсов объекта класса путем вызова деструктора может повысить производительность сравненную с предоставление CLR недетерминированно завершает объект.
Кода, написанного на Visual C#, C-++ и компилироваться с помощью /clr выполняется деструктор типа, если:
Объект, созданный с помощью семантики стека выходит за пределы области действия.Для получения дополнительной информации см. Семантика стека C++ для ссылочных типов.
Исключение во время создания объекта.
Объект член в объекте деструктор которого выполняется.
Следует вызвать оператор удаление для дескриптора (Оператор дескриптора объекта (^) (расширения компонентов C++)).
Необходимо явно вызывает деструктор.
Если тип использовать клиент, на котором написан на другом языке, деструктор вызывается следующим образом:
При вызове Dispose.
При вызове Dispose(void) для типа.
Если тип выходит за пределы области действия в инструкцию C-# using.
При создании объекта ссылочного типа в управляемой куче (используя не семантику стека для ссылочных типов), используйте синтаксис попробуйте - окончательн, чтобы убедиться, что исключение не препятствует деструктор не выполняются.
// clr_destructors.cpp
// compile with: /clr
ref struct A {
~A() {}
};
int main() {
A ^ MyA = gcnew A;
try {
// use MyA
}
finally {
delete MyA;
}
}
Если тип имеет деструктор, компилятор создает метод Dispose, который реализует IDisposable.Если тип, написанного на языке C и C-++ имеет деструктор, используемого из другого языка, вызывая IDisposable::Dispose для этого типа причины деструктор типа, назначив.Если тип будет использоваться из клиента Visual C, C-++, нельзя непосредственно вызов Dispose; вместо этого вызовите деструктор с помощью оператора delete.
Если тип имеет завершение, компилятор создает метод Finalize(void), который переопределяет Finalize.
Если тип имеет или завершение или деструктор, компилятор создает метод Dispose(bool), согласно шаблону разработки.(Дополнительные сведения см. в разделе Implementing Finalize and Dispose to Clean Up Unmanaged Resources).Нельзя явно автор или вызов Dispose(bool) в Visual C# и C-++.
Если тип имеет базовый класс, который соответствует шаблону разработки, деструкторы для всех базовых классов вызываются при вызове деструктора для производного класса.(Если тип записывается в Visual C#, C-++, компилятор гарантирует, что собственные типы реализуют этот шаблон). Другими словами, выполняются цепочек деструктор ссылочного класса в их базы и члены, определенные деструктор стандартн- первого класса C, C-++ и деструкторы для членов в обратном порядке, в котором они были созданы, и наконец деструкторы для базовых классов в обратном порядке, в котором они были созданы.
Не может деструкторам и завершениям внутренние типы значений и интерфейсы.
Завершение можно определить только или объявления в ссылочного типа.Как и конструкторы и деструкторы, завершение не имеет возвращаемый тип.
После завершения выполнения метода завершения объекта, во всех базовых классах также называются, начиная с наименьшим производным типом.Метод завершения для элементов данных автоматически не прикован, на которое расширение класса.
Если завершение удаления собственный указатель в управляемом типе, необходимо убедиться, что ссылки на или через собственный указатель преждевременно не собираются. вызовите деструктор для управляемого типа вместо KeepAlive.
Во время компиляции можно выберите имеет ли тип завершение или деструктор.Дополнительные сведения см. в разделе Поддержка характеристик типов компилятором (расширения компонентов C++).
В следующем примере показаны 2 одного типа, с неуправляемыми ресурсами и один, управляемый ресурсы, являются детерминированными, высвобождаются.
// 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");
}