指向 C++ 中类的指针的 PIMPL 习语
Posted
技术标签:
【中文标题】指向 C++ 中类的指针的 PIMPL 习语【英文标题】:PIMPL idiom for a pointer to a class in C++ 【发布时间】:2018-11-13 19:26:58 【问题描述】:我有两个程序(ProgramA
和 ProgramB
)的工作界面,我想尽可能改进这两个程序的解耦。我要介绍的情况是从ProgramA
调用来自ProgramB
(Compute_Prop
) 的类,该类只能使用一些我现在没有提前进行的参数进行初始化。因此,我在标题中使用了一个指针。目前,我有这样的事情:
interface.h
#include "programB.h" // loads Compute_Prop
class Compute
public:
Compute();
Compute(targ1 arg1, targ2 arg2);
~Compute();
// some methods ...
private:
Compute_Prop* compute;
;
interface.cpp
#include "programB.h"
#include "interface.h"
#include "programA.h"
Compute::Compute() = default;
Compute::~Compute()
delete compute;
Compute::Compute(arg1, arg2)
// do something ... to get data
compute = new Compute_Prop( &data, arg2 );
然后,我尝试用以下方式模仿 PIMPL 成语
interface.h
#include "programB.h" // loads Compute_Prop
class Compute
public:
Compute();
Compute(targ1 arg1, targ2 arg2);
~Compute();
// some methods ...
private:
class PIMPL;
PIMPL* compute;
;
interface.cpp
#include "programB.h"
#include "interface.h"
#include "programA.h"
Compute::PIMPL = Compute_Prop;
Compute::Compute() = default;
Compute::~Compute()
delete compute;
Compute::Compute(arg1, arg2)
// do something ... to get data
compute = new Compute_Prop( &data, arg2 );
但是编译器说:
error: expected unqualified-id
Compute::PIMPL = Compute_Prop;
^
我猜这与 Compute_Prop 没有
一个空的构造函数。我想不出有用的东西。我该怎么办?可能是指向指针的指针之类的东西?作为限制,我不能修改programB
。
注意:正如上面可能已经清楚的那样,我对低级 C++/C 的了解很少。
编辑:我介绍了@n.m 建议的更正。和@Matthieu Brucher
【问题讨论】:
您的成员声明语法自始至终都是错误的。您不应该在类中重复类名。所以没有Compute::Compute()
,只有Compute()
。我不知道为什么有些编译器会接受你使用的语法,这是完全非法的。
您不能在 C++ 中分配类型。一旦你声明了class PIMPL;
,它期望会有一个名为PIMPL
的类。声明为PIMPL* ptr;
的指针将始终具有该承诺类的PIMPL
类型。 Compute::PIMPL = Compute_Prop;
是不可能的,因为 PIMPL
和 Compute_Prop
都是不可变类型。它不会将所有PIMPL
s 转换为Compute_Prop
s。
如果(计算!= nullptr)删除计算;计算 = nullptr; 不!!删除它,或者更好的是,使用 std::unique_ptr!!
Compute::PIMPL
是一种类型,您不能使用这种 type = value
语法。你需要一个名字。也许像Compute::compute
这样的东西。 Compute::PIMPL Compute::compute = ...
.
是的,删除已经做了检查,这是一个无用的冗余。如果您在使用 unique_ptr 时遇到问题,则意味着您还有其他问题需要解决,因为这就是应该处理 pimlp 的方式。
【参考方案1】:
你的实现应该使用一个接口(或者实际上是一个只有抽象方法的类)作为基类。 您不能在 C++ 中分配类型。你只能像这样创建 typedef 和别名:
using PIMPLType = Compute_Prop;
但是,这不适用于您的情况。 这就是它的实现方式(也有多种实现的可能性):
class IImplementation
public:
virtual void saySomething() = 0;
;
class ImplementationA : public IImplementation
public:
virtual void saySomething() override
std::cout << "A";
;
class ImplementationB : public IImplementation
public:
virtual void saySomething() override
std::cout << "B";
;
class Foo
IImplementation *pimpl;
public:
Foo()
: pimpl(new ImplementationA)
~Foo() delete pimpl;
void saySomething()
pimpl->saySomething();
;
【讨论】:
我将研究如何实现它。此外,需要关注的一点是速度,Compute
的方法被调用了数百万次,可能需要几毫秒或几分钟、几小时。【参考方案2】:
我可能遇到了一个简单的解决方案。我把它贴在这里,这样你就可以判断它是否足够,或者即使它可以改进——当然。我确信不需要运行时多态,甚至不需要多态。无论如何,成员变量compute
将是一个指向Compute_Prop
类型的指针。那么,鉴于性能在这里很关键:为什么要运行虚拟成员函数的额外开销?
这里的重点是实现一个隐藏 Compute_Prop
的包含而不损失性能的实现。如何?这个特定的解决方案使用模板类,然后显式实例化。关键是可以在实现中进行实例化。从a Fluent C++ blog post 得到它。此外,this post 提供了如何实现的提示。原型是:
interface.h
template <typename T>
class Compute
public:
Compute();
Compute(targ1 arg1, targ2 arg2);
~Compute();
// some methods ...
private:
T* compute; // No need to state that is going to be T:=Compute_Prop
;
interface_impl.h
#include "interface.h"
#include "programA.h"
template <typename T>
Compute::Compute() = default;
template <typename T>
Compute::~Compute()
delete compute;
template <typename T>
Compute::Compute(arg1, arg2)
// do something ... to get data
compute = new T( &data, arg2 );
interface.cpp
#include "interface.h"
#include "interface_impl.h"
#include "programA.h"
#include "programB.h" // loads Compute_Prop
int main(int argc, char** argv)
template class Compute<Compute_Prop>;
另一个相关的question 可能对那些有同样困境的人有用。
【讨论】:
以上是关于指向 C++ 中类的指针的 PIMPL 习语的主要内容,如果未能解决你的问题,请参考以下文章