Отслеживание ссылки в 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 = ???

Что больше: Я не вижу различия между типом значения и касательно типа, так как на обоих может указать обработчик ;(

7
задан Ben Voigt 1 August 2010 в 02:06
поделиться

1 ответ

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

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

Чтобы ответить на вопросы в комментариях к вашему коду:

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

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

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

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

Разрешены оба варианта

ValueStruct% ref = *gcnew ValueStruct;

и

ValueStruct^ ref = gcnew ValueStruct;

и помещают экземпляр типа boxed value на управляемую кучу (которая вообще не является кучей, а очередью 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

Обратите внимание, что унарный оператор address-of совпадает с обозначением ссылки как в стандартном 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;

Теперь об унарном address-of:

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

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

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

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

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

Пример кода:

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;

Это работает!

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

24
ответ дан 6 December 2019 в 07:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: