给出以下代码:
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
}
答案 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;
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
;然后你单独初始化f
,const
,但从不引用它。
答案 6 :(得分:0)
至少在GCC下,您的构造函数应为explicit foo(int j)
,并带有int
一词。
但是,有两个指向同一个值的指针,一个const
而另一个不是。