[关闭]
@zwh8800 2017-08-23T10:44:50.000000Z 字数 2310 阅读 191387

最近用了下 c++/cli

blog 归档 c++ .net


最近写一些小程序, 需要运行在 windows 下有个界面. 一开始是用 c# 搞, 可是 c# 读写二进制文件实在是太蛋疼了 (可能是我才疏学浅, C# 没学好, 有谁知道 C# 中怎么方便的把二进制文件中的首部直接读到一个结构体中?). 最后还是转向 c 艹大法. 用 c 艹又想方便的拖控件, 又不想带一大堆乱七八糟 dll, 又不想用 MFC. 也就只能试试蛋疼的 c++/cli 了.


下面大致记录一下, 比较乱。

类型

c# 中有六大类型, 在 c++/cli 中对应的是这样:

类型 c# c++/cli
值类型
结构 struct value class
枚举 enum enum class
引用类型
class ref class
数组 type[] array<type>
接口 interface interface class
委托 delegate delegate

在 c# 中声明一个变量时, 不需要指明变量是否为引用类型或值类型

在 c++/cli 中对于引用类型的声明需在类型后加帽子 ^(官方说法为跟踪句柄)

同时在使用变量的成员时, 对于引用类型和值类型应使用. 和 -> 来区分

如:

c#:

  1. Form refInCSharp = new Form();
  2. Point valueInCSharp = new Point(1, 2);
  3. refInCSharp.Show();
  4. valueInCSharp.toString();

c++/cli

  1. Form^ refInCli = gcnew Form();
  2. Point valueInCli = Point(1, 2);
  3. refInCli->Show();
  4. valueInCli.toString();

需要注意的是, 对于一个值类型调用基类函数 (既 Object 类的函数, 结构体只能继承于 Object), 会对值类型进行隐式装箱

析构

在 c# 中实现一个~type() 析构函数时, 在底层实际实现的是一个 Finalize() 函数. 这个函数的调用时机不确定, 只有在 GC 主动收集的时候才会被调用 (对于实现了 Finalize() 函数的对象, 更是会被延迟回收, 因为会在第 0 代回收时被放入第 2 代, 这称之为不确定的终结化(non-deterministic finalization))

参见:https://msdn.microsoft.com/zh-cn/library/ee787088(v=vs.100).aspx

然而,不确定的终结化机制在对象维护一个关键的资源,例如一个数据库连接或者某种类型的锁的时候运转并不好。这种情况下我们需要尽快释放资源。

因此, c# 中提供了 IDispose 模式, 通过显式调用 Dispose() 函数来释放关键资源 (在 Dispose 函数中需调用 SuppressFinalize() 函数来禁止对象被放入第二代)

并且 c# 提供了 using 语法糖来协助调用 Dispose 函数:

下面两段 c# 代码是等价的

  1. {
  2. Font font1 = new Font("Arial", 10.0f);
  3. try
  4. {
  5. byte charset = font1.GdiCharSet;
  6. }
  7. finally
  8. {
  9. if (font1 != null)
  10. ((IDisposable)font1).Dispose();
  11. }
  12. }
  1. using (Font font1 = new Font("Arial", 10.0f))
  2. {
  3. byte charset = font1.GdiCharSet;
  4. }

可见, 在 c# 中使用 using 语句可以避免显式的调用 Dispose 函数.

参见:https://msdn.microsoft.com/zh-cn/library/yh598w02.aspx

在 c++/cli 中, 这变得更加简单:

首先, 和 c# 不同的是, 在 c++/cli 中实现一个~type 析构函数在底层实际上生成的是 Dispose 函数
如:

  1. ref class A
  2. {
  3. public:
  4. virtual ~A()
  5. {
  6. System::GC::SuppressFinalize(this);
  7. A::Finalize();
  8. }
  9. };

等价于下面:

  1. __gc class A : IDisposable
  2. {
  3. public:
  4. void Dispose()
  5. {
  6. System::GC::SuppressFinalize(this);
  7. Console::WriteLine( "in ~A"); }
  8. }
  9. };

而在 c++/cli 中如果需要使用想 using 那样的语法糖, 只需要把引用类型的帽子去掉即可:

  1. void f()
  2. {
  3. Font font("Arial", 10.0f);
  4. Byte charset = font.GdiCharSet;
  5. // ...
  6. // font 被自动析构 -
  7. // 也就是说, font.Dispose() 被调用...
  8. }

等价于:

  1. void f()
  2. {
  3. Font^ font = gcnew Font("Arial", 10.0f); /* 注意gcnew */
  4. Byte charset = font->GdiCharSet; /* 注意把点改为-> */
  5. //......
  6. delete font; /* 显式删除调用Dispose */
  7. }

这点设计简直 32 个赞!

另外, 去掉帽子并不代表 Font 对象被分配到栈上, 上面两段代码是完全等价的, 也就是说, font 还是会被 gcnew 到托管堆上. 这种使用方法只是一种语法糖而已.

另外, 真的想为一个类实现一个 Finalize 函数怎么办?

使用!type 的格式 (叹号 + 类名):

  1. public ref class R {
  2. public:
  3. !R()
  4. { Console::WriteLine( "I am the R::finalizer()!" ); }
  5. };

等价于:

  1. public ref class R {
  2. public:
  3. void Finalize()
  4. { Console::WriteLine( "I am the R::finalizer()!" ); }
  5. };

暂时就先这么多.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注