参考类型的 C++ 堆栈语义
在 Visual Studio 2005 之前,只能使用 new
运算符来创建引用类型的实例(这会在垃圾回收堆上创建对象)。 但是现在,您可以使用用来在堆栈上创建本机类型的实例的相同语法创建引用类型的实例。 因此,无需使用 ref new、gcnew 来创建引用类型的对象。 此外,当对象超出范围时,编译器将调用对象的析构函数。
注解
当您使用堆栈语义创建引用类型的实例时,编译器内部会在垃圾回收堆上创建实例(使用 gcnew
)。
当函数的签名或返回类型包括一个按值引用类型的实例时,会在元数据中将此函数标记为需要特殊处理(使用 modreq)。 此特殊处理当前仅由 Visual C++ 客户端提供;其他语言当前不支持使用借助堆栈语义创建的引用类型的使用函数或数据。
使用 gcnew
(动态分配)而不是堆栈语义的一个原因是此类型是否有析构函数。 此外,如果您希望由 Visual C++ 之外的语言使用您的函数,则无法使用在函数签名中借助堆栈语义创建的引用类型。
编译器将不会为引用类型生成复制构造函数。 因此,如果在签名中定义了使用按值引用类型的函数,则必须为该引用类型定义一个复制构造函数。 引用类型的复制构造函数具有以下形式的签名:R(R%){}
。
编译器不会为引用类型生成默认的赋值运算符。 赋值运算符允许您使用堆栈语义创建一个对象,并且借助使用堆栈语义创建的现有对象对其进行初始化。 引用类型的赋值运算符具有以下形式的签名:void operator=( R% ){}
。
如果您的类型的析构函数释放重要资源并且您将堆栈语义用于引用类型,则不需要显式调用析构函数(或调用 delete
)。 有关引用类型中析构函数的详细信息,请参阅如何:定义和使用类和结构 (C++/CLI) 中的析构函数和终结器。
编译器生成的赋值运算符将遵循标准的 C++ 规则以及以下额外规则:
任何类型为指向引用类型的句柄的非静态数据成员都将被浅表复制(处理方式与类型为指针的非静态数据成员一样)。
任何类型为值类型的非静态数据成员都将被浅表复制。
任何类型为引用类型的实例的非静态数据成员都将调用引用类型的复制构造函数。
编译器还提供 %
一元运算符,以将使用堆栈语义创建的引用类型的实例转换为其基础句柄类型。
以下引用类型不可与堆栈语义一起使用:
示例
说明
以下代码示例演示了如何使用堆栈语义声明引用类型的实例,赋值运算符和复制构造函数如何工作,以及如何借助使用堆栈语义创建的引用类型来初始化跟踪引用。
代码
// 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