为什么编译要花这么长的时间?


  
你的编译器可能有问题。也许它太老了,也许你安装它的时候出了错,也许你用的计算机已
经是个古董。在诸如此类的问题上,我无法帮助你。 

但是,这也是很可能的:你要编译的程序设计得非常糟糕,以至于编译器不得不检查数以百
计的头文件和数万行代码。理论上来说,这是可以避免的。如果这是你购买的库的设计问题,
你对它无计可施(除了换一个更好的库),但你可以将你自己的代码组织得更好一些,以求
得将修改代码后的重新编译工作降到最少。这样的设计会更好,更有可维护性,因为它们展
示了更好的概念上的分离。 
  
看看这个典型的面向对象的程序例子: 
  
    class Shape { 
    public:     // 使用Shapes的用户的接口 
        virtual void draw() const; 
        virtual void rotate(int degrees); 
        // ... 
    protected:  // common data (for implementers of Shapes) 
        Point center; 
        Color col; 
        // ... 
    }; 
  
    class Circle : public Shape { 
    public:  
        void draw() const; 
        void rotate(int) { } 
        // ... 
    protected: 
        int radius; 
        // ... 
    }; 
  
    class Triangle : public Shape { 
    public:  
        void draw() const; 
        void rotate(int); 
        // ... 
    protected: 
        Point a, b, c; 
        // ... 
    };   
  
设计思想是,用户通过 Shape 的 public 接口来操纵它们,而派生类(例如 Circle 和
Triangle )的实现部分则共享由 protected 成员表现的那部分实现
(implementation)。 
  
这不是一件容易的事情:确定哪些实现部分是对所有的派生类都有用的,并将之共享出来。
因此,与public接口相比,protected 成员往往要做多得多的改动。举例来说,虽然理
论上“中心”(center)对所有的图形都是一个有效的概念,但当你要维护一个三角形的“中
心”的时候,是一件非常麻烦的事情——对于三角形,当且仅当它确实被需要的时候,计算
这个中心才是有意义的。 
  
protected 成员很可能要依赖于实现部分的细节,而 Shape的用户(译注:user此处译
为用户,指使用 Shape 类的代码,下同)却不见得必须依赖它们。举例来说,很多(大多
数?)使用Shape 的代码在逻辑上是与“颜色”无关的,但是由于Shape 中“颜色”这个
定义的存在,却可能需要一堆复杂的头文件,来结合操作系统的颜色概念。 

当 protected 部分发生了改变时,使用 Shape 的代码必须重新编译——即使只有派生类
的实现部分才能够访问protected 成员。 
  
于是,基类中的“实现相关的信息”(information helpful to implementers)对
用户来说变成了象接口一样敏感的东西,它的存在导致了实现部分的不稳定,用户代码的无
谓的重编译(当实现部分发生改变时),以及将头文件无节制地包含进用户代码中(因为“实
现相关的信息”需要它们)。有时这被称为“脆弱的基类问题”(brittle base class 
problem)。 
  
一个很明显的解决方案就是,忽略基类中那些象接口一样被使用的“实现相关的信息”。换
句话说,使用接口,纯粹的接口。也就是说,用抽象基类的方式来表示接口: 
  
    class Shape { 
    public:     //使用Shapes 的用户的接口 
        virtual void draw() const = 0; 
        virtual void rotate(int degrees) = 0; 
        virtual Point center() const = 0; 
        // ... 
  
        // 没有数据 
    }; 
  
    class Circle : public Shape { 
    public:  
        void draw() const; 
        void rotate(int) { } 
        Point center() const { return center; } 
        // ... 
    protected: 
        Point cent; 
        Color col; 
        int radius; 
        // ... 
    }; 
  
    class Triangle : public Shape { 
    public:  
        void draw() const; 
        void rotate(int); 
        Point center() const; 
        // ... 
    protected: 
        Color col; 
        Point a, b, c; 
        // ... 
    };   
  
现在,用户代码与派生类的实现部分的变化之间的关系被隔离了。我曾经见过这种技术使得
编译的时间减少了几个数量级。 
  
但是,如果确实存在着对所有派生类(或仅仅对某些派生类)都有用的公共信息时怎么办呢?
可以简单把这些信息封装成类,然后从它派生出实现部分的类: 
  
    class Shape { 
    public:     //使用Shapes 的用户的接口 
        virtual void draw() const = 0; 
        virtual void rotate(int degrees) = 0; 
        virtual Point center() const = 0; 
        // ... 
  
        // no data 
    }; 
  
    struct Common { 
        Color col; 
        // ... 
    }; 
         
    class Circle : public Shape, protected Common { 
    public:  
        void draw() const; 
        void rotate(int) { } 
        Point center() const { return center; } 
        // ... 
    protected: 
        Point cent; 
        int radius; 
    }; 
  
    class Triangle : public Shape, protected Common { 
    public:  
        void draw() const; 
        void rotate(int); 
        Point center() const; 
        // ... 
    protected: 
        Point a, b, c; 
    };

posted on 2007-03-24 09:48 阿刚 阅读(188) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2007年3月>
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

常用链接

留言簿(1)

随笔档案

文章档案

C++ BBS

C++ FAQ

C++ WEBSITE

搜索

最新随笔

最新评论

阅读排行榜

评论排行榜