C++/CLI
托管堆上创建引用类型 ref ^ gcnew nullptr
ref class MyClass {
public:
MyClass() : i(){}
int i;
void Test() {
i++;
System::Console::WriteLine(i);
}
};
int main() {
MyClass ^ p_MyClass = gcnew MyClass;
p_MyClass->Test();
MyClass ^ p_MyClass2;
p_MyClass2 = p_MyClass;
p_MyClass = nullptr;
p_MyClass2->Test();
}
^
C++/CLI中使用gcnew关键字表示在托管堆上分配内存,并且为了与以前的指针区分,用^来替换* ,就语义上来说他们的区别大致如下:
- gcnew返回的是一个句柄(Handle),而new返回的是实际的内存地址;
- gcnew创建的对象由虚拟机托管,而new创建的对象必须自己来管理和释放。
跟踪句柄、跟踪引用及内部指针
与本地C++自己维护堆不同,C++/CLI中动态分配的内存是由CLR来维护的。当不需要堆时,CLR自动将其删除回收,同时CLR还能自动地压缩内存堆以避免产生不必要的内存碎片。这种机制能够避免内存泄露和内存碎片,被称为垃圾回收,而由CLR管理的这种堆被称为CLR堆。它由操作符gcnew创建。由于垃圾回收机制会改变堆中对象的地址,因此不能在CLR堆中使用普通C++指针,因为如果指针指向的对象地址发生了变化,则指针将不再有效。为了能够安全地访问堆对象,CLR提供了跟踪句柄(类似于C++指针)和跟踪引用(类似于C++)引用。
跟踪句柄
跟踪句柄类似于本地C++指针,但能够被CLR垃圾回收器自动更新以反映被跟踪对象的新地址。同时不允许对跟踪句柄进行地址的算术运算,也不能够进行强制类型转换。注意:所有分配在堆上的对象都不能在全局范围内被创建。凡是在CLR堆上创建的对象必须被跟踪句柄引用,这些对象包括:
- 用gcnew操作符显示创建在堆上的对象;
- 所有的引用数据类型(数值类型默认分配在堆栈上)。
跟踪引用
跟踪引用类似于本地C++引用,表示某对象的别名。可以给堆栈上的值对象、CLR堆上的跟踪句柄创建跟踪引用。跟踪引用本身总是在堆栈上创建的。如果垃圾回收移动了被引用的对象,则跟踪引用将被自动更新。跟踪引用用%来定义。 int value = 10; int% trackValue = value;
内部指针
C++/CLI还提供一种用关键字interior_ptr定义的内部指针,它允许进行地址的算术操作。必要时,该指针内存储的地址会由CLR垃圾回收自动更新。注意,内部指针总是函数的局部自动变量。必须给interio_ptr指定内部指针指向的对象类型。此外还应该给指针进行初始化,如果不提供初始值,系统将其默认初始化为nullptr。内部指针在指定类型时应注意:可以包含堆栈上值类型对象的地址,也可以包含指向CLR堆上某对象句柄的地址,还可以是本地类对象或本地指针,但不能是CLR堆上整个对象的地址。也就是说,可以使用内部指针存储作为CLR堆上对象组成部分的数值类对象(如CLR数组元素)的地址,也可以存储System::String对象跟踪句柄的地址,但不能存储String对象本身的地址
array<double>^ data = {1.5, 3.5, 6.7, 4.2, 2.1};
interior_ptr<double> pstart = %data[0];
跟踪引用运算符
跟踪引用 (%) 的行为类似于普通 C++ 引用 (&) 只不过当对象分配给跟踪引用时,对象的引用计数会递增。
- 将对象赋值给跟踪引用会导致对象的引用计数递增。
- 本机引用 (&) 是取消引用 * 时的结果。 跟踪引用 (%) 是取消引用 ^ 时的结果。 只要有指向对象的 %,此对象就会一直保留在内存中。
- 点 (.) 成员访问运算符用于访问对象的成员。
- 跟踪引用对值类型和句柄(例如 String^)有效。
- 无法为跟踪引用分配 null 或 nullptr 值。 根据需要,可以将一个跟踪引用重新分配给另一个有效对象,没有次数限制。
-
跟踪引用不能用作一元获取地址运算符。
Foo^ spFoo = ref new Foo(); Foo% srFoo = *spFoo; Foo^ spFoo2 = %srFoo;
MFC CLI
当再MFC程序中启用CLI后,由于生成代码的CWinApp子类构造函数中,会有一个#ifdef条件编译指令,改指令会促使程序引用System::Windows::Forms命名控件,由于此时MFC程序并没有添加改命名控件,就会导致报错。可以在pch.h中添加#include <afxwinforms.h>
引用afxwinforms头文件指令,解决改问题。afxwinforms头文件会自动通过#using指令加载System.Windows.Forms.dll类库。
#ifdef _MANAGED
// If the application is built using Common Language Runtime support (/clr):
// 1) This additional setting is needed for Restart Manager support to work properly.
// 2) In your project, you must add a reference to System.Windows.Forms in order to build.
System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException);
#endif
//afxwinforms.h
#using <mscorlib.dll>
#using <System.dll>
#using <System.Windows.Forms.dll>
#using <System.Drawing.dll>
#using <mfcmifc80.dll>
Step 1
设置MFC项目支持CLR,需要同时设置版本,否则Visual Studio会自动选择一个.Net版本。注意设置时,需要选择所有配置。
Step 2
设置完CLR支持后,Visual Studio会自动引用mscorlib.dll,但是不会引用其它命名空间,可以通过右击项目的References节点添加System, System.Data, System.XML等命名空间。引用时确认.Net版本是否正确。
Step 3
在代码中引用命名空间,进行编程
using namespace System;
Exception
using namespace System;
using namespace System::IO;
try
{
String^ sPath = Path::Combine(System::AppDomain::CurrentDomain->BaseDirectory, _T("RIS\\RIS2\\logs"));
Directory::CreateDirectory(sPath);
}
catch (System::Exception^ ex)
{
return FALSE;
}