Некоторые особенности перегрузки операций



При реализации операций-функций, особенно для классов, член-данные которых размещаются в динамической памяти, полезно проанализировать порядок вызовов конструкторов, конструкторов копирования и деструкторов.

С этой целью, во-первых, добавим в класс собственный конструктор копирования исключительно для того, чтобы отследить момент его вызова. Во-вторых, добавим в конструкторы и деструктор макросы 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;


Дата добавления: 2018-02-18; просмотров: 466; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!