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中的对象可以固定,这降低了与堆栈的相似性,而相似性就是一切。