彼此使用多个Pimpl类

时间:2018-06-29 12:40:20

标签: c++ pimpl-idiom

我有三个通过pimpl向用户公开的类。 Model是一个可以从文件读取或写入文件的数据容器。 Manipulator是一个对象,可以加载Model,进行更改并将其作为新的Model返回。 Consumer加载Model,并允许用户使用它进行操作(读取其属性,打印等)。

class Model {
    Model(std::string &filename);
    void toFile(std::string &filename);
private:
    class ModelImpl;
    std::unique_ptr<ModelImpl> mImpl;
};

class Manipulator {
    Manipulator(Model &model);
    Model alterModel(...);
private:
    class ManipulatorImpl;
    std::unique_ptr<ManipulatorImpl> mImpl;
};

class Consumer {
    Consumer(Model &model);
    void loadModel(Model &model);
    void doSomething();
private:
    class ConsumerImpl;
    std::unique_ptr<ConsumerImpl> mImpl;
};

使用pimpl的原因主要是为了隐藏Model使用的内部数据类型。用户可见的唯一类型应该是ModelManipulatorConsumer以及标准c ++类型。

我在这里遇到的问题是在实现ConsumerImplManipulatorImpl中:在那些类中,我必须访问ModelImpl的基础数据结构,但是pimpl隐藏了它们: / p>

Consumer::ConsumerImpl::loadModel(Model model) {
    auto someModelValue = model.mImpl->someInternalValue;
}

显然这是行不通的。如何解决呢? pimpl是正确的解决方案吗?

编辑:我的同事提出了这个建议:

class Consumer {
    Consumer(Model &model);
    void loadModel(Model &model);
    void doSomething();
private:
    class ConsumerImpl;
    std::unique_ptr<ConsumerImpl> mImpl;
};

class Model {
    Model(std::string &filename);
    void toFile(std::string &filename);
private:
    void *getObjectPtr();
    class ModelImpl;
    std::unique_ptr<ModelImpl> mImpl;
    friend Consumer;
};

void *Model::Model::getObjectPtr() {
    return mImpl->getObjectPtr();
}



class Model::ModelImpl {
public:
//    [...]
    void *getObjectPtr();

private:
    SecretInternalType mData;
};

void *Model::ModelImpl::getObjectPtr() {
    return static_cast<void*>(&mData);
}


// Access the internal data of Model from Consumer:
void Consumer::ConsumerImpl::doSomething() {
    SecretInternalType* data = static_cast<SecretInternalType*>(mModel->getObjectPtr());
}

基本上,模型具有一种方法,该方法返回指向(隐藏)内部数据的空指针。使用者可以获取此指针,将其强制转换回正确的类型并访问数据。要使其只能从Consumer类进行访问,该方法是私有方法,但对于Consumer是friends

我实现了这种方法,并且对我有用。不过,我仍然很好奇您对此有何看法以及是否有任何问题。

1 个答案:

答案 0 :(得分:3)

您面临的问题与Pimpl习惯用法并没有真正的关系-假设您擦除了代码片段中与pimpl相关的部分,该问题与由于需要访问模型的私有表示而导致的问题相同(或ModelImpl)实例。这就是我尝试解决这种情况的方式:

  • 定义一系列对Consumer实例在Model实例上调用有意义的操作。这些应该是Model的公共接口的一部分。对“模型”和“操纵器”之间的关系执行相同的操作。如果这会使您的Model接口过于混乱,请将其分成两个单独的抽象类,让Model实现从这两个继承,并且Consumer / Maninpulator在针对它们的基类接口上进行操作。
  • 重新考虑模型的哪些部分值得隐藏。如果Model拥有一些需要从使用者那里访问的容器,请将其公开(有效STL,项目2),则可以通过适当的方法进行读取访问。
  • 如果Consumer对象仍然需要更多信息,它们自己的角色可能会比普通消费者更多,那么也许其实现的某些部分应该在另一个类或模型中实现?
  • 引入一个(n)(抽象)类,用于在Model和Consumer之间进行数据交换。将其从“消费者”传递给“模型”,让“模型”选择要传递给中间层的信息。但这已经引入了一定程度的复杂性,这可能是不必要的。

无论您对设计应用了什么更改,您仍然可以选择是否将Pimpl惯用语与转发方法一起使用。最后一个建议是:不要声明friend类之一-这可能是很有道理的,但是您的情况并不意味着需要这样的强耦合。