在 const 对象上间接调用非常量函数
给出以下代码:
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
会很好,但稍后< code>f 由注册表间接修改。
编辑:通过定义的行为,我的意思是:对象是否被放置到某个只能写入一次的特殊内存位置?只读内存是不可能的,至少在 C++1x 的 constexpr 之前是这样。例如,常量基本类型(通常)被放入只读内存中,对其执行 const_cast 可能会导致未定义的行为,例如:
int main()
{
const int i = 42;
const_cast<int&>(i) = 0; // UB
}
Given the following code:
class foo;
foo* instance = NULL;
class foo
{
public:
explicit foo(int j)
: i(j)
{
instance = this;
}
void inc()
{
++i;
}
private:
int i;
};
Is the following using defined behavior?
const foo f(0);
int main()
{
instance->inc();
}
I'm asking because I'm using a class registry, and as I don't directly modify f
it would be nice to make it const
, but then later on f
is modified indirectly by the registry.
EDIT: By defined behavior I mean: Is the object placed into some special memory location which can only be written to once? Read-only memory is out of the question, at least until constexpr of C++1x. Constant primitive types for instance, are (often) placed into read-only memory, and doing a const_cast
on it may result in undefined behavior, for instance:
int main()
{
const int i = 42;
const_cast<int&>(i) = 0; // UB
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
是的,这是未定义的行为,根据 7.1.5.1/4:
请注意,对象的生命周期从构造函数调用完成时开始 (3.8/1)。
Yes, it is undefined behavior, as per 7.1.5.1/4:
Note that object's lifetime begins when the constructor call has completed (3.8/1).
这可能是可以使用不太为人所知的
mutable
关键字的罕见情况之一:mutable int i;
i
现在甚至可以更改如果对象是 const。当对象逻辑上没有改变但实际上却发生了变化时,就会使用它。例如:
在 DoSomething() 中,对象在逻辑上不会改变,但 mMutex 必须改变才能锁定它。因此,使其
可变
是有意义的,否则 SomeClass 的实例不可能是const
(假设您为每个操作锁定 muetx)。This may be one of the rare cases where the not very known
mutable
keyword could be used:mutable int i;
i
can now be changed even if the object is const. It's used when logically the object doesn't change, but in reality it does.For example:
In
DoSomething()
the object doesn't logically change and yet mMutex has to change in order to lock it. So it makes sense to make itmutable
, otherwise no instance of SomeClass could beconst
(assuming you lock the muetx for every operation).如果定义对象的 const 实例,然后放弃 const 性并修改对象的内容,则会得到未定义的行为。
从事情的声音来看,您想要的恰恰相反:创建该对象的非常量实例,然后将指向该对象的常量指针返回给(大多数)客户端,而“所有者”保留一个非常量指针指向对象的指针,以便它可以根据需要修改成员。
通常,您可以通过使用私有构造函数定义类来管理这种情况,因此大多数客户端无法创建该类型的对象。然后,该类会将所有者类声明为友元,因此它可以使用私有构造函数和/或静态成员函数来创建对象的实例(或通常只有一个实例)。然后,所有者类将指针(或引用)传递给 const 对象以供客户端使用。您既不需要可变成员,也不需要放弃常量,因为拥有修改对象的“权利”的所有者总是有一个指向该对象的非常量指针(或者,再次,引用)。它的客户端仅接收 const 指针/引用,从而防止修改。
If you define a const instance of the object, then cast away the const-ness, and modify the contents of the object, you get undefined behavior.
From the sound of things, what you want is exactly the opposite: create a non-const instance of the object, then return a const pointer to that object to (most of) the clients, while the "owner" retains a non-const pointer to the object so it can modify members as it sees fit.
You'd typically manage a situation like this by defining the class with a private ctor, so most clients can't create objects of the type. The class will then declare the owner class as a friend, so it can use the private ctor and/or a static member function to create instances (or often only one instance) of the object. The owner class then passes out pointers (or references) to const objects for clients to use. You need neither a mutable member nor to cast away constness, because the owner, which has the "right" to modify the object, always has a non-const pointer (or, again, reference) to the object. Its clients receive only const pointers/references, preventing modification.
在 const 对象上调用非常量(通过声明)成员函数本身并不非法。您可以使用任何您希望解决编译器限制的方法:显式的 const_cast 或构造函数的技巧,如示例中所示。
然而,只有当您调用的成员函数不尝试实际物理修改对象(即修改常量对象的非可变成员)时,才会定义该行为。一旦尝试执行修改,行为就会变得不确定。在您的情况下,方法
inc
修改对象,这意味着在您的示例中行为未定义。再次强调,仅调用该方法是完全合法的。
Calling a non-const (by declaration) member function on a const object is not illegal per se. You can use whatever method you wish to work around the compiler restrictions: either an explicit
const_cast
or a trick with constructor as in your example.However, the behavior is only defined as long as the member function you are calling does not make an attempt to actually physically modify the object (i.e. modify a non-mutable member of the constant object). Once it makes an attempt to perform a modification, the behavior becomes undefined. In your case, method
inc
modifies the object, meaning that in your example the behavior is undefined.Just calling the method, again, is perfectly legal.
很难说出这些随意名称的意图。如果
i
只是用作使用计数器,并且它并不真正被视为数据的一部分,那么将其声明为mutable int i;
是完全合适的修改i
时,不会违反实例的 const 性。另一方面,如果 i 是正在建模的空间中有意义的数据,那么这将是一件非常糟糕的事情。但除此之外,你的例子对于你似乎要问的问题来说有点混乱。
foo* instance = NULL;
有效地(如果令人困惑)使用NULL
作为数字零并初始化instance
,这不是常量
;然后你单独初始化f
,它是const
,但永远不要引用它。It's hard to tell the intent with these arbitrary names. If
i
is intended as just a use counter, and it isn't really considered part of the data, then it is perfectly appropriate to declare it asmutable int i;
Then theconst
-ness of an instance is not violated wheni
is modified. On the other hand, ifi
is meaningful data in the space being modeled, then that would be a very bad thing to do.Separately from that, though, your example is a bit of a mess for what you seem to be asking.
foo* instance = NULL;
is effectively (if confusingly) using aNULL
as a numeric zero and initializinginstance
, which is notconst
; then you separately initializef
, which isconst
, but never reference it.至少在 GCC 下,您的构造函数应该是带有单词
int
的显式 foo(int j) 。然而,有两个指向相同值的指针是完全可以的,一个是 const,另一个不是。
Under GCC, at least, your constructor should be
explicit foo(int j)
with the wordint
.However, it's perfectly fine to have two pointers to the same value, one
const
and the other not.为什么不使用 const cast ?
有什么理由让对象成为常量,即使它的状态不是恒定的?
还进行以下更改:
Why dont you make use of const cast ?
Any reason to make object as const eventhough its state is not constant?
Also make following change :