11

我写了一个库(不管它做什么),它显然有它的头文件。现在,我想隐藏那个头文件的私有元素,所以如果我将我的库提供给某人,他/她应该只看到公共成员(最好没有类定义,除了函数定义之外什么都没有)。一种方法是创建 C 风格的标头,其中将包含某种“init”方法,该方法将用于创建库的实际类的实例,并且用户必须将该对象的指针传递给每个函数以做这项工作。

这是一个好习惯吗?

还有其他公开接受的方式来做类似的事情吗?

提前致谢。

4

6 回答 6

14

除了工厂模式(在我看来,它可能变得笨拙)之外,您还可以将您的私有成员隐藏在 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(); }

这具有隐藏实现的优点,代价是单指针间接,与典型的基于继承的工厂模式没有太大区别。

这也可以是 ABI 稳定的。对实现的更改不会影响链接,因为程序的其余部分永远不会看到任何更改。例如,在实现共享对象时,这是一个很好的模式。

这也是一个常见的 C++ 习语,因此其他 C++ 程序员会毫无疑问地认出它。

对于遵循单例模式的类,您可以完全避免暴露 PIMPL,只需namespace.cpp文件中以匿名方式编写整个实现,您可以在其中放置任意数量的状态和私有函数,而无需甚至在您的界面中暗示它。

于 2013-07-09T17:52:53.483 回答
6

您可以创建一个公开可见的界面。使用您要公开的功能创建一个抽象类,然后让您的实现扩展它。

例如一个接口:

class Interface {
public:
    virtual void publicMethod() = 0;
...
};

和实施:

class Implementation : Interface {
public:
    virtual void publicMethod();
private:
    int hiddenMethod();
};

然后你只导出接口的符号。现在,为了让库的用户获得实际上是实现的接口实例,您需要提供一个工厂:

class Factory {
public:
    //can create and return an Implementation pointer, but caller will get an Interface pointer
    std::shared_ptr<Interface> getImplementationInstance();
}
于 2013-07-09T17:34:05.327 回答
4

根据Eric Finn 的回答,您可以只声明一个interface类来保存所有被认为是您的 API 的公共方法,并在继承interface类的实现类中隐藏所有实现和私有成员/方法,这是示例:

你的头文件:my_api.h

// your API in header file
// my_api.h
class interface {
public:
    static interface* CreateInstance();
    virtual void draw() = 0;
    virtual void set(int) = 0;
};

你的实现(共享库):my_api.cpp(当你把它变成一个共享库时用户不会看到这个)所以你可以在这里隐藏你的所有实现和私有方法/成员

#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;
}

用户如何使用您的 API:

#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;
}

输出:

Implementation class draws
Private int is: 2
Implementation class draws
Private int is: 1    

在这种模式下,您的 api 只是一个像工厂一样工作的抽象类,您还可以在不同的类中实现虚拟方法并指定要调用的实例。

于 2014-09-25T01:25:39.787 回答
1

我认为您需要创建动态链接库(dll)。

请快速浏览此链接:

于 2013-07-09T17:31:52.330 回答
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();
}
于 2013-07-09T17:37:45.973 回答
-1

实际上有一种方法可以做到这一点,而不必使用 classes。我有同样的问题,这是一个非常简单的解决方案:

只需将您的私人物品放入 .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

从外部调用“公共”函数时,您现在不再需要该类的任何实例:只需将库放在正确的目录中并包含它,但您可能已经处理好了)并通过它们调用函数文件中的名称Lib.h。在此示例中,它看起来像这样:

#include "Lib.h"

int main(int argc, const char * argv[]) {
    function();
    return 0;
}

感谢 Edgar Bonet 帮助我在 Arduino Stackexchange 上找到了这个解决方案!

于 2019-11-28T17:12:23.723 回答