您的位置:首页 > 编程语言

《高效编程十八式》(9/13)代码编辑

2012-01-10 19:19 204 查看
 

代码编辑

王伟冰

    下面列举一些代码编辑器常用的功能,如果你正在使用的代码编辑器有此功能,请充分利用,这样可以提高写代码的效率。没有的话就算了。

   

    添加:

    自动补全:键入单词的前几个字母,自动插入整个单词。

    代码模板:插入一些常用的代码结构或程序框架。

 

    方便查看:

    自动格式化:不用你自己敲Tab缩进代码,自动缩进。

    折叠代码块:一个文件太多行时,把一些类和函数的代码折叠起来。

 

    跳转:

    类视图:列出当前工程的所有类和成员,可随意跳转到任一个类或成员所在的代码。

    书签:在经常改动的代码放一个书签,要找就比较容易。

    转到定义或声明:选中一个变量或函数或类,跳转到它定义或声明的地方。

    键盘跳转:最典型如VIM,通过丰富的命令来实现各种快捷的跳转。

 

    重构:

    重构,指的是不改变代码的功能,但改变其具体实现。前面几节讨论的很多例子其实都涉及重构。比如复数类,把c.add(a,b)改成c=add(a,b),功能没变,但实现方式变了,看起来更为清晰,这就是一种重构。又如数据统计,把m=count(a,30,60)改成m=count(a,30, Func1(60)),也是一种重构,功能没变,但是使用起来可以更灵活了,这也是一种重构。

    重构是整个程序开发过程要不断做的事情。因为很多情况下你往往不能在一开始就清楚什么样的设计是最佳的,而且将会使用这个程序的用户的需求也往往是变化的。所以如果只是一味地向程序添加新功能,那么整个程序的架构可能会越来越混乱,越来越难以维护。就好比我们电脑磁盘中的文件,如果我们总是随意存放新文件,那么时间一长就会很乱,有些东西你就忘记放在哪个文件夹里了,所以时不时要整理整理。写程序也是,时不时要进行重构,虽然没有改变它的功能,但是可以使它变得更清晰、更灵活,方便添加新功能。

    大多数的重构还是要手工完成的,但有些简单的可以用代码编辑器来做:

    文本替换:经常用于变量、类、函数的重命名,但是要非常小心,因为这只是简单地做文本替换,把变量f替换成F会把printf也替换成printF。

    智能重命名:有些编辑器可以做到,比如上例,通过推理识别出哪些f才代表真正的变量f,就不会出现上面的问题了。

    重新排列函数参数的顺序:重排后,所有调用这个函数的代码都会自动重新排列。

    提升局部变量为函数参数:比如第二节的最开始的部分,把60提升为函数参数min,有些编辑器能自动把调用count函数的代码修改过来,比如原来是count(a,30),就改成count(a,30,60)。

    ……

    尽管如此,代码编辑器所能够做的重构还是很有限。我认为未来的代码编辑器应该往更为智能的重构方面发展。

    我认为代码编辑器还应该支持重构的导入和导出,但是似乎现在还没有哪个代码编辑器做到这一点。比如A同学写了一个类库,把它发布到网上,B同学在自己的程序里使用到了A的类库。后来,A同学对类库进行了升级,改进了架构,但是某些类的接口发生了变化,比如有些函数的名字发生了变化,参数个数发生了变化。B同学如果想让原来的程序使用上A同学的新类库,那就要对程序里所有使用到这些类的代码进行修改,那显然很麻烦。所以编程的接口一旦制定,是很难修改的。Win32的API函数,十几年了还是那个样子,因为一旦修改,很多程序就无法在Windows上运行了。但是如果重构可以导入和导出,A同学就可以把它对类库接口所做的更改导出为一个文件,随新类库一起发布,B同学让代码编辑器导入这个文件,自动对B的程序中的代码进行更改,然后重新编译就行了。

 

 

    这一节的题目叫“代码编辑”,不过好像没有太多可说的。不如说一些别的:C++模板元编程。

    什么是元编程?来看一个例子,比如我们要算n的阶乘,可以用下面的递归函数:

    void factorial(int n){

        if(n==0)return 1;

        else return n*factorial(n-1);

    }

    但是你还可以用模板来实现:

    template<int N>

    struct factorial{ //默认情况下用这个版本

        static const int value=N*factorial<N-1>::value;

    };

    template<>

    struct factorial<0>{ //N=0时用这个特化版本

        static const int value=1;

    };

    上面的代码的意思就是:

    如果N=0,则使用factorial的特化版本,factorial<N>::value=1;

    否则使用factorial的普通版本,factorial<N>::value =N*factorial<N-1>::value;

    所以,factorial<3>::value就表示3的阶乘。

    用模板来计算阶乘,和用函数来计算阶乘,有什么不同呢?不同就在于,前者是在程序编译的时候进行计算的,而后者是在程序运行的时候进行计算的,经过编译之后,factorial<3>::value就会变成6。所以你不能这样:

    int x=3;

    int y=factorial<x>::value;

    因为x的值只有在运行的时候才知道,所以上面的代码编译不通过。

    这种在编译时进行计算的程序就称为“元程序”。通过把一些运行时的计算提前到编译时来完成,可以提高程序运行时的性能(当然,编译速度就会变慢)。甚至有人证明,C++的模板元编程是图灵完备的,也就是说,所有普通程序能够完成的计算,元程序也能完成。

   

    除了能够对数值进行计算,元编程还可以对类型进行计算,这是普通编程做不到的。比如判断两个类型是否相等,你总不能用if(int==double)…吧!但是下面的代码可以对类型进行比较:

    template<class T1,class T2>
    struct is_same{ //默认情况下用这个版本
        static const bool value=false;
    };
    template<class T1>
    struct is_same<T1,T1>{
//T1和T2相等时用这个版本
        static const bool value=true;
    };
    上面的代码的意思就是:

    如果T1和T2相等,则使用is_same的特化版本,is_same <T1,T2>::value=true;

    否则使用is_same的普通版本,is_same <T1,T2>::value=false;

    于是is_same<int,doube>::value就是false,is_same<int,int>::value的值就是true。
    可是类型计算有什么意义?难道我不知道int和double相不相等?看一个应用的例子。假设我们写了一个模板类class1,这个类具体做什么不重要,我们只知道它的某个函数func里,需要对浮点型的数值进行特别的处理,而对其它的类型不需要特别处理,那么可以这样:
    template<class T>
    class class1{
        ……
        void func(){
            if(is_same<T,double>::value || is_same<T,float>::value){
                ……
//如果T是double或float型,则做一些特别处理
            }
            ……
//默认情况下做的操作
        }
    };
 
    C++模板元编程,是C++最高深的部分,这里举的都是最简单的例子,涉及的都只是皮毛。C++模板强大的类型运算能力,弥补了C++无法在运行时获取类型信息的缺陷。对于能够在运行时获取类型信息的语言,像C#,提供了对元编程的原生支持,如typeof(int) ==typeof(double),很直观地比较两个类型。当然,由于是在运行时实现的,它的性能肯定不如C++。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息