我试着理解PIMPL的成语。
我有几个文件,比如“Implementation.cpp/Implemente.h”实现了PIMPL成语:它包含一个公共接口和一个私有实现。
"Client.cpp/Client.h“使用公共接口。
另一个文件"main.cpp“只使用客户端类。
我写了一个非常简单的makefile。首先,所有的东西都会编译:
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成语(如果我在公共接口中做了修改),客户机将重新编译。
O Client.o -std=c++11
g++ -o main.o Implement.o Client.o .o -std=c++11
实际上,这是一样的。
我的期望是,如果我在公共接口中修改了什么东西,它应该重新编译"Implementation“和"Client":
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:
#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:
#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:
#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;
}客户:
class Client
{
Client(void);
virtual ~Client(void);
public:
static void Caller(void);
static void Another(void);
};main.cpp:
#include "Client.h"
int main(int argc, char** argv)
{
Client::Caller();
return 0;
}Makefile:
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发布于 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使用翻译单元的散列内容,而不是修改时间来检测更改。
https://stackoverflow.com/questions/60708423
复制相似问题