Эмуляция передачи по ссылке в C++/CLI без использования ссылки отслеживания (%)?

Я хочу подражать следующему методу:

ref class Something{
   void foo(array<double>^% data)
   {
      data = gcnew array<double>(10);
   }
};

Таким образом, массив вызывающего абонента модифицируется/создается. Тем не менее, есть некоторые ограничения:

  1. foo не может использовать ref/out/% или какие-либо дополнительные параметры.
  2. фактический параметр foo должен быть Object^.

Можно ли это сделать с помощью неуправляемых указателей, IntPtr или более непонятным способом?


person Anzurio    schedule 08.01.2010    source источник
comment
Он пытается что-то сделать, не используя для этого язык. Как будто я хочу съесть яблоко, но не могу положить его в рот.   -  person Reed Copsey    schedule 08.01.2010
comment
Я пытаюсь сделать следующее: 1) узнать, возможно ли это, 2) если невозможно, то почему? Обычно я задаю такие глупые вопросы, потому что не знаю, как выразить то, что я имею в виду (по крайней мере, не по-английски). Ответ Рида мне очень помог. Я действительно хотел знать, почему это невозможно.   -  person Anzurio    schedule 08.01.2010
comment
АЗ: Я немного дополню свой ответ для вас.   -  person Reed Copsey    schedule 08.01.2010
comment
Надеюсь, это немного проясняет для вас...   -  person Reed Copsey    schedule 08.01.2010


Ответы (2)


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

Вам нужно использовать ссылку отслеживания, чтобы иметь возможность влиять на дескриптор, переданный в ваш метод.


Изменить в ответ на комментарий AZ:

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

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

Используя ссылку отслеживания, вы передаете ссылку на местоположение в памяти. Это похоже на передачу указателя в C или C++. Когда вы это сделаете, вы можете изменить местоположение, на которое указывает ссылка (его значение), не изменяя саму ссылку — изменяя только то, на что она ссылается.

person Reed Copsey    schedule 08.01.2010

  1. foo не может использовать ref/out/% или какие-либо дополнительные параметры.
  2. фактический параметр foo должен быть Object^.

Вызывающий может выделить массив и передать его, и вы можете изменить элементы внутри массива, но это просто приведение массива к Object и обратно.

Если вы хотите изменить фактический массив — например, присвоить его новому массиву другой длины, не используя ref (% в C++/CLI) или out ([Out] в C++/CLI), вы не можете этого сделать.

Вот почему:

Виртуальная машина .NET работает, помещая объекты в стек и извлекая их из него. То, как вы передаете параметры функциям, за кулисами работает следующим образом:

  1. Вы помещаете параметры (в данном случае ссылку на массив) в стек.
  2. Вы вызываете функцию
  3. Виртуальная машина настраивается на вызов метода, считывая эти значения из стека в указанном порядке и присваивая их локальным переменным в вашей функции.
  4. Код внутри функции запускается.

Если вызывающая сторона не помещает действительные данные в стек перед вызовом функции, происходит сбой. Если вызывающий помещает данные в неправильном порядке - происходит сбой. Если со стеком вообще что-то смешное - он вылетает.

Кроме того, методы не могут изменять объекты, которые уже находятся в стеке. (Если бы они это сделали, они бы испортили стек — он зависает), они могут только вставлять и извлекать новые вещи из стека.

Это означает две вещи:

  1. Чтобы вызвать функцию, вы должны поместить ссылку на массив в стек. Поэтому вызывающая сторона должна выделить сам массив или передать нулевую ссылку.

  2. Вы не можете изменить ссылку на массив, поэтому функция не может предоставить новый массив. Поскольку массивы по своей природе неизменяемы, это означает, что вы не можете добавлять новые элементы (однако вы можете изменять существующие элементы).

Отказ от ответственности: Эрик Липперт, вероятно, придет и объяснит, почему все, что я только что написал, неверно, но, насколько мне известно и проведено исследование, именно так это и работает

person Orion Edwards    schedule 08.01.2010
comment
Нет, это кажется разумным. Ваш рассказ о том, что виртуальная машина считывает значения из стека и помещает их в локальные, немного хитрый - я предполагаю, что под локальными вы имеете в виду формальные параметры. Поскольку переменные — это места хранения, а стек — это место хранения, нет необходимости считывать значения со стека и помещать их куда-то еще; переменные логически являются расположением стека, копирование не требуется. (Конечно, реализация может копировать данные из стека в другие места, но зачем?) - person Eric Lippert; 09.01.2010
comment
Я пошутил, что Эрик Липперт придет и поправит, а потом он это сделает. StackOverflow великолепен :-) - person Orion Edwards; 10.01.2010
comment
Кстати, помещая их в локальные переменные, которые я пытался использовать, они становятся доступными через имена локальных переменных, не увязая в деталях низкого уровня, которые могут запутать ситуацию. Рад, что теперь прояснилось. - person Orion Edwards; 10.01.2010