Struct, Class, Union
Funcții prieten
Clase Prieten
Funcții Inline
Constructori și destructori
Constructor de copiere

Struct, Class, Union

Struct

Singura diferență între struct și class este modificatorul de acces, struct fiind public by default, iar class fiind private.

În C++, struct-urile pot avea funcții membru. Existența `struct asigură compatibilitate cu cod mai vechi.

Union

Membrii sunt publici, dar datele membre folosesc aceeași locație de memorie (se suprascriu).

union Value {
    int i;
    float f;
    char c;
};

int main() { 
	Value v; 

	v.i = 42; 
	std::cout << v.i << "\n"; // show 42

	v.f = 2.34;
	std::cout << v.i << "\n"; // garbage, being overwritten
}
Observații

  • union nu poate moșteni / nu se poate moșteni din union
  • nu poate avea funcții virtuale, variabile statice, referințe
  • pot avea constructori/destructori doar după c++11

Union anonime
union {
    long l;
    double d;
    char s[4]; // all members share the same memory
};

l = 100000;
cout << l << " ";

d = 123.2342;
cout << d << " "; // overwrites l

strcpy(s, "hi");
cout << s; // overwrites both l and d

Pentru union anonim

  • nu pot exista funcții
  • nu există specificatori de acces, standard public
  • dacă sunt globale, trebuie menționate ca statice

Funcții prieten

class obj{
	int a, b;
public:
	friend int sum(obj) x);
};

int sum(obj x) {
	return x.a + x.b; // poate accesa, chiar daca sunt private 
}
class ob2; // trebuie declarat inainte

class ob1{ 
	int x;
public: 
	friend void f(ob1, ob2);
};

class ob2{ 
	int y;
public:
	friend void f(ob1, ob2);
};

void f(ob1 A, ob2 B) { 
	cout << A.x + B.y << "\n";
}
class ob2;

class ob1{ 
	int x;
public: 
	void f(ob2);
};

class ob2{ 
	int y;
public:
	friend void ob1::f(ob2);
}

ob1::f(ob2) { 
	cout << this->x + ob2.y << "\n";
}

Clase prieten

Dacă Y este clasă friend a clasei X, atunci toate funcțiile membre ale lui Y au acces la membrii privați ai clasei X.

class ob1{
	int x;
public:
	friend class ob2;
}

class ob2{
public:
	void set_x(int a, ob1& ob) { ob.x = a};
}

int main() {
	ob1 A;
	ob2 B;
	B.set_x(5, A); // funcționează
}

Funcții Inline

inline este o sugestie pentru compilator de a înlocui apelul funcției cu corpul funcției.

Implicite

class myclass{
	int a;
public:
	void show() { cout << a << "\n"; } // inline automatic
};

Explicite

class myclass{
	int a;
public:
	void show();
}

inline myclass::show() { 
	cout << a << "\n";	
}

Constructori și Destructori

Constructorii

Destructorii

Caracteristici comune
Moștenire constructori

Este posibilă de la C++11

Constructor de copiere

Orice clasă are

  • constructor de inițializare
  • constructor de copiere
  • destructor
  • operator de atribuire

class A{ 
	int a;
	string s;
};

int main(){
	A a; // inițializare
	A b = a; // apel constructor de copiere
	A e(a); // apel constructor de copiere 
	A c; // apel constructor de inițializare
	c = a; // apel constructor de atribuire
}
Warning

Apelul de funcție prin valoare duce la copierea argumentului.

class A{
	int* v;
public:
	A() { 
		v = new int[3];
		v[0] = v[1] = v[2] = 123;
		cout << "C\n";
	}
	~A() {
		delete[] v;
		cout << "D\n";
	}
	void afis() {
		cout << v[2] << "\n";
	}
};
void func(A ob) {
	ob.afis();
}
int main() { 
	A ob1; // a fost apelat constructorul, se afișează C
	 functie(ob1); // nu avem constructor de copiere definit, se face shallow copy (se copiaza pointer-ul), afiseaza 123, dupa care se apeleaza destructorul copiei (afiseaza D). Este sters v atat in copie cat si in ob1
	 ob1.afis(); //garbage
}

Output:

C
123
D
15925584   // ← garbage (undefined memory)
Caz particular: constructor cu un parametru

class X{
	int x;
public:
	X(int j) { x = j; }
	int get { return x; }
};

int main() {
	X ob = 99;
	cout << ob.get() << endl;
	return 0;
}

Constructorul nu este marcat ca explicit deci se face conversia implicită
Se poate evita menționând explicit înaintea constructorului

Tablouri de obiecte
class X{
int a, b, c;
//
};

X v[3] = {X(0,1,2), X(0, 3, 4), X(8, 9, 10)};
class X{
int a;
//
};

X v[3] = {1, 8, 10};
Ordine de apel
class A {
public: 
	A() { cout << "A "; }
};

class B {
	A a;
public: 
	B() {cout << "B "; }
};

void foo(B ob1) {
	A ob2;
}

int main() { 
	B ob; // afișează A B
	// constructorul lui B se execută doar după ce au fost construiți toți membrii lui B (i.e., A)
}

Constructorul de copiere poate fi rescris:

classname(const classname& o) { 
	// se copiază efectiv datele membre
	// la pointer, deepcopy
}

Pot exista și parametri, dar trebuie să fie impliciți.

Info

Pot fi folosiți doar la inițializări

La funcții

MyClass f() { MyClass obiect; … return obiect; }
MyClass x = f();

  • În standardul C++98 - se returnează valoare, implică copiere atât la return, cât și la x = f()
  • Începând cu C++11 - Se încearcă Return value optimization (RVO): se elimină complet copierea și construiește x direct în locul unde obiect ar fi fost.
  • Începând cu C++17: fără apeluri de copiere/mutare (RVO garantat)

Exemplificare:

#include <iostream>
using namespace std;

class A{
	int x;
public:
	A() {
		cout << "Constructor void" << endl;
	}

	A(int x) {
		this->x = x;
		cout << "Constructor " << x << endl;
	}

	A(const A& ob) { // constructor de copiere
		this->x = ob.x;
		cout << "Copy " << this->x << endl;
	}

};

A procedure() {
	A ob(1);
	return ob;
}

int main() {
	A x = procedure();
	return 0;
}

Compilând cu g++ -std=c++98 -fno-elide-constructors avem următorul output:

Constructor 1
Copy 1
Copy 1

Compilând cu g++ -std=c++20 -fno-elide-constructors

Constructor 1
Copy 1

Compilând fără -fno-elide-constructors (RVO asigurat):

Constructor 1