在C++中通过重载运算符[]区分读和写的操作

3

我有一个安全类,其中包含一个预测数组 - 预测是一个只包含double的类。 我想允许更改双倍值,但仅允许正值, 并且在尝试读取双倍值时,如果该值未初始化(在我的代码中等于-1),则抛出异常。 我还有一个运算符double in
大致如此:

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;

我该在哪里区分读写,因为在读和写时我会做不同的事情。

谢谢

4个回答

3

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;
}

2
核心思想如下:不返回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;
}

演示

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


reference_proxy 应该实现 operator double() 而不是 operator double&(),否则有人可以这样做并绕过安全保护:s[0] = 4; double &d = s[0]; d = -4; // 直接更新 Prediction::d! - Remy Lebeau
@RemyLebeau:感谢您的纠正!...我有一种不好的感觉在我的胃里 :-) - davidhigh

1
使用转换运算符和转换构造函数的组合可以实现这种行为。以下示例代码应该可以让您了解如何实现您的类:
class Foo
{
    int value;
public:
    Foo() { value = -1; }
    Foo(int value) {
        if (value < 0) cout << "error\n"; else { cout << "ok\n";  this->value = value; }
    }
    operator int() { if (value < 0) cout << "error\n"; 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

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;
  1. 在返回语句之前,在 Prediction::operator double()Prediction::operator=(const double value) 上添加正值检查;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接