Семантика стека C++ для ссылочных типов
До Visual Studio 2005 экземпляр ссылочного типа можно создать только с помощью new
оператора, который создал объект в мусорной куче. Однако теперь можно создать экземпляр ссылочного типа, используя тот же синтаксис, который будет использоваться для создания экземпляра собственного типа в стеке. Таким образом, вам не нужно использовать ссылку на новый, gcnew для создания объекта ссылочного типа. И когда объект выходит из области, компилятор вызывает деструктор объекта.
Замечания
При создании экземпляра ссылочного типа с семантикой стека компилятор внутренне создает экземпляр в сборке мусора (с помощью gcnew
).
Если тип подписи или возвращаемого типа функции включает экземпляр ссылочного типа по значению, функция будет помечена в метаданных как требующая специальной обработки (с модреком). Эта специальная обработка в настоящее время предоставляется только клиентами Visual C++; Другие языки в настоящее время не поддерживают использование функций или данных, использующих ссылочные типы, созданные с семантикой стека.
Одна из причин использования gcnew
(динамическое выделение) вместо семантики стека будет, если тип не имеет деструктора. Кроме того, использование ссылочных типов, созданных с семантикой стека в сигнатурах функций, невозможно, если вы хотите, чтобы функции использовались языками, отличными от Visual C++.
Компилятор не создаст конструктор копирования для ссылочного типа. Таким образом, если вы определяете функцию, использующую ссылочный тип по значению в сигнатуре, необходимо определить конструктор копирования для ссылочного типа. Конструктор копирования для ссылочного типа имеет подпись следующей формы: R(R%){}
Компилятор не создаст оператор назначения по умолчанию для ссылочного типа. Оператор назначения позволяет создавать объект с помощью семантики стека и инициализировать его с помощью существующего объекта, созданного семантикой стека. Оператор назначения для ссылочного типа имеет сигнатуру следующей формы: void operator=( R% ){}
Если деструктор типа освобождает критически важные ресурсы и используете семантику стека для ссылочных типов, вам не нужно явно вызывать деструктор (или вызов delete
). Дополнительные сведения о деструкторах в ссылочных типах см. в разделе "Деструкторы и методы завершения" в статье "Практическое руководство. Определение и использование классов и структур (C++/CLI)".
Оператор назначения, созданный компилятором, будет соответствовать обычным стандартным правилам C++ со следующими дополнениями:
Любые нестатические члены данных, тип которых является дескриптором ссылочного типа, будут неглубоко скопированы (рассматриваются как нестатический член данных, тип которого является указателем).
Любой нестатический элемент данных, тип которого является типом значения, будет неглубоко скопирован.
Любой нестатический член данных, тип которого является экземпляром ссылочного типа, вызовет вызов конструктора копирования ссылочного типа.
Компилятор также предоставляет %
унарный оператор для преобразования экземпляра ссылочного типа, созданного с помощью семантики стека в базовый тип дескриптора.
Для использования с семантикой стека недоступны следующие ссылочные типы:
Пример
Description
В следующем примере кода показано, как объявить экземпляры ссылочных типов с семантикой стека, как работает оператор назначения и конструктор копирования, а также как инициализировать ссылку на отслеживание с использованием ссылочного типа, созданного с семантикой стека.
Код
// stack_semantics_for_reference_types.cpp
// compile with: /clr
ref class R {
public:
int i;
R(){}
// assignment operator
void operator=(R% r) {
i = r.i;
}
// copy constructor
R(R% r) : i(r.i) {}
};
void Test(R r) {} // requires copy constructor
int main() {
R r1;
r1.i = 98;
R r2(r1); // requires copy constructor
System::Console::WriteLine(r1.i);
System::Console::WriteLine(r2.i);
// use % unary operator to convert instance using stack semantics
// to its underlying handle
R ^ r3 = %r1;
System::Console::WriteLine(r3->i);
Test(r1);
R r4;
R r5;
r5.i = 13;
r4 = r5; // requires a user-defined assignment operator
System::Console::WriteLine(r4.i);
// initialize tracking reference
R % r6 = r4;
System::Console::WriteLine(r6.i);
}
Выходные данные
98
98
98
13
13