When implementing operator[] how should I include bounds checking?

前端 未结 13 1942
清酒与你
清酒与你 2021-01-19 06:12

First of all I apologize for the long lead up to such a simplistic question.

I am implementing a class which serves as a very long 1 dimensional index on a space fil

相关标签:
13条回答
  • 2021-01-19 07:04

    If what you need is some sort of "circular" array of points, then your solution is ok. However, to me, it looks just like hiding missusage of indexing operator behind some "safe" logic, so I would be against your proposed solution.

    If don't want to allow index overflow, then you could check and throw an exception.

    quint16 curvePoint::operator[](size_t index)
    {
        if( index >= dimensions)
        {
           throw std::overflow_error();
        }
        return point[ index ];
    }
    

    If you want to have less overhead, you could avoid exception, by using debug time assertions (assume that the provided index is always valid):

    quint16 curvePoint::operator[](size_t index)
    {
        assert( index < dimensions);
        return point[ index ];
    }
    

    However, I suggest that, instead of using point and dimension members, use a std::vector< quint16> for point storage. It already has an index based access that you could use:

    quint16 curvePoint::operator[](size_t index)
    {
        // points is declared as std::vector< quint16> points;
        return points[ index ];
    }
    
    0 讨论(0)
  • 2021-01-19 07:04

    The modulo operator works surprisingly well for array indices -- it also implements negative indices (ie. point[-3] = point[dimensions - 3]). This is easy to work with, so I'd personally recommend the modulo operator as long as it's well-documented.

    0 讨论(0)
  • 2021-01-19 07:05

    Anyways in the implementation of operator[] I was wondering what the best method to achieve bounds checking is. I want to avoid throwing exceptions if at all possible, and the full range of values is usable for each number in the array so a special value to return in case of an out of bounds error is not possible either;

    Then the remaining options are:

    • Flexible design. What you did. "Fix" the invalid input so that it tries to do something which makes sense. Advantage: Function won't crash. Disadvantage: Clueless callers who access an out of bounds element will get a lie as a result. Imagine a 10-floor building with floors 1 to 10:

    You: "Who lives in the 3rd floor?"

    Me: "Mary".

    You: "Who lives in the 9th floor?"

    Me: "Joe".

    You: "Who lives in the 1,203rd floor?"

    Me: (Wait... 1,203 % 10 = 3...) > "Mary".

    You: "Wow, Mary must enjoy great views from up there. So she owns two apartments then?"

    • A bool output parameter indicates success or failure. This option usually ends up in not very usable code. Many users will ignore the return code. You are still left with what you return in the other return value.

    • Design by Contract. Assert that the caller is within bounds. (For a practical approach in C++, see An exception or a bug? by Miro Samek or Simple Support for Design by Contract in C++ by Pedro Guerreiro.)

    • Return a System.Nullable<quint16>. Oops, wait, this is not C#. Well, you could return a pointer to a quint16. This of course has lots of implications which I shall not discuss here and which probably make this option not usable.

    My favorite choices are:

    • For the public interface of a publicly released library: Input will be checked and an exception will be thrown. You ruled out this option, so it is not an option for you. It is still my choice for the interface of a publicly released library.
    • For internal code: Design by contract.
    0 讨论(0)
  • 2021-01-19 07:06

    The best method to achieve bounds checking would be to add an assert.

    quint16 curvePoint::operator[](size_t index)
    {
        assert(index < dimensions);
        return point[index];
    }
    

    If your code already depends on Boost libraries, you might want to use BOOST_ASSERT instead.

    0 讨论(0)
  • 2021-01-19 07:06

    Thanks to the comment on the C# feature in the post of Daniel Daranas I have managed to figure out a possible solution. As I stated in my question I am using the Qt libraries. There for I can use QVariant. QVariant can be set to an invalid state that can be checked by the function receiving it. So the code would become something like:

    QVariant curvePoint::operator[](size_t index){
        QVariant temp;
        if(index > dimensions){
            temp = QVariant(QVariant::Invalid);
        }
        else{
            temp = QVariant(point[index]);
        }
    
        return temp;
    }
    

    Of course this has the potential of inserting a bit of gnarly overhead into the function so another possibility is to use a pair template.

    std::pair<quint16, bool> curvePoint::operator[](size_t index){
        std::pair<quint16, bool> temp;
        if(index > dimensions){
            temp.second = false;
        }
        else{
            temp.second = true;
            temp.first = point[index];
        }
        return temp;
    }
    

    Or I could use a QPair, which has exactly the same functionality and would make it so that the STL need not be linked in.

    0 讨论(0)
  • 2021-01-19 07:06

    Unless I'm drastically misunderstanding something,

    return point[ index % dimensions ];
    

    is not bounds checking at all. It's returning a real value from a totally different part of the line, which will make it much harder to detect bugs.

    I would either:

    1. Throw an exception or an assertion (though you said you don't want to do so)
    2. Simply dereference point past the array in a "natural" way (i.e. just skip any internal checking). The advantage over the % is that they're more likely (though undefined is undefined) to get "weird" values and/or an access violation

    In the end, the caller is violating your pre-conditions, and you can do whatever you please. But I think these are the most reasonable options.

    Also consider what Cătălin said about incorporating built-in STL collections if that's reasonable.

    0 讨论(0)
提交回复
热议问题