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

时间:2018-12-11 07:13:04

标签: c++ class

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

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或元编程(我对此有一点了解,我可能是错的),但是我只是个初学者,没有经验,或者对此进行了改进方法。

2 个答案:

答案 0 :(得分:1)

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

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的构造函数时;它将f2的{​​{1}}设置为bar_,然后再次设置8。由于static pointerf1的{​​{1}}也从bar更改为3。这是您需要注意的事情!


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

8

-输出-

static storage

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


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

  

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

答案 1 :(得分:0)

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

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

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