[英]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
實例; f1
和f2
分別輸出3
和8
; 那么為什么我們需要謹慎? 讓我們再運行一次以上,但是添加一行代碼。
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
的bar_
設置為8
,然后再次設置static pointer
。 由於static storage
f1
的bar
從3
更改為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.