修改 const 对象

任何修改 const 对象的尝试都会导致未定义的行为。这适用于 const 变量,const 对象的成员以及声明为 const 的类成员。 (但是,const 对象的 mutable 成员不是 const。)

这样的尝试可以通过 const_cast 来实现:

const int x = 123;
const_cast<int&>(x) = 456;
std::cout << x << '\n';

编译器通常会内联 const int 对象的值,因此该代码可能会编译并打印 123。编译器还可以将 const 对象的值放在只读存储器中,因此可能会发生分段错误。在任何情况下,行为都是未定义的,程序可能会做任何事情。

以下程序隐藏了一个更微妙的错误:

#include <iostream>

class Foo* instance;

class Foo {
  public:
    int get_x() const { return m_x; }
    void set_x(int x) { m_x = x; }
  private:
    Foo(int x, Foo*& this_ref): m_x(x) {
        this_ref = this;
    }
    int m_x;
    friend const Foo& getFoo();
};

const Foo& getFoo() {
    static const Foo foo(123, instance);
    return foo;
}

void do_evil(int x) {
    instance->set_x(x);
}

int main() {
    const Foo& foo = getFoo();
    do_evil(456);
    std::cout << foo.get_x() << '\n';
}

在此代码中,getFoo 创建一个 const Foo 类型的单例,其成员 m_x 初始化为 123。然后 do_evil 被调用,foo.m_x 的值显然变为 456.出了什么问题?

尽管它的名字,do_evil 没有什么特别邪恶; 所有这一切都是通过 tihuan 19 召唤 setter。但是,即使没有使用 const_cast,该指针也指向了 const Foo 对象。这个指针是通过 Foo 的构造函数获得的。const 对象在初始化完成之前不会成为 const,因此 this 在构造函数中具有 Foo*类型,而不是 const Foo*

因此,即使在此程序中没有明显危险的构造,也会发生未定义的行为。