Using namespace std; // Добавить
Class CMatrix { // };
Допустим, требуется выполнять операции над прямоугольной матрицей размером NxM, где N – число строк, а М – число столбцов. Тогда выделить память для этой матрицы можно так: double **Mtr; Mtr=new double * [N]; for(int i=0;i<N;i++) Mtr[i]= new double [M];
Соответственно, освободить память можно так: for(int i=0;i<N;i++) delete [] Mtr[i]; delete []Mtr;
Можно, элементарно, выделить память и для треугольной матрицы (в первой строке N столбцов, а в последней – 1): double **Mtr; Mtr=new double * [N]; for(int i=0;i<N;i++) Mtr[i]= new double [N-i];
Реализация перегруженной операции доступа по индексу [] для вектора вам известна, а как ее реализовать для матрицы как двумерного массива? Операции [][] в языке С++ не существует! Как же быть, если требуется передать два индекса (строка и столбец) посредством одного параметра? Да например так: int Index=i/*строка*/*1000+j/*столбец*/; // "Упаковка индексов" int i=Index/1000,j=Index%1000; // "Распаковка индексов"
Еще можно номер строки хранить в двух старших байтах переменной типа int, а номер столбца – в двух младших. Достоинством такого подхода является возможность контроля номера строки и номера столбца, т.е. индексов двумерного массива. Заметим, однако, что можно перегрузить операцию [] и таким образом: double * CMatrix::operator [](int Index) { return Mtr[Index]; }
В этом случае выражение Obj[i] возвращает указатель на i-тую строку матрицы, а Obj[i][j] – значение двумерного массива в i-той строке и j-том столбце. Другими словами, имеет место тождество Obj[i][j] == *(Obj[i]+j). Сказанное относится не только к двумерным массивам, а и к многомерным. Однако при таком подходе сложнее контролировать корректность индексов. Некоторые особенности перегрузки операций При реализации операций-функций, особенно для классов, член-данные которых размещаются в динамической памяти, полезно проанализировать порядок вызовов конструкторов, конструкторов копирования и деструкторов.
С этой целью, во-первых, добавим в класс собственный конструктор копирования исключительно для того, чтобы отследить момент его вызова. Во-вторых, добавим в конструкторы и деструктор макросы TRACE, с помощью которых будем отслеживать вызовы конструкторов и деструктора. Рассмотрим сначала класс комплексных чисел: Class Complex { double Re,Im; char * ObjName; // для хранения имени объекта public: Complex(double iRe=0, double iIm=0, char * ObjName="") { Re=iRe; Im=iIm; this->ObjName=ObjName; TRACE("Complex ObjName=%s this=%d\n",ObjName,(int)this); } ~Complex() { if((!ObjName) || ((int)ObjName==0xcccccccc)) ObjName="Bad_Ptr"; TRACE("~Complex ObjName=%s this=%d\n",ObjName,(int)this); } Complex(const Complex & Org) { TRACE("CopyCTR Org.ObjName=%s this=%d\n",Org.ObjName,(int)this); Re=Org.Re; Im=Org.Im; ObjName=Org.ObjName; } Complex & operator=(const Complex & Org) { TRACE("operator= Org.ObjName=%s this=%d\n",Org.ObjName,(int)this); Re=Org.Re; Im=Org.Im; return *this; } Complex operator +(Complex &X) { return Complex(Re+X.Re,Im+X.Im,"Return operator+"); } Complex operator -(Complex X); Complex operator -=(Complex X) { Re-=X.Re; Im-=X.Im; return *this;} friend Complex operator *(Complex X,Complex Y); friend ostream & operator <<(ostream &Out, Complex X) { return Out<< '('<<X.Re<<"+i"<<X.Im<<')';} }; Complex Complex::operator -(Complex X) {return Complex(Re-X.Re,Im-X.Im);} Complex operator *(Complex X,Complex Y) { return Complex(X.Re*Y.Re-X.Im*Y.Im, X.Re*Y.Im+Y.Re*X.Im); }
Проверка корректности значения указателя ObjName в деструкторе выполнена для того, чтобы избежать ошибки времени выполнения в некоторых сложных случаях. Теперь внимательно проанализируйте результаты выполнения следующего программного кода. В комментариях приведены результаты работы макросов TRACE. Void main() { { Complex a(1.23,4,"a"), b(2.34,5,"b"), c(0,0,"C"); //Complex ObjName=a this=1244980 //Complex ObjName=b this=1244948 //Complex ObjName=C this=1244916 c=a+b; //CopyCTR Org.ObjName=b this=1244604 //Complex ObjName=Return operator+ this=1244628 //~Complex ObjName=b this=1244604 //operator= Org.ObjName=Return operator+ this=1244824 //~Complex ObjName=Return operator+ this=1244628
//CopyCTR Org.ObjName=C this=1244624 //~Complex ObjName=C this=1244624 // Если параметр операции-функции сложения объявить ссылкой // Complex operator +(Complex &X), // то получим такую последовательность вызовов методов: //Complex ObjName=Return operator+ this=1244648 //operator= Org.ObjName=Return operator+ this=1244916 //~Complex ObjName=Return operator+ this=1244648 //CopyCTR Org.ObjName=C this=1244644 //~Complex ObjName=C this=1244644 // Если операцию-функцию присвоить объявить как // Complex & operator=(const Complex & Org) // (и при этом операцию-функцию сложения объявить как // Complex operator +(Complex &X), // то получим такую последовательность вызовов методов: //Complex ObjName=Return operator+ this=1244680 //operator= Org.ObjName=Return operator+ this=1244916 //~Complex ObjName=Return operator+ this=1244680 cout<<c<<endl; //CopyCTR Org.ObjName=C this=1244624 //~Complex ObjName=C this=1244624 } //~Complex ObjName=C this=1244916 //~Complex ObjName=b this=1244948 //~Complex ObjName=a this=1244980 } Для того чтобы отследить порядок вызова конструкторов и деструктора, необходимо выполнить трассировку программы, причем с заходом в функции. Какие можно сделать основные выводы из результатов выполнения этой программы, точнее, реализации операций-функций? Во-первых, если объект передается в функцию как параметр-значение, то его копия создается с помощью конструктора копирования. Это значит, что если реализация конструктора копирования по умолчанию нас не устраивает, то требуется разработать его корректную реализацию. Например, если среди член-данных класса есть указатели и для них выделяется динамическая память, то требуется разработать корректный конструктор копирования. Также стоит отметить, что конструктор копирования вызывается и при возврате объекта из функции с помощью оператора return. В качестве еще одного примера рассмотрим класс CArr, предназначенный для обработки одномерных массивов.
Реализация операции вычитания массивов, как и других полезных операций, отдана на ваше усмотрение. После выполнения этой работы использование объектов класса может быть реализовано следующим образом: Void main() { setlocale(LC_ALL,"rus"); srand((unsigned)time(NULL)); int N=7; CArr A(N,"A"); CArr B(N,"B"),*C; for(int i=0;i<N;i++) { double Val=(double)rand()/RAND_MAX*10; A.setElem(i,Val); Val=(double)rand()/RAND_MAX*10;
Воспользуйтесь поиском по сайту: ![]() ©2015 - 2025 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|