• 读书笔记_Effective_C++_条款三十一:将文件间的编译依存关系降至最低(第二部分)


    下面再来看书,去理解书上说的Handler classes就简单多了,我们大概过一下。

    假设我们要写一个Person类,如下:

     1 class Person
     2 {
     3 private:
     4     string name;
     5     MyDate birthday;
     6     MyAddress address;
     7 
     8 public:
     9     // fallows functions
    10     // ...
    11 };

    这个Person类里面包含有人的名字,人的生日以及地址,还有一些没有列出来的方法。注意到这里用到了string(它不是一个class,只是一个typedef,大多数情况下,我们认为它不会有任何改变),以及自定义的类MyDate与MyAddress,一种常见的做法是采用include头文件,像这样:

    1 #include <string>
    2 #include "MyDate.h"
    3 #include "MyAddress.h"

    在MyDate.h里面写好日期类相关的成员变量与方法,而在MyAddress.h里面写好地址类相关的成员变量与方法。但如果此后要往MyDate类或者MyAddresss类添加成员变量,那么不仅仅所有用到MyDate或者MyAddress对象的文件需要重新编译,而且所有用到Person对象的文件也需要重编译,一个小改动竟然会牵涉到这么多的地方!

    可以把问题归结为“C++并没有把将接口从实现中分离这件事做好”,因为包含头文件的做法很直观很方便,用的人也很普遍,而C++并没有对这种做法加以限制。

    如果要把编译的依赖性降低,就要换一种思路来处理,不能出现定义式,只能出现声明式,代价是增加代码的复杂度以及性能上的一些损失。

    书上提到了两种方法,第一种是采用Handler Classes(用指针指向真正实现的方法),第二种是Interface Classes(抽象基类)。

    对于第一种Handler Class,一句话,就是.h里面不包含类的自定义头文件,用“class 类名”的声明方式进行代替(也要把相应的成员变量替换成指针或引用的形式),在.cpp文件里面包含类的自定义头文件去实现具体的方法。改造之后的程序看起来是这样子的:

     1 // Person.h
     2 #include <string>
     3 using namespace std;
     4 
     5 class PersonImp;
     6 
     7 class Person
     8 {
     9 private:
    10     //string Name;
    11     //MyDate Birthday;
    12     //MyAddress Address;
    13     PersonImp* MemberImp;
    14 
    15 public:
    16     string GetName() const;
    17     string GetBirthday() const;
    18     string GetAddress() const;
    19     // follows functions
    20     // ...
    21 };
     1 // Person.cpp
     2 #include "PersonImp.h"
     3 #include "Person.h"
     4 
     5 string Person::GetName() const
     6 {
     7     return MemberImp->GetName();
     8 }
     9 string Person::GetBirthday() const
    10 {
    11     return MemberImp->GetName();
    12 }
    13 string Person::GetAddress() const
    14 {
    15     return MemberImp->GetAddress();
    16 }
     1 // PersonImp.h
     2 #ifndef PersonImp_H
     3 #define PersonImp_H
     4 
     5 #include <string>
     6 #include "MyAddress.h"
     7 #include "MyDate.h"
     8 using namespace std;
     9 
    10 class PersonImp
    11 {
    12 private:
    13     string Name;
    14     MyAddress Address;
    15     MyDate Birthday;
    16 
    17 public:
    18     string GetName() const
    19     {
    20         return Name;
    21     }
    22 
    23     string GetAddress() const
    24     {
    25         return Address.ToString();
    26     }
    27 
    28     string GetBirthday() const
    29     {
    30         return Birthday.ToString();
    31     }
    32 };
    33 
    34 #endif /* PersonImp_H */
     1 // MyDate.h
     2 #ifndef MY_DATE_H
     3 #define MY_DATE_H
     4 
     5 #include <string>
     6 using namespace std;
     7 
     8 class MyDate
     9 {
    10 private:
    11     int Year;
    12     int Month;
    13     int DayOfMonth;
    14 
    15 public:
    16     string ToString() const;
    17 }
    18 #endif /* MY_DATE_H */
     1 // MyAddress.h
     2 #ifndef MY_ADDRESS_H
     3 #define MY_ADDRESS_H
     4 
     5 #include <string>
     6 using namespace std;
     7 
     8 class MyAddress
     9 {
    10 private:
    11     string Country;
    12     string Province;
    13     string City;
    14     string Street;
    15 
    16 public:
    17     string ToString() const;
    18 };
    19 
    20 #endif /* MY_ADDRESS_H */

    这里有一点要说一下,在Person.h里面并没有使用MyDate*和MyAddress*,而是用了PersonImp*,由PersonImp里面包含MyDate与MyAddress,这样做的好处就是方便统一化管理,它要求PersonImp里面的方法与Person的方法是一致的。以后Person添加成员变量,可以直接在PersonImp中进行添加了,从而起到了隔离和隐藏的作用,因为客户端代码大量使用的将是Person,而不必关心PersonImp,用于幕后实现的PersonImp只面向于软件开发者而不是使用者。

    书上是用shared_ptr来管理PersonImp的,使资源管理上更加科学与合理。

    另外,书上也提倡把class x; class xx; class xxx;的声明放至一个名为”xxxfwd.h”的头文件里,比如”datefwd.h”,这个头文件里面只有声明式,而没有具体的类细节。也就是说,对于某个类,比如MyDate,应该分出三个文件,一个是datefwd.h,里面是一些用到的外来的class声明式;一个是MyDate.h里面是MyDate类的结构声明;一个是MyDate.cpp,它与MyDate.h配对,给出具体的实现细节。

  • 相关阅读:
    pytest.4.Fixture
    pytest.3.Assert
    pytest.2.运行多个文件
    [LeetCode 378.] Kth Smallest Element in a Sorted Matrix
    priority_queue 自定义 comparator
    原地调整法查找数组元素
    [LeetCode 436.] Find Right Interval
    [LeetCode 611.] Valid Triangle Number
    二叉树Morris遍历
    用户态IO:DPDK
  • 原文地址:https://www.cnblogs.com/jerry19880126/p/3551839.html
Copyright © 2020-2023  润新知