我编写了一个库(无关紧要),它显然有自己的头文件。现在,我想隐藏该头文件的私有元素,所以如果我向某人提供我的库,他/她应该只看到公共成员(最好没有类定义,除了函数定义之外)。一种方法是创建C风格的头文件,它将包含某种“init”方法,该方法将用于创建实际类库的实例,并且用户必须将该对象的指针传递给每个函数做好这份工作。
这是一个好习惯吗?
还有其他公开接受的做法吗?
提前致谢。
答案 0 :(得分:11)
除了工厂模式(在我看来,它可能变得笨拙)之外,您还可以隐藏PIMPL背后的私有成员(指向IMPLementation的指针):
// Interface.hpp
class Implementation;
class Interface {
public:
Interface() : pimpl(new Implementation()) {}
void publicMethod();
private:
std::unique_ptr<Implementation> pimpl;
};
// Interface.cpp
class Implementation {
public:
void PrivateMember();
};
void Interface::publicMethod() { pimpl->PrivateMember(); }
这具有以单指针间接为代价隐藏实现的优点,与典型的基于继承的Factory模式没有太大区别。
这也可以是ABI稳定的。对实现的更改不会影响链接,因为程序的其余部分不会显示任何更改。例如,这是在实现共享对象时使用的好模式。
这也是一种常见的C ++习惯用法,因此其他C ++程序员会毫无疑问地认出它。
对于将遵循Singleton模式的类,您可以完全避免暴露PIMPL,只需将整个实现写入namespace
文件中的匿名.cpp
即可。可以根据需要添加任意数量的状态和私有函数,甚至不需要在界面中暗示它。
答案 1 :(得分:6)
您可以创建一个公开可见的界面。使用您想要公开的函数创建一个抽象类,然后让您的实现扩展它。
例如,界面:
class Interface {
public:
virtual void publicMethod() = 0;
...
};
实施:
class Implementation : Interface {
public:
virtual void publicMethod();
private:
int hiddenMethod();
};
然后您只导出Interface的符号。现在,为了让库的用户获得实际上是实现的接口实例,你需要提供一个工厂:
class Factory {
public:
//can create and return an Implementation pointer, but caller will get an Interface pointer
std::shared_ptr<Interface> getImplementationInstance();
}
答案 2 :(得分:2)
基于Eric Finn's answer,您可以声明一个interface
类来保存所有被认为是您的API的公共方法,并隐藏继承{的实现类中的所有实现和私有成员/方法{1}}课程,以下是示例:
您的头文件:my_api.h
interface
您的实现(共享库):my_api.cpp(当您将其设为共享库时,用户不会看到此内容) 所以你可以在这里隐藏所有的实现和私有方法/成员
// your API in header file
// my_api.h
class interface {
public:
static interface* CreateInstance();
virtual void draw() = 0;
virtual void set(int) = 0;
};
用户如何使用您的API:
#include "my_api.h"
// implementation -> in .cc file
class implementation : public interface {
int private_int_;
void ReportValue_();
public:
implementation();
void draw();
void set(int new_int);
};
implementation::implementation() {
// your actual constructor goes here
}
void implementation::draw() {
cout << "Implementation class draws something" << endl;
ReportValue_();
}
void implementation::ReportValue_() {
cout << "Private value is: " << private_int_ << endl;
}
void implementation::set(int new_int) {
private_int_ = new_int;
}
interface* interface::CreateInstance() {
return new implementation;
}
输出:
#include <iostream>
#include "my_api.h"
int main(int argc, const char * argv[])
{
using namespace std;
interface* a; interface* b;
a = interface::CreateInstance();
a->set(1);
b = interface::CreateInstance();
b->set(2);
b->draw();
a->draw();
return 0;
}
在这种模式中,你的api只是一个像工厂一样工作的抽象类,你也可以在不同的类中实现虚方法,并指定你想要调用的实例。
答案 3 :(得分:1)
我认为你需要创建动态链接库(dll)。
答案 4 :(得分:0)
您可能需要查看信封/字母惯用语,网桥设计模式或代理模式。基本上,您将创建一个外部(公共)类,它只是将您的公共方法调用转发给内部(私有)类。您的InnerClass.h标头只需要在OuterClass.cpp和InnerClass.cpp源文件中可见/已知。
这些模式中的每一个都提供了一种将实现与接口分离的机制,以便调用者不会耦合到实现。有时希望减少编译器对大型C ++项目的依赖性。想要这样做的另一个常见原因就是当你想隐藏实现细节时,调用者只能看到一个不透明的指针。
======= OuterClass.h =====
class InnerClass; // forward declaration is all that's needed
class OuterClass {
private:
InnerClass *pInner;
public:
InnerClass();
bool doSomething();
};
======= OuterClass.cpp ======
#include "OuterClass.h"
#include "InnerClass.h"
OuterClass::OuterClass() :
pInner(new InnerClass())
{
}
bool OuterClass::doSomething()
{
return pInner->doSomething();
}
答案 5 :(得分:0)
实际上,有一种方法可以不必使用类。我遇到了同样的问题,这是一个非常简单的解决方案:
只需将您的私人物品放入.cpp文件中。您的头文件将如下所示:
// These will be visible to everyone using this library
void function();
int someNumber = 2;
和您的.cpp文件:
void function() {
// whatever this function does
}
// This will be only visible to the library itself
static void secretFunction() {
doSomeSecretStuff;
}
static int PIN = 1234;
// Okay, if you write this Number into your library and expect it to be safe,
// then screw you, but at least no one will be able to access it with code
从外部调用“ public”函数时,您现在不再需要该类的任何实例:只需将库放置在正确的目录中并包含它,但您可能已经做好了处理),然后调用Lib.h
文件中的函数名称。在本例中,它看起来像这样:
#include "Lib.h"
int main(int argc, const char * argv[]) {
function();
return 0;
}
感谢Edgar Bonet帮助我在Arduino Stackexchange上找到了该解决方案!