百科问答小站 logo
百科问答小站 font logo



如何理解《Effective C++》第31条将文件间的编译依赖关系降低的方法? 第1页

  

user avatar   fredriklo 网友的相关建议: 
      

这个其实书里面已经说的很清楚了。

虽然各个编译器有自己的 trick, C++ 的文件依赖实现基本来说非常的简单。 如果你写一个

       #include "person.h"     

预编译器真的就是把这个文件拼接在了 #include 那里..

我们假设 person.h 里面包含

       class Person { public:  std::string name() const; ... private:  std::string mName; }     

而 main.cpp 依赖于 person.h。 那么基本上,不管你是怎么改 person.h, 甚至就算是 touch 了一下, 大部分依赖管理器也会让编译器重新编译一次,区别可能仅仅是好一点的编译器很快发现其实 person.h 根本没有变化, 编译时间稍微短一些而已。

那么问题来了, 我们现在看到 main.cpp -> person.h, 意味着任何时候 person.h 的改变都会导致 main.cpp 重新被编译。在现实情况中, 甚至会有上百的个文件都会依赖于 person.h, 我们并不想因为 person.h 被修改就导致所有依赖于它的文件被编译,那么怎么做呢。 书里面说了一个小 trick, 把类和类的具体实现分开 - pimpl idom (PIMPL, Rule of Zero and Scott Meyers).

基本上是把这个 Person 类拆开

       class Person { public:   std::string name() const;     ... private:   class PersonImpl * pImpl; }     

in Person.cpp

       #include "personimpl.h" std::string Person::name() {     return pImpl->name(); }      

PersonImpl 作为一个具体的实现类

       class PersonImpl { public:  std::string name() const {     return mName;  }  ... private:  std::string mName; }      

这里你可以看到, person.h 里面只是包含了接口信息,具体的实现挪到了PersonImpl 这个类。 由于 Person 对 PersonImpl 的引用是一个指针, 而指针大小在同一平台是固定的。 所以 person.h 根本就不需要包含 personimp.h ( 注意 person.cpp 需要包含 personimpl.h, 因为需要具体使用到 PersonImpl 的函数)。

于是文件关系依赖改变为:

       main.cpp -> person.h person.cpp -> person.h, personimpl.h personimpl.cpp -> personimpl.h     

所以你看到, main.cpp 和 personimpl.h 彻底解除了依赖关系。如果还有一百个文件依赖于 person.h, 而你又想改 Person 的实现。 由于 Person 的实现在 personimpl.cpp/h 里面, 不管你怎么去搞, 由于你压根就不会去碰 person.h , 所有的依赖 person.h 文件都不会被重新编。

说了怎么多好处, 那么这里给题主提几个问题思考下:

- 你真的想把你的实现藏在另外一个文件嘛, 你确定看代码的人看到这种一层套一层的实现到处找你的代码的时候不会想砍死你。。

- 这个依赖关系又会把编译时间降低多少呢?如果仅仅是几个文件依赖这个类定义,多搞一个类出来是否值得?

- 说到把接口和实现分开, 你肯定在想, 尼玛, 我直接把 Person 搞成一个接口不就行了嘛。 为啥还要这么麻烦。

=========

如果你思考过这些问题,说明你已经摆脱了教科书里面这些条条款款, 进入真正的工程实践领域了。

这种降低文件依赖关系的做法一般并不会在开始写代码的时候做, 这个叫 Premature optimization, 因为你可能在优化一个根本就不存在的问题。

在大工程里面, 把代码写得清晰,容易懂, 比什么优化都重要, 而如果不是大工程, 编译时间本来也就不长。

而你代码写好了以后,如果发现很多文件依赖一个类实现,再把这个类改成 pimpl idom 也不迟。

甚至关于优化编译时间, 也有非常多的技巧, 比如, 另外一个极端是,就算你不想拆代码, 构造一个新的 cpp, 把其他所有的 cpp 全部包含进去。

all.cpp

       #include "person.cpp" #include "personimpl.cpp" #include "main.cpp"     

依赖关系不是复杂难搞嘛, 我全部包含在一起,虽然不管改个啥都要重新编,但是只用编一个 cpp 啊,不管编译还是链接都要快几个数量级。 ( opera 用了这个 trick 编译时间从 半个小时降到了 5分钟。。)

对了,对于最后一个问题,接口 + 实现的最大问题是接口本身不能被直接构建出来, 所以

- 没法在栈上面用

- 需要一个辅助的工厂类




  

相关话题

  C++对一个map不断insert delete元素(多任务中的一个任务),是否存在内存碎片问题? 
  python是用C实现的,Java是用C++实现的,那为什么不直接用C或C++呢? 
  C++中如何将string类型转换为int类型? 
  C语言能判断一个变量是int还是float吗? 
  C++中函数返回的过程是什么样的? 
  网上有对于C++编程要避免使用cin、cout、fstream;而是使用scanf、printf、FILE *的说法, 请问是正确的吗? 
  c++怎么在1到n这些数中随机产生k(k<n)个?当然,k个数互不相等。有什么比较好的写法吗? 
  C++ 11为什么引入nullptr? 
  对于C#与C++之间的数据传输,用GCHandle或Marshal是否就是最好的方案了? 
  C 语言float和double哪个算得快? 

前一个讨论
小米宣布开始造车会对五菱汽车带来冲击吗?小米的优势会体现在哪些方面?
下一个讨论
中国税务局和美国国税局在权力上的差别如何?





© 2024-11-21 - tinynew.org. All Rights Reserved.
© 2024-11-21 - tinynew.org. 保留所有权利