【问题标题】:Hiding private members of c++ library隐藏 C++ 库的私有成员
【发布时间】:2013-07-07 10:20:34
【问题描述】:

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

这是一个好习惯吗?

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

提前致谢。

【问题讨论】:

  • 看起来你可以使用 Pimpl 成语。但是无论你最终使用什么方法,你都需要知道隐藏实现会带来性能问题(编译器不能内联太多)。

标签: c++ shared-libraries static-libraries


【解决方案1】:

除了工厂模式(在我看来,它可能会变得笨拙)之外,您还可以将您的私有成员隐藏在 PIMPL(指向 IPLementation 的指针)后面:

// 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,只需将整个实现写在您的 .cpp 文件中的匿名 namespace 中,您可以在其中放置尽可能多的内容状态和私有函数随心所欲,甚至无需在界面中提示。

【讨论】:

  • 致提问者:至少在我看来,这是一个比我更好的答案。
  • @EricFinn 是的,我其实也是这么想的。谢谢你的回答,你们帮了我很多:)
【解决方案2】:

您可以创建一个公开可见的界面。使用您要公开的函数创建一个抽象类,然后让您的实现对其进行扩展。

例如一个接口:

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

【讨论】:

  • 好的,假设我是该界面的用户。所以,我创建了一个接口类的实例,然后调用函数但是它怎么知道我正在调用实现类的方法(因为我们只创建了接口的实例)
  • @khajvah 你不需要知道。但是该接口应该为您提供一种方法来实例化不同的实现,并为您返回一个指向抽象基类型的智能指针。
  • 整洁!这完全隐藏了实现,我认为将接口类设置为抽象类会更好
  • @CharlesChow 很好,我忘了把虚拟成员函数设为纯虚拟。
【解决方案3】:

基于Eric Finn's answer,你可以只声明一个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 只是一个抽象类,像工厂一样工作,你也可以在不同的类中实现虚方法,并指定你想调用哪个实例。

【讨论】:

    【解决方案4】:

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

    Please take a quick look at this link:

    【讨论】:

    • 感谢您的回答。问题是我是 linux 用户,我认为没有办法在 linux 中创建 dll。有哪些替代方案?
    • 其实我对linux一窍不通。但我发现这个答案与你的答案很接近......希望它有所帮助! stackoverflow.com/questions/206272/…
    • 谢谢,我会看看那些成语:)
    • @khajvah Dlls 存在于 linux 中,名为“共享对象”
    • 我不明白为什么共享库可以解决他的问题。能详细点吗?
    【解决方案5】:

    您可能想了解一下信封/字母习语、桥接设计模式或代理模式。基本上,您将创建一个外部(公共)类,它将您的公共方法调用转发到内部(私有)类。您的 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();
    }
    

    【讨论】:

    • 你能扩展你的答案吗?如果您添加对这些模式的描述、它们的使用方式以及建议它们的理由,您的答案会更有帮助。
    • 这是 pimpl 成语的一个糟糕变体
    【解决方案6】:

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

    只需将您的私人内容放入 .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 上找到这个解决方案!

    【讨论】:

      猜你喜欢
      • 2010-09-17
      • 2021-01-17
      • 1970-01-01
      • 2022-11-13
      • 1970-01-01
      • 1970-01-01
      • 2012-06-07
      • 1970-01-01
      • 2021-01-16
      相关资源
      最近更新 更多