class matrix_t
{
union
{
float p[16];
float e[4][4];
};
...
float &operator ()(const int i, const int j)
{
return e[j];
}
float operator ()(const int i, const int j) const
{
return e[j];
}
...
}
Двумерные массивы на С++
(матрица), как объявить operator[], чтобы он принимал два параметра и
возвращал нужный елемент?
P.S. operator[] (int,int) не катит - ... must have one parameter...
А принципиально использовать []?
При перегрузке оператора Вы не можете менять колическво параметров).
Абсолютно справедливо. Жалко только, что красивая идея [i, j] переходит в банальное [j]... :)
оператор запятая может быть перегружен;)
Но можно сделать так, что array [j] будет реализован как array.operator[] (i).operator[] (j)
Двумерный массив можно реализовать двумя способами:
1. Массив массивов - объект возвращает ссылку на другой объект, который также может трактоваться как массив.
2. Создавать временный объект с оператором [] (int Second_Index), который будет вызывать функцию чтения значения основного объекта. Я пробовал это сделать - получилось, но было использовано столько дополнительных команд :eek:
Код:
template <typename T>
class array2d
{
public:
template<class T1,class T2>
T operator[](const std::pair<T1,T2>& p)
{
std::cout<<"first="<<p.first<<" second="<<p.second<<std::endl;
return (T)0;
};
};
template <typename T>
struct index
{
template <typename T2>
std::pair<T,T2> operator,(const T2& j)
{
return std::make_pair(val,j);
};
operator T() const
{
return val;
};
index(T _val) : val(_val) {};
private:
T val;
};
int _tmain(int argc, _TCHAR* argv[])
{
array2d<float> array;
index<int> a=654;
index<bool> b=true;
index<int> c=a;
array[a,b];
array[a,890];
array[a,c];
std::cin.get();
return 0;
}
class array2d
{
public:
template<class T1,class T2>
T operator[](const std::pair<T1,T2>& p)
{
std::cout<<"first="<<p.first<<" second="<<p.second<<std::endl;
return (T)0;
};
};
template <typename T>
struct index
{
template <typename T2>
std::pair<T,T2> operator,(const T2& j)
{
return std::make_pair(val,j);
};
operator T() const
{
return val;
};
index(T _val) : val(_val) {};
private:
T val;
};
int _tmain(int argc, _TCHAR* argv[])
{
array2d<float> array;
index<int> a=654;
index<bool> b=true;
index<int> c=a;
array[a,b];
array[a,890];
array[a,c];
std::cin.get();
return 0;
}
Код:
class MyClass
{
...
MyType operator [] (const std::vector<int> &point)
{
if (point.size() != 2)
{
// Генерировать исключение
}
// Вернуть требуемый элемент
}
...
}
{
...
MyType operator [] (const std::vector<int> &point)
{
if (point.size() != 2)
{
// Генерировать исключение
}
// Вернуть требуемый элемент
}
...
}
И как, позволю себе спросить, это будет выглядеть :) ?
Код:
Item = Array [TIndex (x, y)];
Довольно таки неуклюже, но иначе не получится :(
Тогда уж лучше вместо переопределения использовать именованые методы константного и изменяемого чтения
Код:
TMyArray
{
private:
// ......
public:
TItem& Items (int Index1, int Index2); // Чтение элемента для изменения
const TItem& Items (int Index1, int Index2) const; // Чтение константного элемента
// .....
};
{
private:
// ......
public:
TItem& Items (int Index1, int Index2); // Чтение элемента для изменения
const TItem& Items (int Index1, int Index2) const; // Чтение константного элемента
// .....
};
Согласен, несколько отличается от исходных условий задачи, зато дёшево, надёжно и практично :D
Другими славами: стоит ли проблема выеденного яйца?
IMHO нет, более того, подобное смешение стилей в последствии может выйти боком.
Класс-матрица, о котором говорит автор, это (пусть в простейшем случае) vector< vector<ElementType> >. Т.о. можно вообще не заморачиваться со специальным классом.
Если дойдет до необходимости замены vector<vector> на спец.класс, созданный собственоручно или взятый из готовых мат.библиотек, то это произойдет просто и безболезнено, если изначально придерживаться концепции языка С++: индексация многомерных массивов выглядит как [j].
P.S. Для любителей паскаля можно ввести макросы:
#define BEGIN {
#define END }
Цитата:
А мне кто-нибудь может объяснить, чем не подходит С++ формат [j] и чем лучше и красивее в данном случае паскаль-формат [i,j] ?
Я могу - C++ не позволяет объявить метод объекта operator [] (int x, int y) :D. Компилятор говорит, что [] должен иметь только один параметр :(. Что есть большой недостаток.
Вот и приходится бедным людям, которые хотят, чтобы их объект выглядел как двухмерный массив, затылки чесать....
Сам я, в своё время, придумал что-то подобное этому
Код:
class TD2Array
{[INDENT]class TItem
{
friend TD2Array; // Объявляем "другом" для доступа к закрытым функциям
private:[INDENT]TD2Array *pArray;
int First;
[/INDENT]public:[INDENT]TItem (TD2Array *Owner, int Index): pArray (Owner), First (Index)
{} // Конструктор временного объекта
TResult& operator[] (int Index) // Второй оператор индексации
{return pArray->Read_Item (First, Index);}
[/INDENT]};
[/INDENT]private:
TResult& Read_Item (int First, int Second); //Закрытая функция чтения результата
public:
TItem operator[] (int Index) // Первый оператор индексации
{return TItem (this, Index);} // Возвращаем объект для обработки второго оператора индексации
};
{[INDENT]class TItem
{
friend TD2Array; // Объявляем "другом" для доступа к закрытым функциям
private:[INDENT]TD2Array *pArray;
int First;
[/INDENT]public:[INDENT]TItem (TD2Array *Owner, int Index): pArray (Owner), First (Index)
{} // Конструктор временного объекта
TResult& operator[] (int Index) // Второй оператор индексации
{return pArray->Read_Item (First, Index);}
[/INDENT]};
[/INDENT]private:
TResult& Read_Item (int First, int Second); //Закрытая функция чтения результата
public:
TItem operator[] (int Index) // Первый оператор индексации
{return TItem (this, Index);} // Возвращаем объект для обработки второго оператора индексации
};
При наличии стольких промежуточный За скорость работы программы вполне предсказуема :)
P.S.
За мелкие погрешности прошу сильно не пинать - пишу по памяти :)
Как будет выглядеть алгоритм при записи [i,j] ?
Не так ли:
1) находим i-ю строку;
2) выбираем из неё j-й элемент.
Как будет работать алгоритм при записи [j]:
1) возвращаем i-б строку;
2) возвращаем из неё j-й элемент.
Не вижу изменения в сложности (количестве операций) между двумя вариантами.
Почему бы для твоего примера не сделать так:
Код:
class TD2Array
{
public:
class TLine
{
public:
TResult& operator[] (int Index); // Второй оператор индексации
};
TLine& operator[] (int Index) // Первый оператор индексации
};
{
public:
class TLine
{
public:
TResult& operator[] (int Index); // Второй оператор индексации
};
TLine& operator[] (int Index) // Первый оператор индексации
};
Не учел того, что если operator[] вернет (Item *), то к нему можно
применить стандартный [], получится то, что надо: matrix[j]; :)
А вышеизложенные идеи интересные, но нет времени на их реализацию.
Класс-матрица, о котором говорит автор, это (пусть в простейшем случае) vector< vector<ElementType> >. Т.о. можно вообще не заморачиваться со специальным классом.[/QUOTE]
Класс должен определять математические ф-ции матрицы.
Цитата:
P.S. Для любителей паскаля можно ввести макросы:
#define BEGIN {
#define END }
Я паскаль не изучал, только сейчас в универе азы учу. Отсюда следуюций вопрос:
Нельзя ли подобным образом заменить паскалевские begin на родное '}' ?
И долбаные одинарные кавычки двойными? :) :) :)
Почему? :)
Согласен, использовать двумерный массив как массив массивов - проще всего. Просто бывают ситуации, когда всё должно быть реализовано в одном объекте. Правда, при таком условии проще всего будет создать свойство чтения/записи с двойной индексацией - это сделать очень легко.
Хотя ограничение оператора индексации одним параметром - большой недостаток C++.
Хотя ограничение оператора индексации одним параметром - большой недостаток C++.[/QUOTE]
IMHO это не недостаток, а наоборот, некая стандартизация, приведение к единому интерфейсу. И я описал это уже чуть выше по теме.
Удобно, конечно, было бы если напряжение в электросети было бы таким, как нужно большинству приборов, но пусть уж лучше будет везде одинаковым - 220В. :)
Код:
class TD2Array
{
public:
class TLine
{
public:
TResult& operator[] (int Index); // Второй оператор индексации
};
TLine& operator[] (int Index) // Первый оператор индексации
};
{
public:
class TLine
{
public:
TResult& operator[] (int Index); // Второй оператор индексации
};
TLine& operator[] (int Index) // Первый оператор индексации
};
Вынужден признать, у стандартного С++-подхода есть свои недостатки.
1. При получении ij-го элемента матрицы мы должны получать именно элемент непосредственно. Почему это должно выглядеть как получение j-го элемента i-й строки? Почему тогда не i-го элемента j-го столбца?
2. Ладно. Фиг с ним. Получаем мы элемент через строку. Т. е. мы можем обратиться одним оператором индексации и получить всю строку как объект.
А столбец?
Лично я считаю, что в определенных математических задачах "естественнее" и логичнее применить, скажем, такой подход.
1. Оператор [] выбирает непосредственно элемент матрицы и потому в качестве параметра берет пару чисел. В общем (не матричном) случае - n-мерный или даже произвольномерный вектор (к примеру, если представить в виде класса n-мерное пространство).
2. Оператором [] нелогично получать строки и столбцы матрицы. Предположим, мы перегрузили оператор [] с параметром int. Что он будет возвращать - столбец или строку?
Лучше сделать:
2A. Методы типа RowVector& row (int) и ColumnVector& column (int), которые вернут вектор-строку и вектор-столбец соответственно (в крайнем случае, опять же матрицу, содержащую соответственно одну строку или один столбец - но в таком случае придётся для получения элемента такой матрицы явно указывать единицу в параметре оператора [], а если создать специализированные объекты - вектор-строки и вектор-столбцы - для них можно перегрузить оператор [] с параметром просто int). Термины "вектор-строка" и "вектор-столбец" здесь употреблены в математическом смысле. Кто не понимает их - обращайтесь к соответствующей литературе :)
2B. Методы, возвращающие коллекцию (в терминологии С++ - контейнер) строк и столбцов, к которой уже можно обратиться оператором [] для получения конкретной строки или столбца.
Считаю, что 2А подходит больше, т. к. в математике нет такого понятия, как "коллекция строк" :) и незачем "портить" красивый математический интерфейс "непонятными" методами и дополнительными классами.
Это не недостаток С++, а недостаток структуры памяти современных ЦЭВМ,-она линейна, а сл-но n-мерные массивы эмулируются набором одномерных.
В C++ не существует реализации такого понятия, как многомерный массив.
Всё, что мы называем "многомерными" массивам - это либо указатели n-го порядка (указатель на указатель на объект в памяти), индексируемые на каждом этапе обращения по адресу, либо линейные массивы, индексируемые по следующей формуле "i1*s2+i2".
А для реализации "строк" и "столбцов" можно написать свой класс, который будет иметь подобные свойства, наподобие Cols и Rows в TStringGrid.
Называется Matrix Template Library, MTL http://osl.iu.edu/research/mtl/
class Index{
friend operator[] (const Index);
protected:
int i; int j;
public:
Index(in_i,in_j):i(in_i),j()in_j{}
}
float& operator [] (const Index index)
{
return ...;
}
//gdeto tam
FloatMas[Index(1,2)]=FloatMas[Index(1,1)];
Извращение :) . Кроме того, не соответствует условию задачи - создать объект, который будет иметь интерфейс двухмерного массива ("Array[x][y]") :(
А если "думать иначе", то проще сразу объявить свойство с двойной индексацией - реализуется без всяких "временных" объектов.
Код:
int* CMatrix::operator[] (int i)
{
// w- ширина матрицы
// **table - таблица коэффициентов матрицы
return (i < 0 || i >= w) ? NULL : table;
}
{
// w- ширина матрицы
// **table - таблица коэффициентов матрицы
return (i < 0 || i >= w) ? NULL : table;
}
то есть в результате действия оператора[] возвращается i-я строка матрицы. Ну а так как она представляет собой массив int-овый, значит к ней в свою очередь применим оператор[].
m[j] (m - объект класса matrix)
раскрывается в
(m.operator[](i)) [j];
Код:
// matrix.h
#include <memory.h>
#include <iostream>
#include <cstdio>
using namespace std;
class matrix
{
int **mas; // собственно таблица
int w, h; // ширина и высота
public:
matrix (int,int);
~matrix();
int* operator[] (int);
void show();
};
matrix::matrix(int _w, int _h) : w(_w), h(_h)
{
mas = new int*[w];
for(int i=0; i<w; ++i)
{
mas = new int[h];
memset( mas, 0, h * sizeof(int) );
}
}
matrix::~matrix()
{
for(int i=0; i<w; ++i)
delete [] mas;
delete [] mas;
}
int* matrix::operator[] (int i)
{
return (i < 0 || i >= w) ? NULL : mas;
}
void matrix::show() // демонстрационный вывод на экран
{
cout << endl;
for(int j=0; j<h; ++j)
{
for(int i=0; i<w; ++i)
{
cout << mas[j] << " ";
}
cout << endl;
}
}
#include <memory.h>
#include <iostream>
#include <cstdio>
using namespace std;
class matrix
{
int **mas; // собственно таблица
int w, h; // ширина и высота
public:
matrix (int,int);
~matrix();
int* operator[] (int);
void show();
};
matrix::matrix(int _w, int _h) : w(_w), h(_h)
{
mas = new int*[w];
for(int i=0; i<w; ++i)
{
mas = new int[h];
memset( mas, 0, h * sizeof(int) );
}
}
matrix::~matrix()
{
for(int i=0; i<w; ++i)
delete [] mas;
delete [] mas;
}
int* matrix::operator[] (int i)
{
return (i < 0 || i >= w) ? NULL : mas;
}
void matrix::show() // демонстрационный вывод на экран
{
cout << endl;
for(int j=0; j<h; ++j)
{
for(int i=0; i<w; ++i)
{
cout << mas[j] << " ";
}
cout << endl;
}
}
Код:
// main.cpp
#include "matrix.h"
void main()
{
matrix m(3,4); // матрица 3х4
m.show();
m[0][1] = 1;
m[1][2] = 3;
m[2][3] = 5;
m.show();
}
#include "matrix.h"
void main()
{
matrix m(3,4); // матрица 3х4
m.show();
m[0][1] = 1;
m[1][2] = 3;
m[2][3] = 5;
m.show();
}
нюанс: в этом примере проверка границ (bound-check) как таковая отсутствует! То есть, если написать m[10][100] = 1000, то программа рухнет ;)
Верно мыслите :)
Если писать объект "многомерный массив", то он сам должен проверять допустимость индексации для каждой размерности.
А то, что нельзя перегрузить оператор [] с несколькими параметрами - чей недостаток?
А эмуляция многомерных массивов одномерными какое имеет отношение к интерфейсу класса?
С чего бы оператор [] обязательно должен иметь дело с массивом?
А что молотком сложно шурупы закручивать, чей недостаток: молотка или шурупа?
На самом деле спрашивалось, как перегрузить оператор [] так, чтоб он принимал два параметра. Согласно спецификации языка - никак. Единственный способ так извращатся. А возвращение ссылки на объект соответствующий переданому индексу сосздает "впечатление" работы с массивом.
Тогда как
int* CMatrix::operator[] (int i)
потенциально опасно
при вызове
matrix m(3,4);
m[10][1]=5;
ксласс проверит и послушно вернет NULL для m5[5], а далее последует обращение к адресу (NULL+sizeof(int))
что будет дальше, думаю, объяснять не надо :)
С чего бы оператор [] обязательно должен иметь дело с массивом?[/QUOTE]
Конечно же, реализация данных внутри класса может быть любой. Просто здесь действует одно из основных правил синтаксиса - реализация должна соответствовать описанию.
ation
На самом деле, для объекта-массива matrix m(3,4) использование "m[10][1]=5" безопасно - просто хороший класс выдаст ERangeError :)
Куда хуже будет "m[1][10]=5" - проверка индексации у второго оператора отсутствует, посему значение запишется в случайную область памяти, которая, по закону Мерфи, будет занята архиважными для программы данными :D
Хороший класс в данной ветке не появлялся. Если планировать с умом, то возвращение указателя неприемлимо как таково. Поэтому лучше реализовать одномернывй массив. Например
template <class T>
class Massive
{
private: T* mas;
...
T& operator [] (const int index) {//проверка
return mas[index]; }
}
main()
{
Massive<Massive<int>> m(10);
//установка размеров
m[n][z]=m[z][n];
}
полностью заменяет двухмерный массив. Немножко уродливая инициализация полностью оправдывается интерфесом доступа к ячейкам, идентичным двумерному массиву.
Вот только не вижу смысла в замене стандартных средств массивов.
Собственно говоря, здесь можно просто использовать стандартный шаблон DynamicArray из Builer'а.
И конструкции типа "DynamicArray <DynamicArray <TMyStruct>>" мне использовать приходилось не раз.
Так что "америку" никто не открыл :)
Молоток для этого не предназначен. Выходит, С++ не предназначен для математических задач?
А кто сказал, что молоток в метафоре это C++ ?
Вроде бы про оператор [] говорили.
У тебя не возникает вопроса: почему в С++ нельзя определить оператор + восемью параметрами? :)
А кто сказал, что молоток в метафоре это C++ ?
Вроде бы про оператор [] говорили.
У тебя не возникает вопроса: почему в С++ нельзя определить оператор + восемью параметрами? :)
Или оператор -> десятью параметрами?
Собственно говоря, здесь можно просто использовать стандартный шаблон DynamicArray из Builer'а.
И конструкции типа "DynamicArray <DynamicArray <TMyStruct>>" мне использовать приходилось не раз.
Так что "америку" никто не открыл :)[/QUOTE]
А кто говорит что нельзя? :)
Только вот код становится зависим от компилятора. Лучше СТЛ.
Только вот код становится зависим от компилятора. Лучше СТЛ.[/QUOTE]
А разве бывают "независимые" коды :)?
Кто-то пишет программы, используя MFC, другие эксплуатируют VCL...
А DynamicArray можно переписать при особом желании либо для "независимости", или же изучения языка ради (что, собственно я как-то и сделал :D )
[/QUOTE]
А почему бы нет?
[QUOTE=el scorpio]
Кто-то пишет программы, используя MFC, другие эксплуатируют VCL...
[/QUOTE]
Только вот бизнес-логика в большинстве своем не зависит от оконных библиотек.
Вернемся к баранам...
А что если перегрузить оператор (). И представить, что это [] ;)
только вот обращение к элементу типа
SomeName m;
m(5,4)=3;
окажет сильное влияние на ум увидевшего, вплоть до полного перехода на бейсик )
А что если перегрузить оператор (). И представить, что это [] ;)
только вот обращение к элементу типа
SomeName m;
m(5,4)=3;
[/QUOTE]
А потом, через пол-года, открыть старые исходники и долго ломать голову, откуда это за функция и что она делаеть :D
Вроде бы про оператор [] говорили.
У тебя не возникает вопроса: почему в С++ нельзя определить оператор + восемью параметрами? :)[/QUOTE]
Нет. Сложение - по определению двухместная операция.
Вернемся к баранам...
А что если перегрузить оператор (). И представить, что это [] ;)
только вот обращение к элементу типа
SomeName m;
m(5,4)=3;
окажет сильное влияние на ум увидевшего, вплоть до полного перехода на бейсик )[/QUOTE]
А шо, неплохая идея, только выглядеть будет действительно немного путано