Безопасный индекс массива
От: igna Россия  
Дата: 02.05.08 13:30
Оценка: 71 (4)
В некоторых случаях проверку индекса при доступе к элементу массива можно заменить проверкой при изменении индекса. Для этого в качестве типа индекса используется специальный класс, в котором определены необходимые операции, причем те из них, которые изменяют значение индекса, проверяются; при доступе же к элементу массива разрешено использовать только объекты этого индексного класса, а проверка не производится. Вот пример:

// safe_array.hpp

#ifndef safe_array_hpp
#define safe_array_hpp

#include <cstddef>
#include <stdexcept>

template <std::size_t N>
class safe_array_index {
    std::size_t i_;
public:
    explicit safe_array_index(std::size_t i)
    {
        if (i >= N)
            throw std::out_of_range("initial value is out of range");
        i_ = i;
    }
    safe_array_index& operator=(std::size_t i)
    {
        if (i >= N)
            throw std::out_of_range("assigned value is out of range");
        i_ = i;
        return *this;
    }
    bool increment()
    {
        if (i_ == N - 1)
            return false;
        i_++;
        return true;
    }
    operator std::size_t() { return i_; }
};

template <typename T, std::size_t N>
class safe_array {
    T a_[N];
public:
    T const& operator[](safe_array_index<N> i) const
    {
        return a_[i];
    }
    T& operator[](safe_array_index<N> i)
    {
        return a_[i];
    }
};

#endif


// safe_array_test.cpp

#include "safe_array.hpp"

#include <iomanip>
#include <iostream>

int main()
{
    safe_array<int, 10> a, b, c, d;
    safe_array_index<10> i(0);
    do {
        a[i] = static_cast<int>(i);
        b[i] = a[i] * a[i];
        c[i] = b[i] * a[i];
        d[i] = c[i] * a[i];
    } while (i.increment());
    i = 0;
    do {
        std::cout
            << std::setw(1) << a[i] << ' '
            << std::setw(2) << b[i] << ' '
            << std::setw(3) << c[i] << ' '
            << std::setw(4) << d[i] << '\n';
    } while (i.increment());
}


Вместо 140 проверок при доступе к элементам массивов программа использует 22 проверки при изменении индекса, причем 20 из них практически не в счет, поскольку одновременно заменяют проверку условия окончания цикла. Оставшихся двух проверок в данном случае тоже можно избежать, добавив конструктор по умолчанию, инициализирующий индекс нулем, и функцию-член для замены присваивания нуля:

    . . .
    safe_array_index() : i_(0) {}
    void reset() { i_ = 0; }
    . . .


    . . .
    safe_array_index<10> i;
    do {
        a[i] = static_cast<int>(i);
        b[i] = a[i] * a[i];
        c[i] = b[i] * a[i];
        d[i] = c[i] * a[i];
    } while (i.increment());
    i.reset();
    . . .


(Инициализация нулем и присваивание нуля безопасны, поскольку C++ запрещает массивы нулевой длины.)


Хотя проверки во время исполнения программы не производятся, ошибки, связанные с обращением за пределы массива, исключены.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.