Note de curs, programare orientată pe obiecte, 27 aprilie 2013

From Algopedia
Revision as of 04:22, 1 May 2013 by Dan (talk | contribs) (Created page with "= Transmiterea parametrilor funcțiilor prin referință = Să se implementeze funcția <tt>swap</tt>, care interschimbă valorile a două variabile de tip <tt>int</tt>, cu urmă...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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;
}