c++减少编译依赖

查资料的过程中有一句话印象深刻,这里记录下来:
教科书上的条条款款和真正的工程实践是有出入的,所以不要纸上谈兵

问题引入

为什么会有这个问题的出现呢?编译器需要在编译期间需要知晓类型的大小!

在大工程中,.cpp代码很多,而如果不管它的依赖性问题,那么很可能更改一个.h文件,就会导致众多包含这个.h文件的.cpp文件一起被重新编译,即使其他文件什么也没有改动,这个问题的本质在于C++并没有把"接口从实现中分离"做得很好, 举个例子:

// person.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 theName;
    Date theBirthDate;
    Address theAddress;
};
// person.cpp

person.h 这个头文件中包含了整个Person的实现细节,

// test.h
#include "person.h"
class Test {
private:
    Person person;
}
// test.cpp

test.h包含了这个.h文件

// main.cpp
#include "test.h"

main.cpp 包含了test.h

这个时候我对person.h做了一些改动,无论是什么改动,哪怕其他地方没有使用, main.cpp,person.cpp,test.cpp全部得重编,在大项目中,编译时间是相当长的,所以我们要减少编译依赖。

.h文件中包含了十足的细节上的实现,只要实现被改变了,其他include 它的 .h文件的.cpp文件都得进行重编

把所有cpp代码包含在一起

先讲简单的方法 all.cpp !!!

#include 指令的本质就是把那个文件的东西全部盖过来,那么我创建一个 all.cpp, 然后如下操作

// all.cpp
#include "main.cpp"
#include "person.cpp"
#include "test.cpp"

我们的编译只需要编译一个all.cpp文件,其他文件都不用编译!虽然不管改个什么东西都需要进行重编,但是只有一个文件啊!快了不止一个数量级,cmake这样写,

cmake_minimum_required(VERSION 3.15)
project(make_depend)

include_directories(${CMAKE_BINARY_DIR})

add_executable(test_make "all.cpp")

可能这个比较粗糙,但是在工程实践中有相当成功的例子,Opera 浏览器使用这种技巧将编译时间从半小时降到 5 分钟!并且没有什么缺陷了,要硬说缺陷,我也可以例几条:

  1. 增量编译失效,随便改个代码都需要重编(但是好像问题不大)
  2. 并行编译失效,无论是分布式编译还是利用本地的多核都没用了,因为只编译一个文件了(这好像是好事)
  3. 内存占用高,一次性加载了整个项目的代码

pimpl idom(类和类的实现分开)

我在.h文件中隐藏真实的实现,而只提供接口不就好了

接口如下:

// person.h
#pragma once

class Person {
public:
    Person();
    ~Person();
    void get_name();
private:
    class PersonImpl *imp; // 定义实现类的指针,指针的大小是确定的!
};

// person.cpp
#include "person.h"
#include "person_impl.h"

Person::Person() {
    imp = new PersonImpl;
}

Person::~Person() {
    delete imp;
}

void Person::get_name() {
    imp->get_name();
}

实现如下:

// person_impl.h
#pragma once
#include <string>
class PersonImpl {
public:
    void get_name();
private:
    std::string name = "testa";
};

// person_impl.cpp
#include "person_impl.h"
#include <iostream>
void PersonImpl::get_name() {
    std::cout << name << std::endl;
}

main 文件:

// main.cpp
#include <bits/stdc++.h>
#include "person.h"
#include "test.h"
using namespace std;

int main() {
    Person person;
person.get_name();
}

现在的编译依赖变成了这样:

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

现在我更改person 的实现部分,也就是 person_impl.h person_impl.cpp 就再也不会影响到main了,

但是这种方式的缺陷也很明显:

  1. 在多年以后,再看整个代码时你确定你不会被你自己的代码恶心到吗?找个实现跳转不知道要多少层,就是代码不清晰
  2. 对于关键.h, 被大多数文件所引用的,使用这种方法有效。但是很多.h没有被太多文件所引用
  3. 就因为它是指针,你没办法在栈上。。。还得写个辅助的工厂类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值