背景资料:
所述PIMPL成语(指针实现)是用于执行隐藏在其中一个公共类包装的结构或类,可以不在库的公共类是的一部分外部看到的技术.
这会隐藏来自库用户的内部实现细节和数据.
在实现这个习惯用法时,为什么要将公共方法放在pimpl类而不是公共类上,因为公共类方法实现会被编译到库中,而用户只有头文件?
为了说明,此代码将Purr()实现放在impl类上并将其包装起来.
为什么不直接在公共类上实现Purr?
// header file:
class Cat {
private:
class CatImpl; // Not defined here
CatImpl *cat_; // Handle
public:
Cat(); // Constructor
~Cat(); // Destructor
// Other operations...
Purr();
};
// CPP file:
#include "cat.h"
class Cat::CatImpl {
Purr();
... // The actual implementation can be anything
};
Cat::Cat() {
cat_ = new CatImpl;
}
Cat::~Cat() {
delete cat_;
}
Cat::Purr(){ cat_->Purr(); }
CatImpl::Purr(){
printf("purrrrrr");
}
Run Code Online (Sandbox Code Playgroud) 几个星期休息之后,我正在尝试使用David Vandevoorde和Nicolai M. Josuttis 所着的模板 - 完整指南来扩展和扩展我的模板知识,我现在想要了解的是模板的显式实例化.
我实际上并没有这样的机制问题,但我无法想象我想要或想要使用此功能的情况.如果有人能向我解释,我将不仅仅是感激.
这是我在尝试将unique_ptr用于pimpl时所看到的简化.我选择了unique_ptr,因为我真的希望类拥有指针 - 我希望pimpl指针和类的生命周期相同.
无论如何,这是标题:
#ifndef HELP
#define HELP 1
#include <memory>
class Help
{
public:
Help(int ii);
~Help() = default;
private:
class Impl;
std::unique_ptr<Impl> _M_impl;
};
#endif // HELP
Run Code Online (Sandbox Code Playgroud)
这是来源:
#include "Help.h"
class Help::Impl
{
public:
Impl(int ii)
: _M_i{ii}
{ }
private:
int _M_i;
};
Help::Help(int ii)
: _M_impl{new Help::Impl{ii}}
{ }
Run Code Online (Sandbox Code Playgroud)
我可以将它们编译成一个库就好了.但是当我尝试在测试程序中使用它时,我得到了
ed@bad-horse:~/ext_distribution$ ../bin/bin/g++ -std=c++0x -o test_help test_help.cpp Help.cpp
In file included from /home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
from Help.h:4,
from test_help.cpp:3:
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = …Run Code Online (Sandbox Code Playgroud) PIMPL代表P ointer到IMPL ementation.实现代表"实现细节":类的用户不必关心的东西.
Qt自己的类实现通过使用PIMPL惯用法将接口与实现完全分开.然而,Qt提供的机制没有记录.怎么用?
我想这是关于Qt中"我如何进行PIMPL"的规范性问题.答案将由下面显示的简单坐标输入对话框界面激发.
当我们有任何半复杂的实现时,使用PIMPL的动机就变得明显了.这个问题给出了进一步的动机.即使是一个相当简单的类也必须在其界面中引入许多其他头文件.

基于PIMPL的接口非常干净且易读.
// CoordinateDialog.h
#include <QDialog>
#include <QVector3D>
class CoordinateDialogPrivate;
class CoordinateDialog : public QDialog
{
Q_OBJECT
Q_DECLARE_PRIVATE(CoordinateDialog)
#if QT_VERSION <= QT_VERSION_CHECK(5,0,0)
Q_PRIVATE_SLOT(d_func(), void onAccepted())
#endif
QScopedPointer<CoordinateDialogPrivate> const d_ptr;
public:
CoordinateDialog(QWidget * parent = 0, Qt::WindowFlags flags = 0);
~CoordinateDialog();
QVector3D coordinates() const;
Q_SIGNAL void acceptedCoordinates(const QVector3D &);
};
Q_DECLARE_METATYPE(QVector3D)
Run Code Online (Sandbox Code Playgroud)
基于Qt 5,C++ 11的接口不需要该Q_PRIVATE_SLOT行.
将其与非PIMPL接口进行比较,该接口将实现细节隐藏在接口的私有部分中.请注意必须包含多少其他代码.
// CoordinateDialog.h
#include <QDialog>
#include <QVector3D>
#include <QFormLayout>
#include <QDoubleSpinBox>
#include <QDialogButtonBox>
class CoordinateDialog : …Run Code Online (Sandbox Code Playgroud) 我刚刚注意到一个新术语pimpl成语,这个成语与Bridge设计模式有什么区别?我很困惑.
我也注意到pimpl成语总是用于交换功能,那是什么?有人可以举个例子吗?
采用以下头文件示例,其中Bar是结构:
class Foo
{
...
private:
Bar _bar;
};
Run Code Online (Sandbox Code Playgroud)
我只想Bar作为私有成员变量访问Foo.声明和定义的正确方法是什么Bar?
选项1:在标题中定义?
我想避免这种情况,因为我不想Bar在Foo课堂范围之外使用.
struct Bar
{
int a;
int b;
...
};
class Foo
{
...
private:
Bar _bar;
};
Run Code Online (Sandbox Code Playgroud)
选项2:在头文件中转发声明,在cpp中定义?
不确定这是否合法,如果编译器的定义不直接可用,编译器将如何Foo严格地知道标题的大小Bar?此外,这会隐藏Bar其他包含标题的文件吗?
标题:
struct Bar;
class Foo
{
...
private:
Bar _bar;
};
Run Code Online (Sandbox Code Playgroud)
实施文件:
struct Bar
{
int a;
int b;
...
};
Run Code Online (Sandbox Code Playgroud)
选项3:在课堂上宣布
也许是限制范围的最佳选择,但可能是凌乱的?
class Foo
{
...
private:
struct Bar
{ …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用延迟计算创建类.所以我需要struct来保存以前计算过的变量,我想把这个类放到未命名的命名空间中(不想污染全局范围).这是解释我想要的最小代码
calculator.h:
#ifndef CALCULATOR_H
#define CALCULATOR_H
class PrevCalc;
class Calculator
{
public:
Calculator();
PrevCalc* prevCalc;
};
#endif // CALCULATOR_H
Run Code Online (Sandbox Code Playgroud)
calculator.cpp:
#include "calculator.h"
namespace{
struct PrevCalc{
double prevA = -1;
double prevB = -1;
double prevC = -1;
};
}
Calculator::Calculator()
{
prevCalc = new PrevCalc();
}
Run Code Online (Sandbox Code Playgroud)
当然它给出了一个错误expected type-specifier before 'PrevCalc',如果我定义PrevCalc没有命名空间一切正常.我的问题是如何声明将在.cpp文件中的未命名命名空间中定义的类
有几个问题讨论为什么我们应该有单独的编译单元以便改进编译时间(例如,不包括hpp文件中的任何代码,但仅包括cpp文件中的代码).
但后来我发现了这个问题:
如果我们可以忽略可维护性的问题,如果我们只看一下编译/链接时间,以及优化代码,那么只有一个hpp和cpp文件会带来什么好处和陷阱?
请注意,我链接的帖子谈论单个cpp文件(虽然有许多头文件).我问如果我们只有一个hpp文件和一个cpp文件会发生什么.....
编辑:如果我们可以忽略更改单行的事实,将导致整个代码被重新编译,它是否仍然比从头开始重新编译1000个单独的文件更快...
编辑:我对可维护性的讨论不感兴趣.我试图理解是什么让编译器编译得更快.这个问题与实际问题无关,但更多的是与理解一个简单问题有关:
一个大的hpp&cpp文件,编译速度比使用单个核心分割许多hpp和cpp文件的代码要快.
编辑:我认为人们越来越偏向于谈论什么是实际的,应该做什么.这个问题不是关于人们应该做什么 - 它只是帮助我理解编译器在幕后做了什么 - 直到现在还没有人回答这个问题,而是谈论它是否实用.
编辑:除了一个实际上试图回答这个问题的人 - 我觉得这个问题没有得到应有的正义,并且被不必要地投票.SO是关于分享信息,而不是惩罚问题,因为人们要求的人不知道答案.
我有项目1与Boost和GLM的依赖关系.对于Boost和GLM,我已经指定了"附加包含目录"来引用每个目录的C++文件.项目1创建为静态库项目.当我构建项目1时,一切都很好.项目2通过参考管理器引用项目1,但是当我构建项目2时,我得到了
fatal error C1083: Cannot open include file: 'boost/something/etc.
对于项目1中的文件.为什么在构建Project 2时会出现Project 1的错误?项目1还使用Boost中的正则表达式库,该库必须.lib在使用之前构建.如何使我的Project 1静态库将构建的Boost正则表达式库和GLM包含文件合并到其中?仅供参考,项目2是项目1的测试项目.我想要这样的事情:
(Boost正则表达式lib + GLM包括) - >项目1 ==> Project_1.lib
(Boost单元测试lib + Project_1.lib) - > Project 2 ==> Project_2.exe
-->表示依赖关系/引用并==>表示输出.
这可能吗?我得到了更多的编译错误和链接器错误,因为我在这上面旋转我的轮子.
c++ dependencies boost visual-studio-2010 dependency-management
c++ ×10
pimpl-idiom ×3
boost ×1
c++11 ×1
c++20 ×1
dependencies ×1
oop ×1
qt ×1
templates ×1
unique-ptr ×1