Иногда возникает потребность описывать и применять к созданным программистом типам данных операции, по смыслу эквивалентные уже имеющимся в языке. Классический пример — библиотека для работы с комплексными числами. Они, как и обычные числовые типы, поддерживают арифметические операции, и естественным было бы создать для данного типа операции «плюс», «минус», «умножить», «разделить», обозначив их теми же самыми знаками операций, что и для других числовых типов. Запрет на использование определённых в языке элементов вынуждает создавать множество функций с именами вида ComplexPlusComplex, IntegerPlusComplex, ComplexMinusFloat и так далее.
Когда одинаковые по смыслу операции применяются к операндам различных типов, их вынужденно приходится называть по-разному. Невозможность применять для разных типов функции с одним именем приводит к необходимости выдумывать различные имена для одного и того же, что создаёт путаницу, а может и приводить к ошибкам. Например, в классическом языке Си существует два варианта стандартной библиотечной функции нахождения модуля числа: abs() и fabs() — первый предназначен для целого аргумента, второй — для вещественного. Такое положение, в сочетании со слабым контролем типов Си, может привести к труднообнаруживаемой ошибке: если программист напишет в вычислении abs(x), где x — вещественная переменная, то некоторые компиляторы без предупреждений сгенерируют код, который будет преобразовывать x к целому путём отбрасывания дробной части и вычислять модуль от полученного целого числа!
Отчасти проблема решается средствами объектного программирования — когда новые типы данных объявляются как классы, операции над ними могут быть оформлены как методы классов, в том числе и одноимённые (поскольку методы разных классов не обязаны иметь различные имена), но, во-первых, оформление подобным образом операций над значениями разных типов неудобно, а во-вторых, это не решает проблему создания новых операторов.
Средства, позволяющие расширять язык, дополнять его новыми операциями и синтаксическими конструкциями (а перегрузка операций является одним из таких средств, наряду с объектами, макрокомандами, функционалами, замыканиями) превращают его уже в метаязык — средство описания языков, ориентированных на конкретные задачи. С его помощью можно для каждой конкретной задачи построить языковое расширение, наиболее ей соответствующее, которое позволит описывать её решение в наиболее естественной, понятной и простой форме. Например, в приложении к перегрузке операций: создание библиотеки сложных математических типов (векторы, матрицы) и описание операций с ними в естественной, «математической» форме, создаёт «язык для векторных операций», в котором сложность вычислений скрыта, и возможно описывать решение задач в терминах векторных и матричных операций, концентрируясь на сути задачи, а не на технике. Именно из этих соображений подобные средства были в своё время включены в язык Алгол-68.
Чтобы разрешить существование нескольких одноимённых операций, достаточно ввести в язык правило, согласно которому операция (процедура, функция или оператор) опознаются компилятором не только по имени (обозначению), но и по типам их параметров. Таким образом, abs(i), где i объявлено как целое, и abs(x), где x объявлено как вещественное — это две разные операции. Принципиально в обеспечении именно такой трактовки нет никаких сложностей.
Чтобы дать возможность определять и переопределять операции, необходимо ввести в язык соответствующие синтаксические конструкции. Вариантов их может быть достаточно много, но по сути они ничем друг от друга не отличаются, достаточно помнить, что запись вида «<операнд1> <знакОперации> <операнд2>» принципиально аналогична вызову функции «<знакОперации>(<операнд1>,<операнд2>)». Достаточно разрешить программисту описывать поведение операторов в виде функций — и проблема описания решена.
2.1 Постановка задачи
Задача заключается в создании динамического класса для работы с матрицами.
Чтение матриц происходит из файлов в котором они находятся, и после решений все полученные результаты выводятся в другой файл.
Интерфейс.
2.2 Средства разработки
В качестве средства разработки выбран MSVisualStudio 2008 Express.
2.3 Описание процесса компиляции и запуска программы
Для компиляции программы используется IDEMSVisualStudio. В папке с проектом должны присутствовать файлы:file1.txt, file2.txt, file3.txt
Скомпиллированная программа состоит из следующих файлов:
1) папка cnf (конфигурационные файлы);
2) matrix.exe (исполняемый файл);
3. Реализация
Программа содержит компоненты, отвечающие за:
1) интерфейс,
2) математическую логику,
3) взаимодействие объектов класса,
4) перегрузку операторов.
В ходе выполнения курсовой работы была получена работоспособная программа, удовлетворяющая начальному заданию.
В ходе разработке были проанализированы и использованы следующие технологии:
1) Stl;
2) потоков данных;
3) перегрузка операторов;
В качестве дальнейшего совершенствования программы представляется возможным увеличение функциональности класса и интерфейса (с целью увеличения информативности).
Литература
1. Свободная энциклопедия http://ru.wikipedia.org/
2. Книга У. Форд, У. Топп «Структура данных в С++» ООО «Бином-Пресс»2006г.
3. Беллман Р. Введение в теорию матриц. - М.: Мир, 1969.
4. Курош А.Г.Курс высшей алгебры: Учебник для вузов 15-е изд., стереотип. - М.: Лань, 2006. - 432 с.
5. Дж. Голуб, Ч. Ван Лоун Матричные вычисления. - М.: Мир, 1999.
6. Сайт «Знакомимся с вектором». http://www.cyberguru.ru/
7. Б. Страуструп. «Язык программирования C++. Специальное издание.», 2004 г.
Приложение. Исходный код программы. Заголовочные файлы.
1. matrix.h
#pragma once
#include <list>
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
using std::istream;
using std::ostream;
class _matrix
{
private:
vector< vector<float> > vvf;
int stroka;
int stolbec;
public:
_matrix() {};
_matrix(int str, int stolb)
{
stroka = str;
stolbec = stolb;
vvf.resize(stroka, vector<float>(stolbec));
}
_matrix (const _matrix &obj)
{
stroka = obj.stroka;
stolbec = obj.stolbec;
vvf = obj.vvf;
}
~_matrix()
{
vvf.clear();
}
_matrix& operator+(_matrix &obj2)
{
_matrix* obj = new _matrix(*this);
for(int y = 0; y < obj2.stroka; y++)
for(int x = 0; x < obj2.stolbec; x++)
(*obj)(y, x) = (*this)(y, x) + obj2(y, x);
return *obj;
}
_matrix& operator-(_matrix &obj2)
{
_matrix* obj = new _matrix(*this);
for(int y = 0; y < obj2.stroka; y++)
for(int x = 0; x < obj2.stolbec; x++)
(*obj)(y, x) = (*this)(y, x) - obj2(y, x);
return *obj;
}
_matrix &operator*(_matrix &obj2)
{
_matrix* obj = new _matrix(*this);
for(int y = 0; y < obj->stroka; y++)
for(int x = 0; x < obj->stolbec; x++)
{
(*obj)(y, x) = 0;
for(int k = 0; k < obj->stroka; k++)
(*obj)(y, x) += obj2(k,x) * (*this)(y, k);
}
return *obj;
}
_matrix &operator=(const _matrix &obj)
{
stroka = obj.stroka;
stolbec = obj.stolbec;
vvf = obj.vvf;
return *this;
}
float &operator()(int& i, int& j)
{
return vvf[i][j];
}
_matrix &transpon()
{
_matrix transobj(*this);
for(int y = 0; y < stroka; y++)
for(int x = 0; x < stolbec; x++)
(*this)(y, x) = transobj(x, y);
return *this;
}
vector <float> multvec(int str, float value)
{
vector<float> temp(stroka);
for(int x = 0; x < stroka; x++)
{
temp[x] = value * vvf[str][x];
}
return temp;
}
void norm(int str, float value)
{
for(int x = 0; x < stroka; x++)
{
vvf[str][x] = vvf[str][x]/value;
}
}
void subvec(int str,vector<float> temp)
{
for(int x = 0; x < stroka; x++)
{
vvf[str][x] = vvf[str][x] - temp[x];
}
}
_matrix Inversion()
{
_matrix obj(*this);
_matrix invobj(stroka, stolbec);
float f;
for(int y = 0; y < stroka; y++)
invobj(y,y) = 1;
for(int x = 0; x < stolbec; x++)
{
for(int y = 1; y < stroka; y++)
{
if(x < y)
{
f =obj(y, x)/obj(x,x);
obj.subvec(y, obj.multvec(x, f));
invobj.subvec(y, invobj.multvec(x, f));
}
}
}
for(int x = stolbec-1; x > -1; x--)
{
for(int y = stroka-1; y > -1; y--)
{
f = obj(x,x);
obj.norm(x, f);//cout << obj;
invobj.norm(x, f);
if(x > y)
{
f =obj(y, x)/obj(x,x);
obj.subvec(y, obj.multvec(x, f));
invobj.subvec(y, invobj.multvec(x, f));
}
}
}
cout << obj;
cout << invobj;
return invobj;
}
friend ostream &operator<<(ostream &stream, _matrix &obj);
friend istream &operator>>(istream &stream, _matrix &obj);
};
ostream &operator<<(ostream &stream, _matrix &obj)
{
for(int y = 0; y < obj.stroka; y++)
{
for(int x = 0; x < obj.stolbec; x++)
{
stream << obj(y, x) << " ";
}
stream << "\n";
}
return stream;
}
istream &operator>>(istream &stream, _matrix &obj)
{
for(int y = 0; y < obj.stroka; y++)
for(int x = 0; x < obj.stolbec; x++)
stream >> obj(y, x);
return stream;
}
Приложение 2. Исходный код программы. Файлы cpp.
1. main.cpp
#include <iostream>
#include <conio.h>
#include <fstream>
#include "matrix.h"
using std::cout;
using std::ofstream;
using std::ifstream;
using std::cin;
using std::endl;
int primer1(_matrix &, _matrix &);
int primer2(_matrix &);
int primer1(_matrix &_objA, _matrix &_objB)
{
ofstream fout1;
_matrix _objC;
fout1.open("file3.txt");
if(!fout1)
{
cout << "Error with open output file" << endl;
exit(1);
}
_objC = _objA + _objB;
fout1 << "Сумма: \n";
fout1 << _objC;
_objC = _objA - _objB;
fout1 << "Разность: \n";
fout1 << _objC;
_objC = _objA * _objB;
fout1 << "Произведение: \n";
fout1 << _objC;
return 0;
}
int primer2(_matrix &_objA)
{
ofstream fout1;
fout1.open("file3.txt");
_matrix _objC;
if(!fout1)
{
cout << "nevozmojno open file3" << endl;
exit(1);
}
_objC = _objA.Inversion();
fout1 << "matrix Inversion \n";
fout1 << _objC;
_objC = _objA * _objC;
fout1 << "matrix * matrix.Inversion = \n";
fout1.precision(3);
fout1 << "%f" <<_objC;
return 0;
}
bool freadMatrix(_matrix& mtr, char* fileName)
{
ifstream fin;
fin.open(fileName);
if(!fin) return false;
fin>>mtr;
fin.close();
return true;
}
int menu()
{
int primer;
_matrix objA(5, 5);
_matrix objB(5, 5);
//чтение файлов
if(!freadMatrix(objA, "file1.txt") || !freadMatrix(objB, "file2.txt"))
{
cout << "Error with open input file" << endl;
return 1;
}
cout << "select action \n 1. + - * matrix \n 2. seatch Inversion matrix" << endl;
cin >> primer;
switch(primer)
{
case(1):
primer1(objA, objB);
break;
case(2):
primer2(objA);
break;
}
cout << "ok";
return 0;
}
int main(int argc, char** argv)
{
menu();
_getch();
return 0;
}