首页 > 其他分享 >Item31:最小化文件之间的编译依赖

Item31:最小化文件之间的编译依赖

时间:2024-04-11 14:57:14浏览次数:32  
标签:std const string Person 编译 Item31 最小化 class name

芝士wa
2024.4.11
Item31链接


引子

“你进入到你的程序中,并对一个类的实现进行了细微的改变。提醒你一下,不是类的接口,只是实现,仅仅是 private 的东西。然后你重建(rebuild)这个程序,预计这个任务应该只花费几秒钟。毕竟只有一个类被改变。你在 Build 上点击或者键入 make(或者其它等价行为),接着你被惊呆了,继而被郁闷,就像你突然意识到整个世界都被重新编译和连接!当这样的事情发生的时候,你不讨厌它吗?”

问题在于C++没有做好从实现中剥离接口的工作,有两种方法可以实现:Handles和Interface。


Handles方法

采用前向声明:

class Date;                    // forward declaration
class Address;                 // forward declaration

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;
  ...
};

这个分离的关键就是用对声明的依赖替代对定义的依赖。这就是最小化编译依赖的精髓。

更进一步,采用pointer to implement的方法,将函数调用方法写成Person类的数据成员,

Person.h

#include <string>
#include <memory>

class PersonImpl;  // Person实现类的前向声明
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::shared_ptr<PersonImpl> pImpl;  // 指向PersonImpl的智能指针
};

Person.cpp

#include "Person.h"          // we're implementing the Person class,
                             // so we must #include its class definition

#include "PersonImpl.h"      // we must also #include PersonImpl's class
                             // definition, otherwise we couldn't call
                             // its member functions; note that 
                             // PersonImpl has exactly the same
                             // member functions as Person — their
                             // interfaces are identical

Person::Person(const std::string& name, const Date& birthday,
               const Address& addr)
: pImpl(new PersonImpl(name, birthday, addr))
{}

std::string Person::name() const
{
  return pImpl->name();
}

在Person.h中采用了前向声明,在Person.cpp中包含了Person的头文件和PersonImpl的头文件。


Interface方法

将Person类写成纯虚类,作为模板提供接口,

class Person {
public:
  virtual ~Person();

  virtual std::string name() const = 0;
  virtual std::string birthDate() const = 0;
  virtual std::string address() const = 0;
  ...
};

在派生类中实现Person,

class RealPerson: public Person {
public:
  RealPerson(const std::string& name, const Date& birthday,
             const Address& addr)
  : theName(name), theBirthDate(birthday), theAddress(addr)
  {}

  virtual ~RealPerson() {}

  std::string name() const;        // implementations of these 
  std::string birthDate() const;   // functions are not shown, but
  std::string address() const;     // they are easy to imagine

private:
  std::string theName;
  Date theBirthDate;
  Address theAddress;
};

这种设计方式遵循了面向对象编程中的"依赖倒置原则"(Dependency Inversion Principle)。根据这个原则,高层模块(接口类)不应该依赖于低层模块(派生类)的具体实现,而是应该依赖于抽象(接口)。由于接口类的定义不依赖于派生类,当需要修改或添加新的派生类时,不会影响到接口类和其他派生类的编译。只需编译和链接新的派生类即可,而不需要重新编译接口类或其他派生类。
这种减少文件之间相互依赖的设计有助于降低代码的耦合性(coupling),提高代码的可维护性和可扩展性。它允许独立地修改和扩展派生类,而不会对其他部分产生意外的影响。同时,它也提供了更好的代码组织和模块化,使得代码更易于理解和维护。

标签:std,const,string,Person,编译,Item31,最小化,class,name
From: https://www.cnblogs.com/cheese-wa/p/18129176

相关文章

  • ZOMI的AI编译原理4
    为什么需要AI编译器面临的问题挑战类别描述算子挑战越来越多新算子被提出,导致算子库的开发、维护、优化和测试工作量指数上升。1.硬件不仅需要实现新算子,还需要结合硬件进行特性优化和测试,以充分发挥硬件性能。例如,对于Convolution运算,需要将其转换为GEMM矩阵乘......
  • LeetCode 2439. 最小化数组中的最大值
    给你一个下标从 0 开始的数组 nums ,它含有 n 个非负整数。每一步操作中,你需要:选择一个满足 1<=i<n 的整数 i ,且 nums[i]>0 。将 nums[i] 减1。将 nums[i-1] 加1。你可以对数组执行 任意 次上述操作,请你返回可以得到的 nums 数组中 最大值......
  • 看不懂来打我,vue3如何将template编译成render函数
    前言在之前的通过debug搞清楚.vue文件怎么变成.js文件文章中我们讲过了vue文件是如何编译成js文件,通过那篇文章我们知道了,template编译为render函数底层就是调用了@vue/compiler-sfc包暴露出来的compileTemplate函数。由于文章篇幅有限,我们没有去深入探索compileTemplate函数是......
  • vue编译器
    ast-编译成代码import*aspathfrom'path'importtype{Plugin,ResolvedConfig}from'vite'import{NodePath}from'@babel/traverse';import{JSXElement}from'@babel/types';import{compile,generate,transform......
  • 如何让WSL2使用自己编译的内核
    目录wsl基本介绍以及安装编译内核下载linux源码使用wsl内核配置添加uvc内核驱动编译内核切换wsl内核重启内核最近有一个摄像头的项目,想着为什么不直接使用wsl呢。查阅了网络上大量的资料,修改了WSL2内核来支持UVCwsl基本介绍以及安装wsl(windowssubsystemforlinux)是wind......
  • 千万不要将centos中python 默认2.7的编译器改为3.x的,会出现File “ usr bin yum“, li
    千万不要将centos中python默认2.7的编译器改为3.x的,在使用yum时,会报各种错,1、File"/usr/bin/yum",line30  exceptKeyboardInterrupt,e:原因是yum按python3.6解析2.7的语法出错了修改/usr/bin/yum文件中的第一行为#!/usr/bin/python2.72、 File"/usr/libexec/url......
  • ZOMI的AI编译原理3
    LLVM设计架构LLVMIR与GCCIR对比特性LLVMIRGCCIR(GIMPLE)独立性和库化架构高度模块化,前端和后端分离,易于添加新语言和目标平台传统GCC架构,前端和后端耦合较紧密表达形式人类可读的汇编形式、C++对象形式、序列化后的bitcode形式GIMPLE表示形式,三地址代码,SS......
  • Notepad--文本编译工具推荐
    推荐一个全平台的文本、代码编辑工具Notepad--,支持Windows、Mac以及国产uos深度系统、redhat/ubutu/centos等系统。可以替换你目前手头使用的Notpad++,这个软件能不用就别用了,懂得都懂。废话不多说,附上Notepad--作者爬山虎的gitee链接ndd发行版-Gitee.com这个软件挺轻量化的......
  • boost编译失败
    概述本文档介绍再编译openGauss-third_party三方库时候,boost编译失败解决方法。目前在1.1.02.0.0版本都适用。错误分析和处理在编译openGauss-third_party三方库时候,我们首先在build目录下执行shbuild.sh,三方库自行编译。但是经常遇到在编译到boost的时候,脚本终......
  • Delphi 编译的app无法在android 14安装
    今天用delphi12.1升级旧项目,编译正常,但安装时出下面的提示:08:48:26E/SplitApkInstaller:Failedtofinalizesession:INSTALL_PARSE_FAILED_MANIFEST_MALFORMED:FailedparseduringinstallPackageLI:/data/app/vmdl1849586780.tmp/base.apk(atBinaryXMLfileline#......