使用 PIMPL 习惯用法,实现是否应该始终是 class 的私有成员?
Using the PIMPL idiom, should the implementation always be a private member of the class?
我见过 PIMPL idiom 以两种不同的方式实现。一种方法是始终使实现成为 class 的私有成员。这确保无论如何都无法从 class 外部访问该实现。
例如:
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");
}
我看到的另一种方法是使实现成为一个完全独立的 class,它有自己的 .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
class 即可访问实现。不过,我个人认为没有任何理由需要这样做。
这两种方法似乎都有效并满足了最终目标,但是选择一种方法比另一种方法有什么真正的优势吗?
这显然是一个见仁见智的问题。
我认为使用第二种方法违背了 Pimpl 习语的精神。
- 现在外部可以看到实施细节。
- 如果对
Example
的界面进行任何更改,很可能会影响四个文件而不是两个。
- 如果对
ExampleImpl
的实施方式进行任何更改,很可能会影响三个文件,而不是一个。
鉴于以上几点,我建议使用嵌套 class 方法。
我实际上发现了第二种方法明显胜出的情况。如果您需要从使用 PIMPL 惯用语的 class 派生来创建也使用 PIMPL 惯用语的派生 class,则可以 运行 解决继承问题。
如果派生 class 的实现 class 没有继承自基础 class 的实现 class,您将无法在其中使用任何继承的函数派生的执行class这可以是一个总的表演塞子!
除非有人能找到解决这个问题的简单方法,否则我将被迫使用第二种方法。
编辑:
原来不用走第二种方法也能解决这个问题!您可以简单地使派生 class 的实现 class 继承自基础 class 的非实现 class!这为您提供了 none 的所有功能!
我见过 PIMPL idiom 以两种不同的方式实现。一种方法是始终使实现成为 class 的私有成员。这确保无论如何都无法从 class 外部访问该实现。
例如:
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");
}
我看到的另一种方法是使实现成为一个完全独立的 class,它有自己的 .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
class 即可访问实现。不过,我个人认为没有任何理由需要这样做。
这两种方法似乎都有效并满足了最终目标,但是选择一种方法比另一种方法有什么真正的优势吗?
这显然是一个见仁见智的问题。
我认为使用第二种方法违背了 Pimpl 习语的精神。
- 现在外部可以看到实施细节。
- 如果对
Example
的界面进行任何更改,很可能会影响四个文件而不是两个。 - 如果对
ExampleImpl
的实施方式进行任何更改,很可能会影响三个文件,而不是一个。
鉴于以上几点,我建议使用嵌套 class 方法。
我实际上发现了第二种方法明显胜出的情况。如果您需要从使用 PIMPL 惯用语的 class 派生来创建也使用 PIMPL 惯用语的派生 class,则可以 运行 解决继承问题。
如果派生 class 的实现 class 没有继承自基础 class 的实现 class,您将无法在其中使用任何继承的函数派生的执行class这可以是一个总的表演塞子!
除非有人能找到解决这个问题的简单方法,否则我将被迫使用第二种方法。
编辑:
原来不用走第二种方法也能解决这个问题!您可以简单地使派生 class 的实现 class 继承自基础 class 的非实现 class!这为您提供了 none 的所有功能!