【问题标题】:Can I get reference of an object with address of member variable in C++?我可以在 C++ 中获取具有成员变量地址的对象的引用吗?
【发布时间】:2020-07-25 22:55:22
【问题描述】:

如果我只有一个对象的成员变量的地址,是否可以获得对该对象的引用?

class example{
    public:
        int var;
};


int main(){
    
    example exampleObject;
    
    int* point=&exampleObject.var;
    
    example* objPointer; // can i get objPointer to point to exampleObject only using point
    return 1;
}

【问题讨论】:

  • 问:如果不是“地址”,您认为引用是什么?我不知道你的语法......或你的意图。但基本思想是合理的......而且非常像 C ;)
  • 不,你不能只使用point,至少不会陷入一些未定义的行为陷阱
  • 我认为不,除非您以特殊方式构造成员 var 以获得指向对象 (this) 的指针,否则您不能这样做。
  • Using offsetof to get owner object from member variableStandard way to find base address of struct from a member。也就是说,这不是您通常会做的事情,如果您必须这样做,那么不同的设计可能会更有意义。
  • @Peter 你需要使用((char*)point)reinterpret_cast<char*>(point)point 而不是&point)。您需要point 指向的地址,而不是point 本身的地址。

标签: c++ pointers


【解决方案1】:

我可以在 C++ 中获取具有变量地址的对象的引用吗?

根据示例代码,此问题中似乎缺少一个关键词。您似乎希望通过该对象的 成员 变量的地址而不是类类型的变量来引用该对象。

可能是,但有限制。

只有当所讨论的类是标准布局类时,这才有可能。在这种情况下,您可以将指向第一个成员的指针重新解释为指向类本身的指针。然后可以通过指针间接获取引用:

static_assert(std::is_standard_layout_v<example>);
example* objPointer = reinterpret_cast<example*>(point);
assert(objPointer == &exampleObject); // guaranteed to pass

使用offsetof 宏可以基于除第一个成员之外的成员获取指向标准布局类的指针,尽管我看到的所有实现该想法的尝试都依赖于技术上未定义的行为,或者至少定义了实现。

这是来自 Linux 内核的实现(不是用 C++ 编写的,而是用 C 编写的,在允许的范围内可能更宽松):

#define container_of(ptr, type, member) ({            \
  const typeof(((type *)0)->member) * __mptr = (ptr); \
  (type *)((char *)__mptr - offsetof(type, member)); })

typeof 不在标准 C++ 中,甚至不在标准 C 中,而是 GNU 语言扩展。这也使用了语句表达式(不要与表达式语句混淆),这是另一个 GNU 扩展。

【讨论】:

  • 我认为可以肯定地说,如果一个实现提供了offsetof(),那么它就是一个被定义的不透明的东西。引擎盖下的实现是否根据标准调用 UB 是无关紧要的,因为实现知道它是如何工作的并且可以做在其他地方可能是 UB 的事情。换句话说,实现提供的东西是黑盒子,如果实现说它已定义,它是用于该实现的。
  • 例如,GCC 和 LLVM 都将宏扩展为 __builtin_offsetof 并转发参数。不管那件事发生了什么,如果论点有意义,我们可以假设它做了正确的事情。
  • @cdhowie 所有实现都需要提供offsetofWhether the implementation under the hood invokes UB according to the standard is irrelevant 正确。无论如何,offsetof 不会给你一个指向类的指针/引用。它给你一个偏移量。并且在 C++ 中没有标准的方法来使用偏移量获取指向对象的指针。据我所知,只有一些方法依赖于实现定义的细节或技术 UB。
  • @eerorika 我想我误解了你的意思 “我见过的所有实现都依赖于技术上未定义的行为。” 我以为你在谈论offsetof() 但现在我认为您在谈论 使用 offsetof() 来执行 OP 请求。在这种情况下,您在回答中所说的话是有道理的。我想我们是一致的,我只是没明白那句话指的是什么。
  • @cdhowie 啊,我明白它是多么模棱两可。我添加了更多的词,希望能对此有所帮助。
【解决方案2】:

警告:不要经常这样做,因为这很容易搞砸

#include <iostream>

class example{
public:
    int var;
};


int main(){

    example exampleObject{15};

    int* point=&exampleObject.var;

    example* objPointer = reinterpret_cast<example*>(point);
    std::cout << objPointer->var;
    return 0;
}

现在objPointer 将指向原始对象,但这只是因为对象是如何管理的,因为var 是没有基类的类的第一个属性,所以它的地址将是相同的的对象,但如果你有这样的东西:

class example{
public:
    int var;
    int var2;
};


int main(){

    example exampleObject{15, 16};

    int* point=&exampleObject.var2;

    example* objPointer = reinterpret_cast<example*>(point);
    std::cout << objPointer->var2;
    return 1;
}

它不起作用,因为var2没有保存在保存对象的同一地址,因为它之前保存了var

【讨论】:

  • 在这种情况下没有UB。
  • 这只是因为varexample 的第一个(也是唯一的)数据成员,所以var 的地址与exampleObject 的地址相同。如果example 有其他数据成员,并且var 不是第一个成员,那么这种方法将不再像所示那样工作。您必须使用 offsetof(example, var) 调整地址
  • @RemyLebeau 是的,我在第二个例子中提到过
  • @eerorika mh,真的吗?我以为reinterpret_cast的使用会自动导致UB
  • @Berto99 reinterpret_cast 永远不会导致 UB 本身。重新解释的指针的使用方式可能是UB。而且,使用重新解释的指针并不总是 UB。如果是这样的话,那么语言就没有任何意义允许这样的演员表了。这是允许重新解释的特殊情况之一。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 1970-01-01
相关资源
最近更新 更多