Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекцій по С.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
7.72 Mб
Скачать

5.7 5.8. Довизначення (overloading) операцій

Хотілося б вирахувати суму до сплати як

total = chicken + wine + pie;

Для цього потрібна позакласна

float operator+(

const MenuItem& item1, const MenuItem& item2)

{

return(item1.getPrice() + item2.getPrice() );

}

або класна

float MenuItem :: operator+(const MenuItem& item )

{

return( GetPrice() + item.GetPrice() );

}

Спробуємо розібратися, скільки довизначень оператора додавання необхідно тепер.

Зокрема ясно, як буде опрацьовано вираз

chicken + houseWine

але неясно, що робити з виразом

chicken + houseWine + applePie

Дійсно, групуючи операції зліва направо, одержимо вираз типу

<float> + <MenuItem>,

справа наліво:

<MenuItem> + <float>,

жоден з яких не відповідає сигнатурі довизначення

<MenuItem>+<MenuItem>.

Насправді можливі всі чотири випадки:

MenuItem MenuItem

MenuItem float

float MenuItem

float float

А тому повне визначення оператора додавання складатиметься з чотирьох частин:

class MenuItem

{

private:

float price;

string name;

public:

MenuItem( float, string);

MenuItem( float, char[]);

MenuItem(const MenuItem&);

float getPrice();

float operator+(const MenuItem&);

float operator+(float);

};

float MenuItem::operator+(const MenuItem& item )

{

return( getPrice() + item.getPrice() );

}

float MenuItem::operator+( float subtotal )

{

return( GetPrice() + subtotal );

}

плюс ще одна утиліта класу

float operator+( float subtotal,const MenuItem& item )

{

return( subtotal + item.getPrice() );

}

плюс стандартна операція додавання дійсних чисел.

Другий варіант полягає у визначенні перетворення MenuItem в дійсне число

MenuItem::operator float(){return price;}

Перетворення типів обходиться меншою кількістю функцій, але вимагає більшої

кількості викликів.

Асоціативність операцій може призвести до великих втрат пам'яті. Розглянемо

приклад матриць

class Matrix

{

static const int size;

double **M;

public:

//обчислення добутку з копіюванням результату

Matrix operator*(const Matrix&);

};

реалізація множення:

const int Matrix::size=10;

Matrix Matrix::operator+(const Matrix& b)

{

Matrix res;

for(int i=0; i<size; i++)

for(int j=0; j<size; j++)

{

double s=0;

for(int k=0; k<size; k++)

s+=this->M[i][k]*b.M[k][j];

res.M[i][j]=s;

}

return res;

}

Як буде виконуватися множення?

Matrix A, B, C, D;

D = (A * B) * C;

Чи не буде великих втрат ефективності при копіюванні проміжного результату

(A*B) до тимчасового об'єкту this , що активізує друге множення.

_this = A*B;

D= _this*C;

Було б ефективніше утримати проміжний результат на місці безпоседньо до його

використання в наступному множенні. Спробуємо так

class Matrix

{

static const int size;

double **M;

public:

//обчислення добутку з копіюванням результату

Matrix& operator*(const Matrix&);

};

Matrix& Matrix::operator*(const Matrix& b)

{

Matrix *res = new Matrix;

for(int i=0; i<size; i++)

for(int j=0; j<size; j++)

{

double s=0;

for(int k=0; k<size; k++)

s+=this->M[i][k]*b.M[k][j];

res->M[i][j]=s;

}

return *res;

}

Тепер з'явилася інша проблема: ніхто більше не відповідає за знищення проміжного

результату. Раніше об'єкт res знищувався при виході із функції. Проблема ефективності

іменованого результату так і залишається проблемою. З точки зору ефективності

доцільніше використовувати функції з модифікованими параметрами, а не операції

void mult(const Matrix& a, const Matrix& b, Matrix& res);

Правда тепер стане неможливим запис матричних виразів, а також можливі інші

несподіванки, наприклад, при виклику

mult(A, B, A);

або навіть

mult(A, A, A);

Це відома проблема кратних параметрів: якщо результат формується в одному

з параметрів, то параметр може спотворитися раніше, ніж його повністю використають.

Клас Matrix вимагає також довизначення присвоєння

Matrix& operator=(const Matrix&);

яке зводиться до поелементного копіювання двох масивів, розмірності яких співпадають.

Matrix& Matrix::operator=(const Matrix& b)

{

for(int i=0; i<size; i++)

for(int j=0; j<size; j++)

M[i][j]=b.M[i][j];

return *this;

}

Ось трохи складніший варіант

class string

{

private:

char *s;

short stringLength;

public:

string();

string( char *thestring );

~string();

void displayAddress();

string& operator=( const string &fromstring );

};

Тепер розміри об'єктів у лівій і правій частині присвоєння різні

string::string()

{

stringLength = 2;

s = new char[ stringLength + 1 ];

strcpy(s,"NN");

}

string::string( char *thestring )

{

stringLength = strlen( thestring );

s = new char[ stringLength + 1 ];

strcpy( s, thestring );

}

string::~string()

{

delete [] s;

}

void string::displayAddress()

{

cout<<s<<" at address: "<<(unsigned long)s<<"\n";

}

string& string::operator=(const string &fromstring)

{

delete [] s;

stringLength = fromstring.stringLength;

s = new char[ stringLength + 1 ];

strcpy(s, fromstring.s );

return *this;

}

Ось ілюстрація того, як використовуватиметься пам’ять

int main()

{

string theSwiss("Niklaus Wirth, TFH Zurich");

string theDutchman("Edsger Wybe Dijkstra, \

University of Texas at Austin & \

Burroughs Corporation");

string anAmerican;

string aEuropean;

theSwiss.displayAddress();

theDutchman.displayAddress();

anAmerican.displayAddress();

aEuropean.displayAddress();

cout << "-----\n";

aEuropean = theSwiss;

aEuropean.displayAddress();

cout <<"-----\n";

anAmerican = aEuropean = theDutchman;

anAmerican.displayAddress();

aEuropean.displayAddress();

return 0;

}