Note de curs, programare orientată pe obiecte, 27 aprilie 2013
Transmiterea parametrilor funcțiilor prin referință
Să se implementeze funcția swap, care interschimbă valorile a două variabile de tip int, cu următorul prototip:
void swap(int arg1, int arg2);
Se constată că implementarea următoare nu are efectul dorit.
void swap(int arg1, int arg2) { int tmp = arg1; arg1 = arg2; arg2 = tmp; } int var1 = 7; int var2 = 3; swap(var1, var2); assert(var1 == 3); // eroare! assert(var2 == 7); // eroare!
Funcția implementată nu poate realiza interschimbarea variabilelor, deoarece aceasta lucrează cu copii ale variabilelor din funcția apelantă și nu cu variabilele efective.
În libajul C această problemă este rezolvată prin transmiterea către funcție de copii ale pointerilor care indică spre variabilele care trebuie interschimbate:
void swap(int* arg1, int* arg2) { // funcția primește ca parametri copii după pointeri int tmp = *arg1; // se lucrează cu valorile indicate de pointeri *arg1 = *arg2; *arg2 = tmp; } int var1 = 7; int var2 = 3; swap(&var1, &var2); // se transmit funcției pointeri care indică la variabile assert(var1 == 3); assert(var2 == 7);
Această soluție adaugă complexitate codului, atât în funcția apelantă cât și în funcția apelată.
Limbajul C++ introduce noțiunea de transmitere a parametrilor prin referință:
void swap(int& arg1, int& arg2) { int tmp = arg1; arg1 = arg2; arg2 = tmp; } int var1 = 7; int var2 = 3; swap(var1, var2); assert(var1 == 3); assert(var2 == 7);
Prin acest mecanism, compilatorul introduce complexitatea prezentată anterior, fără ca programatorul să o descrie explicit, păstrând lizibilitatea codului.
În final, dacă dorim să putem interschimba două variabile de orice tip, putem scrie funcția swap folosind template-uri:
template<class T> void swap(T& arg1, T& arg2) { T aux = arg1; arg1 = arg2; arg2 = aux; } char var1 = 7; char var2 = 3; char* pointerVar1 = &var1; char* pointerVar2 = &var2; swap(var1, var2); swap(pointerVar1, pointerVar2); assert(*pointerVar1 == 7); assert(*pointerVar2 == 3);
Transmiterea prin referință și clasele
Atunci când o funcție primește ca parametru un obiect, acesta este copiat pe stivă. Operația de copiere este cu atât mai costisitoare ca timp de execuție, cu cât obiectul conține mai multe câmpuri sau câmpuri care sunt la rândul lor obiecte sau câmpuri care sunt vectori. De asemenea, fiind copiat pe stivă, obiectul copiat consumă spațiu pe stivă. În cazuri extreme, când obiectul este foarte mare și stiva foarte mică, apelarea unei astfel de funcții este imposibilă.
Pentru a evita toate aceste probleme, există două soluții: funcția care trebuie să lucreze cu obiectul respectiv poate primi ca parametru doar un pointer la obiectul respectiv sau, mai simplu, poate primi ca parametru obiectul prin referință.
În lecția anterioară am omis să transmitem obiectele prin referință atunci când am scris metodele claselor Vector3 și ModInteger, fapt ce trebuie corectat.
Implementarea
Până în prezent am obținut următorul cod:
#include <stdio.h> #include <assert.h> #include <iostream> template <class T1 = int, class T2 = int, class T3 = int> class Vector3 { public: T1 x; T2 y; T3 z; // a + b // a.operator+(b) Vector3 operator +(const Vector3& b) const { Vector3 s; s.x = this->x + b.x; s.y = this->y + b.y; s.z = this->z + b.z; return s; } Vector3 operator -() const { Vector3 inv; inv.x = -this->x; inv.y = -this->y; inv.z = -this->z; return inv; } //T norma2() const { // return (this -> x * this -> x + this ->y * this -> y + this -> z * this -> z); //} bool operator <(const Vector3& b) const { return this->x < b.x; } Vector3 operator -(const Vector3& b) const { return *this + (-b); } Vector3 operator *(const int& b) const { Vector3 p; p.x = b * this->x; p.y = b * this->y; p.z = b * this->z; return p; } bool operator ==(const Vector3& b) const { return this->x == b.x && this->y == b.y && this->z == b.z; } }; template<class T> T min(T arg1, T arg2) { return arg1 < arg2 ? arg1 : arg2; } /* int min(const int arg1, const int arg2) { return arg1 < arg2 ? arg1 : arg2; } double min(const double arg1, const double arg2){ return arg1 < arg2 ? arg1 : arg2; }*/ //const int MOD = 666013; template<unsigned int MOD, class T = unsigned long long> class ModInteger { public: T value; ModInteger() { this->value = 0; } ModInteger(const T& arg) { this->value = arg % MOD; } ModInteger operator + (const ModInteger& arg) const { return (ModInteger)(this->value + arg.value); } ModInteger operator - (const ModInteger& arg) const{ return (ModInteger)(this->value + (MOD - arg.value)); //return *this + -arg; } ModInteger operator - () const { return (ModInteger)(MOD - this->value); } ModInteger operator * (const ModInteger& arg) const { return (ModInteger)(this->value * arg.value); } ModInteger pow(const int& p) const { if (p == 0) { return 1; } else { ModInteger t = this->pow(p / 2); if (p % 2 == 1) { return t * t * (*this); } else { return t * t; } } } ModInteger multiplicationInverse() const { ModInteger answer; //this * answer = 1; //this->pow(MOD - 1) == 1; //this * this->pow(MOD - 2) == 1; answer = this->pow(MOD - 2); return answer; } ModInteger operator / (const ModInteger& arg) const { return (ModInteger)(*this * arg.multiplicationInverse()); } bool operator == (const ModInteger& arg) const { return this->value == arg.value; } friend std::ostream& operator<< (std::ostream& stream, const ModInteger& arg) { stream << arg.value; return stream; } friend std::istream& operator>> (std::istream& stream, ModInteger& arg) { stream >> arg.value; arg.value %= MOD; return stream; } }; template<class T> void swap(T& arg1, T& arg2) { T aux = arg1; arg1 = arg2; arg2 = aux; } int main(void) { char var1 = 7; char var2 = 3; char* pointerVar1 = &var1; char* pointerVar2 = &var2; swap(var1, var2); swap(pointerVar1, pointerVar2); assert(*pointerVar1 == 7); assert(*pointerVar2 == 3); //int value = 7; //std::cout << value; ModInteger<666013> modA, modB; ModInteger<666013> expModS, expModD, expModP; modA = 222222; modB = 444444; expModS = 653; expModD = 443791; expModP = 168759; assert(expModS == modA + modB); assert(expModD == modA - modB); assert(expModP == modA * modB); assert(modA + -modA == 0); assert(modA * modA.multiplicationInverse() == 1); assert((modA / modB) * modB == modA); std::cout << modA << " " << modB; const ModInteger<666013> modX = 123; std::cout << modX; //std::cout << modA + modB << " " << modA - modB; //(((std::cout << (modA + modB)) << " ") << (modA - modB)); //std::cin >> modA; //std::cout << modA; ModInteger<7, unsigned short int> mod7a = 5, mod7b = 4, mod7s = 2; assert(mod7a + mod7b == mod7s); //std::cin >> mod7a >> mod7b; //std::cout << mod7a + mod7b; int x = 5; int y = 4; int expMin = 4; assert(expMin == min(x, y)); double dx = 4.4; double dy = 4.3; double expDMin = 4.3; assert(expDMin == min(dx, dy)); long long lx = 1234567890123L; long long ly = 1234567890124L; long long expLMin = 1234567890123L; assert(expLMin == min(lx, ly)); Vector3<int> a, b; Vector3<int> sExp; Vector3<int> dExp; Vector3<int> mExp; a.x = 1; a.y = 2; a.z = 3; b.x = 6; b.y = 7; b.z = 8; sExp.x = 7; sExp.y = 9; sExp.z = 11; assert( sExp == a + b ); dExp.x = -5; dExp.y = -5; dExp.z = -5; assert( dExp == a - b ); mExp.x = a.x; mExp.y = a.y; mExp.z = a.z; assert(mExp == min(a, b)); Vector3<long long, long long, long long> vecD1, vecD2, vecDD; vecD1.x = 1234567890123L; vecD1.y = 1234567890123L; vecD1.z = 1234567890123L; vecD2.x = 1234567890124L; vecD2.y = 1234567890124L; vecD2.z = 1234567890124L; vecDD.x = -1; vecDD.y = -1; vecDD.z = -1; assert(vecD1 - vecD2 == vecDD); Vector3<ModInteger<31911>, ModInteger<32003>, ModInteger<32009> > valoare; return 0; }