const在类和类外部转换int

时间:2011-09-08 05:26:17

标签: c++

我在维基百科页面上看到Null_pointer Bjarne Stroustrup建议将NULL定义为

const int NULL = 0;

如果“你觉得你必须定义NULL”。我立刻想到了,嘿..等一下,怎么样const_cast?

经过一些实验,我发现了

int main() {
    const int MyNull = 0;
    const int*  ToNull = &MyNull;
    int* myptr = const_cast<int*>(ToNull);
    *myptr = 5;
    printf("MyNull is %d\n", MyNull);
    return 0;
}

会打印“MyNull为0”,但是如果我让const int属于一个类:

class test {
public:
    test() : p(0) { }
    const int p;
};
int main() {
    test t;
    const int* pptr = &(t.p);
    int* myptr = const_cast<int*>(pptr);
    *myptr = 5;
    printf("t.p is %d\n", t.p);
    return 0;
}

然后打印“t.p为5”!

为什么两者之间存在差异?为什么“* myptr = 5;”在我的第一个例子中默默地失败了,如果有的话,它正在执行什么动作?

3 个答案:

答案 0 :(得分:4)

首先,您通过尝试修改常量变量来调用两种情况下的未定义行为。

在第一种情况下,编译器看到MyNull被声明为常量,并用0替换main()中对它的所有引用。

在第二种情况下,由于p在一个类中,编译器无法确定它只能用classInstance.p替换所有0,因此您可以看到修改的结果

答案 1 :(得分:3)

首先,第一种情况发生的情况是编译器最有可能翻译你的

printf("MyNull is %d\n", MyNull);

立即进入

printf("MyNull is %d\n", 0);

因为它知道const个对象永远不会在有效程序中更改。您尝试更改const对象会导致未定义的行为,这正是您所观察到的。因此,从实际的角度来看,忽略未定义的行为一秒,您的*myptr = 5很可能成功修改了Null。只是你的程序并不真正关心你现在Null中的内容。它知道Null为零并且始终为零并且相应地起作用。

其次,为了根据您所指的建议定义NULL,您必须将其明确定义为积分常量表达式(ICE)。你的第一个变种确实是ICE。你的第二个变种不是。 ICE中不允许使用类成员访问,这意味着您的第二个变体与第一个变体有很大不同。第二个变体不会为NULL生成可行的定义,即使将test::p声明为const int并设置为零,也无法使用SomeType *ptr1 = Null; // OK test t; SomeType *ptr2 = t.p; // ERROR: cannot use an `int` value to initialize a pointer 初始化指针

const

对于第二种情况中的不同输出...未定义的行为是未定义的行为。这是不可预测的。从实际的角度来看,你的第二个上下文比较复杂,所以编译器无法从上面的优化中做出预测。即你确实成功地突破了语言级限制并修改了一个const限定变量。语言规范不会让编译器轻松(或可能)优化类的p成员,因此在物理层面const_cast只是驻留在内存中的类的另一个成员,在该类的每个对象中。你的黑客只是修改了那个记忆。但它并不合法。行为仍然未定义。

当然,这一切都是毫无意义的。看起来这一切都始于“const_cast”问题。那么,它呢? const从未打算用于此目的。您不能修改const_cast个对象。使用const_cast或没有{{1}} - 无关紧要。

答案 2 :(得分:1)

您的代码正在修改声明为常量的变量,因此任何事情都可能发生。讨论为什么某个事情发生而不是另一个事情是完全没有意义的,除非你讨论不可移植的编译器内部问题......从C ++的角度来看,代码根本没有任何意义。

关于const_cast要理解的一件重要事情是const const不是为了搞乱变量声明的常量,而是关于引用指针声明不变。

在C ++中,const int *通常被理解为“指向常数整数的指针”,而此描述完全错误。对于编译器而言,它完全不同:“指针不能用于写入整数对象”

这显然可能是一个微小的差异,但确实是一个巨大的差异,因为

  1. “constness”是指针的属性,而不是指向对象的属性。

  2. 没有任何关于指向物体是否恒定的事实。

  3. “常量”这个词与意义无关(这就是为什么我认为使用const这是一个错误的命名选择)。 const int *并不是在谈论任何事情的常数,只是关于“只读”“读/写”

  4. const_cast允许您在指针和可用于写入的引用和引用之间进行转换,因为它们是“只读”而不能。指向的对象永远不会成为这个过程的一部分,标准只是说在“抛弃”常量之后采用const指针并将其用于写入是合法的,但前提是指向对象尚未声明为常量。 / p>

    指针和引用的常量永远不会影响将由编译器生成的机器代码(另一个常见的误解是,如果使用const引用和指针,编译器可以生成更好的代码,但这完全是假的...对于优化器,const引用和const指针只是一个引用和一个指针。

    指针和引用的常量已经被引入以帮助程序员,而不是选择器(顺便说一下,我认为这对程序员的这种所谓的帮助也是值得怀疑的,但这是另一个故事)。

    const_cast是一种武器,可以帮助程序员使用破坏的指针和引用的常量声明(例如在库中),并使用破坏的引用和指针的常量概念(在mutable之前)抛弃constness的例子是许多现实生活中唯一合理的解决方案。)

    对const引用的误解也是一个非常常见的C ++反模式(甚至在标准库中使用)的基础,它表示传递const引用是一种传递值的智能方法。有关详细信息,请参阅this answer