委托
C++中是有函数指针的,例如:long (*pf)(int,int)
,声明了一个函数指针,要求获取两个int并返回一个long的任意函数。在C#中是有委托概念的,其实原理就是将函数的执行委托给一个中间对象,和C++中的函数指针功能类似,但更强大(例如多播委托)。其使用与C#中使用委托基本相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| delegate double NumbericOP(double);
ref class OPs { public: static double Square(double d) { return d * d; }
double Circle(double r) { return Math::PI * r * r; } }; int main(array<System::String^>^ args) { NumbericOP^ op = gcnew NumbericOP(&OPs::Square); double area = op->Invoke(3); double area1 = op(5); OPs^ ops = gcnew OPs(); NumbericOP^ op1 = gcnew NumbericOP(ops,&OPs::Circle); double area2 = op1(3); NumbericOP^ op2; op2 += op1; op2 += op; double area3= op2(2); }
|
事件
事件是基于委托的,只不过事件的触发必须在定义事件的类中,在外面只能调用+=和-=进行操作。其使用和C#中基本相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| delegate void MyEventHandler(String^);
ref class EvtSrc { public: event MyEventHandler^ OnMyEvent; void Raise(String^ msg) { OnMyEvent(msg); } };
ref class EvtRcv { public: EvtRcv(EvtSrc^ s) { if (s == nullptr) { throw gcnew ArgumentNullException("句柄无效"); } src = s; src->OnMyEvent += gcnew MyEventHandler(this,&EvtRcv::EventMethod); } void EventMethod(String^ msg) { Console::WriteLine(msg); } private: EvtSrc^ src; };
int main(array<System::String^>^ args) {
EvtSrc^ src = gcnew EvtSrc(); EvtRcv^ rcv = gcnew EvtRcv(src);
src->Raise("调用啦");
Console::WriteLine("程序结束"); }
|

反射
在C#中,反射是经常使用的功能之一,在C++/CLI中也支持反射,使用方法和C#使用反射的方法基本一致。
混合非托管代码
使用C++/CLI一般都是作为非托管代码和托管代码的中介,所以混合非托管代码在实际开发中经常用到。
混合类
1 2 3 4
| ref class ManagedClass { UnManagedClas* puc; }
|
不能直接这样做
1 2 3 4
| ref class ManagedClass { UnManagedClas puc; }
|
并且,不能在非托管代码中直接使用托管代码,因为在标准C++中无法识别^,也没有gc
1 2 3 4
| class UnManagedClass { ManagedClas^ puc; }
|
GCHandle类型
GCHandle类型可以实现托管类型作为非托管类型的一部分使用。使用静态GCHandle::Alloc方法创建句柄,使用句柄的Free方法释放它。将托管对象的指针传给非托管代码的步骤如下:
- 创建一个GCHandle对象来引用你的对象,GCHandle可以和整数互换。
- 将GCHandle传给非托管代码
- 非托管对象在不需你的对象时,调用Free释放对象
官方提供了gcroot辅助模板类,以避免亲自和Alloc和free打交道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include "gcroot.h" using namespace System; using namespace System::Runtime::InteropServices;
ref class MyClass { public: int val; MyClass(int n) :val(n) {}
};
class UClass { public: gcroot<MyClass^> mc; UClass(gcroot<MyClass^>pmc) :mc(pmc) {} int getValue() { return mc->val; } };
int main(array<System::String^>^ args) {
MyClass^ pm = gcnew MyClass(3); UClass uc(pm); int v= uc.getValue(); Console::WriteLine(v); Console::WriteLine("程序结束"); }
|
固定
因为有了gc的存在,非托管对象引用托管对象会出现错误,因为托管对象会发生移动,对象内部的成员也会跟着移动。如果要将托管对象的指针传给非托管参数,则需要将托管对象的指针进行固定
。可根据需要固定部分或者整个托管对象的指针,但是如果固定了成员指针则整个对象也会被固定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void someFun(int* o) { int n = *o; } int main(array<System::String^>^ args) { array<int>^ arr1 = gcnew array<int>(3); pin_ptr<int> pin = &arr1[0]; someFun(pin); pin = nullptr; Console::WriteLine("程序结束"); }
|
拆装箱
和C#一样,装箱就是将值类型转成引用类型,拆箱就是将引用类型转为值类型。