在 C++ 中使用运算符 [] 重载区分读取和写入

distinguish between read and write using operator [] overloading in c++

本文关键字:重载区 读取 C++ 运算符      更新时间:2023-10-16

我得到了一个安全类,它有一个预测数组 - 预测是一个类,它只包含一个双精度。我想允许更改双精度值,但只允许正值,当尝试读取双精度时,如果值未初始化(在我的代码中等于 -1),则抛出异常。我也有双运算符
像这样:

class Prediction{
    double value;
public:
    .....
    Prediction::operator double() const {
        return this->prediction;
    }
    Prediction::operator=(const double value){
       ...
        //check value
    }
}
class Security{
   ...
    Prediction& Security::operator[](int index){
        return predArray[index];
    }
}
Prediction *predArray = new Prediction[4];
//default constructor set the value -1;
double a = predArray[0] //should throw an exception, because predArray[0] = -1
predArray[0] = 4; //should be O.K. because I want to change the value
predArray[1] = -4; //should throw exception, because trying to put negative value;

我在哪里定义阅读和写作,因为我在阅读和写作时做不同的事情。

谢谢

你不能在operator[]中这样做。运算符无法知道它将返回的值将如何使用。因此,您必须将其作为返回对象的函数来执行。您可以通过抛出返回对象的赋值运算符来轻松处理负值的赋值。

Prediction::operator=(const double value){
    if (value < 0)
        throw something;
    ...
}

如果您希望此语句抛出:

double a = predArray[0];

您必须在转换为双精度运算符时执行此操作。

Prediction::operator double() const {
    if (value < 0)
        throw something;
    return value;
}

使用转换运算符和转换构造函数的组合,可以获得此行为。 此示例代码应让您了解需要如何实现类:

class Foo
{
    int value;
public:
    Foo() { value = -1; }
    Foo(int value) {
        if (value < 0) cout << "errorn"; else { cout << "okn";  this->value = value; }
    }
    operator int() { if (value < 0) cout << "errorn"; else return value; }
};
class Bar
{
    Foo * fooArray;
public:
    Bar() { fooArray = new Foo[4]; }
    Foo & operator [](int i) { return fooArray[i]; }
};
int main()
{
    Bar test;
    int foobar = test[0];
    test[1] = 4;
    test[2] = -4;
    cin.get();
    return 0;
}

输出:

error
ok
error

中心思想如下:与其返回double&,不如返回带有重载operator=(以及其他所有必要的)的代理。然后,代理执行检查。

struct reference_proxy
{
    reference_proxy(double &_d) : d(_d) {}
    reference_proxy& operator=(double rhs)  //operator= throws when rhs<0
    {
        if(rhs<0.0)
        {
            std::cout<<"throw exception"<<std::endl;
        }
        else
        {
            std::cout<<"ok"<<std::endl;
            d = rhs;
        }
        return *this;
    }
    operator double ()  //cast to double gives an error when uninitialized
    {
        if(d<0.0)
        {
            std::cout<<"throw exception"<<std::endl;
        }
        return d;
    }
    // add further required functions like operator+= etc.
private:
    double& d;
};

然后,您可以在其他类中使用它:

struct Prediction
{
     operator double& () { return d; }
     double d = -1.0;
};
struct Security
{
    template<typename ... Args>
    Security(Args&& ... args) : v(std::forward<Args>(args) ...) {}
     auto operator[](int i)
     {
         return reference_proxy(v[i]);
     }
    std::vector<Prediction> v;
};

应用:

int main()
{
    Security s(10);   
    double a = s[0]; //prints "throw exception"
    s[0] = 4;        //prints "ok"
    s[1] = -4;       //prints "throw exception"
    return 0;
}

演示。

请注意,此方案也可用于更复杂的操作。例如:通知观察者模式中的依赖类。

几点

  1. predArray 数组应该是 Security 的成员(未在示例代码中显示)

  2. 通过 [] 运算符进行的索引访问应该在 Security 实例上,而不是 predArray 上。变量 predArray 是对象的原始数组,而不是保存数组的对象。

例如:

Security o = new Security();
double a = o[0] //should throw an exception, because predArray[0] = -1
o[0] = 4; //should be O.K. because I want to change the value
o[1] = -4; //should throw exception, because trying to put negative value;
    在 Prediction::operator double()
  1. Prediction::operator=(const double 值) 上添加正值检查,在 return 语句之前;