如何定义触发计算的常量吸盘?

How to define const getters that trigger a calculation?

本文关键字:计算 常量 何定义 定义      更新时间:2023-10-16

我有一个类,其中包含一个偶尔需要重新计算的数据成员,尽管在需要重新计算之前可能会多次访问它。 我已经为数据定义了常量和非常量 getter 方法,在 getter 方法中,我想检查是否需要运行计算,如果需要,请调用适当的方法。 在非 const getter 中,我检查该值是否是最新的,如果没有,则在返回数据之前运行计算。在 const getter 中执行相同的操作会导致编译器抱怨此对象是 const 类型,而计算方法是非常量类型。因此,如果我简单地在常量 getter 中调用非 const getter,代码就会编译,我感到很惊讶。有人可以解释一下为什么这有效,因为我有点困惑。

class A {
bool isUpToDate=false;
double someData=0;
// Do some calculation
void calcData() 
{
someData = doSomeCalc();
isUpToDate = true; // data is now up-to-date
}
// non-const getter
double& data() 
{
if(!isUpToDate) 
{
// run the calculation only if data is not up-to-date
calcData()
}
return someData;
}
// const getter that doesn't work
const double& data() const 
{
if(!isUpToDate)
{
calcData() // compiler error: "this object is type const A but 
calcData is non-const"
}
return someData;
}
// const getter that (mysteriously) works
const double& data() const 
{
return data(); // why doesn't the compiler complain that data() is non-const?
}

我确信这种行为实际上是合理且定义明确的,我只是好奇它为什么有效,因为我不明白这里发生了什么。我正在使用 g++ 作为我的编译器和 c++11 标准,以防这是为什么它工作的相关因素。

你不是在称呼非常量getter;你是在称呼自己:

const double& data() const
{
return data();
}

是无限递归。

由于data() const*this在方法中被有效地const。调用data()等效于this->data()。因为this是一个const A *,这会再次选择constgetter。

因此,我很惊讶代码编译

当然它可以编译,但它实际上不起作用。它要么进入无限循环并挂起,要么只是崩溃。

在这种情况下,您可以将someDataisUpToDate声明为mutable。这意味着甚至可以从const成员函数修改它们。然后使calcData()const,以便可以从另一个const成员函数调用它。

这种做法往往会被怀疑!我将其用于互斥体和缓存。我担心你的可能有点过分了。

真的需要data()才能const吗?你应该确保...

使该字段可变,并使用更新函数:

bool updateNeeded; 
double getNewValue(); 
class MyResource {
mutable double someData = 0.0;
void update() const {
// Because someData is mutable, this works fine
if(updateNeeded()) {
someData = getNewValue(); 
}
}
public:
double& getData() { 
update(); 
return someData;
}
double const& getData() const {
update();
return someData; 
}
};

当你有一个像const double& data() const这样的成员函数时,data调用的所有函数也必须是 const 的,并且不能修改对象。对于那些偶然发现这一点的人:"const"在类的函数声明中最后的含义?


有一些方法可以解决这个问题:

  1. 您可以将void calcData()更改为void calcData() const这将告诉编译器计算方法是常量。这在这里不起作用,因为您正在分配结果,因此它是非常量(它修改成员变量),因此编译器会对您大喊大叫。

  2. 您可以将double const& getData() const更改为double const& getData()。但是,如果您有时需要A,这将不起作用。

  3. 另一个解决方案是

    class A {
    bool isUpToDate=false;
    double someData=0;
    double& data() 
    {
    if(!isUpToDate) 
    {
    // run the calculation only if data is not up-to-date
    someData = doSomeCalc();
    isUpToDate = true;
    }
    return someData;
    }
    double data() const // Don't change anything, just return by value
    {
    if(!isUpToDate)
    {
    return doSomeCalc();
    }
    return someData;
    }
    }
    

这将按值返回并重新计算所有内容。缺点是,如果doSomeCalc()密集,则性能可能会很差,因为它每次都需要调用它并且无法更新someData

  1. 其他答案已经提到使用mutable限定符来表示someData(和isUpToDate)。这将正常工作,但有些人可能反对使用可变关键字,因为它们通常用于特定目的,如果处理不当可能会很危险。