CLR指针

pin_ptr ——定身法

千万不要小看了pin_ptr的能力,它是Native世界和Managed世界之间的桥梁。在通常情况下,任何时候,GC都会启动,一旦进行GC,托管堆就会被压缩,对象的位置就会被移动,这时候所有指向对象的Handle都会被更新。但是,往往有时候程序员会希望能够把托管堆上的数据(的地址)传给Native接口,比如,为了复用一个Native的高效算法,或者为了高效的做某些其它事情,这种情况下普通的Native指针显然不能胜任,因为如果允许Native指针指向托管堆上的对象,那么一旦发生了GC,这些得不到更新的Native指针将指向错误的位置,造成严重的后果。办法是先把对象“定”在Managed堆上,然后再把地址传给Native接口,这个“定身法”就是pin_ptr——它告诉GC:在压缩堆的时候请不要移动该对象!

array<char>^ arr = gcnew array<char>(3); //托管类 arr[0] = 'C'; arr[1] = '+'; arr[2] = '+'; pin_ptr<char> p = &arr[0];   // 整个arr都被定在堆上 char* pbegin=p; std::sort(pbegin,pbegin+3); //复用Native的算法! std::cout<输出 “++C”

      在上面的代码中,我们复用了STL里的sort算法。事实上,既然有了pin_ptr,我们可以复用绝大部分的Native算法。这就为我们构建一个紧凑高效的程序内核提供了途径。

       值得注意的是,一旦对象中的成员被定在了堆上,那么该对象整个就被定在了堆上——这很好理解,因为对象移动必然意味着其成员的移动。

       还有另一个值得注意的地方就是:pin_ptr只能指向某些特定的类型如基本类型,值类型等。因为这些类型的内存布局都是特定的,所以对于Native代码来说,通过Native指针访问它们不会引起意外的后果。但是,ref class的内存布局是动态的,CLR可以对它的布局进行重整以做某些优化(如调整数据成员排布以更好的利用空间),从而不再是Native世界所能理解的静态结构。然而,这里最主要的问题还是:ref class底层的对象模型和Native世界的对象模型根本就不一致(比如vtbl的结构和vptr的位置),所以用Native指针来接受一个ref class实例的地址并调用它的方法简直肯定是一种灾难。由于这个原因,编译器严格禁止pin_ptr指向ref class的实例。

 

interior_ptr ——托管环境下的Native指针

    Handle的缺憾是不能进行指针运算(由于其固有的语义要求,毕竟Handle面对的是一个要求“安全”的托管环境),所以Handle的能力较为有限,不如标准C++程序员所熟悉的Native指针那么强大。在STL中,iterator是一种极为强大也极具效率的工具,其底层实现往往用到Native指针。而到了托管堆上,我们还有Native指针吗?当然,原来的形如T*的指针是不能再用了,因为它不能跟踪托管堆上对象的移动。所以C++/CLI中引入了一种新的指针形式——interior_ptr。interior_ptr和Native指针的语义几乎完全一样,只不过interior_ptr指向托管堆,在GC时interior_ptr能够得到更新,除此之外,interior_ptr允许你进行指针运算,允许你解引用,一切和Native指针并无二致。interior_ptr为你操纵托管堆上的数据序列(如array)提供了强大而高效的工具,iterator模式因此可以原版照搬到托管环境中,例如:

 

template<typename T>

void sort2(interior_ptr begin,interior_ptr end)

{

    ... //排序算法

    for(interior_ptr pn=begin;pn!=end;++pn)

    {

       System::Console::WriteLine(*pn);

    }

}

 

int main()

{

array<char>^ arr = gcnew array<char>(3);

    ... //赋值

    interior_ptr<char> begin = &arr[0]; //指向头部的指针

    interior_ptr<char> end = begin + 3;  //注意,不能写&arr[3],会下标越界

    sort2(begin,end); //类似STL的排序方式!

}

 

T*pin_ptrinterior_ptr——把它们放到一起

       T*,pin_ptr,interior_ptr是C++/CLI中三种最为重要的指针形式。它们之间的关系像这样:

 

      

 

强大的Override机制

        在标准C++中,虚函数重写机制是隐式的,只要两个函数的签名(Signature)一样,并且基类的同名函数为虚函数,那么不管派生类的函数是否为virtual,都会发生虚函数重写。某种程度上,这就限制了用户对它的派生类的控制能力——虚函数的版本问题就是其一。而在C++/CLI中,你拥有最为强大的override机制,你可以更为明显的来表示你的意图,例如下面的代码:

 

class B

{

public:

       virtual void f() ;

       virtual void g() abstract; //纯虚函数,需要派生类重写,否则派生类就是纯虚类

       virtual void h() sealed; //阻止派生类重写该函数

       virtual void i() ;

}

class D:public B

{

       virtual void f() new ; //新版本的f,虽然名字和B::f相同,但是并没有重写B::f。

       virtual void h() override ; //错误!sealed函数不能被重写

       virtual void k() = B::i ; //“命名式”重写!

}

 

       通过正确的使用这些强大的override机制,你可以获得对类成员函数更强大的描述能力,避免出乎意料的隐式重写和版本错误。不过需要提醒的是,“命名式”重写是一种强大的能力,但是需要谨慎使用,如果使用不当或滥用很可能导致名字错乱。

原文链接: https://www.cnblogs.com/Daywei/archive/2012/08/15/2639645.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    CLR指针

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/59516

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午9:08
下一篇 2023年2月9日 上午9:08

相关推荐