如何在不欺骗编译器的情况下保持这个常量正确?

Posted

技术标签:

【中文标题】如何在不欺骗编译器的情况下保持这个常量正确?【英文标题】:How to keep this const-correct without cheating the compiler? 【发布时间】:2011-12-17 13:57:26 【问题描述】:

我有一个这样的 C++ 类:

class Example 

    public:

        int getSomeProperty(int id) const;

    private:

        lazilyLoadSomeData();


基本上getSomeProperty() 返回一些已经使用lazilyLoadSomeData() 加载的数据。由于我不想在需要之前加载这些数据,所以我在 getSomeProperty() 中调用此方法

int Example::getSomeProperty(int id) const 
    lazilyLoadSomeData(); // Now the data is loaded
    return loadedData[id];

这不起作用,因为 lazilyLoadSomeData() 不是 const。即使它只更改可变数据成员,编译器也不会允许它。我能想到的唯一两个解决方案是:

在类构造函数中加载数据,但我不想这样做,因为延迟加载所有内容会使应用程序更快。

lazilyLoadSomeData() 设为常量。它会起作用,因为它只更改可变成员,但它似乎并不正确,因为从名称来看,该方法显然正在加载某些内容并且显然正在进行一些更改。

关于什么是处理这个问题的正确方法有什么建议,而不必欺骗编译器(或完全放弃 const 正确性)?

【问题讨论】:

¤ 这就是 mutable 关键字的用途。更倾向于“作弊”方向的另一种选择是保留指向数据的指针。我提到这一点是为了明确识别反模式并避免它;对惰性数据使用mutable。干杯&hth., 【参考方案1】:

您可以创建一个您声明mutable 并封装延迟加载策略的代理成员对象。该代理本身可以从您的 const 函数中使用。作为奖励,您最终可能会得到一些可重用的代码。

【讨论】:

除了...从语义上讲,您只是做完全相同的事情,因此有人可能会说这是同一个问题。老实说,问题是“愚蠢”是某种方式,因为mutable 是关于作弊,以一种受控的方式。【参考方案2】:

我会将调用转发到一个代理对象,它是此类的 mutable 成员,如下所示:

class Example 

    public:

        int getSomeProperty(int id) const
        
            m_proxy.LazyLoad();
            return m_proxy.getProperty(id);
        

    private:

        struct LazilyLoadableData
        
             int GetProperty(int id) const;
             void LazyLoad();
       ;

     mutable LazilyLoadableData m_proxy;
;

【讨论】:

当您不使用“pImpl”时确实如此。但如果你这样做,就不需要可变的。您可以只修改 pImpl 指向的内容,因为只有 pImpl 本身是 const..【参考方案3】:

将 lazilyLoadSomeData() 设为常量。它会起作用,因为它只更改可变成员,但它似乎并不正确,因为从名称来看,该方法显然正在加载某些内容并且显然正在进行一些更改。

不,它没有做出一些改变,至少从调用 getSomeProperty 的人的角度来看是这样。如果你做对了,所有的变化都是纯粹的内部变化,从外部看不到。这是我会选择的解决方案。

【讨论】:

以上是关于如何在不欺骗编译器的情况下保持这个常量正确?的主要内容,如果未能解决你的问题,请参考以下文章

在不丢失 .data 常量的情况下混合汇编和 C 的正确标志是啥

如何在不反编译的情况下更改已编译的 .class 文件?

如何在不使用模拟位置的情况下在 android 上欺骗位置?

讨论一下如何在有ARP欺骗的情况下获取正确的网关MAC地址

Deno 如何在不以“watch”模式运行 TypeScript 编译器的情况下快速启动

如何在我自己的代码中使用 Visual C++ 在不运行静态代码分析的情况下导致 SAL 编译器警告