【问题标题】:Use of a friend class to hide private static callback methods使用友元类隐藏私有静态回调方法
【发布时间】:2016-03-26 23:17:42
【问题描述】:

我需要用一些 API 注册一个函数指针表。假设 API 如下所示:

void (*FuncPtr)(void*);
void RegisterCallbacks(FuncPtr const (&callbacks)[10], void* context);

由于我正在编写 OOP 代码,因此我在我的类中创建了静态“thunk”方法,这些方法依次调用我的类的对象上的方法。我使用context 指针将指针传递给我的对象。我想在静态初始化时静态分配整个回调指针表。所以我最终得到了这个:

class MyClass
{
public:

    static void Thunk (void* context)
    {
        reinterpret_cast<MyClass*>(context)->Method();
    }

    void Method ();

    //
    // More callback thunks and methods...
    //

    MyClass ()
    {
        RegisterCallbacks(s_callbackTable, this);
    }

private:

    static const FuncPtr s_callbackTable[];
}

//
// In the .cpp file:
//
const FuncPtr MyClass::s_callbackTable[] =
{
    &MyClass::Thunk,
    ...
};

这种方法的问题是我需要让MyClass::Thunk公开可见,以便静态初始化可以访问它。

编辑:我错了。显然你不需要公开它。

我宁愿不要。我可以想到两种方法来解决这个问题。

哪种方法更好?

  1. 创建私有静态方法,并将回调表设为方法内部的静态变量。这样我也可以使用指向私有方法的指针来初始化它。从构造函数内部调用私有静态方法来获取表。

  2. 创建一个包含公共静态 thunk 方法的单独类,并使该类成为 MyClass 的朋友。在 MyClass 的标题中前向声明该类,只是为了允许朋友声明。我喜欢这个,因为它还完全对任何外部消费者隐藏了 thunk 方法。不过,我不确定这是否有切实的好处。

    像这样:

    // .h file:
    
    class MyClassInternals
    
    class MyClass
    {
    public:
    
        void Method ();
    
    private:
    
        friend class MyClassInternals;
    
        static const FuncPtr s_callbackTable[];
    }
    
    // .cpp file:
    
    class MyClassInternals
    {
    public:
    
        static void Thunk(void* context)
        {
            reinterpret_cast<MyClass*>(context)->Method();
        }
    }
    
    const FuncPtr MyClass::s_callbackTable[] =
    {
        &MyClassInternals::Thunk,
        ...
    };
    

【问题讨论】:

    标签: c++ callback private friend


    【解决方案1】:

    您可以简单地将所有蹦床机器保留在构造函数实现 (.cpp) 中,无需在接口级别 (.h) 公开有关蹦床的任何内容 ):

    MyClass::MyClass() {
        struct trampolines {
            static void method1(void *ctx) { ((MyClass *)ctx)->method1(); }
            static void method2(void *ctx) { ((MyClass *)ctx)->method2(); }
        };
        static void (*callbacks[])(void*) = { trampolines::method1,
                                              trampolines::method2 };
        register_callbacks(callbacks, this);
    }
    

    您甚至不需要在 (.h) 中包含使用回调/上下文接口的 C 库的标头。这更简洁,因为您的模块的客户端将不依赖于那些 C 细节。

    【讨论】:

    • 这些方法甚至不需要公开。 trampolines 结构,由于定义在MyClass 的范围内,继承了与内部类相同的语义,即它可以访问包含类的私有方法。见ideone.com/Ai7WW7。另请参阅stackoverflow.com/a/5714720/179895,它提出了与您相同的建议。
    • @TripShock:感谢您的澄清。我不是private/protected 的大力支持者,也不知道构造函数/方法范围内的本地类的这种微妙之处。固定。
    【解决方案2】:

    在初始化同一类的成员时,您无需使 Thunk 可见即可使用它。

    修复一些小的语法错误后,当 Thunkprivate 时,您的原始示例编译得很好。

    https://ideone.com/VmSasp

    【讨论】:

    • 这很奇怪......我到底在做什么:P
    • 能够从MyClass的接口完全隐藏私有thunk方法有什么价值吗?
    猜你喜欢
    • 2016-01-05
    • 2015-06-26
    • 2017-01-13
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    • 2019-07-26
    • 2013-07-12
    • 2011-05-04
    相关资源
    最近更新 更多