C++ cli C+中的跟踪引用+/CLI

C++ cli C+中的跟踪引用+/CLI,c++-cli,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 =

有人能给我解释一下下面的代码片段吗

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类型之间有什么区别,因为它们都可以由处理程序指向;(

请记住,C++/CLI的主要用途是开发类库,供其他.NET语言中构建的GUI/web服务使用。因此,C++/CLI必须同时支持引用类型和值类型,因为其他.NET语言都支持

此外,C#也可以具有值类型的
ref
参数,这不是C++/CLI所独有的,它不会以任何方式使值类型等同于引用类型

要回答代码注释中的问题,请执行以下操作:

我是在复制还是什么

是的,SetValueTwo按值获取其参数,因此创建了一个副本

即使值类型没有析构函数,此副本也是一次性的吗

不正确。值类型可以具有析构函数。值类型不能具有终结器。由于此特定值类型具有普通析构函数,C++/CLI编译器将不会使其实现IDisposable。在任何情况下,如果参数是IDisposable值类型,C++/CLI编译器将确保在变量变位时调用Dispose超出范围,就像局部变量的堆栈语义一样。这包括异常终止(抛出异常),并允许托管类型与RAII一起使用

两者

,并将装箱值类型实例放在托管堆上(该堆根本不是堆,而是FIFO队列,但Microsoft选择将其称为堆,类似于用于动态分配的本机内存区域)

与C#不同,C++/CLI可以将类型化句柄保留到已装箱的对象

如果跟踪引用指向堆栈上的值类型实例或嵌入到另一个对象中,则必须在形成引用的过程中装箱值类型内容

跟踪引用也可以与引用类型一起使用,获取句柄的语法相同:

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

RefClass stacksem;
RefClass^ ssh = %stacksem;
一个我完全无法完全记住的事情是,语法与本地C++相比,并不是100%的一致性。 声明引用:

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
int& iref = *pi;
DateTime% dtref = *dth;
形成指针:

int* pi; // native
Stream^ sh; // tracking handle
int* pi = &ri; // address-of native object
DateTime^ dth = %dtr; // address-of managed object
int& iref = *pi;
DateTime% dtref = *dth;
<> P>注意,操作符的一元地址与标准C++和C++ +CLI中的引用符号相同,这与我将在第二页中返回的矛盾。

但首先,不一致性:

从指针形成引用:

int* pi; // native
Stream^ sh; // tracking handle
int* pi = &ri; // address-of native object
DateTime^ dth = %dtr; // address-of managed object
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运算符

正确的说法是:

%
是操作员创建跟踪句柄的地址。但其使用受到以下限制:

跟踪句柄必须指向托管堆上的对象。引用类型始终存在于托管堆上,因此没有问题。但是,值类型和本机类型可能位于堆栈上(对于本地变量),也可能嵌入到另一个对象中(值类型的成员变量)。尝试形成跟踪句柄将形成变量装箱副本的句柄:句柄未链接到原始变量。装箱过程需要本机类型不存在的元数据,因此,本机类型的实例永远不可能具有跟踪句柄

示例代码:

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名称,
%
(表单跟踪引用)操作符也无法执行装箱操作。

您确定吗“RefStruct^ref=gcnew RefStruct;”(其中RefStruct为引用类型)将一个装箱的值类型实例放在堆上?在上面的一行中,您有一个错误,您的意思是*gcnew而不是^gcnew吗?对不起,我同时使用C++/CLI和C#,深夜我将注意力集中在
RefStruct
的struct部分而不是ref部分。我将修复我的答案。是的,回答
^gcnew
vs
*gcnew
花了一段时间,并讨论了
%
vs
&
^
vs
*
@Qwertie的区别:你说这是为什么?堆是一种具有特定特征的数据结构。“堆栈”具有其他特征。使用中的数据结构是“堆栈”。有一个“生成结束”“只是为了给新对象(如堆栈)腾出空间而升级的指针。没有像堆那样跟踪使用过的区域和孔(中间的空闲区域)。这就是.NET内存分配如此之快的原因。堆栈的特点是从末端(仅末端)添加和删除项。”。托管堆由三个部分组成(或者是四个?),其中一个部分(gen 0)类似于堆栈,因为项目被添加到末尾。但是,东西不会从末尾移除;它们会从整个区域移除,并折叠或移动到其他地方,这与堆栈完全不同。gen 0中的对象可以固定,这降低了与堆栈的相似性,而相似性就是一切。