隐藏c++库的私有成员

Hiding private members of c++ library

本文关键字:成员 c++ 隐藏      更新时间:2023-10-16

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

这是一个好的做法吗?

有没有其他被公众接受的方式来做这样的事情?

提前谢谢。

除了工厂模式(在我看来,这可能会变得难以处理)之外,您还可以将您的私人成员隐藏在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,只需在.cpp文件中的匿名namespace中编写整个实现,在那里您可以放置任意多的状态和私有函数,甚至不需要在接口中暗示它。

您可以创建一个公开可见的接口。创建一个带有您想要公开的函数的抽象类,然后让您的实现对其进行扩展

例如,一个接口:

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();
}

根据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;
};

您的实现(共享库):myapi.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只是一个抽象类,它的工作方式就像工厂一样,你也可以在不同的类中实现虚拟方法,并指定你想调用哪个实例。

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

请快速查看此链接:

您可能需要了解信封/信件习惯用法、桥接设计模式或代理模式。基本上,您将创建一个外部(公共)类,它只会将您的公共方法调用转发到内部(私有)类。您的InnerClass.h标头只需要对OuterClass.cpp和InnerClass.cp源文件可见/已知即可。

这些模式中的每一个都提供了一种将实现与接口分离的机制,这样调用者就不会耦合到实现。有时,这是为了减少编译器对大型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();
}

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

只需将您的私人物品放入.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 Stackeexchange上找到此解决方案!