艾迪神总结的VS IDE使用(很文艺的说)

来源:互联网 发布:佳能ip1180打印机端口 编辑:程序博客网 时间:2024/05/09 12:10
这个问题一直以来都很想探究清楚,但是相关内容比较杂乱,又一直懒于查找官方文档。这次也只是记录一部分的心得,希望将来能一直更新下去吧。

0. 问题的产生
    初学C++时,相信不少人都采用VC++6.0这个版本(或者其他编译环境的)。不论怎样,最初采用的工程一般也都是控制台模板(即Console,在C++编译器的命令行参数里面可以窥见相关内容),再加上程序短小,因而很难预见VC++工程的管理问题。随着学习的进一步深入,由于项目上的各种原因,VC++工程的一些高级问题也就来临了,例如
    头文件的互相包含/头文件的重定义
    工程文件的组织
    项目的目录管理
    DLL库的使用/LIB库的使用
    等等……

1. Cpp与h
    对于很多功能强大的语言(嗯,我确实想到了Perl),大多数程序只需要一个文件甚至一页篇幅,就能实现需要的功能。然而对于C++这一现在已显刻板的语言,这是可望更不可及的:头文件包含、函数申明、变量定义、字符串操作以及各种操作上的不便利……嗯,咱还是不望了。
    既然使用了C++,是因为与硬件层有紧密的联系;或者程序真心比较庞大,而我们又有足够的时间来组织语言;或者是我们希望减轻程序使用者的负担,将他们从复杂的运行环境设置中解脱出来(VC针对的是Windows,你懂的);又或者,也可能是最大的原因,不希望别人了解我们程序中的奥妙(对于这一点,很多标榜“自由与开放”的脚本语言也在做...真心重要啊)。
    那么第一步就是要了解.Cpp和.h的奸情。为什么说是奸情呢?因为在C++编译连接器的眼里,cpp和h文件是可以三角恋、四角恋以及多角恋的。让我们从编译器认可的合法开始,再回归到现行的通用规则中去。

    使用一个小例子简单地描述一下cpp与h文件的多角虐恋:从前有一个Main.cpp,使用得好好的突然想从别人那里偷几个功能来用,于是包含了两个头文件A.h和B.h,编译通过了,Oh yeah。可是链接的时候是怎么样生成Main.exe的呢?h文件里没有这两个功能的具体实现啊。
    不得不提到C.cpp和D.cpp(这里暂不讨论重复定义、重复包含之类的问题),他们俩都包含了A.h和B.h,并且分别提供了这两个h文件中申明函数的一半实现。编译没有问题,链接的时候相同的一个函数在Main.obj、C.o、D.o中是一个相同的ID,自然也就搞定了他们之间的对应关系。
    说了这么多其实只是想说明一下几个特征:h文件和cpp文件不一定要同名,随便你怎么取名字,只要实现部分的cpp文件包含了申明部分的h文件,那么任何包含了这个h文件希望调用相关函数的文件都能够链接成功;一个cpp文件可以包含多个h文件,这个很好理解;一个h文件的申明可以在多个cpp文件中分别实现(自然就不可能全同名啦)。

    鉴于编译连接器对cpp和h混乱关系的容忍,如果你仅仅为编写出一个能够成功生成的程序而沾沾自喜的时候,肯定有其他工程师面对你的程序仰天长叹,既生你何生他。
    良好的cpp-h关系应当使用同名,A.h包含A.cpp需要向外申明的函数、数据结构,A.cpp可以包含很多h文件,但是他只是想实现A.h中申明的函数。这是才是稳定的二人关系,易于其他人所接受,尤其是在类的编写时!

2. 全局变量
    其实我想表达的,不能用这个词汇来表示。大家都知道每个变量都有他的作用域,一对花括号、一个循环、一个函数、一个文件都可能是这个变量永远跃不出的场景。全局指的是多大,我的理解是至少在一个文件内自由活动的才能算是吧。
    全局变量的处理不同人有不同的看法和不同的做法,此处就简要说一下我的看法。
    a.在单个Cpp文件内部需要使用全局变量的时候,将该变量定义在cpp中;
    b.外部需要使用时,将该变量定义在cpp中,extern申明在h中。(有人说应该哪里需要哪里定义...这也没错,不过改起来太费事了,比较靠谱的方法是给到处使用的全局变量取一个好名字并且尽量减少全局变量的外部使用(而不是不用,该用的时候大胆用,不要迷信教科书说的全局变量破坏可读性(Goto也真的是一个好命令(。。。这个注释的调用有点深度))))。

3. 包含关系
    每一个C++程序员都肯定碰到过重复定义的问题,产生的原因无外乎两种——真的重复定义和不小心的重复包含。前者就不讨论了,积累一些实战经验就不会再犯;后者则是要处理好下面提到的包含关系。
    首先举例说明重复包含的产生:A.h里包含了B.h,B.h包含了A.h,Main.cpp包含A.h(b.h或者两个也行,被Cpp包含以后h文件才会被拷贝到cpp中进行编译),一编译出错了。Main.cpp包含了A.h,A.h包含了B.h的,B.h又包含了A.h……一直重复包含下去没有尽头(重复包含错误),假设你有幸从循环包含中返回,也会应为多次包含A.h或B.h而报错(重复定义错误)。
    为了防止头文件相互包含而产生的多重定义,很多头文件都应该有如下格式:

使用GUID号的一些Windows标准头文件:
    #ifndef AFX_STDAFX_H__2E6B6299_78FC_4514_953C_D3F5DA24A99E__INCLUDED_
    #define AFX_STDAFX_H__2E6B6299_78FC_4514_953C_D3F5DA24A99E__INCLUDED_
        内容……
    #endif

自己编写的懒得计算GUID号的头文件:
    #ifndef _STDAFX_H
    #define _STDAFX_H
        内容…… 
    #endif

VS2010中的超文艺写法:
    #pragma once

4. 文件组织与目录管理
    这两个话题必须放在一起讨论,才能说清楚。
    VC++中文件组织分为两部分,工程中的筛选器(Filter)和实际硬盘中的文件夹。
    工程中默认会有三个筛选器,资源文件、源文件和头文件。看很多人推荐使用默认值,适当的增加几个子筛选器和特殊的筛选器是可以的。但是如果层数过多,你就应该考虑是不是本应该分成多个项目来进行管理。
    筛选器中的层级关系只是为了你查看的方便,和实际路径无关,因此程序在编写引用部分的时候,目录引用是一个大问题。个人认为除非那些大佬级的库,如Windows、C++标准库或者居然需要安装的Kinect SDK,其他开源库没有必要给他们在目录里增加新的条目,应当把他们挪到项目文件夹中使用相对路径来使用。这一点将给移植带来极大的方便。(即使要增加,也请使用相对目录,不然挪到别人电脑时引起的一堆问题不能忍啊!)

    在这里提一下把自己的项目移植到同平台的问题(注意是同平台啊),目录设置什么的都不是重点,我要说的是工程的存储空间……VS2010下一个工程动少说也是五十兆,这是为什么呢?主要是三个来源ipch、debug和*.sdf,痛快地删掉他们然后随意传给别人吧。(至于他们是什么作用,请Google...)

5. DLL与LIB
    相信每个刚接触dll和lib的人,最开始都头晕目眩。其实我们可以一秒钟区分开exe, dll, lib:打开项目的属性页面,在常规标签页中的配置类型里有“应用程序(.exe)”、“静态库(.lib)”、“动态库(.dll)”等。这一小节的核心思想其实是,lib和dll用法不同,目的一样。
    他们都是用来对程序进行封装的。简单的说,他们是给程序使用的程序,而不是直接给用户调用。通常dll或lib包含的是一系列的函数。在偏硬件的环境下,他们承担着应用层与硬件层沟通的作用。
    在调用他们之前,请首先检查库目录的设置!顾名思义,动态库dll是动态的,静态库lib是静态的。继续文艺地解释是不必要的,让我们直接进入三种调用方式的介绍:
    a.静态库的调用:在库目录中设置好路径,在文件里包含lib文件及其头文件(放着lib中函数的原型),就可以使用了;这是静态库的唯一用法,程序将在启动时就将静态库中的程序段拷贝到内存以备调用,在程序结束后释放。
    b.动态库的静态调用:在库目录中设置好路径,类似lib文件,在文件中显式地包含dll文件及其头文件;程序运行起来同lib区别不大,不体现动态库之动态;
    c.动态库的动态调用:在库目录中设置好路径,dll加载由程序控制,在需要的时候进行加载,使用完毕后释放;难点在于对dll调用和释放的掌握,以及dll中函数接口需要有与lib头文件不同的复杂的定义头文件;优点在于,即使地释放了空间,这在windows这样的大型程序中太重要了。

6. 名空间
    名空间这个东西,刚上手的时候觉得有种多此一举的感觉,因而一直都很排斥。虽然知道新的C++标准明确表示淘汰了h文件的包含而采用名空间来管理全局。
    比如我总认为
        #include <iostream.h>
要比
        #include<iostream>
        using namespace std;
方便多了。
    不过在渡过了漫长的菜鸟期(什么你觉得你很快就渡过了?那你肯定是从高级语言移民过来的!),名空间这一个概念肯定回重回到你的视线中。要问为什么,我还真只能说你要自己体会,我怎么会告诉你名空间用法更高级呢?

    个人认为名空间这个东西,是C++语言为向高级语言靠近而做的诸多努力中的一部分(Cpp虽然很老,但是真心很努力;什么STL之类的为C++增添了很多高级语言中的特性,只可惜C++平台不统一)。尝试过高级语言的童鞋肯定对包这一概念记忆犹新。名空间就是包一样的东西,可以认为它是比类更高一级的集合吧。它负责将类、全局函数再进行一次分类包装(不是封装)
    名空间这一机制是鼓励开放的(C++本身很自由开放的,VC++被微软弄死板了而已),具体表现在代码的编写上。比如前面那个<iostream>它到底是什么呢?它是一个代号,实际上指代了某个或某些文件吗?不是的,它就是指一个叫iostream的文件!(这一点我其实是才认识到)
    使用名空间的代码有很多开源库,函数名字取太长了吧,不好用;太短了吧,肯定会跟别的库打架。那么我们各自申请一个名空间吧,然后名字在里面爱怎么取怎么取,跟std空间打架都可以(就是自己别打架了)。那么既然这么开源,又何必写什么头文件、源文件呢,多麻烦,都丢在一个文件里给别人包含不好么?一派人觉得这个文件虽然是直接用来被别人包含的,但是怎么说也是cpp和h的结晶啊,扩展名应该叫hpp的;另一派人觉得,既然不是h也不是cpp,那干脆不取名字,于是不要扩展名了。有一些库是前者,一些是后者包括C++标准库。

    随后要提一下using这个命令。在漫长的岁月中如果你习惯了使用using namespace,还是要多长一个心眼。很多时候能直接使用名空间就不要使用,比如说,你喜欢用boost库……标准库的N多内容被boost重写了,using两个名空间,同名函数、同名对象打架很厉害。这个时候就不要using namespace std了,具体使用到可以强制说明一下名空间,比如std::count<<"HaHa"就搞定了。
    最后一点,不知道你想到了没有。说了这么多名空间的加载,名空间怎么卸载啊?!我中间一长串代码需要用标准库,那我加载再使用再卸载不就OK了吗?
    C++中没有名空间的卸载,所以在不适合直接使用名空间的时候调用函数,还是乖乖地加上名空间吧。(Tip:可以使用花括号的特性,将using namespace局限在一对括号中;跟全局变量和Goto一样,请胆心细地小心使用。)
原创粉丝点击