维护头文件通常是一个冗长乏味并且容易出错的过程,感谢.NET assembly类库抹去了对这些文件的倚赖。
微软.NET的可管理的C++最终简化了我们创建类库的方法,或更准确些,.NET assembly类库,这种简化得以实现主要是由于assembly类库是可以自我描述的。这对一个Managed C++程序员意味着什么呢?基本上讲,一旦 assembly编译成功,你在编译传统C++类库的时谨慎创建的所有头文件除了维护的需要外,都不再是必需的。
就个人而言,我感觉维护头文件是一种痛苦,所以我很高兴看到这种对头文件的倚赖正在消失。我经常会忘记需要在库中引用的一个或多个头文件的名称,为了找到缺失的数据类型定义,我不得不对所有含有头文件的内容进行搜索,更糟糕的是,将源代码拆分为两半增加了我犯错误的机会。我经常犯的错误包括忘记将所有的包含头文件打包、使.h文件和.cpp脱离同步。
既然对头文件的程序集不再是必需的,你可以使用一种改进了的新方法来创建源文件。但在考察这种方法之前,先让我们先来回顾一下创建程序集的传统方法
传统方法
在C++中,传统的库文件创建方法是建立一系列头文件用以描述类库中所有的功能;接下来,你要在一个单独的源文件中实现这些头文件所定义了的所有功能。然后,在编译器中运行每一个源代码文件连同其相关的头文件,以生成目标文件;再将所有的目标文件相互链接来创建库文件
图A描述了这种传统方法。
图A
创建库的传统方法
使用头文件的原因在于,今后引用这个类的时候,所有的类、结构体、变量等等都可以很方便的定义。
程序集可以使用同样的方法生成,这一过程的唯一区别在于Managed C++的标志是事先确定的。
在下面的例子中,列表A、B、C和D演示了如何用传统C++的方法创建一个 assembly。
列表A给出了头文件Cards.h的定义,这个文件定义了一个包含扑克牌和Card类的枚举(enmu),注意,使用ManagedC++时,关键字public要放在enum和class之前的,因为这两者都需要在全局范围内可以被访问。
列表B展示了类的构造器和成员方法的实现,列表C定义了第二个类Deck,注意,虽然从来没有在头文件中定义,但Card类是在Deck类之内使用的,使用这一技巧需要谨记:在编译过程中,头文件基本上是大批量地粘贴到源文件中的。既然是这样的情况,我们只需简单地在Deck.cpp源文件的包含文件列表中将Card.h放在Deck.h的前面,如此操作之后,Card类将首先被粘贴,继而被定义为Deck类所必需的类。
列表D显示了在这个迷你库中的源文件,注意,正如我们刚刚所提到的,Card.h包含在Deck.h之前。
在命令行中执行建立 assembly库的命令并不像火箭科学一样艰难,其语法(没有这些省略号)非常简单:
cl source1.cpp source2.cpp ... sourceN.cpp /CLR /LD /o OutputName.dll
C++编译器取走这一系列源文件的名称,然后是/CLR参数,它告诉编译器我们正在使用managed的扩展,继而/LD参数告诉连接器去创建一个.dll文件,最后/o参数将说明这个.dll文件的名称。
为了编译以上的例子,你可能会用到这行命令:
clcard.cppdeck.cpp /CLR /LD /o cards.dll
新的 assembly方法
不再受困于分离头文件和源文件的工作,就为新的库文件编码方式提供了可能性。在兜了一个圈子之后,我将开始介绍我的简便方法,首先,将所有的类以类文件(.h)形式进行编码,其中包括所有的定义和实现;然后使用一个单独的连接器文件(.cpp)来包含所有的类文件;这种方法唯一有些棘手的部分在于,你要确定所有的类文件是按照正确的顺序排列的,以保证一切需要调用的数据类型在使用之前都已经定义好了,即使使用传统方法,这一问题同样需要处理。图B展示了这种新的 assembly方法。
图B
新的 assembly方法
采用这种方法,你只需维护半数文件,既然定义和源码是一同定义的,就不会造成不同步的。这样一来,维护库的工作就简单了很多,另外,开发文档只需保存在一个地方,也就更容易阅读,所有这些都说说明了这是一种更好的方法。
列表E、F和G展示了使用新方法生成的同样的库。
正如你看到的那样,列表E是传统方法中Card.h和Card.cpp这两个文件相结合而形成的,我基本上只是把实现部分直接放在类的定义中,而不是在一个单独的文件中进行定义。
同样的,列表F是传统方法中Deck.h和Deck.cpp这两个文件相结合形成的,如果使用传统方法,你可以在这个类中访问其他的类而不必在文件中定义他们,连接器文件需要确保所有类的定义是按照正确的顺序排列的,以保证这些类在使用之前都已经定义好了。
在列表G中显示的连接器文件是唯一一个与众不同的文件,它除了包含语句之外没有任何其他的东西,ManagedC++编译器只编译.cpp文件,为了让编译器完成所有的工作,你必须有这个文件。这种便利所带来的副作用是从库中添加或删除类变得容易了,而你已经将组成库的类名称记录在了一个单一的地方。在开发过程中,连接器文件还是一个放置测试代码的理想场所。
以这种方法建立一个assembly类库的额外的优点是,在命令行条件下使用时,其命令很简单:
clcards.cpp /CLR /LD
结论
具有自我描述功能的 assembly器简化了我们编码的方法,这主要是因为源代码模块不再需要被分离为头文件和源码文件,正因为得益于此,我们展示了一种构造 assembly库的新方法。
责任编辑:李宁
|