托管C++笔记(一)原创
最近前一个项目结束,新的项目还未开始,并且下一个项目有可能需要用到C#/C++混合编程,于是乎开始了托管C++的学习过程。现在公司的游戏编辑器就是采用了C#做编辑器,C++做游戏引擎,托管C++做封装连接编辑器和引擎。不可否认.NET的功能强大,个人现在也偏好使用C#做工具。不过之前托管C++从未接触,想来凭借多年C++的底子和C#的经验,学习托管C++应该也不是件难事。
学习教材:Visual C++ .NET 托管扩展编程中文版,PDF扫描版,很不清晰………………勉强能看。
看完第一章,并经过动手试验以后发现书本上讲的很多东西已经过时了,我使用的环境是Visual Studio .NET 2005/2008,而教程应该是基于.NET 2003写的。
很多关键字已经在.NET 2005中更改过,具体更新列表可以参考mdsn或者点击这里
几个最基础的也是最重要的更新:
__gc class改成了ref class
__property 改成了 property
__abstract 改成了 abstract
__gc __interface 改成了 interface class
__event 改成了 event
不难看出来,这些改动只是把前面的下滑线去掉了。不过这只是字面上的改动
- property的格式改成跟C#类似了
之前的格式是
__property void set_Name(String^ name);
__property String^ get_Name();
在2005里面变成了
property String^ Name
{
String^ get()
{
return mName;
}
void set(String^ name)
{
mName = name;
}
}
对于熟悉C#的人来说是一个非常不错的改进。
- abstract改动让我很无语:
__abstract变成abstract以后,还必须放在类名字后面
原来的格式
__gc __abstract class Test : public Base
{
}
新的格式
ref class Test abstract : public Base
{
}
- 空指针的判断
不再使用0或者null来判断指针是否为空了,而是用nullptr
- new操作符的改动
因为托管类必须要在托管堆上分配,否则会引起错误。所以在旧版本里面要使用__gc new来创建实例,但是也允许直接new。这个有可能会造成误会。
新的版本里面,所有的托管类必须要用gcnew来创建实例,否则会产生编译错误
- interface的实现必须使用virtual关键字
这跟旧版本有些区别,书上的范例:
__gc __interface IPrint
{
void Print();
__event OnPrinted* printed;
}
__delegate void OnPrinted(String*);
__gc class PrintedDoc : public IPrint
{
public:
void Print()
{
}
__event virtual OnPrinted* printed
}
旧版本只有成员才需要在实现的时候使用virtual,而方法没有要求。
但是在新版本里面必须要使用virtual,包括方法
下面是我做的一个测试
public interface class TestInterface
{
event CallMethod^ TestEvent;
property String^ Name
{
String^ get();
void set(String^ name);
}
void TestFunc();
}
ref class Test : public TestInterface
{
public:
virtual event CallMethod^ TestEvent;
property String^ Name
{
virtual void set(Strin^ name)
{
mName = name;
}
virtual String^ get()
{
return mName;
}
}
virtual void TestFunc()
{
}
protected:
String^ mName;
}
这里的interface class让我很困惑,为什么一定要加class,难道还可以用interface struct?
如果在实现类里面没有使用virtual,编译器会报错。另外补充一点,如果有派生类需要重写基类的虚方法,必须使用override,否则编译器报错
范例如下:
ref class Derived : public Test
{
public:
virtual void TestFunc() override
{
__super::TestFunc();
}
}
这里出现了__super关键字,跟C#的base和Java里面的super类似,通常在C++里面要调用基类的方法,我们都是采用Test::TestFunc()的形势,而且你也可以调用更上层的方法。而__super则限定了只能是直接基类的方法。
- 奇妙的实现
.NET里面只允许单继承,也就是所有托管类只能有一个托管基类。
如果某个派生类实现了某个接口,但是没有提供实现方法,不过该派生类的基类有一个完全匹配实现,这个实现会被当成是接口的实现。
范例:
public interface class TestInterface
{
void TestFunc();
}
ref class TestFuncClass
{
public:
virtual void TestFunc()
{
}
}
ref class Test : public TestFuncClass, public TestInterface
{
}
上面的代码是可以编译通过的
- 托管类可以delete
delete会调用该类的析构函数,但是该对象仍然存在于托管堆中,依然是有效的,只是析构里面可能更改了对象的状态,导致潜在的问题。
- 托管C++里的event不需要判断是否为空指针
很神奇的发现这一点,在C#里面必须得检查这个event是否有人监听,否则直接触发这个事件会造成运行出错。
if (null != TestEvent)
{
TestEvent(....);
}
但是同样的代码放在托管C++里面反而会报编译错误。