this指针在调用成员函数时改变
this-Pointer changing on call of member function
首先,我要为这个问题的不清晰表达提前道歉——如果我知道该问什么,我可能就知道如何解决它了。…但是我甚至没有一个模糊的理论,尽管有七年的c++经验。如有任何有用的指示(hè),将不胜感激。
- 可能与这个问题有关。(症状)
- 与那个无关。(这里没有明确的函数指针)
问题:具有某些属性的对象的计数,这些对象本身由其类的成员函数检查,结果不正确。问题的根源在于检查是用乱码值进行的。原因在于指针"this"在调用函数和进入函数体之间发生了变化。离开主体后,指针又正确了。
遗憾的是,相关问题的解决方案在这里不起作用。
我不能举出这个问题的一个最小的例子。此外,据我所知,在同一个程序中正确调用了数百个成员函数,而没有表现出这种行为。
- 我能做什么?作为权宜之计,用函数体的副本替换函数调用是有效的,但这不是正确的解决方案。
我完全不知道如何进行诊断。
简明:我可以遵循哪些步骤来更深入地了解问题的本质?
已处理事项的简短清单:
- 所讨论的对象在调用时被正确初始化。
- 所有优化都关闭。没有内联。这是一个正确设置生效的调试版本。
- 清理和重建项目没有产生不同的结果。
- 在bandaid解决方案测试成功后,使用原始(但重新键入的)函数调用重新编译导致问题返回。
- 所涉及的编译单元中没有编译器警告(警告级别3),在项目范围内特别禁用的是:
- C4005(宏重新定义,由于兼容性原因使用自定义/黑客Windows SDK -这最初是一个Win95程序)
- C4244(隐式转换为较小的类型,由于遗留代码等待重构-这些都是缺乏显式转换的浮点到int转换,所有800多个实例)
- C4995(调用带有#pragma标记的函数已弃用,因为C-lib函数被调用时没有前面的下划线-希望最终切换到GCC)
- "控制流保护"answers"基本运行时检查"已启用,但未触发。
和一个可能相关也可能不相关的提示,但我现在无法解释:
- 对于第一个十六进制,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;
}
额外的输出(以及dump的初始化和关闭)来自控制流的下一个更高级别,另一个类中的另一个函数,其中循环遍历所有六边形。我现在省略了这一点,但如果有人认为这很重要,我会添加的。
和相应的。现在在我看来,这个错误似乎是由工具中的错误造成的,而不是代码中的错误。事实上,即使函数现在计算正确,调试器仍然显示了之前的错误指针及其无意义成员。
EDIT for OP EDIT:
现在闻起来更像是违反了ODR。更改内联函数并使其更改程序行为,正是由于违反ODR而导致的未定义行为可能发生的情况。你在任何地方都使用模板吗?此外,尝试在原始版本中取消内联IsSea()
,看看是否有帮助。
(原始回答):对我来说,这味道像三样东西中的一种。
首先,它可能违反了所讨论的函数的单定义规则。一定要确保在不同的翻译单元中没有多个版本,或者在不同的单元中没有不同的编译设置。
其次,编译器可能会因为使用保留名称_Area
而做一些事情。无论如何,你都应该解决这个问题。
第三,vc++可以为成员函数指针使用不同的机制,其中一个可能会影响到你这里的情况(即使你没有展示成员函数指针的使用)。请参阅https://msdn.microsoft.com/en-us/library/83cch5a6.aspx?f=255&MSPPError=-2147217396了解一些信息。另一种可能性是,这些指针的编译器选项在不同的翻译单元之间是不同的。
这是一个非常悲伤的答案,但是,唉,有时悲伤的答案仍然是正确的。
让我们从较小但至少有点用的部分开始:
VS2015的调试器为this指针显示的值-扩展为指向的对象的所有成员-确实是不正确的,并且以一种非常可复制的方式:
如果在头文件中定义的成员函数上设置了断点,调试器中显示的"this"将显示该函数的入口点。它将仍然具有所讨论的对象的类型并显示所有成员…但是,由于这些值是从上述函数的入口点作为偏移量填充的,因此它们显示的内容当然是无意义的。所有这些都是纯粹的UI问题,并不影响实际的程序执行
现在是无用和令人沮丧的部分:
最初促使我打开这个主题的错误,它持续存在于不同构建设置的几个编译中-在我输入fprinf命令将指针地址转储到文件以发现上面描述的错误后,它已经消失并且不能再重现。尽管代码与之前的错误代码逐字相同,但现在它可以完美地工作。进一步的改变并没有改变这一点。不管我怎么努力,我都无法挽回。
这一切都是侥幸。 。这意味着它随时可能再次发生,没有明显的原因,没有任何预防措施。很好,不是吗?
…
无论如何,衷心感谢@Ben Voigt提出的概念,这些调试器的输出可能首先与现实无关。同样,感谢@dyp指出并解释了一个不相关的潜在问题(名称以'_'前缀,后跟大写字母是保留表达式),我以前没有意识到。感谢@Mark-B提供了关于这个问题的假设,即使没有一个被证明是正确的。
- 变量没有改变?通过向量的函数调用
- 为什么提升图库的 read_graphviz() 函数会改变节点的索引
- 为什么我的函数接受"std::string"进行排序不会改变它?
- 通过从构造函数中的'this'复制的指针改变常量对象
- 为什么我可以改变常量对象中的成员变量,这是返回常量对象函数的结果?
- 有没有办法创建一个不改变程序中参数的函数
- 为什么我的函数不改变对象的属性
- 类中的枚举在调用自己的函数时不会改变
- C - 如何将各处的静态函数称为改变其私有变量
- 基于继承的类型参数改变父虚拟函数的行为
- 对列表中的每个对象调用成员函数,以改变对象所在的列表
- 为什么调用 CUDA 内核函数时这个类成员变量没有改变
- 当函数应该改变对象的状态时,是选择成员函数还是朋友函数
- 一个什么都不做的函数可以改变一个对象吗?
- ifstream 函数"get"如何改变其字符参数?
- 为什么一个函数的末尾有常量这个词,即使它确实改变了一个变量
- visual C++未执行的代码会改变函数行为
- 不被类函数改变的私有int值
- 谷歌模拟函数改变参数值
- 被非const成员函数改变的可变变量