在 C++ 中,我希望我的接口 .h 表示 int GetSomeInt() const;.... 但实际上方法 *确实* 更新“this”。

发布于 2024-07-09 22:39:11 字数 314 浏览 5 评论 0原文

我向 const 方法添加了一些惰性初始化逻辑,这使得该方法实际上不是 const。 有没有办法让我不必从公共接口中删除“const”就可以做到这一点?

int MyClass::GetSomeInt() const
{
    // lazy logic
    if (m_bFirstTime)
    {
        m_bFirstTime = false;
        Do something once
    }

    return some int...

}

编辑:“mutable”关键字在这里起作用吗?

I'm adding some lazy initialization logic to a const method, which makes the method in fact not const. Is there a way for me to do this without having to remove the "const" from the public interface?

int MyClass::GetSomeInt() const
{
    // lazy logic
    if (m_bFirstTime)
    {
        m_bFirstTime = false;
        Do something once
    }

    return some int...

}

EDIT: Does the "mutable" keyword play a role here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(6

世俗缘 2024-07-16 22:39:11

使 m_bFirstTime 可变:

class MyClass
{
  : :
  mutable bool m_bFirstTime;
};

...但这也常常表明存在设计缺陷。 所以要小心。

Make m_bFirstTime mutable:

class MyClass
{
  : :
  mutable bool m_bFirstTime;
};

...but this is also very often an indication of a design flaw. So beware.

紫瑟鸿黎 2024-07-16 22:39:11

实际上,你说你不想改变头文件。 因此,您唯一的选择是放弃 this 指针的常量性...

int MyClass::GetSomeInt() const
{
    MyClass* that = const_cast<MyClass*>(this);

    // lazy logic
    if (that->m_bFirstTime)
    {
        that->m_bFirstTime = false;
        Do something once
    }

    return some int...

}

如果使用可变会引发危险信号,则会在轨道中启动危险信号存储。 做这样的事情通常是一个非常糟糕的主意。

Actually, you said that you didn't want to change the header file. So your only option is to cast away the constness of the this pointer...

int MyClass::GetSomeInt() const
{
    MyClass* that = const_cast<MyClass*>(this);

    // lazy logic
    if (that->m_bFirstTime)
    {
        that->m_bFirstTime = false;
        Do something once
    }

    return some int...

}

If using mutable raises a red flag, this launches a red flag store in to orbit. Doing stuff like this is usually a really bad idea.

纸伞微斜 2024-07-16 22:39:11

我认为这个问题涉及两个概念:(1)“逻辑常量”和(2)“按位常量”。 我的意思是,从类中获取一些 int,逻辑上不会改变类,并且在大多数情况下,它不会改变班级成员。 然而,在某些情况下,比如你的情况,确实如此。

在这些情况下,如果该方法是逻辑 const 但不是按位 const,则编译器无法知道这一点。 这就是 mutable 关键字存在的原因。 按照 John Dibling 的演示使用它,但这不是设计缺陷。 相反,在很多情况下这是必要的。 在你的例子中,我认为 int 的计算是昂贵的,所以如果不需要的话我们不想计算它。 在其他情况下,您可能希望缓存方法的结果以供以后使用等。

顺便说一句,即使您已经接受“可变”答案是正确的,您也必须更新 .h!

I think of this problem as involving two concepts: (1) "logically const" and (2) "bitwise const". By this I mean that getting some int from a class, does not logically change the class and in most cases it does not change the bits of the class members. However, in some cases, like yours, it does.

In these cases, where the method is logically const but not bitwise const, the compiler cannot know this. This is the reason for the existence of the mutable keyword. Use it as John Dibling shows, but it is not a design flaw. On the contrary, there are many cases where this is necessary. In your example, I presume that the calculation of the int is expensive, so we do not want to calculate it if it is not needed. In other cases, you may wish to cache results of methods for later use, etc.

BTW, even though you have accepted the "mutable" answer as correct, you do have to update the .h!

甜妞爱困 2024-07-16 22:39:11

将 m_bFirstTime 成员设置为可变

set the m_bFirstTime member to be mutable

懷念過去 2024-07-16 22:39:11

作为 John Dibling 说,将更改的字段标记为可变。 重要的部分是 ypnos 的评论:“不要真正改变对象的状态”(正如外界所认为的那样)。 也就是说,const 方法调用之前和之后的任何方法调用都必须产生相同的结果。 否则你的设计就有缺陷。

一些有意义的事情是可变的:

  • 互斥锁或其他锁类型
  • 缓存的结果(不会改变)

互斥锁不是对象状态的一部分,它们只是保证数据完整性的阻塞机制。 从类中检索值的方法确实需要更改互斥体,但在执行 const 方法后,类数据和状态将与之前完全相同。

对于缓存,您必须考虑仅对检索成本高昂且假定不会更改的数据有意义(例如 DNS 结果)。 否则,您可能会将过时的数据返回给您的用户。

在 const 方法中不应更改的一些内容:

  • 任何修改 const 状态的内容
    对象
  • 任何影响这个或其他的东西
    方法结果

执行 const 方法的类的任何用户都将假定您的类(从外界看来)在执行期间不会发生变化。 如果不一样,就会产生很大的误导并且容易出错。 举个例子,假设 dump() 方法更改了一些内部变量(状态、值),并且在调试期间,类的用户决定在给定点转储()对象:您的类在使用跟踪时的行为与不使用跟踪时的行为不同:完美的调试噩梦。

请注意,如果您执行惰性优化,则必须执行它们才能访问不可变数据。 也就是说,如果您的界面规定在构造过程中您将从数据库中检索一个元素,稍后可以通过常量方法访问该元素,那么如果您延迟获取数据,您最终可能会遇到用户构造对象的情况为了保留旧数据的副本,修改数据库,然后决定将以前的数据恢复到数据库中。 如果您执行了延迟获取,您最终将丢失原始值。 一个相反的例子是配置文件解析,如果在程序执行期间不允许修改配置文件。 您可以避免在需要的地方解析文件,因为知道在开始或稍后执行读取将产生相同的结果。

As John Dibling said, mark the fields that are changed as mutable. The important part is in the comment by ypnos: 'don't really change the state of the object' (as perceived by the outside world). That is, any method call before and after the const method call must yield the same results. Else your design is flawed.

Some things that make sense to be mutable:

  • mutex or other lock types
  • cached results (that will not change)

Mutex are not part of your objects state, they are only a blocking mechanism to guarantee data integrity. A method that will retrieve a value from your class, does need to change the mutex, but your class data and state will be exactly the same after the execution of the const method as it was before.

With caching, you must consider that only data that it makes sense for data that is expensive to retrieve and assumed not to change (DNS result, as an example). Else you could be returning stale data to your user.

Some things that should not be changed inside const methods:

  • Anything that modifies the state of
    the object
  • Anything that affects this or other
    method results

Any user of your class that executes const methods will assume that your class (as seen from the outside world) will not change during the execution. It will be quite misleading and error prone if it were not the same. As an example, assume that a dump() method changes some internal variable -state, value- and that during debug the user of your class decides to dump() your object at a given point: your class will behave differently with traces than without: perfect debug nightmare.

Note that if you do lazy optimizations you must do them to access immutable data. That is, if your interface dictates that during construction you will retrieve an element from the database that can be later accessed through a constant method, then if you do lazy fetching of the data you can end up in a situation where the user constructs your object to keep a copy of the old data, modifies the database and later on decides to restore the previous data into the database. If you have performed a lazy fetch you will end up loosing the original value. An opposite example would be configuration file parsing if the config file is not allowed to be modified during the execution of the program. You can avoid parsing the file up the point where it is needed knowing that performing the read in the beginning or at a later time will yield the same result.

天气好吗我好吗 2024-07-16 22:39:11

无论如何 - 请注意这将不再是线程安全的。 如果对象仅具有 const 方法(或者在初始化后仅使用 const 方法),则通常可以依赖该对象是线程安全的。 但如果这些 const 方法只是逻辑上的 const,那么你就会失去这个好处(当然除非你开始一直锁定它)。

唯一可能导致 havok 的编译器优化是编译器发现您使用相同的参数调用该方法两次,并且只重用第一个返回值 - 只要该函数在逻辑上确实是 const 就可以了,并为给定的一组参数(和对象状态)返回相同的值。 如果任何人(包括另一个线程)都可以访问该对象并对其调用非常量方法,那么即使该优化也是无效的。

专门针对这种情况将 mutable 添加到语言中。 C++ 是一种务实的语言,并且很乐意在需要时允许这样的极端情况存在。

In any case - make note that this is no longer going to be thread safe. You can often rely on an object to be thread safe if it only has const methods (or you use only the const methods after initialization). But if those const methods are only logically const, then you lose that benefit (unless of course you start locking it all the time).

The only compiler optimization that could cause havok would be for the compiler to figure out that you're calling the method twice with the same arguments, and just reuse the first return value - which is fine as long as the function truly is logically const, and returns the same value for a given set of arguments (and object state). Even that optimization isn't valid if it's possible that anyone (including another thread) has had access to the object to call non-const methods on it.

mutable was added to the language specifically for this case. C++ is a pragmatic language, and is happy to allow corner cases like this to exist for when they are needed.

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