簡體   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