Ссылка отслеживания в C++/CLI

может кто-нибудь объяснить мне следующий фрагмент кода?

value struct ValueStruct {
    int x;
};

void SetValueOne(ValueStruct% ref) {
    ref.x = 1;
}

void SetValueTwo(ValueStruct ref) {
    ref.x = 2;
}

void SetValueThree(ValueStruct^ ref) {
    ref->x = 3;
}

ValueStruct^ first = gcnew ValueStruct;
first->x = 0;
SetValueOne(*first);

ValueStruct second;
second.x = 0;
SetValueTwo(second); // am I creating a copy or what? is this copy Disposable even though value types don't have destructors?

ValueStruct^ third = gcnew ValueStruct;
third->x = 0;
SetValueThree(third); // same as the first ?

и второй вопрос: есть ли основания что-то подобное?:

ref struct RefStruct {
    int x;
};

RefStruct% ref = *gcnew RefStruct;
// rather than:
// RefStruct^ ref = gcnew RefStruct;

// can I retrieve my handle from ref?
// RefStruct^ myref = ???

более того: я не вижу разницы между типом значения и типом ref, так как оба могут быть указаны обработчиком ; (

1 ответов


помните, что основное использование C++ / CLI предназначено для разработки библиотек классов для использования GUIs / веб-службами, построенными на других языках .NET. Так в C++должен поддерживать обе ссылки и типы значений, потому что другие .Чистые языки.

кроме того, C# может иметь ref параметры, которые также являются значениями, это не уникально для C++ / CLI, и это никоим образом не делает типы значений эквивалентными ссылочным типам.

ответить на вопросы в комментарии к коду:

я создаю копию или что?

да, SetValueTwo принимает свой параметр по значению, поэтому делается копия.

является ли эта копия одноразовой, даже если типы значений не имеют деструкторов?

неправильные. Типы значений могут иметь деструкторы. Типы значений не могут иметь завершителей. Поскольку этот конкретный тип значения имеет тривиальный деструктор, компилятор C++/CLI не заставит его реализовать Интерфейс IDisposable. В любом случае, если параметр является типом значения IDisposable, компилятор C++/CLI гарантирует, что Dispose вызывается, когда переменная выходит из области видимости, так же, как семантика стека для локальных переменных. Это включает ненормальное завершение (исключение) и позволяет использовать управляемые типы с RAII.

и

ValueStruct% ref = *gcnew ValueStruct;

и

ValueStruct^ ref = gcnew ValueStruct;

разрешены и помещают экземпляр типа коробочного значения в управляемую кучу (которая вообще не является кучей, но очередь FIFO, однако Microsoft предпочитает называть ее кучей, такой как собственная область памяти для динамического распределения).

в отличие от C#, C++/CLI может хранить типизированные дескрипторы для коробочных объектов.

если ссылка отслеживания относится к экземпляру типа значения в стеке или встроена в другой объект, то содержимое типа значения должно быть упаковано в процессе формирования ссылки.

ссылки отслеживания также могут использоваться со ссылочными типами и синтаксисом для получения ручка такая же:

RefClass^ newinst = gcnew RefClass();
RefClass% reftoinst = *newinst;
RefClass^% reftohandle = newinst;

RefClass stacksem;
RefClass^ ssh = %stacksem;

одна вещь, которую я никогда не могу вспомнить полностью, заключается в том, что синтаксис не на 100% согласован по сравнению с родным C++.

объявить ссылка:

int& ri = i; // native
DateTime% dtr = dt; // managed tracking reference

объявить указатель:

int* pi; // native
Stream^ sh; // tracking handle

форма указателя:

int* pi = &ri; // address-of native object
DateTime^ dth = %dtr; // address-of managed object

обратите внимание, что унарный адрес оператора совпадает с ссылочной нотацией как в стандартном C++, так и в C++/CLI. Это, кажется, противоречит ссылка для отслеживания не может использоваться в качестве унарного оператора take-address (MSDN) к которому я вернусь через секунду.

во-первых, противоречивость:

сформировать ссылку из Указателя:

int& iref = *pi;
DateTime% dtref = *dth;

обратите внимание, что унарный оператор разыменования-это всегда *. Это то же самое, что и обозначение указателя только в родном мире, который полностью противоположен адресу-из которых, как упоминалось выше, всегда являются тем же символом, что и ссылка нотация.

компилируемый пример:

DateTime^ dth = gcnew DateTime();
DateTime% dtr = *dth;

DateTime dt = DateTime::Now;
DateTime^ dtbox = %dt;

FileInfo fi("temp.txt");
// FileInfo^ fih = &fi;  causes error C3072
FileInfo^ fih = %fi;

теперь об унарном адресе:

во-первых, статья MSDN неверна, когда она говорит:

в следующем примере показано, что ссылка отслеживания не может использоваться в качестве унарного оператора take-address.

правильное утверждение:

% - адрес оператора для создания дескриптора отслеживания. Однако его использование ограничено как следует:

отслеживание дескриптор должен указывать на объект в управляемой куче. Ссылочные типы всегда существуют в управляемой куче, поэтому проблем нет. Однако типы значений и собственные типы могут быть в стеке (для локальных переменных) или внедрены в другой объект (переменные-члены типа value). Попытки сформировать дескриптор отслеживания образуют дескриптор для коробочной копии переменной: дескриптор не связан с исходной переменной. Как следствие бокса процесс, требующий метаданных, которые не существуют для собственных типов, никогда не может иметь дескриптор отслеживания для экземпляра собственного типа.

пример кода:

int i = 5;
// int^ ih = %i;  causes error C3071

System::Int32 si = 5;
// System::Int32^ sih = %si; causes error C3071
// error C3071: operator '%' can only be applied to an instance 
//              of a ref class or a value-type

если System::Int32 не является типом значения, тогда я не знаю, что такое. Давайте попробуем System::DateTime это не примитивный тип значения:

DateTime dt = DateTime::Now;
DateTime^ dtbox = %dt;

это работает!

в качестве еще одного неудачного ограничения примитивные типы, которые имеют двойную идентичность (например, native int и тип управляемого значения System::Int32) не обрабатываются правильно,% (ссылка отслеживания формы) оператор не может выполнять бокс даже если имя .NET для типа задано.