使用PIMPL习语,实现是否应始终是该类的私有成员?

时间:2017-07-31 16:46:05

标签: c++ oop optimization dependency-management pimpl-idiom

我看到PIMPL idiom以两种不同的方式实施。一种方法是始终使实现成为类的私有成员。这确保了无论如何都无法从课外访问实现。

例如:

example.h文件

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

class Example {
public:
    void doSomething();
private:
    struct Impl;
    std::shared_ptr<Impl> pimpl_;
};

#endif /* EXAMPLE_H */

Example.cpp

#include "Example.h"
#include <iostream>

struct Example::Impl {
    void doSomething() {
        std::cout << "Hello World!" << std::endl;
    }
};

void Example::doSomething() {
    pimpl_->doSomething();
}

的main.cpp

#include "Example.h"

int main() {
    Example ex;
    ex.doSomething();
    system("pause");
}

我看到的另一种方法是使用自己的.h文件和自己的.cpp文件使实现成为一个完全独立的类。

例如:

example.h文件

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

struct ExampleImpl;

class Example {
public:
    void doSomething();
private:
    std::shared_ptr<ExampleImpl> pimpl_;
};

#endif /* EXAMPLE_H */

ExampleImpl.h

#ifndef EXAMPLE_IMPL_H
#define EXAMPLE_IMPL_H

struct ExampleImpl {
public:
    void doSomething();
};

#endif /* EXAMPLE_IMPL_H */

Example.cpp

#include "Example.h"
#include "ExampleImpl.h"
#include <iostream>

void Example::doSomething() {
    pimpl_->doSomething();
}

ExampleImpl.cpp

#include "ExampleImpl.h"
#include <iostream>

void ExampleImpl::doSomething() {
    std::cout << "Hello World!" << std::endl;
}

的main.cpp

#include "Example.h"

int main() {
    Example ex;
    ex.doSomething();
    system("pause");
}

我能看到的唯一值得注意的差异是使用第二种方法,您可以访问实现而无需通过Example类。我个人没有看到任何人为什么需要这样做的原因。

这两种方法似乎都能起作用并满足最终目标,但选择一种方法比另一种方法有什么真正的优势吗?

2 个答案:

答案 0 :(得分:5)

这显然是一个意见问题。

我认为使用第二种方法违背了Pimpl习语的精神。

  1. 现在可以看到外部可见的实施细节。
  2. 如果对Example的界面进行了任何更改,则很可能会影响四个文件,而不是两个。
  3. 如果对ExampleImpl的实施方式进行了任何更改,则很可能会影响三个文件,而不是一个。
  4. 鉴于以上几点,我建议使用嵌套类方法。

答案 1 :(得分:0)

我实际上发现了第二种方法明显胜出的情况。如果需要从使用PIMPL习惯用法的类派生来创建也使用PIMPL习语的派生类,则可能会遇到继承问题。

如果派生类的实现类没有继承自基类的实现类,那么在派生类的实现中你将无法使用任何继承的函数,这可能是一个完全显示的句子!

除非有人能找到一个简单的方法,否则我将被迫使用第二种方法。

编辑:

事实证明,如果不采用第二种方法,就可以解决这个问题!您可以简单地使派生类的实现类继承自基类的NON实现类!这为您提供了所有功能,没有任何麻烦!