考虑以下:
PImpl.hpp
class Impl;
class PImpl
{
Impl* pimpl;
PImpl() : pimpl(new Impl) { }
~PImpl() { delete pimpl; }
void DoSomething();
};
Run Code Online (Sandbox Code Playgroud)
PImpl.cpp
#include "PImpl.hpp"
#include "Impl.hpp"
void PImpl::DoSomething() { pimpl->DoSomething(); }
Run Code Online (Sandbox Code Playgroud)
Impl.hpp
class Impl
{
int data;
public:
void DoSomething() {}
}
Run Code Online (Sandbox Code Playgroud)
client.cpp
#include "Pimpl.hpp"
int main()
{
PImpl unitUnderTest;
unitUnderTest.DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
这种模式背后的想法是,Impl界面可以改变,但客户端不必重新编译.然而,我没有看到这是如何真实的情况.假设我想在这个类中添加一个方法 - 客户端仍然需要重新编译.
基本上,只有种这样的,我可以看到的变化不断需要改变头文件一类的东西,其中的类发生变化的接口.当发生这种情况时,pimpl或没有pimpl,客户端必须重新编译.
这里的哪种编辑在不重新编译客户端代码方面给我们带来了好处?
昨天我遇到了痛苦,让我感到沮丧24小时.问题归结为随机发生的意外崩溃.更复杂的是,调试报告也具有绝对随机的模式.更复杂的是,所有调试跟踪都会导致随机 Qt源或本机DLL,即证明每次问题都不在我身边.
这里有几个这样可爱的报告的例子:
Program received signal SIGSEGV, Segmentation fault.
0x0000000077864324 in ntdll!RtlAppendStringToString () from C:\Windows\system32\ntdll.dll
(gdb) bt
#0 0x0000000077864324 in ntdll!RtlAppendStringToString () from C:\Windows\system32\ntdll.dll
#1 0x000000002efc0230 in ?? ()
#2 0x0000000002070005 in ?? ()
#3 0x000000002efc0000 in ?? ()
#4 0x000000007787969f in ntdll!RtlIsValidHandle () from C:\Windows\system32\ntdll.dll
#5 0x0000000000000000 in ?? ()
Run Code Online (Sandbox Code Playgroud)
warning: HEAP: Free Heap block 307e5950 modified at 307e59c0 after it was freed
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000778bf0b2 in ntdll!ExpInterlockedPopEntrySListFault16 () from …Run Code Online (Sandbox Code Playgroud) 我正在使用pimpl成语来隐藏接口的实现细节,以便我可以有一些ABI保护措施.我不是那么精通MS的细节......在我的大部分开发生涯中使用Linux.
我无法从调试器检查窗口查看pimpl的内部.我的类型只扩展到impl的原始指针(它使用智能指针).我已经尝试导出符号,但这似乎不起作用.我想我实际上想要查看的符号没有被导入或者其他东西.
我该如何解决这个问题?可视化器可能会调用函数吗?
编辑 - 也许我把人们与出口事物混为一谈.
我正在尝试这个:
object.h:
struct EXPORT object {
struct EXPORT impl;
impl * pimpl;
};
Run Code Online (Sandbox Code Playgroud)
object.cpp
struct EXPORT object::impl {
char member;
};
Run Code Online (Sandbox Code Playgroud)
我能想到解决问题的唯一方法是将impls放在包含在库的调试版本中的头文件中.希望有更好的解决方案.
我有以下内容:
// foo.h:
class foo {
public:
foo();
~foo();
// note: the param type repetition here is only incidental, assume the
// functions can't easily be made to share type signatures
void bar(a b, c d);
void baz(a b, c d, e f, g h, i j);
void quux(a b, c d, e f, g h, i j);
private:
class impl;
impl *m_pimpl;
}
Run Code Online (Sandbox Code Playgroud)
然后:
// foo.cpp:
class foo::impl {
public:
void bar(a b, c d);
void baz(a b, c …Run Code Online (Sandbox Code Playgroud) 我想设计一个类Foo来存储不同类型的各种数据并返回它们的迭代器。它应该是通用的,因此用户Foo不知道数据是如何存储的(Foo可能正在使用std::set或std::vector其他)。
我很想写一个这样的界面:
class Foo {
class FooImpl;
FooImpl* impl_;
public:
const Iterator<std::string>& GetStrings() const;
const Iterator<int>& GetInts() const;
};
Run Code Online (Sandbox Code Playgroud)
哪里Iterator有类似这样的东西(比如 .NET 中的迭代器):
template<class T>
class Iterator {
public:
const T& Value() const = 0;
bool Done() const = 0;
void Next() = 0;
};
Run Code Online (Sandbox Code Playgroud)
但我知道这种迭代器在C++中不是标准的,最好像STL那样使用迭代器,这样你就可以在它们上使用STL算法。
我怎样才能做到这一点?(我有需要iterator_traits吗?)
我最近从Java和Ruby切换回C++,令我惊讶的是,当我更改私有方法的方法签名时,我必须重新编译使用公共接口的文件,因为私有部分也在.h文件中.
我很快想出了一个解决方案,我想这对于Java程序员来说是典型的:接口(=纯虚拟基类).例如:
BananaTree.h:
class Banana;
class BananaTree
{
public:
virtual Banana* getBanana(std::string const& name) = 0;
static BananaTree* create(std::string const& name);
};
Run Code Online (Sandbox Code Playgroud)
BananaTree.cpp:
class BananaTreeImpl : public BananaTree
{
private:
string name;
Banana* findBanana(string const& name)
{
return //obtain banana, somehow;
}
public:
BananaTreeImpl(string name)
: name(name)
{}
virtual Banana* getBanana(string const& name)
{
return findBanana(name);
}
};
BananaTree* BananaTree::create(string const& name)
{
return new BananaTreeImpl(name);
}
Run Code Online (Sandbox Code Playgroud)
这里唯一的麻烦就是我不能使用new,而必须打电话BananaTree::create().我不认为这确实是一个问题,特别是因为我希望无论如何都要使用很多工厂.
然而,现在,C++成名的聪明人提出了另一个解决方案,即pImpl成语.有了它,如果我理解正确,我的代码将如下所示:
BananaTree.h:
class BananaTree
{
public: …Run Code Online (Sandbox Code Playgroud) 在阅读了关于pimpl成语之后,我感到震惊!
是不是有一个工具可以检查.h/.cpp文件并推断可以放弃哪些依赖项?
我正在试验C11和VLA,试图在堆栈上声明一个只有不完整声明的结构变量.目标是提供一种机制来创建一些结构类型的变量而不显示内部(如PIMPL惯用法),但不需要在堆上创建变量并返回指向它的指针.此外,如果结构布局发生更改,我不想重新编译使用该结构的每个文件.
我设法编写了以下内容:
private.h:
#ifndef PRIVATE_H_
#define PRIVATE_H_
typedef struct A{
int value;
}A;
#endif /* PRIVATE_H_ */
Run Code Online (Sandbox Code Playgroud)
public.h:
#ifndef PUBLIC_H_
#define PUBLIC_H_
typedef struct A A;
size_t A_getSizeOf(void);
void A_setValue(A * a, int value);
void A_printValue(A * a);
#endif /* PUBLIC_H_ */
Run Code Online (Sandbox Code Playgroud)
implementation.c:
#include "private.h"
#include "stdio.h"
size_t A_getSizeOf(void)
{
return sizeof(A);
}
void A_setValue(A * a, int value)
{
a->value = value;
}
void A_printValue(A * a)
{
printf("%d\n", a->value);
}
Run Code Online (Sandbox Code Playgroud)
main.c中:
#include <stdalign.h>
#include <stddef.h>
#include "public.h"
#define …Run Code Online (Sandbox Code Playgroud) pimpl习语的规范形式(来自Herb Sutter的"Exceptional C++")如下:
class X
{
public:
/* ... public members ... */
protected:
/* ... protected members? ... */
private:
/* ... private members? ... */
struct XImpl;
XImpl* pimpl_; // opaque pointer to
// forward-declared class
};
Run Code Online (Sandbox Code Playgroud)
我的问题是,为什么XImpl声明为结构而不是类?
pimpl(也称为编译器防火墙)习惯用于缩短编译时间,但代价是可读性和一点运行时性能.目前一个项目需要很长时间才能编译,如何衡量最佳的pimpl候选人?
我有使用pimpl的经验,将项目的编译时间从两小时缩短到十分钟,但我只是按照我的直觉做了这个:我推断出类头文件包括(1)很多源代码(2)复杂/模板类,是使用疙瘩成语的最佳人选.
是否有一个工具可以客观地指出哪些类是优秀的pimpl候选人?
pimpl-idiom ×10
c++ ×9
c ×1
c11 ×1
c99 ×1
destructor ×1
dll ×1
header ×1
iterator ×1
profiler ×1
pure-virtual ×1
qt ×1
stl ×1