zoukankan      html  css  js  c++  java
  • C ++ / CLI 语法

    目录:

    1. What's C++/CLI  什么是C++/CLI
    2. Handles and Pointers 句柄和指针
    3. Hello World
    4. Classes and UDTs  类和用户自定义类型
    5. Arrays  数组
    6. Parameter Array  可变参数
    7. Properties  属性
    8. Wrapping Around a Native C++ Class     包装C++类
    9. Wrapping Around C Callbacks   包装C回调
    10. The Other Way Round: From Managed to C Callbacks  另一种方式:从托管调C回调

    1、什么是C++ /CLI

    众所周知,C ++是一种高级语言,主要被认为是C语言的超集,它添加了许多功能,例如OOP和模板,但是什么是CLI?

    CLI代表公共语言基础结构。它在整个Web上都有详尽的解释,但总而言之:这是一个开放的规范,描述了可执行代码和运行时环境,该环境允许多种高级语言在不同的计算机平台上使用,而无需为特定的架构进行重写[2]

    现在,C ++ / CLI是用C ++编写.NET的方法,类似于使用C#或VB.NET。

    2、Handles and Pointers(句柄与指针)

    您可能已经在C ++ / CLI代码中看到标点符号“ ^”并对此感到疑惑。如您所知,在C ++中,表示指针,在C ++ / CLI中,表示句柄。现在,“ *”指定驻留在CRT堆上的本机指针,而句柄则指定“安全指针”并驻留在托管堆上。可以将这些句柄视为引用,并且与本机指针不同,如果未正确删除它们,它们将不会引起内存泄漏,因为GC会处理这些问题,并且它们没有固定的内存地址,因此将在执行过程中移动。

    要创建某个特定类或值类型的新引用,我们必须使用“ gcnew”关键字进行分配;例如:

    System::Object ^x = gcnew System::Object();

    值得注意的是,nullptr关键字“ ”表示空引用。除了标点符号“ ^”外,我们还有百分比“ %”代表跟踪参考;我想引用ECMA-372:

    N* pn = new N;   // allocate on native heap
    N& rn = *pn;     // bind ordinary reference to native object
    R^ hr = gcnew R; // allocate on CLI heap
    R% rr = *hr;     // bind tracking reference to gc-lvalue

    在一般情况下,加标点%^因为加标点&*

    2、Classes and UDTs (user defined types)

    注意:在public后面跟着ref关键字

    public ref class MyClass
    {
    private:
    public:
      MyClass()
      {
    
      }
    }

    3、Hello World

    在本节中,您将学习如何创建一个简单的C ++ / CLI框架程序。首先,您需要知道如何定义正确的“ main”。您会注意到,两个原型(C main和C ++ / CLI main)都需要将字符串数组传递给它们。

    #using <mscorlib.dll>
    
    using namespace System;
    
    int main(array<System::String ^> ^args)
    {
      System::Console::WriteLine("Hello world");
      return 0;
    }

    4、类和用户自定义类型

    Class (类)

    在此示例中,我们将说明如何创建类和用户定义的类型。要创建托管类,您要做的就是在类定义的前面加上保护修饰符,然后加上“ ref”,从而:

    public ref class MyClass
    {
    private:
    public:
      MyClass()
      {
    
      }
    }

    要创建本机类,您只需按照自己的方式创建即可。现在,您可能想知道C ++ / CLI中的析构函数,如果它们仍然表现相同,答案是肯定的,则析构函数(确定性)的使用方式仍与在C ++中使用的方式相同。但是,在为您Dispose()透明实现IDisposable接口之后,编译器会将析构函数调用转换为调用除此之外,还有由GC调用的所谓终结器(非确定性),它的定义如下:“ !MyClass()”。在终结器中,您可能要检查是否调用了析构函数,如果没有,则可以调用它。

    #using <mscorlib.dll>
    
    using namespace System;
    
    public ref class MyNamesSplitterClass
    {
    private:
      System::String ^_FName, ^_LName;
    public:
      MyNamesSplitterClass(System::String ^FullName)
      {
        int pos = FullName->IndexOf(" ");
        if (pos < 0)
          throw gcnew System::Exception("Invalid full name!");
        _FName = FullName->Substring(0, pos);
        _LName = FullName->Substring(pos+1, FullName->Length - pos -1);
      }
    
      void Print()
      {
        Console::WriteLine("First name: {0}
    LastName: {1}", _FName, _LName);
      }
    };
    
    int main(array<System::String ^> ^args)
    {
      // local copy
    
      MyNamesSplitterClass s("John Doe");
      s.Print();
    
      // managed heap
    
      MyNamesSplitterClass ^ms = gcnew MyNamesSplitterClass("Managed C++");
      ms->Print();
    
      return 0;
    }

    Value types (值类型)

    值类型是一种允许用户创建超出原始类型的新类型的方法。所有值类型都源自System::ValueType值类型可以存储在堆栈中,也可以使用equal运算符进行赋值。

    public value struct MyPoint
    {
      int x, y, z, time;
      MyPoint(int x, int y, int z, int t)
      {
        this->x = x;
        this->y = y;
        this->z = z;
        this->time = t;
      }
    };

    enum (枚举)

    同样,您可以使用以下语法创建枚举:

    public enum class SomeColors { Red, Yellow, Blue};

    甚至指定元素的类型,例如:

    public enum class SomeColors: char { Red, Yellow, Blue};

    array (数组)

    数组的创建再简单不过了,这个例子可以帮助您入门:

    cli::array<int> ^a = gcnew cli::array<int> {1, 2, 3};

    这将创建一个由三个整数组成的数组,而:

    array<int> ^a = gcnew array<int>(100) {1, 2, 3};

    将创建一个包含100个元素的数组,并初始化前三个元素。要遍历数组,可以像使用Length普通数组一样使用属性和索引,并使用foreach

    foreach (int v in a)
    {
      Console::WriteLine("value={0}", v);
    }

    为了创建多维数组,在这种情况下,将3D像4x5x2一样都初始化为零:

    array<int, 3> ^threed = gcnew array<int, 3>(4,5,2);
    
    Console::WriteLine(threed[0,0,0]);

    字符串类的数组可以像这样完成:

    array<String ^> ^strs = gcnew array<String ^> {"Hello", "World"}

    for循环中初始化的字符串数组

    array<String ^> ^strs = gcnew array<String ^>(5);
    int cnt = 0;
    
    // We use the tracking reference to access the references inside the array
    // since normally strings are passed by value
    
    for each (String ^%s in strs)
    {
        s = gcnew String( (cnt++).ToString() );
    }

    有关的更多参考cli::array,请检查System::Array类,如果要添加/删除元素,请参考ArrayList类。

    Parameter Array (可变参数)

    这等效于C ++中的变量参数。可变参数必须是函数中的最后一个参数。通过放置“ ...”,然后是所需类型的数组来定义它:

    using namespace System;
    
    void avg(String ^msg, ... array<int> ^values)
    {
      int tot = 0;
      for each (int v in values)
        tot += v;
      Console::WriteLine("{0} {1}", msg, tot / values->Length);
    }
    
    int main(array<String ^> ^args)
    {
      avg("The avg is:", 1,2,3,4,5);
      return 0;
    }

    Properties(属性)

    public ref class Xyz
    {
    private:
      int _x, _y;
        String ^_name;
    public:
      property int X
        {
          int get()
            {
              return _x;
            }
            void set(int x)
            {
              _x = x;
            }
        }
      property String ^Name
      {
        void set(String ^N)
        {
          _name = N;
        }
        String ^get()
        {
          return _name;
        }
      }
    };

    Wrapping Around a Native C++ Class(包装本机C ++类)

    在本节中,我们将说明如何为本地C ++类创建C ++ / CLI包装器。考虑以下本地类:

    // native class
    
    class Student
    {
    private:
      char *_fullname;
      double _gpa;
    public:
      Student(char *name, double gpa)
      {
        _fullname = new char [ strlen(name+1) ];
        strcpy(_fullname, name);
        _gpa = gpa;
      }
      ~Student()
      {
        delete [] _fullname;
      }
      double getGpa()
      {
        return _gpa;
      }
      char *getName()
      {
        return _fullname;
      }
    };

    现在,要包装它,我们遵循以下简单准则:

    1. 创建托管类,并使其具有指向本机类的成员变量。
    2. 在构造函数或其他合适的地方,在本机堆上构造本机类(使用“ new”)。
    3. 根据需要将参数传递给构造函数;从托管变为非托管时,您需要封送某些类型的数据。
    4. 为要从托管类公开的所有功能创建存根。
    5. 确保删除托管类的析构函数中的本机指针。

    这是Student该类的托管包装器

    // Managed class
    
    ref class StudentWrapper
    {
    private:
      Student *_stu;
    public:
      StudentWrapper(String ^fullname, double gpa)
      {
        _stu = new Student((char *) 
               System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(
               fullname).ToPointer(), 
          gpa);
      }
      ~StudentWrapper()
      {
        delete _stu;
        _stu = 0;
      }
    
      property String ^Name
      {
        String ^get()
        {
          return gcnew String(_stu->getName());
        }
      }
      property double Gpa
      {
        double get()
        {
          return _stu->getGpa();
        }
      }
    };

    Wrapping Around C Callbacks(包装C回调)

    在本节中,我们将演示如何让C回调调用.NET。出于说明目的,我们将包装EnumWindows()API。这是下面的代码的概述:

    1. 创建具有委托或到达本机回调时要调用的函数的托管类。
    2. 创建一个引用我们托管类的本机类。我们可以通过使用做到这一点gcroot_autovcclr.h头。
    3. 创建本机C回调过程,并将其作为上下文参数(在本例中为lParam)传递给本机类的指针。
    4. 现在,在本机回调内部,并具有作为本机类的上下文,我们可以获取对托管类的引用并调用所需的方法。

    以下是一个简短的示例:

     

    using namespace System;
    #include <vcclr.h>
    
    
    // Managed class with the desired delegate
    
    public ref class MyClass
    {
    public:
      delegate bool delOnEnum(int h);
      event delOnEnum ^OnEnum;
    
      bool handler(int h)
      {
        System::Console::WriteLine("Found a new window {0}", h);
        return true;
      }
    
      MyClass()
      {
        OnEnum = gcnew delOnEnum(this, &MyClass::handler);
      }
    };

    创建本机类是为了保留对托管类的引用并托管本机回调过程:

    class EnumWindowsProcThunk
    {
    private:
      // hold reference to the managed class
    
      msclr::auto_gcroot<MyClass^> m_clr;
    public:
    
      // the native callback
    
        static BOOL CALLBACK fwd(
        HWND hwnd,
        LPARAM lParam)
      {
          // cast the lParam into the Thunk (native) class,
          // then get is managed class reference,
          // finally call the managed delegate
    
          return static_cast<EnumWindowsProcThunk *>(
                (void *)lParam)->m_clr->OnEnum((int)hwnd) ? TRUE : FALSE;
      }
    
        // Constructor of native class that takes a reference to the managed class
    
      EnumWindowsProcThunk(MyClass ^clr)
      {
        m_clr = clr;
      }
    };

    放在一起:

    int main(array<System::String ^> ^args)
    {
      // our native class
    
      MyClass ^mc = gcnew MyClass();
    
        // create a thunk and link it to the managed class
    
      EnumWindowsProcThunk t(mc);
    
        // Call Window's EnumWindows() C API with the pointer
        // to the callback and our thunk as context parameter
    
      ::EnumWindows(&EnumWindowsProcThunk::fwd, (LPARAM)&t);
    
      return 0;
    }

    The Other Way Round: From Managed to C Callbacks(相反:从托管回调到C回调)

    现在,这个问题甚至更容易了,因为我们可以在托管类中拥有一个指向本机类的指针。解决方案可以描述为:

    1. 创建具有所需委托的托管类,该委托应触发本机回调。
    2. 创建将在本机类(包含回调)和上一个托管类(事件生成器)之间绑定的托管类。
    3. 创建一个包含给定回调的本机类。

    为了演示的目的,我们创建了一个TickGenerator托管类,该托管类OnTick每次都会生成一个事件,然后是一个INativeHandler由托管类调用类(接口)TickGeneratorThunkMyNativeHandler类是一个简单实现的INativeHandler向您展示如何设置自己的处理程序。

    滴答生成器委托:

    public delegate void delOnTick(int tickCount);

    托管的滴答生成器类:

    ref class TickGenerator
    {
    private:
      System::Threading::Thread ^_tickThread;
      int _tickCounts;
      int _tickFrequency;
      bool _bStop;
    
      void ThreadProc()
      {
        while (!_bStop)
        {
          _tickCounts++;
          OnTick(_tickCounts);
          System::Threading::Thread::Sleep(_tickFrequency);
        }
      }
    
    public:
      event delOnTick ^OnTick;
    
      TickGenerator()
      {
        _tickThread = nullptr;
      }
    
      void Start(int tickFrequency)
      {
        // already started
    
        if (_tickThread != nullptr)
          return;
    
        // p.s: no need to check if the event was set,
        // an unset event does nothing when raised!
    
        _tickCounts = 0;
        _bStop = false;
        _tickFrequency = tickFrequency;
    
        System::Threading::ThreadStart ^ts = 
          gcnew System::Threading::ThreadStart(this, &TickGenerator::ThreadProc);
        _tickThread = gcnew System::Threading::Thread(ts);
        _tickThread->Start();
      }
      
      ~TickGenerator()
      {
        Stop();
      }
    
      void Stop()
      {
        // not started?
    
        if (_tickThread == nullptr)
          return;
        _bStop = true;
    
        _tickThread->Join();
        _tickThread = nullptr;
      }
    };

    现在,非托管的滴答处理程序接口:

    #pragma unmanaged
    // Create a simple native interface for handling ticks
    
    // Native classes implement this class to add custom OnTick handlers
    
    class INativeOnTickHandler
    {
    public:
      virtual void OnTick(int tickCount) = 0;
    };

    一个简单的实现:

    class MyNativeHandler: public INativeOnTickHandler
    {
    public:
      virtual void OnTick(int tickCount)
      {
        printf("MyNativeHandler: called with %d
    ", tickCount);
      }
    };

    现在,回到托管创建重排,在托管和非托管之间建立桥梁:

    #pragma managed
    // Create the managed thunk for binding between the native
    // tick handler and the tick generator managed class
    
    ref class TickGeneratorThunk
    {
    private:
      INativeOnTickHandler *_handler;
    public:
      TickGeneratorThunk(INativeOnTickHandler *handler)
      {
        _handler = handler;
      }
    
      void OnTick(int tickCount)
      {
        _handler->OnTick(tickCount);
      }
    };

    放在一起:

    int main(array<System::String ^> ^args)
    {
      // Initiate the native handler
    
      MyNativeHandler NativeHandler;
    
      // Create the tick generator class
    
      TickGenerator ^tg = gcnew TickGenerator();
    
      // Create the thunk and bind it with our native handler
    
      TickGeneratorThunk ^thunk = gcnew TickGeneratorThunk(&NativeHandler);
    
      // Bind the ontick event with the thunk's onclick event
    
      tg->OnTick += gcnew delOnTick(thunk, &TickGeneratorThunk::OnTick);
    
      // Start the tick generator
    
      tg->Start(1000);
    
      // Wait for user input
    
      Console::ReadLine();
    
      // Stop the generator
    
      tg->Stop();
    
      return 0;
    }

    结论

    希望您在阅读本文时学习并喜欢。它应该足以让您在短时间内入门,其余的取决于您。确保您浏览了本文提供的参考文献列表。

    引用:

    1. Pure C++: Hello, C++/CLI
    2. Common Language Infrastructure - Wikipedia
    3. C++/CLI - Wikipedia
    4. Pro Visual C++/CLI
    5. Applied Microsoft .NET Framework Programming
    6. ECMA 372 - C++/CLI Specification
    7. A first look at C++/CLI
    8. Managed C++ - Learn by Example - Part 1
    9. MSDN
  • 相关阅读:
    好玩的原生js的简单拖拽
    原生js的简单倒计时
    五分钟了解node,cnpm和yarn
    计算水仙花数
    首师大附中科创教育平台 我的刷题记录(1)
    [暑假集训--数位dp]hdu3652 B-number
    [暑假集训--数位dp]hdu2089 不要62
    cf711E ZS and The Birthday Paradox
    Spoj-NETADMIN Smart Network Administrator
    cf449C Jzzhu and Apples
  • 原文地址:https://www.cnblogs.com/jshchg/p/12899793.html
Copyright © 2011-2022 走看看