繁体   English   中英

调用成员函数时更改此指针

[英]this-Pointer changing on call of member function

首先,对于这个不清楚的问题,我们先表示歉意-如果我知道要问什么,我可能会知道如何解决。 ...尽管有七年的C ++经验,但我什至没有模糊的理论。 任何有用的指针(hè)都将不胜感激。

  • 可能与此问题有关 (症状相同)
  • 无关。 (此处没有显式函数指针)

问题: 具有某些属性的对象计数(本身由其类的成员函数检查)显示错误的结果。 来源是检查发生在乱码上。 其来源是指针“ this”在调用函数和进入函数体之间改变。 一旦离开身体,指针再次正确。

可悲的是,相关问题的解决方案在这里不起作用。

我无法提供此问题的最小示例。 而且,据我所知,实际上在同一个程序中正确地调用了数百个成员函数,而没有表现出这种行为。

  • 我能做什么? 作为创可贴,用函数主体的副本替换函数调用是可行的,但这不是适当的解决方案。

我对如何进行诊断完全不知所措。

简洁: 我可以采取哪些步骤来深入了解问题的本质?

一份简短的清单,说明已经处理的事情:

  • 有问题的对象在调用时已正确初始化。
  • 所有优化均已关闭。 没有内联。 这是具有适当设置有效的调试版本。
  • 清洁和重建项目并未产生不同的结果。
  • 成功测试了创可贴解决方案后,使用原始(但已重新键入)的函数调用进行重新编译导致问题再次出现。
  • 涉及的编译单元中没有编译器警告(警告级别3),特别是在整个项目范围内,禁用以下警告:
    • C4005(宏重新定义,由于出于兼容性原因使用自定义/被黑客入侵的Windows SDK-最初是Win95程序)
    • C4244(由于遗留代码等待重构,因此隐式转换为较小的类型-这些都是缺乏显式转换的浮点到整数转换,它们全部有800多个实例)
    • C4995(标有#pragma的调用函数已弃用,因为在不带下划线的情况下调用了C-lib函数-希望最终切换到GCC)
  • “控制流防护”和“基本运行时检查”已启用,但不触发。

还有一个提示可能会或可能不会相关,但我目前无法解释:

  • 对于第一个十六进制,通常称为IsSea,即:内部的“ this”与外部的“ this”相同
  • 该错误仅在随后的所有十六进制中发生。
    • 更改后的“ this”指针虽然未指向第一个十六进制,但似乎命中了未分配的空间。

以下是其外观的摘录:

标题:

// These three are actually included from other files.
constexpr unsigned int AREA_none = 0u;
constexpr unsigned int AREA_Lake = 1u;
enum Terrain
{
  OCEAN = 8,
  TERRA_INCOGNITA = 10
};

class CHex
{
public:
  CHex(); // initialises ALL members with defined error values
  bool ReadHex(); // Init-type function calling the problematic one
  bool IsSea() const // problematic function
  { return this->_Area != AREA_none && this->_Area != AREA_LAKE && this->_nTerrain == Terrain::OCEAN; }
  // The body does the right thing - just WITH the wrong thing.
protected:
  unsigned int _Area;
  int _nNavalIndex;
  Terrain _nTerrain;

  static int _nNavalCount = 0;

  // There are a lot more functions in here, both public and protected.
  // The class also inherits a bunch from three other classes, but no virtual functions and no overlaps are involved.
}

资源:

CHex::CHex() : _Area{0u}, _nNavalIndex{0}, _nTerrain{Terrain::TERRA_INCOGNITA}
{}
bool CHex::ReadHex()
{
  // Calls a lexer/parser pair to procure values from several files.
  // _Area and _nTerrain are being initialised in this process.
  // All functions called here work as expected and produce data matching the source files.

  // _Area and _nTerrain have the correct values seen in the source files at this point.
  if(this->IsSea()) // but inside that it looks as if they were uninitialised
    // This ALWAYS happens because the function always returns true.
    _nNavalIndex = _nNavalCount++;
  // Stopping at the next instruction, all values are again correct
  // - with the notable exception of the two modified by the instruction that should not have happened.

  // If I replace that with the following, I receive the correct result:
  /*
  // direct copy of the function's body
  if(this->_Area != AREA_none && this->_Area != AREA_Lake && this->_nTerrain == Terrain::OCEAN)
    _nNavalIndex = _nNavalCount++; // only happens when it should; at the end, the count is correct
  */

  // Sanity checks follow here.
  // They too work correctly and produce results appropriate for the data.

  return true; // earlier returns exist in the commented-out parts
}

再次抱歉造成这么大的混乱,但是,现在很混乱。 就像看到物理学的基本定律发生变化一样。

-

在@Ben Voigt的建议下,我侵入了一个诊断程序,该诊断程序将指针转储到文件中。 看吧:

Before ReadHex: 20A30050 (direct array access) On ReadHex:  20A30050    On IsSea:   20A30050 (with members: 0, 8) After ReadHex:    20A30050 
Before ReadHex: 20A33EAC (direct array access) On ReadHex:  20A33EAC    On IsSea:   20A33EAC (with members: 2, 0) After ReadHex:    20A33EAC 
Before ReadHex: 20A37D08 (direct array access) On ReadHex:  20A37D08    On IsSea:   20A37D08 (with members: 2, 0) After ReadHex:    20A37D08 
Before ReadHex: 20A3BB64 (direct array access) On ReadHex:  20A3BB64    On IsSea:   20A3BB64 (with members: 3, 0) After ReadHex:    20A3BB64 
Before ReadHex: 20A3F9C0 (direct array access) On ReadHex:  20A3F9C0    On IsSea:   20A3F9C0 (with members: 4, 3) After ReadHex:    20A3F9C0 
Before ReadHex: 20A4381C (direct array access) On ReadHex:  20A4381C    On IsSea:   20A4381C (with members: 3, 0) After ReadHex:    20A4381C 
[...]

他们都是正确的。 他们每个人。 甚至更好:该函数现在可以正确评估! 这是更改的来源(这次我省略了评论):

标题:

// These three are actually included from other files.
constexpr unsigned int AREA_none = 0u;
constexpr unsigned int AREA_Lake = 1u;
enum Terrain
{
  OCEAN = 8,
  TERRA_INCOGNITA = 10
};

extern FILE * dump;
class CHex
{
public:
  CHex();
  bool ReadHex();
  bool IsSea() const {
    fprintf(dump, "\tOn IsSea:\t%p (with members: %u, %i) ", (void*)this, this->_Area, this->_nTerrain);
    return this->_Area != AREA_none && this->_Area != AREA_LAKE && this->_nTerrain == Terrain::OCEAN; }
protected:
  unsigned int _Area;
  int _nNavalIndex;
  Terrain _nTerrain;

  static int _nNavalCount = 0;

  // lots more functions and attributes
}

资源:

CHex::CHex() : _Area{0u}, _nNavalIndex{0}, _nTerrain{Terrain::TERRA_INCOGNITA}
{}
bool CHex::ReadHex()
{
  fprintf(dump, "On ReadHex:\t%p ", (void*)this);

  // Calls a lexer/parser pair to procure values from several files.
  // _Area and _nTerrain are being initialised in this process.

  if(this->IsSea()) // Suddenly works!?
    _nNavalIndex = _nNavalCount++;

  // Sanity checks follow here.

  fprintf(dump, "After ReadHex:\t%p ", (void*)this);
  return true;
}

额外的输出(以及转储的初始化和关闭)来自控制流中的下一个更高级别,另一个类中的另一个函数,所有十六进制的循环都位于其中。 我暂时省略了它,但是如果有人认为它很重要,将添加它。

还有那个。 现在,在我看来,该错误是由工具 (而非代码)中的错误导致的。 实际上,即使函数现在可以正确评估,调试器仍会显示以前的错误指针及其无意义的成员。

编辑OP编辑:

现在,这气味甚至更像是一个ODR违反。 更改内联函数并更改程序行为,正是由违反ODR引起的未定义行为可能发生的情况。 您在任何地方都使用模板吗? 另外,请尝试在原始版本中取消内联IsSea()以查看是否有帮助。

(原始答案):这闻起来对我来说就像三件事之一。

首先,它可能是有关功能违反一定义规则的情况。 绝对确定在不同的翻译单元中没有多个版本,在不同的单元中没有不同的编译设置。

其次,由于使用了保留名称_Area因此编译器可能正在执行某些_Area 无论采取任何其他措施,您都应解决此问题。

第三,VC ++可以对成员函数指针使用不同的机制,并且其中一种可能会影响您的情况(即使假设您未显示成员函数指针的使用)。 有关某些信息,请参见https://msdn.microsoft.com/zh-cn/library/83cch5a6.aspx?f=255&MSPPError=-2147217396 另一种可能性是,跨翻译单元,此类指针的编译器选项会有所不同。

这是一个非常悲伤的答案,但是,有时候,一个悲伤的答案仍然是正确的。

让我们从较小但至少有点有用的部分开始:

VS2015的调试器为此this指针显示的值-并在扩展中指向该对象的所有成员-确实是错误的,并且是以非常可重复的方式显示的:

如果在头文件中定义的成员函数上设置了断点,则在调试器中显示的“ this”将显示-该函数的入口点。 它仍然具有所讨论对象的类型并显示所有成员...但是由于这些值被填充为与所述函数的入口点的偏移量,因此它们的显示内容当然是无意义的。 所有这些纯属UI问题,并不影响实际程序执行。

现在,无用和令人沮丧的部分是:

最初促使我打开此主题的故障已消失,该故障在具有不同构建设置的多个编译中仍然存在-已消失,并且在我输入fprinf命令以将指针地址转储到文件中以发现该错误后,不再可以重现该错误。如上所述。 即使该代码逐个字母地与以前的错误代码相同 ,现在也可以正常工作。 进一步的更改无济于事。 我无法将其恢复,请尝试尝试。

这整个混蛋的事情-是fl幸。 不知何故。 这意味着它可能会在没有任何明显原因的情况下,在任何时间,没有任何预防措施,再次发生。 很好,不是吗?

...

无论如何,都要感谢@Ben Voigt提出的想法,即这些调试器输出可能最初可能与现实无关。 类似地,感谢@dyp指出并解释了一个不相关的潜在问题(名称以'_'开头,后跟一个大写字母表示为保留表达式),我以前并不知道。 感谢@ Mark-B实际上提供了有关该问题的假设,即使没有一个被证明是正确的。 至少有一些可能一个。

暂无
暂无

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

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