我是靠谱客的博主 无情钥匙,这篇文章主要介绍《Effective C++》条款31:将文件间的编译依存关系降至最低,现在分享给大家,希望可以做个参考。

在学习了一段时间的C++后,开始读《Effective C++》。由于缺乏项目经验,平时写的程序大都也是在LeetCode和牛客上的C with STL,对编译过程也不太了解,所以读这一章时感觉有很多不理解的地方。在查阅了网上其他大佬对这一章的理解后,我想谈谈自己对这一条款的理解。

#include <string>
#include "date.h"
#include "address.h"

class Person {
 public:
     Person(const std::string& name, const Date& birthday, const Address& addr);
     std::string name() const;
     std::string birthDate() const;
     std::string address() const;
     //...
 private:
     /*成员变量*/
     std::string m_Name;
     Date m_BirthDate;
     Address m_Address;
 };

首先是开头的这一段程序,在Person类中,有三个成员变量,它们的类型是string、Date和Address。由于Person类的三个成员变量既不是指针也不是引用,所以编译器在编译这个文件时必须访问这三个类型的定义式,否则无法得知该给这三个变量分配多少内存。也就是说,如果你修改了Date或Address的定义,然后重新编译,Person类所在的文件也不得不重新编译。

现在,假如Person类的三个成员变量不是Object,而是指针或引用,那么你可以不用include date.h和address.h,而用两个声明代替它们(class Date;,class Address;)。此时,你再修改Date和Address的定义,然后重新编译,Person类所在的文件就不用重新编译。

#include <string>

class Date;
class Address;

class Person {
 public:
     Person(const std::string& name, const Date& birthday, const Address& addr);
     std::string name() const;
     std::string birthDate() const;
     std::string address() const;
     //...
 private:
     /*成员变量*/
     std::string* m_Name;
     Date* m_BirthDate;
     Address* m_Address;
 };

为什么这样就不用重新编译Person所在的文件呢?

首先,对于指针类型,它的大小是固定的,在64位的系统上是8字节,32位系统上是4字节,编译器不需要访问定义式就可以知道所需要的内存大小。由此可见,对于编译器来说,Person类的每一个成员变量类型和分配给成员变量的内存是明确的。

其次,编译器在编译的时候,是将每个文件单独编译,最后再将所有文件连接。因此,就算你修改了Date和Address的定义式,也不会影响到Person,因为编译person的时候不需要访问Date和Address。

如果是引用原理也是一样,因为引用的本质还是指针。

基于此,作者提出了两条建议:尽量用指针和引用代替Object;尽量用class声明代替class定义式。

为什么要用指针和引用代替object,上面已经说明。而为什么用class声明代替class定义,是因为在函数声明中,即使用到某个class,并不需要访问定义式,即使是使用by value的方式传参。(我个人看来,使用class定义式并不方便修改,因为一旦修改了定义式,整个文件都需要重新编译,哪怕你只是做了一个小小的修改)

#include <string>
#include <memory>
 
class PersonImpl;
class Date;
class Address;
class Person  {
public:
    Person( const std::string& name,const Date& birthday,const Address& addr);
    std::string name() const;
    std::string birthDate() const;
    std::address() const;
    ...
private:
   std::tr1::shared_ptr<PersonImpl> pImpl;
};

上述代码就应用了作者的两个建议。用class声明代替了class定义式,用指针代替了Object。使用这种方式的好处是降低了耦合。因为将类型的实现细节都放在了 PersonImpl中,如果你修改了PersonImpl的定义式,可以不用重新编译Person。

想象一下,假如你有数百个文件使用了Person类型的对象,如果你将所有细节都放在Person中,一旦你修改了Person类,这就意味着这数百个文件都要重新编译。万幸的是,你将Peson类的细节都放在了PersonImpl中,而Person类只使用了指针,就算你修改了PersonImpl,也只需要重新编译PersonImpl所在的文件就可以了。

像上文中的Person类被称为Handle class,而定义Handle class的方法还有另一种。具体细节就不表了,书本上都有,再写下去就变成抄书了。

如有错误请指出,万分感谢。

最后

以上就是无情钥匙最近收集整理的关于《Effective C++》条款31:将文件间的编译依存关系降至最低的全部内容,更多相关《Effective内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部