3

给定以下代码:

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 之前是这样。例如,常量原始类型(通常)被放置在只读内存中,并且对其执行 aconst_cast可能会导致未定义的行为,例如:

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

7 回答 7

4

是的,根据 7.1.5.1/4,这是未定义的行为:

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

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

于 2009-12-19T23:27:04.733 回答
2

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

mutable int i;

i即使对象是 const,现在也可以更改。当逻辑上对象没有改变但实际上它确实改变时使用它。


例如:

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

InDoSomething()对象在逻辑上没有改变,但 mMutex 必须改变才能锁定它。所以它是有意义的mutable,否则没有 SomeClass 的实例可能是const(假设您为每个操作锁定 muetx)。

于 2009-12-19T23:02:48.993 回答
2

如果您定义对象的 const 实例,然后抛弃 const-ness 并修改对象的内容,您将获得未定义的行为。

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

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

于 2009-12-19T23:52:09.677 回答
1

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

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

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

于 2009-12-19T23:24:35.923 回答
0

你为什么不使用 const cast ?

即使对象的状态不是恒定的,有任何理由将对象设为 const 吗?

还要进行以下更改:

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

{    instance = this;   }
于 2009-12-19T23:05:22.907 回答
0

用这些随意的名字很难说出意图。如果i仅打算作为一个使用计数器,并且它并没有真正被视为数据的一部分,那么将其声明为是完全合适的,mutable int i;那么在修改const时不会违反实例的 -ness i。另一方面,如果i正在建模的空间中有有意义的数据,那将是一件非常糟糕的事情。

但是,除此之外,您的示例对于您似乎要问的内容有点混乱。foo* instance = NULL;有效地(如果令人困惑)使用 aNULL作为数字零并初始化instance,而不是const; 然后你单独初始化f,即const,但永远不要引用它。

于 2009-12-19T23:18:54.983 回答
0

至少在 GCC 下,您的构造函数应该explicit foo(int j)带有int.

但是,有两个指向相同值的指针是完全可以的,一个const没有一个。

于 2009-12-19T23:21:18.490 回答