[英]this-Pointer changing on call of member function
首先,对于这个不清楚的问题,我们先表示歉意-如果我知道要问什么,我可能会知道如何解决。 ...尽管有七年的C ++经验,但我什至没有模糊的理论。 任何有用的指针(hè)都将不胜感激。
问题: 具有某些属性的对象计数(本身由其类的成员函数检查)显示错误的结果。 来源是检查发生在乱码上。 其来源是指针“ 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.