const_cast的奇怪行为

3

我知道使用const_cast通常是个坏主意,但是我正在尝试它并遇到了一个奇怪的行为,即:

两个指针具有相同的地址值,但是当取消引用时,它们给出不同的数据值。

有人能解释一下吗?

代码

#include <iostream>

int main()
{
    const int M = 10;

    int* MPtr = const_cast<int*>(&M);

    (*MPtr)++;

    std::cout << "MPtr = " << MPtr << "   (*MPtr) = " << (*MPtr) << std::endl;
    std::cout << "  &M = " << &M << "         M = " << M << std::endl;
}

输出

MPtr = 0x7fff9b4b6ce0   (*MPtr) = 11
  &M = 0x7fff9b4b6ce0         M = 10
2个回答

5

该程序具有未定义行为,因为您可能不会更改const对象。

来自C++标准

4 某些其他操作在此国际标准中被描述为未定义(例如试图修改const对象的效果)。[注意:此国际标准对包含未定义行为的程序的行为不提出要求。-注]


2
在这种情况下,由于您的 M 是 const,输出行甚至不会检查存储在其地址处的数字是什么。它只是硬编码了“打印10”,因为您允许编译器假定 M 总是10。 - Sneftel

2

除了“这是未定义的行为”(它确实是),编译器可以完全使用M是一个常量,因此不会改变,在评估cout ... << M << ...时,因此可以使用具有立即值10的指令,而不是存储在M内存中的实际值。(当然,标准不会说明这是如何工作的,只会说“这是未定义的”,编译器能够在不同情况下选择不同的解决方案等等,所以如果您修改代码、使用不同的编译器或编译器版本,或风向改变,可能会得到不同的结果)。

“未定义行为”的棘手之处在于,它包括一些“完全符合您的期望”的事情,以及“几乎符合您的期望”的事情。如果编译器发现您正在做这件事,它也可以决定开始玩俄罗斯方块。

是的,这很大程度上是为什么您不应该使用const_cast的原因之一。至少不要对最初是const的东西使用 - 如果您有类似以下内容的东西,则可以使用:

int x;

void func(const int* p)
{
  ...
  int *q = const_cast<int *>(p);

  *q = 7;
}


...

 func(&x);

在这种情况下,x实际上并不是const,只有当我们将其传递给func时才变为const。 当然,编译器可能仍然假定xfunc中没有改变,因此可能会出现问题...

或者至少,为什么你应该只在实际上不是常量的变量上使用const_cast,但你有一个指向常量的指针。 - Sneftel
const_cast是一个关键工具,它存在是有原因的。你只需要知道它的用途,才能正确地使用它。 - shawn1874
1
当然,它存在是有道理的-语言架构师(大多数情况下)不会只是因为喜欢就添加功能。但是,我已经使用C++工作了大约7年,从来没有需要使用const_cast。我的一个同事曾经需要这样做-因为某个声明为“const”的函数需要修改成员变量,所以将this与const-cast进行转换是唯一的出路(重新定义函数将是一个更糟糕的解决方案)。所以,“不应该使用”对我来说还可以。 - Mats Petersson
1
@MatsPetersson 很好的回答,谢谢。只是提醒一下,有一个关键字可以满足你的同事的需求:将成员声明为“mutable”。 - MGA
@mga:是的,那也是一种可能的选择,但由于“原始类定义无法更改”,某种原因似乎不可行。距离我上次参与该项目已经有4年了,而且那项工作并不是由我完成的(但我记得解决方案是“const_cast”)。 - Mats Petersson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接