@zwh8800
2017-08-23T10:44:50.000000Z
字数 2310
阅读 191408
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#:
Form refInCSharp = new Form();
Point valueInCSharp = new Point(1, 2);
refInCSharp.Show();
valueInCSharp.toString();
c++/cli
Form^ refInCli = gcnew Form();
Point valueInCli = Point(1, 2);
refInCli->Show();
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# 代码是等价的
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
using (Font font1 = new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
可见, 在 c# 中使用 using 语句可以避免显式的调用 Dispose 函数.
参见:https://msdn.microsoft.com/zh-cn/library/yh598w02.aspx
在 c++/cli 中, 这变得更加简单:
首先, 和 c# 不同的是, 在 c++/cli 中实现一个~type 析构函数在底层实际上生成的是 Dispose 函数
如:
ref class A
{
public:
virtual ~A()
{
System::GC::SuppressFinalize(this);
A::Finalize();
}
};
等价于下面:
__gc class A : IDisposable
{
public:
void Dispose()
{
System::GC::SuppressFinalize(this);
Console::WriteLine( "in ~A"); }
}
};
而在 c++/cli 中如果需要使用想 using 那样的语法糖, 只需要把引用类型的帽子去掉即可:
void f()
{
Font font("Arial", 10.0f);
Byte charset = font.GdiCharSet;
// ...
// font 被自动析构 -
// 也就是说, font.Dispose() 被调用...
}
等价于:
void f()
{
Font^ font = gcnew Font("Arial", 10.0f); /* 注意gcnew */
Byte charset = font->GdiCharSet; /* 注意把点改为-> */
//......
delete font; /* 显式删除调用Dispose */
}
这点设计简直 32 个赞!
另外, 去掉帽子并不代表 Font 对象被分配到栈上, 上面两段代码是完全等价的, 也就是说, font 还是会被 gcnew 到托管堆上. 这种使用方法只是一种语法糖而已.
另外, 真的想为一个类实现一个 Finalize 函数怎么办?
使用!type 的格式 (叹号 + 类名):
public ref class R {
public:
!R()
{ Console::WriteLine( "I am the R::finalizer()!" ); }
};
等价于:
public ref class R {
public:
void Finalize()
{ Console::WriteLine( "I am the R::finalizer()!" ); }
};
暂时就先这么多.