繁体   English   中英

在静态类函数成员中获取类对象指针

[英]Getting class object pointer in static class function member

有任何“智能”方式可从静态类函数成员获取类对象指针,例如:

class my_class
{
public:

    my_class(int a) : v_a(a) {};

    static void static_f()
    {
        my_class* p = GET OBJECT POINTER
        std::cout << p->v_a << std::endl;
    }

private:
    int v_a;
};

我知道这可能是不可能的,但是我已经挣扎了2天,到目前为止,我已经能够使用模板并在全局范围内声明对象来实现它:

    template<my_class* tp>
    static void static_f()
    {
        my_class* p = tp;
        std::cout << p->v_a << std::endl;
    }

  my_class v1(100); // global variable
  my_class v2(200); // global variable

  v1.static_f<&v1>(); // main function
  v2.static_f<&v2>(); // main function

并分别打印出100和200。 如果我将其声明为局部变量,这将行不通。 “智能”是指我不知道的c ++ 11或更高版本的一些新功能。

目的:

我试图封装一个C库的几个函数,其中的一个需要指向用作回调函数的指针,这个回调函数指针不会由我自己调用,而是由库调用,并且不接受传递的其他参数作为上下文,所以我不能直接调用它(我在代码中做了,因为我想使示例尽可能小)或仅传递对象指针。

可能的解决方案:

到目前为止,我已经能够通过使用模板和每个对象一个全局变量来实现这一目标:

template<void** pp>
class my_class
{
public:

    my_class(int v1, int v2)
    {
        x_var1 = v1;
        x_var2 = v2;
        *pp = this;
    }

    void call_callback_for_test()
    {
        callback();
    }

private:

    static void callback()
    {
        my_class* p = reinterpret_cast<my_class*>(*pp);
        std::cout << p->x_var1 << " - " << p->x_var2 << std::endl;
    }


    int x_var1;
    int x_var2;
};


void* pv1;
void* pv2;
my_class<&pv1> v1(100, 200);


int main()
{
    my_class<&pv2> v2(300, 400);

    v1.call_callback_for_test();
    v2.call_callback_for_test();
    return 0;
}

是的,我觉得使用更新的c ++功能可能会有更好的解决方案,例如通过使用constexpr或元编程(我对此有一点了解,我可能是错的),但是我只是一个初学者,没有经验,或者正在改进这种方法。

要做您要问的事并不难; 它可能看起来像这样:

class Foo {
private:
    int bar_;
    static Foo* pFoo;

public:
    explicit Foo( int a ) : bar_( a ) {
        pFoo = this;
    }

    static void printBar() {
        std::cout << pFoo->bar_ << '\n';
    }
};

Foo* Foo::pFoo = nullptr;

int main() {
    Foo f( 3 );
    f1.printBar();

    return 0;
}

-输出-

3

这可以工作,但是您必须对静态指针的工作方式保持谨慎。 以这个程序为例:

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    return 0;
}

-输出-

3
8

好的,我们有2个Foo实例; f1f2分别输出38 那么为什么我们需要谨慎? 让我们再运行一​​次以上,但是添加一行代码。

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    // call f1's printBar() again
    f1.printBar(); // What do you think the output will be? 
    // I will tell you that it is not 3!

    return 0;
}

-输出-

3
8
8

这也许是您正在寻找的东西,但您必须了解类的成员静态指针以及它们在类的多个实例中的行为。

在上面的示例中, f1的成员bar_在其构造函数中设置为3 ,然后将静态指针设置为此。 然后,当我们创建f2并调用它的值为8的构造函数时; 它将f2bar_设置为8 ,然后再次设置static pointer 由于static storage f1bar3更改为8 这是您需要注意的事情!


即使在值初始化之后由类的范围解析运算符调用静态方法,它仍然会产生相同的结果。

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    f1.printBar(); 

    Foo::printBar();

    return 0;
}

-输出-

3
8
8
8

这只是类的成员静态指针以及静态方法如何工作的一般思路。


用户Alan Birtles在编写此答案时提到了我没有想到的警告或陷阱。 他说:

请注意,如果最近创建的对象已被破坏,则回调将具有未定义的行为。

一种选择是使用标记结构作为模板参数,以为每个回调生成不同的类型:

#include <iostream>
#include <string>
#include <memory>

template < typename T >
class Foo
{
public:
    static void test()
    {
        std::cout << instance() << "\n";        
    }

    static std::shared_ptr< Foo< T > > instance()
    {
        static std::shared_ptr< Foo< T > > foo( new Foo< T >() );
        return foo;
    }

private:
    Foo() {}
};

struct instance1 {};
struct instance2 {};

typedef void(*callback_t)();

int main()
{
    callback_t callback1 = Foo< instance1 >::test;
    callback_t callback2 = Foo< instance2 >::test;
    callback1();
    callback2();
}

我使用共享指针来确保在执行回调时创建的对象仍然存在(请注意,这些对象在程序的生命周期中将一直存在)。 如果您想自己控制生命周期,则可以使用弱指针:

#include <iostream>
#include <string>
#include <memory>

template < typename T >
class Foo
{
public:
    static void test()
    {
        std::cout << instance() << "\n";        
    }

    static std::shared_ptr< Foo< T > > instance()
    {
        static std::weak_ptr< Foo< T > > foo;
        std::shared_ptr< Foo< T > > result = foo.lock();
        if ( !result )
        {
            // will need a mutex here in multi-threaded applications
            result.reset( new Foo< T >() );
            foo = result;
        }
        return result;
    }

private:
    Foo() {}
};

struct instance1 {};
struct instance2 {};

typedef void(*callback_t)();

int main()
{
    {
        auto foo = Foo< instance1 >::instance();
        callback_t callback1 = decltype(foo)::element_type::test;
        callback1();
    }
    {
        auto foo = Foo< instance2 >::instance();
        callback_t callback2 = decltype(foo)::element_type::test;
        callback2();
    }
    {
        auto foo = Foo< instance1 >::instance();
        // using a different object to callback1
        callback_t callback3 = decltype(foo)::element_type::test;
        callback3();
    }
}

如果在没有活动对象的情况下调用该回调,则将创建并销毁一个新对象,而不是使程序崩溃。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM