一开始,就问题的模糊描述提前道歉 - 如果我知道要求什么,我可能会知道如何解决它。 ...但我甚至没有一个微弱的理论,尽管有七年的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;
}
附加输出(以及转储的初始化和关闭)来自控制流中的下一个更高级别,另一个类中的另一个函数,其中所有六边形的循环驻留。我暂时省略了,但如果有人认为这很重要,我会加上它。
顺便说一下。它现在看起来好像这个错误是工具中的错误导致的,而不是代码中的错误。事实上,即使函数现在正确评估,调试器仍然显示来自之前的错误指针及其无意义的成员。
答案 0 :(得分:1)
编辑OP编辑:
这现在闻起来甚至更多,就像ODR违规一样。更改内联函数,并具有该更改程序行为正是由于ODR违规引起的未定义行为可能发生的情况。你在任何地方使用模板吗?另外,请尝试在原始版本中取消内联IsSea()
以查看是否有帮助。
(原始答案): 这对我来说就像三件事之一。
首先,它可能是相关函数的单定义规则违规。绝对确定不同翻译单元中没有多个版本,或者不同单元中的编译设置不同。
其次,由于您使用保留名称_Area
,编译器可能正在执行某些操作。无论如何,你都应该解决这个问题。
第三,VC ++可以使用不同的成员函数指针机制,其中一个可能会影响你的情况(即使你没有显示使用成员函数指针)。有关一些信息,请参阅https://msdn.microsoft.com/en-us/library/83cch5a6.aspx?f=255&MSPPError=-2147217396。另一种可能性是这些指针的编译器选项在翻译单元中是不同的。
答案 1 :(得分:1)
这是一个非常悲伤的答案,但唉,有时一个悲伤的答案仍然是正确的。
让我们从较小但至少有些有用的部分开始:
值VS2015的调试器显示为this指针 - 并且在扩展中指向的对象的所有成员 - 确实是错误的并且以非常可重复的方式显示:
如果在头文件中定义的成员函数上设置了断点,"这个"显示在调试器中将显示 - 此函数的入口点。它仍将具有所讨论的对象的类型并显示所有成员......但是当这些值被填充为来自所述函数的入口点的偏移时,它们的显示内容当然是无意义的。 所有这些纯粹是一个UI问题,不会影响实际的程序执行。
现在是无用而令人沮丧的部分:
最初促使我打开这个主题的错误,通过几个具有不同构建设置的编译持续存在 - 已经消失,在我输入fprinf命令将指针地址转储到文件后再也无法再现发现上面描述的错误。 即使代码是一字母一致的,与之前有缺陷的代码完全相同,但它现在可以完美运行。进一步的改动没有改变这一点。我不能把它带回去,尽我所能。
这整个卑鄙的事情 - 是一个侥幸。 不知何故。这意味着它可能在任何时候再次发生,没有明显的理由,没有任何预防手段。好的,不是吗?
...
无论如何,衷心感谢@Ben Voigt提出的可能与调试器输出相关的概念可能与现实无关。 同样,感谢@dyp指出并解释了一个不相关的潜在问题(名称前缀为' _'后跟大写字母为保留表达式)我以前没有意识到。 感谢@ Mark-B实际提供关于问题的假设,即使它们都没有被证明是正确的。至少有一些,也可能有一个。