我渴望将接口与实现分开.这主要是为了保护使用库的代码来改变所述库的实现,尽管减少编译时间肯定是受欢迎的.
对此的标准解决方案是指向实现习惯用法的指针,最有可能通过使用unique_ptr并使用实现仔细定义类析构函数来实现.
这不可避免地引起了对堆分配的担忧.我熟悉"让它工作,然后快速完成","简介再优化"等智慧.还有在线文章,例如gotw,它宣称明显的解决方法是脆弱和不可移植.我有一个目前不包含任何堆分配的库 - 我想保持这种方式 - 所以让我们有一些代码.
#ifndef PIMPL_HPP
#define PIMPL_HPP
#include <cstddef>
namespace detail
{
// Keeping these up to date is unfortunate
// More hassle when supporting various platforms
// with different ideas about these values.
const std::size_t capacity = 24;
const std::size_t alignment = 8;
}
class example final
{
public:
// Constructors
example();
example(int);
// Some methods
void first_method(int);
int second_method();
// Set of standard operations
~example();
example(const example &);
example &operator=(const example &); …Run Code Online (Sandbox Code Playgroud) 有时,C++的隐私概念让我感到困惑:-)
class Foo
{
struct Bar;
Bar* p;
public:
Bar* operator->() const
{
return p;
}
};
struct Foo::Bar
{
void baz()
{
std::cout << "inside baz\n";
}
};
int main()
{
Foo::Bar b; // error: 'struct Foo::Bar' is private within this context
Foo f;
f->baz(); // fine
}
Run Code Online (Sandbox Code Playgroud)
自从Foo::Bar就是private,我不能宣布b在main.然而,我可以从方法中调用方法Foo::Bar.为什么这是允许的?这是意外还是设计?
哦等等,它会变得更好:
Foo f;
auto x = f.operator->(); // :-)
x->baz();
Run Code Online (Sandbox Code Playgroud)
即使我不被允许命名类型Foo::Bar,它也适用于auto......
诺亚写道:
在类定义中定义的类型名称不能在没有限定条件的类之外使用.
只是为了好玩,以下是从外面获取类型的方法:
#include <type_traits> …Run Code Online (Sandbox Code Playgroud) Pimpl是许多C++代码中的样板来源.它们似乎是宏,模板和一些外部工具帮助可以解决的组合,但我不确定最简单的方法是什么.我已经看到模板有助于完成一些提升但不是很多 - 你仍然需要为你试图包装的类的每个方法编写转发函数.有没有更简单的方法?
我正在想象一个用作制作过程一部分的工具.你希望你的公共标题是pimpl'd类,所以你提供一些输入文件,比如pimpl.in,列出你要包装的类(实现un-pimpl'd),然后检查该文件,生成pimpl类,并且在'make install'期间只安装它们的标题(不是原始类的标题).问题是如果没有完整的C++解析器,我没有看到任何方法可以做到这一点,甚至编译器供应商都无法做到这一点.也许这些类可以用某种方式编写,这使得外部工具的工作变得更容易,但我确信我最终会错过各种角落案例(例如模板化类和/或模板化成员函数).
有任何想法吗?有没有其他人为此问题提供解决方案?
d指针在Qt中大量使用,它们是pimpl习语的实现.我知道pimpl成语的优点和缺点.但我错过了d-pointers实现的优点.这里和这里是d指针的样本.使用它不是更容易吗?
class MyClassPrivate;
class MyClass {
// interface methods
private:
MyClassPrivate *pimpl_;
};
Run Code Online (Sandbox Code Playgroud) 我在一些代码中找到了这个构造.
私有静态类实现A有什么好处吗?这让我想起了C++中的Pimpl成语.在Java中使用Pimpl习语是否有任何好处?
public abstract class A {
public void doStuff();
public static A getNewInstance() {
return new AImpl();
}
private static class AImpl extends A {
public void doStuff() {
....
}
}
}
Run Code Online (Sandbox Code Playgroud) 是否可能,以实现以下目标:
x.hpp - 许多其他类都包含此文件
class x_impl; //forward declare
class x {
public:
//methods...
private:
x_impl* impl_;
};
Run Code Online (Sandbox Code Playgroud)
x.cpp - 实现
#include <conrete_x>
typedef concrete_x x_impl; //obviously this doesn't work
//implementation of methods...
Run Code Online (Sandbox Code Playgroud)
所以基本上,我希望用户包含文件x.hpp,但不知道conrete_x.hpp标头.
由于我concrete_x只能通过指针使用并且它只显示为私有数据成员,因此前向声明应该足以让编译器知道准备多少空间.它看起来很像着名的"pimpl成语".
你能帮帮我吗?
PS.我不想使用它void*并将其投射..
可以使用哪种技巧来最小化实现pImpl类的工作量?
标题:
class Foo {
struct Impl;
boost::scoped_ptr<Impl> self;
public:
Foo(int arg);
~Foo();
// Public member functions go here
};
Run Code Online (Sandbox Code Playgroud)
执行:
struct Foo::Impl {
Impl(int arg): something(arg) {}
// All data members and private functions go here
};
Foo::Foo(int arg): self(new Impl(arg)) {}
Foo::~Foo() {}
// Foo's public functions go here (and they refer to data as self->something)
Run Code Online (Sandbox Code Playgroud)
您如何使用Boost,可能继承,CRTP或其他技巧来避免尽可能多的样板代码?运行时性能不是问题.
让我们考虑以下示例(使用c ++ 11)
A.hpp:
#include <memory>
class A
{
public:
//A();
//~A();
private:
struct AImpl;
std::unique_ptr<AImpl> pImpl;
};
Run Code Online (Sandbox Code Playgroud)
main.cpp中:
#include "A.hpp"
int main()
{
A a;
}
Run Code Online (Sandbox Code Playgroud)
使用默认构造函数和析构函数.不编译.发生以下错误:
在文件中包含/usr/include/c++/4.8/memory:81:0,来自A.hpp:2,来自main.cpp:2:/usr/include/c++/4.8/bits/unique_ptr.h:实例化'void std :: default_delete <_Tp> :: operator()(_ Tp*)const [with _Tp = A :: AImpl]':/ usr /include/c++/4.8/bits/unique_ptr.h:184:16: 'std :: unique_ptr <_Tp,_Dp> ::〜unique_ptr()[与_Tp = A :: AImpl; _Dp = std :: default_delete]'A.hpp:3:7:从这里需要/usr/include/c++/4.8/bits/unique_ptr.h:65:22:错误:'sizeof'无效应用于不完整类型' A :: AImpl'static_assert
(sizeof(_Tp)> 0,
使用boost :: scoped_ptr而不是std :: unique_ptr时会发生类似的错误.我是否理解正确 - 这意味着,AImpl的前向声明是不够的?
添加构造函数和析构函数时,一切正常.是什么原因?是因为默认是内联的,因此看不到AImpl的大小?在添加构造函数和析构函数时,编译器假定这些定义知道AImpl的大小?
我正在上课STFT.在标题中编译就好了:
class STFT; // pimpl off to prevent point name clash
class Whatever
{
private:
STFT* stft;
Run Code Online (Sandbox Code Playgroud)
这在实施中:
#include "STFT.h"
Whatever::Whatever() : stft(new STFT()) {
// blah blah
}
Whatever::~Whatever() {
delete stft; // pure evil
}
Run Code Online (Sandbox Code Playgroud)
但是,切换到std::unique_ptr<STFT> stft;标题中的原始指针,并删除析构函数,我得到
错误:'sizeof'无效应用于不完整类型'STFT'static_assert(sizeof(_Tp)> 0,"default_delete无法删除不完整类型");
但是,如果我只提供一个空的析构函数Whatever::~Whatever(){},那么它编译得很好.这让我完全难过.请填写我这个毫无意义的析构函数为我做的事情.
大型C++项目中的一个问题可能是构建时间.您的依赖树中有一些类需要处理,但通常您会避免这样做,因为每个构建都需要很长时间.您不一定要更改其公共接口,但也许您想要更改其私有成员(添加缓存变量,提取私有方法,...).您面临的问题是,在C++中,甚至私有成员都在公共头文件中声明,因此您的构建系统需要重新编译所有内容.
你在这种情况下做了什么?
我已经勾画了两个我所知道的解决方案,但它们都有它们的缺点,也许还有一个我尚未想到的更好的解决方案.
pimpl-idiom ×10
c++ ×9
c++11 ×3
delegation ×2
boost ×1
dependencies ×1
java ×1
macros ×1
private ×1
qt ×1
templates ×1
unique-ptr ×1