Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 9, средняя оценка - 4.78
Ildjarn
0 / 0 / 0
Регистрация: 22.02.2013
Сообщений: 9
#1

Перегрузка двойного индексного оператора - C++

26.02.2013, 15:25. Просмотров 1384. Ответов 10
Метки нет (Все метки)

Делаю задачу из одной книги. Нужно создать класс DoubleSubscriptedArray.Как видно из названия, класс должен работать с двумерными массивами. Он включает несколько задач, связанных с перегрузкой операторов, но это не столь важно. Реализация всего этого не вызывает у меня затруднений, тем более, что в предшествующем разделе книги есть аналогичный пример для одномерного массива
Но в отличие от примера, новый класс должен работать с двумерными массивами, и обращение к элементам массива
должно производиться посредством перегруженного оператора (), в такой форме:

DoubleSubscriptedArray( row, column )

вместо:

DoubleSubscriptedArray[ row ] [ column ]

Так вот, с реализацией варианта с оператором () все понятно.
Гораздо интереснее реализовать обращение в стандартном формате [ ] [ ].
Но при потпытках это провернуть, возник вопрос: "Возможно ли это вообще сделать?".
Сам двумерный массив у меня реализуется при помощи массива указателей:

C++
1
int **ptr;
А вот мой конструктор:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DoubleSubscriptedArray::DoubleSubscriptedArray( int arrayRow, int arrayColumn )
{    
   if( arrayRow > 0) 
   {
      row = arrayRow;
      column = arrayColumn;
   }
   else
      throw invalid_argument( "Number of columns must be greater than 0" );
      
   ptr = new int*[ arrayRow ];
   
   for( int i = 0; i < arrayRow; ++i )
      ptr[ i ] = new int[ arrayColumn ];
      
   for( int a = 0; a < arrayRow; ++a ) {
      for( int b = 0; b < arrayColumn; ++b )
         ptr[ a ][ b ] = 0;
   }
}
В результате, к элементам массива можно обращаться в формате ptr[ row ][ column ].
Теперь нужно сделать так, чтобы к элементам объекта класса можно было обращаться в таком-же формате.
Надо перегружать оператор [ ]. Появляется вопрос: "Как он вообще работает?".

The result of a subscript expression e1[ e2 ] is given by: *( (e2) + (e1) )
Все понятно - адрес первого элемента + смещение, в конце разыменование. Кроме того, оператор [ ] принимает только один элемент. В случае с multiple subscripts, все происходит так-же:

expression1 [expression2] [expression3]...
Subscript expressions associate from left to right. The leftmost subscript expression, expression1[expression2], is evaluated first. The address that results from adding expression1 and expression2 forms a pointer expression; then expression3 is added to this pointer expression to form a new pointer expression, and so on until the last subscript expression has been added. The indirection operator (*) is applied after the last subscripted expression is evaluated, unless the final pointer value addresses an array type.
То есть, получается, e1[ e3 ][ e2 ] должно быть: *( (e3) + (e2) + (e1) ).
С моим массивом указателей происходит немного по-другому. Если, к примеру, ptr[ 1 ][ 2 ], происходит вот такое:

*( *( ptr + 1 ) + 2 )

Насколько я понимаю, ptr содержит адрес элемента матрицы [ 0 ][ 0 ]. К нему прибавляем значение смещения x, и получаем адрес указателя на элемент [ x ] [ 0 ]. Разыменовываем, получаем сам указатель на элемент [ x ] [ 0 ], к нему прибавляем второе смещение y и получаем адрес элемента матрицы [ x ] [ y ], разыменовываем его, и получаем уже само значение элемента в формате int. Как-то так, насколько я смог разобраться. Это работает.
Дальше "самое простое" - перегрузить соответствующим образом оператор [ ]. Для одномерного массива все просто как доска:

int &Array::operator[]( int subscript )
{
if( subscript < 0 || subscript >= size )
throw out_of_range( "Subscript out of range" );

return *(ptr + subscript );
}

Возвращаем уже готовый, разыменованный указатель, значение int, lvalue. Здесь не массив указателей, просто указатель *ptr.
С двумерным так сделать не получится. Во-первых, оператор [] принимает только один параметр. Значит, передать туда адреса двух смещений сразу не получится. Надо как-то разделять, что-то запоминать. К примеру, ptr[ 1 ][ 2 ], при первом вызове вычислит ptr[ 1 ], запомнит его, а при втором вызове прибавит [ 2 ], и возвратит результат. Все бы хорошо, но тип возвращаемого значения, как не крути, должен оказаться в конце int &. Получается, что при первом вызове возвращается значение типа int, а дальше попытка вычислить выражение типа int[int], что приводит к ошибке:

invalid types `int[int]' for array subscript

То есть, каскадирование не работает. И ничего придумать не получится. Изменить тип возвращаемого значения на какой-нибудь DoubleSubscriptedArray &? Но в конце-концов все равно должен быть int &, то есть значение элемента по заданному адресу, а не объект. Да и как вообще это все будет работать.
То есть, получается, это невозможно реализовать? Как тогда [][] работает со стандартными массивами? Вероятно, так как в англоязычном примере, приводимом мной выше. Во время написания поста у меня появилась мысль, на счет неправильности использования массива указателей, но какая альтернатива?
Буду очень благодарен за любую помощь. Не знаю, насколько понятно удалось это все описать, надеюсь что достаточно подробно.
Да, и еще: я не совсем уверен, что это тема для новичков. Прав создания тем в разделе для экспертов у меня нету. Возможно, модератор согласился-бы перенести эту тему туда? Мне кажется, там ей место.
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.02.2013, 15:25
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Перегрузка двойного индексного оператора (C++):

Перегрузка оператора << - C++
Добрый пень! подскажите как написать код для реалилизации следующего требования: SpecialPrint &lt;&lt; &quot;text&quot; &lt;&lt; 'a' &lt;&lt; 1; ...

Перегрузка оператора [] - C++
Всем привет! Как можно перегрузить оператор так, чтобы к нему можно было обращаться так: class_arr = 5; Где class_arr - переменная...

Перегрузка оператора = - C++
StringOne&amp; StringOne::operator=(char *s) { char b; strcpy(b, s); StringOne a = b; return a;//возвращает тоже самое почемуто...

перегрузка оператора - C++
Как перегрузить оператор , чтобы его можно было использовать для операций присваивания новых значений определённого члена массива? покажите...

Перегрузка оператора = - C++
Допустим я имею две перегрузки: Zakaz&amp; operator=(Zakaz&amp;); Zakaz operator=(Zakaz); Когда какая из них системой будет...

Перегрузка оператора = - C++
Здравствуйте! При попытке заменить объект пользовательского класса q4 на q6 в контейнере set появляется ошибка: error C2678: binary '=' :...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
CheshireCat
Эксперт С++
2892 / 1241 / 78
Регистрация: 27.05.2008
Сообщений: 3,370
26.02.2013, 15:36 #2
Ключевой кусок кода:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
template <class T>
class Matrix
{
    // здесь детали реализации матрицы...........
    
    // это позволит обращаться к матрице: m[i][k] = ...;
    template<class U>
    class Helper
    {
        template<typename U> class Matrix;
 
        Matrix<U>&   matrix;
        unsigned int i;
 
        Helper(Matrix<U>& _mc, unsigned int _i): matrix(_mc), i(_i) {};
        Helper(const Matrix<U>& _mc, unsigned int _i): matrix(const_cast<Matrix&>(_mc)), i(_i) {};
        Helper(const Helper&);
        Helper& operator=(const Helper&);
 
        friend class Matrix<U>;
 
    public:
        U& operator[](unsigned int k) const
        {
            assert(i < matrix.rows);
            assert(k < matrix.cols);
            if (i >= matrix.rows || k >= matrix.cols)
                throw std::range_error("Range error in Matrix::Helper::operator[]() const");
 
            return matrix.data[matrix.cols * i + k];
        }
 
        U& operator[](unsigned int k)
        {
            assert(i < matrix.rows);
            assert(k < matrix.cols);
            if (i >= matrix.rows || k >= matrix.cols)
                throw std::range_error("Range error in Matrix::Helper::operator[]()");
 
            return matrix.data[matrix.cols * i + k];
        }
    };
 
    Helper<T> operator[](unsigned int i) const
    {
        assert(i < rows);
        if (i >= rows)
            throw std::range_error("Range error in Matrix::operator[]() const");
 
        return Helper<T>(*this, i);
    }
 
    Helper<T> operator[](unsigned int i)
    {
        assert(i < rows);
        if (i >= rows)
            throw std::range_error("Range error in Matrix::operator[]()");
 
        return Helper<T>(*this, i);
    }
};
1
Ildjarn
0 / 0 / 0
Регистрация: 22.02.2013
Сообщений: 9
26.02.2013, 15:39  [ТС] #3
интересно, вечером разберусь. думаю, это немного выходит за рамки рассматриваемого раздела книги, но попробую прикрутить к своему классу.
0
HighPredator
5542 / 1848 / 345
Регистрация: 10.12.2010
Сообщений: 5,455
Записей в блоге: 2
26.02.2013, 17:38 #4
Для вашего случая рискну предложить такой вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
//...
  int **ptr;
  //...
public:
  int *operator [](unsigned int indx);
  //...
};
 
int *ClassName::operator [](unsigned int indx)
{
  return ptr[indx];
}
0
Avazart
Эксперт С++
7191 / 5365 / 280
Регистрация: 10.12.2010
Сообщений: 23,673
Записей в блоге: 17
26.02.2013, 17:49 #5
Перегрузка оператора индексации
2
Ildjarn
0 / 0 / 0
Регистрация: 22.02.2013
Сообщений: 9
26.02.2013, 22:06  [ТС] #6
Цитата Сообщение от HighPredator Посмотреть сообщение
Для вашего случая рискну предложить такой вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
//...
  int **ptr;
  //...
public:
  int *operator [](unsigned int indx);
  //...
};
 
int *ClassName::operator [](unsigned int indx)
{
  return ptr[indx];
}
конечно же не работает возвращаемый тип - int *. Возвращается указатель, содержащий адресс строки [indx], а потом ошибка invalid types `int[int]' for array subscript. Да и в конце должен быть разыменованный указатель.

Добавлено через 1 минуту
и что, получается, что без всяких дополнительных средств и нельзя ничего придумать, а то что мы можем использовать нотацию [][] с базовыми типами - подарок судьбы?

Добавлено через 58 секунд
Вариант CheshireCat очень туманен и непонятен, не могу разобраться.
0
Jupiter
Каратель
Эксперт С++
6554 / 3975 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
26.02.2013, 22:13 #7
Цитата Сообщение от Ildjarn Посмотреть сообщение
конечно же не работает возвращаемый тип - int *. Возвращается указатель, содержащий адресс строки [indx], а потом ошибка invalid types `int[int]' for array subscript. Да и в конце должен быть разыменованный указатель.
где код?

Цитата Сообщение от Ildjarn Посмотреть сообщение
Вариант CheshireCat очень туманен и непонятен, не могу разобраться.
что неясно?
0
Ildjarn
0 / 0 / 0
Регистрация: 22.02.2013
Сообщений: 9
26.02.2013, 22:40  [ТС] #8
C++
1
template<typename U> class Matrix;
что это такое, зачем оно?

И как оно будет работать на практике, какой принцип - в двух словах? Обработка выражения типа array[x][y] - как я понимаю, сначало, при обработке array[x], должно вернуть проинициализорованный объект хелпера, в переменной

C++
1
unsigned int i;
будет значение чмещения [x]. Далее уже будет производиться операция с перегруженным оператором [] объекта класса Helper, с [y] в качестве аргумента? В результате - возвращается нужный элелемент. Я правильно понимаю? Такой принцип?
Только что разобрался
0
Jupiter
Каратель
Эксперт С++
6554 / 3975 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
26.02.2013, 22:42 #9
Цитата Сообщение от Ildjarn Посмотреть сообщение
что это такое, зачем оно?
шаблоны с++
при желании можете убрать и вместо U подставить свой тип
Цитата Сообщение от Ildjarn Посмотреть сообщение
Я правильно понимаю? Такой принцип?
все верно
1
Ildjarn
0 / 0 / 0
Регистрация: 22.02.2013
Сообщений: 9
26.02.2013, 22:56  [ТС] #10
там ведь определение класса должно быть вроде.
ну ничего, главное что основной принцип понял.
да, с двумерными должно сработать, но если уже 3d и больше - надо будет переделывать.
0
Avazart
Эксперт С++
7191 / 5365 / 280
Регистрация: 10.12.2010
Сообщений: 23,673
Записей в блоге: 17
27.02.2013, 00:34 #11
Цитата Сообщение от Ildjarn Посмотреть сообщение
и что, получается, что без всяких дополнительных средств и нельзя ничего придумать, а то что мы можем использовать нотацию [][] с базовыми типами - подарок судьбы?
Подарок разработчиков языка...
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
27.02.2013, 00:34
Привет! Вот еще темы с ответами:

Перегрузка оператора *= - C++
как для заданного класса point перегрузить оператор *= всеми возможными способами (член класса, друг класса)? P.S. оператор *= должен...

Перегрузка оператора == - C++
Я уже встречала подобный вопрос на этом форуме, но у меня всё равно что-то не получается Вот виртуальная ф-ция, которую я вызываю в...

Перегрузка оператора - C++
Не могу разобратся с перегрузками, помогите пожалуйста ребята Дать определение перегрузке функции и шаблона. 2. Написать алгоритм...

Перегрузка оператора += - C++
Есть два класса: Vec4(вектор из 4х float) и Mat4(вектор из 4х Vec4). Проблема в том что при перегрузке в Mat4 операции += я получаю ошибку...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
27.02.2013, 00:34
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru