在 const 对象上间接调用非常量函数

发布于 2024-08-15 00:32:54 字数 682 浏览 5 评论 0原文

给出以下代码:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

只等公子 2024-08-22 00:32:54

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

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

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

Yes, it is undefined behavior, as per 7.1.5.1/4:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Note that object's lifetime begins when the constructor call has completed (3.8/1).

傲娇萝莉攻 2024-08-22 00:32:54

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

mutable int i;

i 现在甚至可以更改如果对象是 const。当对象逻辑上没有改变但实际上却发生了变化时,就会使用它。


例如:

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

在 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:

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

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 it mutable, otherwise no instance of SomeClass could be const (assuming you lock the muetx for every operation).

迷离° 2024-08-22 00:32:54

如果定义对象的 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.

安稳善良 2024-08-22 00:32:54

在 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.

傻比既视感 2024-08-22 00:32:54

很难说出这些随意名称的意图。如果 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 as mutable int i; Then the const-ness of an instance is not violated when i is modified. On the other hand, if i 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 a NULL as a numeric zero and initializing instance, which is not const; then you separately initialize f, which is const, but never reference it.

旧梦荧光笔 2024-08-22 00:32:54

至少在 GCC 下,您的构造函数应该是带有单词 int 的显式 foo(int j) 。

然而,有两个指向相同值的指针是完全可以的,一个是 const,另一个不是。

Under GCC, at least, your constructor should be explicit foo(int j) with the word int.

However, it's perfectly fine to have two pointers to the same value, one const and the other not.

谷夏 2024-08-22 00:32:54

为什么不使用 const cast ?

有什么理由让对象成为常量,即使它的状态不是恒定的?

还进行以下更改:

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

{    instance = this;   }

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 :

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

{    instance = this;   }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文