简体   繁体   English

从成员指针获取类/结构对象

[英]Getting a class/struct object from the member pointer

I have C++ structure as 我有C ++结构

struct myStruct {
    int a;
    int b;
    int c;
}; 

myStruct b;
int *ptr = &b.c;

How can I get myStruct object back from ptr? 如何从ptr中恢复myStruct对象?

(I know I can do this using pointer arithmatic like container_Of() in C. Basically something like (我知道我可以使用像C中的container_Of()这样的指针算术来实现这一点。基本上就像

reinterpret_cast<myStruct*>(reinterpret_cast<char *>(ptr) - offsetof(myStruct, c));

I am asking if there is any recommended/elegant way?) 我问是否有任何推荐/优雅的方式?)

There's certainly no recommended way, as doing this is definitely not recommended at all in C++. 肯定没有推荐的方法,因为在C ++中绝对不推荐这样做。 This is one of those questions where the correct answer has to be "Don't do that!" 这是正确答案必须是“不要那样做!”的问题之一。

The whole reason for using C++ instead of C is that you want to encapsulate the structure of data inside classes with sensible operations defined on them, instead of allowing the whole program to have knowledge of the internal layout of data structures. 使用C ++而不是C的全部原因是,您希望将类数据结构封装在类中,并在其上定义合理的操作,而不是让整个程序了解数据结构的内部布局。

That said, the offsetof technique you describe will work on plain old data objects, because they are no different to C structs. 也就是说,您描述的offsetof技术将适用于普通的旧数据对象,因为它们与C结构没有区别。

Because ptr has no knowledge of its overlaying struct, I don't think there's an elegant way of getting back to myStruct . 因为ptr不知道它的重叠结构,所以我认为没有一种优雅的方式可以回到myStruct I just recommend to not do this! 我只是建议不要这样做!

Your reinterpret_cast solution is the standard way to achieve what you want. 您的reinterpret_cast解决方案是实现您想要的标准方法。 The reason that it must involve at least one casts is that the operation that you want to perform is inherently unsafe. 它必须涉及至少一个演员表的原因是你想要执行的操作本质上是不安全的。 Obviously, not every pointer to an int is a pointer to the third member of a myStruct so there can't be a simple type-safe way to perform the conversion. 显然,并非每个指向int的指针都是指向myStruct第三个成员的指针,因此不能使用简单的类型安全方式来执行转换。

Your text & example use a POD struct; 您的文本和示例使用POD结构; your title talks about classes as well, however. 但是,你的标题也会讨论类。 The answer below won't work for non-POD types. 以下答案不适用于非POD类型。

For POD types, check out the "offsetof" macro .... 对于POD类型,请查看“offsetof”宏 ....

Find the offset, use that & pointer arithmetic to back up say a char * the base. 找到偏移量,使用&指针算法来备份char * the base。

You can maybe use the fact, that when you make a cast from a pointer to a base class, that is not the only base class, the compiler will adjust the pointer so that it will point to the beginning of the super class. 您可以使用这样的事实:当您从指向基类的指针进行转换时,这不是唯一的基类,编译器将调整指针以使其指向超类的开头。 Therefore, if your "c" member would exist at the beginning of some base class, you could do a trick like: 因此,如果你的“c”成员存在于某个基类的开头,你可以这样做:

struct myStructBase1 { int a; };
struct myStructBase2 { int b; };
struct myStructBase3 { int c; };

struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
};

int main() {
    myStruct my;

    int * bptr = &my.b;
    int * cptr = &my.c;
    // The only dirty part in the code...
    myStructBase2 * base2ptr = reinterpret_cast<myStructBase2*> (bptr);
    myStructBase3 * base3ptr = reinterpret_cast<myStructBase3*> (cptr);

    // In each (myStruct*) cast the pointers are adjusted to point to the super class.
    cout << &my << endl <<
            (myStruct*) base2ptr << endl <<
            (myStruct*) base3ptr << endl;
    return 0;
}
// Output:
// 0xbfbc779c
// 0xbfbc779c
// 0xbfbc779c

The requirement for this to work is: if a member is the first member in its class, then its address in a object is equal to address of the object (of that class). 对此工作的要求是: 如果成员是其类中的第一个成员,那么它在对象中的地址等于该对象(该类的)的地址。 I'm not sure if this is true. 我不确定这是不是真的。

EDIT: it should hold when the wrapper-base classes will be PODs. 编辑:当包装基类为POD时,它应该成立。 After following modifications: 经过以下修改:

struct myStructBase1 { int a; virtual void g() {} };
struct myStructBase2 { int b; virtual void f() {} };
struct myStructBase3 { int c;  };
struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
    virtual void h() {}
};

The output is: 输出是:

0xbfa305f4
0xbfa305f8
0xbfa305f4

For the c member, the constraint still holds. 对于c成员,约束仍然成立。 So generally the answer is: yes, there is an alternative way. 所以一般来说答案是:是的,还有另一种方法。 However, defining a base class for each "reversible"-member is probably not wise way.. 但是,为每个“可逆”成员定义基类可能不是明智的方法。

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

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