间接调用const对象上的非const函数

时间:2009-12-19 22:57:21

标签: c++ function const undefined-behavior

给出以下代码:

class foo;

foo* instance = NULL;

class foo
{
public:
   explicit foo(int j)
    : i(j)
   {
      instance = this;
   }

   void inc()
   {
      ++i;
   }

private:
   int i;
};

以下是否使用定义的行为?

const foo f(0);

int main()
{
   instance->inc();
}

我问,因为我正在使用类注册表,并且因为我没有直接修改f,所以最好将其设为const,但稍后会f }由注册表间接修改。

编辑:通过定义的行为,我的意思是:对象是否放置在一些只能写入一次的特殊内存位置?只读存储器是不可能的,至少在C ++ 1x的constexpr之前。例如,常量基本类型(通常)放入只读内存中,对其执行const_cast可能会导致未定义的行为,例如:

int main()
{
    const int i = 42;
    const_cast<int&>(i) = 0; // UB
}

7 个答案:

答案 0 :(得分:3)

是的,根据7.1.5.1/4:

,它是未定义的行为
  

除了可以修改声明为mutable(7.1.1)的任何类成员之外,任何在其生命周期内修改const对象的尝试(3.8)都会导致未定义的行为。

请注意,对象的生命周期在构造函数调用完成后开始(3.8 / 1)。

答案 1 :(得分:2)

如果定义了对象的const实例,然后抛弃了const-ness,并修改了对象的内容,则会得到未定义的行为。

从事物的声音来看,你想要的恰恰相反:创建一个非const的对象实例,然后将一个指向该对象的const指针返回给(大多数)客户端,而“owner”保留一个指向对象的非const指针,以便它可以根据需要修改成员。

您通常通过使用私有ctor定义类来管理这种情况,因此大多数客户端无法创建该类型的对象。然后,该类将所有者类声明为朋友,因此它可以使用私有ctor和/或静态成员函数来创建对象的实例(或通常只有一个实例)。然后,所有者类将指针(或引用)传递给const对象以供客户端使用。你既不需要一个可变成员也不需要抛弃constness,因为拥有修改对象“权利”的所有者总是有一个非const指针(或者,再次引用)。它的客户端只接收const指针/引用,阻止修改。

答案 2 :(得分:1)

这可能是罕见的情况之一,可能会使用不太知名的mutable关键字:

mutable int i;

即使对象是const,现在也可以更改

i。当逻辑对象没有改变时使用它,但实际上确实如此。


例如:

class SomeClass
{
// ....
    void DoSomething() { mMutex.lock(); ...; }
    mutable Mutex mMutex;
}

DoSomething()中,对象不会在逻辑上发生变化,但是为了锁定它,必须更改mMutex。因此将其设为mutable是有意义的,否则SomeClass的任何实例都不能const(假设您为每个操作锁定了muetx)。

答案 3 :(得分:1)

在const对象上调用非const(通过声明)成员函数本身并不违法。您可以使用任何您希望解决编译器限制的方法:显式const_cast或带有构造函数的技巧,如示例所示。

但是,只有在您调用的成员函数没有尝试实际物理修改对象(即修改常量对象的不可变成员)时,才会定义行为。一旦尝试执行修改,行为就变得不确定。在您的情况下,方法inc修改对象,这意味着在您的示例中行为未定义。

再次调用该方法是完全合法的。

答案 4 :(得分:0)

为什么不使用const cast?

任何使对象成为const的任何理由,尽管它的状态不是常数?

还要进行以下更改:

explicit foo(int j = 0)    : i(j)   

{    instance = this;   }

答案 5 :(得分:0)

很难用这些任意名称来表达意图。如果i仅仅是一个使用计数器,并且它实际上不被视为数据的一部分,那么将它声明为mutable int i;然后const - 是非常合适的。修改i时,不会违反实例。另一方面,如果i在被建模的空间中是有意义的数据,那么这将是一件非常糟糕的事情。

除此之外,你的例子对于你似乎要问的东西来说有点混乱。 foo* instance = NULL;有效(如果令人困惑)使用NULL作为数字零并初始化instance,而不是const;然后你单独初始化fconst,但从不引用它。

答案 6 :(得分:0)

至少在GCC下,您的构造函数应为explicit foo(int j),并带有int一词。

但是,有两个指向同一个值的指针,一个const而另一个不是。