【问题标题】:What does the pointer 'this+1' refer to in C++?C++ 中的指针“this+1”指的是什么?
【发布时间】:2020-07-11 04:05:09
【问题描述】:

我在Sequitur G2P的代码中徘徊,发现了一行非常奇怪的代码:

public:
    ...
    const Node *childrenEnd() const { return (this+1)->finalized.firstChild_; }

我知道this是指向当前对象的指针,既然是指针,那么操作是完全合法的,但是this+1到底指的是什么?

【问题讨论】:

  • C++ 的问题之一,以及智能指针被添加到标准库的原因,是原始指针的角色太多。作为迭代器和拥有句柄。然而,这段代码 sn-p 似乎喜欢这种模糊性及其滥用。
  • 它只在这里使用过 return binarySearch(childrenBegin(), childrenEnd() - 1, t);-1 有区别吗?
  • @JeffUK 不,它没有,如果 this+1 指向不同的东西但仍然尝试使用 ->finalized 取消引用,那么它会立即崩溃
  • 这段代码刺痛了我的眼睛。我希望这是在一个非常受控的环境中调用的。
  • 我认为新标题不合适,this+1 不是增量,this++ 是。显然,您不能在 C++ 中重新分配 this 的值。我回到这个问题,被标题的非意义所迷惑,认为这是另一个问题。

标签: c++


【解决方案1】:

推测this 是数组的一部分,因此this+1 将引用该数组中的下一个对象。

【讨论】:

  • 那么这个操作是不是非常危险?因为不能保证这个对象来自一个数组或者下一个对象属于同一个类
  • 但这绝对是最后一个对象的UB。对吧?
  • 不,只是做this + 1 不是UB。可以使用 ->finalized 取消引用结果。
  • @AtheS21:C++ 数组是同构的。如果有下一个元素,则它具有相同的类型。只有“没有下一个元素”才会构成风险。
  • 它实际上是 std:vector<> 的一部分,而不是 C 样式数组的一部分,但标准也保证这些内存是连续的。这段代码是私有实现类的一部分,实际上只从一个地方调用。对象的构造保证它是安全的。
【解决方案2】:

this 只是一个指向 this 对象的指针。由于它是一个指针,因此您可以应用指针算法甚至数组索引。

如果 this 对象是数组中的一个元素,this+1 将指向数组中的下一个对象。

如果不是,那么它只会将该内存中的任何内容与 this 对象相同,除非它是相同的类型,否则这将是未定义的行为。

【讨论】:

  • 是的,我看到了问题,但是由于这是 NLP 中一个非常知名的工具的代码,我认为这样做一定有充分的理由。这是我第一次看到这样的代码
  • “如果不是(一些明确的声明)”不,未定义的行为是未定义的。它可以做任何事情。如果编译器可以证明你总是跑到最后它可以发出一个空程序,或者一个格式化你的高清或任何它想要的程序
  • @M.M:缺乏定义的行为也是未定义的行为。 C++ 标准没有列举所有未定义的行为。 (C 确实,IIRC,在附录中)。
  • @M.M 错了。如果你取消引用一个指向数组末尾之外的指针,你就有了 UB。 (如果该指针恰好与另一个相同类型的对象的地址相比较,您可能不会崩溃,但您仍然有 UB - 除非我找不到相应的章节。)
  • @AtheS21 不,UB 比这更深。它控制编译器可用的优化;如果某事是 UB,编译器可以将其视为“这永远不会发生”,并进行相应的优化,包括(如 Caleth 指出的)将整个程序更改为 return 1 或其他。例如,如果您取消引用一个指针,然后对同一指针进行空值检查,编译器将得出结论,该代码块将永远不会执行,因为指针不可能为空,因为您试图取消引用它。但是如果取消引用被优化掉,你不会得到运行时错误:)
【解决方案3】:

由于它是 NLP,因此优化内存管理是有意义的。我假设您也会发现重载的新/删除方法。

this+1 结构假定所有对象都驻留在一个数组中。该方法的名称“childrenEnd”表示它返回一个指向当前节点子节点末尾地址的指针。

因此,您正在查看树结构的实现。所有兄弟姐妹和他们的孩子都是相邻的。

【讨论】:

    【解决方案4】:

    C++类中的“this + 1”表示:

    如果“this”对象是另一个对象的成员,它将指向在“this”对象变量之后声明的父对象的下一个变量的地址:

    例子:

    class B
    {
    public:
        void* data()
        {
            return this + 1;
        }
    };
    
    class A
    {
    public:
        B m_b;
        char m_test;
    };
    
    int main(int argc, char* argv[])
    {
        A a;
        a.m_test = 'H';
    
        void* p = a.m_b.data();
        char c;
    
        memcpy(&c, p, sizeof(char));
        return 0;
    }
    

    c 等于“H”。

    长话短说,它允许访问父类的数据,而无需将父类的指针传递给子类。在本例中 this + 1 指向 A 类的 m_test 成员。

    【讨论】:

      【解决方案5】:

      其实有一种情况,这个东西是可以用的。我不建议使用这种方法,但它确实有效。

      我相信,在 NLP 代码中它是这样使用的:

      当您希望您的对象表现为一个集合(数组等)以类似地使用它作为具有基于范围等的数组时,您可以这样做:

      struct Obj {
      ...
      Obj* begin() { return this; }
      Obj* end() { return this+1; }
      ...
      }
      

      现在,您可以在例如基于范围的 for 循环中使用此对象... 有时这一切都是必要的......但即使在那里你最好使用“nullptr”甚至进行重构,而不是使用这个技巧。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-05
        • 2011-05-25
        • 2020-04-04
        • 2013-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多