首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何检查PIMPL没有重新编译客户端类

如何检查PIMPL没有重新编译客户端类
EN

Stack Overflow用户
提问于 2020-03-16 15:08:05
回答 1查看 67关注 0票数 2

我试着理解PIMPL的成语。

我有几个文件,比如“Implementation.cpp/Implemente.h”实现了PIMPL成语:它包含一个公共接口和一个私有实现。

"Client.cpp/Client.h“使用公共接口。

另一个文件"main.cpp“只使用客户端类。

我写了一个非常简单的makefile。首先,所有的东西都会编译:

代码语言:javascript
复制
g++ -std=c++11 -c main.cpp  
g++ -std=c++11 -c Implementation.cpp  
g++ -std=c++11 -c Client.cpp  
g++ -o main main.o Implementation.o Client.o -std=c++11

我想引以为豪的是,如果我在PIMPL实现中修改了什么,客户机将不会重新编译,如果我不使用PIMPL成语(如果我在公共接口中做了修改),客户机将重新编译。

  • 编译器在私有实现为modified:g++ -std=c++11 -c Implementation.cpp时的输出

O Client.o -std=c++11

  • Compiler输出时公共接口(新方法、新成员和初始化等)是modified:g++ -std=c++11 -c Implementation.cpp

g++ -o main.o Implement.o Client.o .o -std=c++11

实际上,这是一样的。

我的期望是,如果我在公共接口中修改了什么东西,它应该重新编译"Implementation“和"Client":

代码语言:javascript
复制
g++ -std=c++11 -c Implementation.cpp  
g++ -std=c++11 -c Client.cpp  
g++ -o main main.o Implementation.o Client.o -std=c++11

编译器真正做的是什么,我如何检查编译器在使用PIMPL成语时是否只编译必需的?

编辑(添加代码):

Implementation.cpp:

代码语言:javascript
复制
#include "Implementation.h"

class PublicInterface::PrivateImplementation
{
public:
  PrivateImplementation(std::string name) : name(name), id(0){};
  virtual ~PrivateImplementation(void){};
  std::string name; 
  int id; 
}; 
PublicInterface::PublicInterface(std::string name) : pImplPrivate(new PrivateImplementation(name)){} 
PublicInterface::~PublicInterface() = default; 
int PublicInterface::GetID(void) const { return this->pImplPrivate->id;} 
void PublicInterface::SetID(const int id) { this->pImplPrivate->id = id;} 

实施h:

代码语言:javascript
复制
#include <memory>
#include <string> 

class PublicInterface
{
public:
  PublicInterface(std::string name); 
  virtual ~PublicInterface(void);
  int GetID(void) const; 
  void SetID(const int id);
private: 
  class PrivateImplementation;
  std::unique_ptr<PrivateImplementation> pImplPrivate;
};  

client.cpp:

代码语言:javascript
复制
#include <iostream>
#include "Client.h"
#include "Implementation.h"

Client::Client(void){}
Client::~Client(void){}
void Client::Caller(void)
{
  PublicInterface interface(std::string("Interface"));
  std::cout << "Interface ID " << interface.GetID() << std::endl;
  interface.SetID(5);
  std::cout << "Interface ID " << interface.GetID() << std::endl;
}

客户:

代码语言:javascript
复制
class Client
{
    Client(void);
    virtual ~Client(void);
public:
    static void Caller(void);
    static void Another(void);
};

main.cpp:

代码语言:javascript
复制
#include "Client.h"

int main(int argc, char** argv)
{
    Client::Caller();
    return 0;
}

Makefile:

代码语言:javascript
复制
CPPFLAGS=-std=c++11

main : main.o Implementation.o Client.o
    g++ -o main main.o Implementation.o Client.o $(CPPFLAGS)

main.o : main.cpp
    g++ $(CPPFLAGS) -c main.cpp
Implementation.o : Implementation.cpp
    g++ $(CPPFLAGS) -c Implementation.cpp
Client.o : Client.cpp
    g++ $(CPPFLAGS) -c Client.cpp

clean :
    rm main main.o Implementation.o Client.o
EN

回答 1

Stack Overflow用户

发布于 2020-03-16 16:03:20

编译器真正做了什么?

编译器执行被要求做的事情。在此:

g++ -std=c++11 -c Implementation.cpp g++ -o主要实施

..。Implementation.cpp被编译,并与以前编译的main.o和Client.o链接。Client.cpp和main.cpp都没有编译。

我的期望是,如果我在公共接口中修改了什么东西,它应该同时编译“实现”和“客户端”。

如果您确实修改了Implement.h中的定义,那么所有包含它的翻译单元都必须重新编译。如果没有完成,并且链接器被告知使用不兼容的对象文件,那么程序很可能违反了“一个定义规则”。这种违反使得程序格式不正确,但是不需要语言实现来诊断这个问题。有些违规行为被链接者抓住,而另一些则没有。

如何检查编译器在使用PIMPL成语时是否只编译必需的?

因为有了PIMPL,您不需要修改标题,也因为不需要修改标头,所以您知道不需要重新编译包含该标头的翻译单元。

生成系统(如make和ninja )通常会跟踪翻译单元中包含的所有文件的修改时间,并在比以前编译的对象文件更早的时候跳过重新编译。通常可以通过检查工具的输出来检查此类工具是否重新编译特定的源文件。

此外,如果您使用类似ccache前端的内容,那么您会注意到重新编译未经修改的翻译单元要快得多,因为它实际上不会因为从缓存加载而被重新编译。Ccache使用翻译单元的散列内容,而不是修改时间来检测更改。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60708423

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档