You are on page 1of 12

Mihai TOGAN

Cap. 4 – Metode statice. Funcții şi clase friend

Obiective:
 struct vs. class
 Membrii statici într-o clasă
 Constructori și destructori de tip private.
 Exemplu de utilizare: implementarea unui Singleton (pattern de programare)
 Funcții și clase friend
 Funcții inline

1
Mihai TOGAN
struct vs. class (ȋn C++)

 struct: datele și funcțiile membre, implicit sunt public


 class: datele și funcțiile membre, implicit sunt private

2
Mihai TOGAN
Declaraţie pentru tipul Point folosind o construcţie de tip struct:
/*****************************************************************************/
struct Point { /* Declaratia tipului Point (am folosit struct) */

int x; /* membru public */


int y; /* membru public */
public:

Point (); /* constructor implicit */


Point (int a, int b); /* constructor explicit */

//...
void set(int a, int b);
void print();
void move (int off_x, int off_y);
int getx() const {return x;}
int gety() const {return y;}

};

Point::Point ()
{
cout << "\nConstructor implicit in clasa Point ";
x = 0; y = 0;
}
//...

/*****************************************************************************/
struct Vector { /* Declaratia tipului Vector (am folosit struct) */

private:
char *nume; /* membru private */
Point start; /* membru private */
Point end; /* membru private */

public:

Vector ();
Vector (const char *nume, int xs, int ys, int xe, int ye);
Vector (const char *nume, const Point& start, const Point& end);
Vector (const Vector &V); /* declaratia pentru constructorul de copiere
*/

~Vector ();
void set (const char *nume, int xs, int ys, int xe, int ye);
void print ();
//...
};
//...
/*****************************************************************************/
void main () {
Vector V;
Point P (2, 3);
//...
}
/*****************************************************************************/

3
Mihai TOGAN
Membrii statici într-o clasă

Discuţie privind utilizarea datelor (variabilelor) și a funcțiilor statice:


 variabile locale statice (ȋn interiorul unei funcții)
 variabile globale statice
 funcții statice (non-membre)

În plus, în limbajul C++ mai pot fi folosite:


 variabile membre statice
 funcții membre statice

Exemplu variabilă membru statică:


/*****************************************************************************/
class Vector { /* Declaratia clasei Vector */

//...
public:
static int counter; /* var membra statica */
};

/*****************************************************************************/
int Get_ActiveVectorInstances ()
{
cout << "\nNumarul de instante (obiecte) active ale clasei Vector: "
cout << Vector::counter;

return Vector::counter;
}

int Vector::counter;

/*****************************************************************************/
void main ()
{
Vector V1 ("V1", 1, 2, 3, 4);
Vector V2 = V1;
Vector *V3 = new Vector ();

Get_ActiveVectorInstances ();
//...

delete V3;
Get_ActiveVectorInstances ();
//...
}
/*****************************************************************************/

4
Mihai TOGAN
Observaţii:
 O variabilă membră statică nu aparţine nici unui obiect (aceasta aparţine clasei).
 Chiar dacă nu există obiecte instanţiate, variabila membru statică există alocată.
 Accesibilitatea se face folosind numele clasei și access-scope-operatorul.
 Este ca o variabilă globală însă este accesibilă doar prin intermediul clasei.
 Ca orice variabila membru, poate fi public sau private.
 Este obligatoriu să fie definită (opţional și iniţializată) ȋn afara clasei. Exemplu:

int Vector::counter = 0;

Dacă nu se declara ȋn afara clasei, se obţine eroare de linker (nu se alocă)


error LNK2001: unresolved external symbol "public: static int Vector::counter"
(?counter@Vector@@2HA) main.obj

Exemplu funcție membru statică:


/****************************************************************************/
class Vector {
//...
public:
static int counter; /* var membru statica */

static int getCounter (); /* metoda statica */


};

int Vector::getCounter ()
{
return counter;
}

int Get_ActiveVectorInstances ()
{
cout << "\nNumarul de instante (obiecte) active ale clasei Vector: ";
cout << Vector::getCounter();
return Vector::getCounter();
}
/******************************************************************************/

Observaţii:
 Apelul unei metode statice se face folosind numele clasei și access-scope-operatorul.
 Practic este ca o funcție standalone insa accesibila doar prin intermediul clasei.
 Ca orice metoda, poate fi public sau private.
 O metodă statică aparţine clasei și nu trebuie gândită ca acţionând asupra datelor unui
obiect specific.
 Nu poate accesa date ale clasei care nu sunt statice.
 Se poate apela și printr-un obiect V2.getCounter();

 De regulă, metodele statice sunt cele care nu au nevoie de datele interne ale unui obiect.

5
Mihai TOGAN
Constructori și destructori de tip private

Discuţie privind constructori și destructori de tip private (consecinţe).


Exemplu de utilizare a constructorilor privaţi: crearea unei clase instantiabilă într-un mod
controlat (de exemplu, de tip Singleton).

class Singleton {
public:
static Singleton& GetInstance ();
int GetVal () { return val; }

//...
private:
/* constructor private */
Singleton() : val(5) {
cout << "\nConstructor...";
//...
}

int val;
//...
static Singleton *mpInstance;
};

/******************************************************************************
/
Singleton* Singleton::mpInstance = NULL;

Singleton& Singleton::GetInstance()
{
if (mpInstance == NULL)
mpInstance = new Singleton;

return *mpInstance;
}

/******************************************************************************
/
void main ()
{
// Singleton X; /* eroare (de compilare) */
// Signleton* Y = new Singleton /* eroare (de compilare) */

Singleton &Z = Singleton::GetInstance(); /* OK */


cout << "Z.val = "<< Z.GetVal();
}

Dacă încercăm instanţierea directă, ca în exemplul de mai jos:

Singleton X;
Signleton* Y = new Singleton

se obţine eroarea următoare:


error C2248: 'Singleton::Singleton' : cannot access private member declared in class 'Singleton'
d:\scoala\c++\__curs\programe\curs_membstatici\curs_membstatici\main.cpp

6
Mihai TOGAN
Pornind de la exemplul de mai sus, putem construi totuşi obiecte folosind mecanismul de
copiere:

void main ()
{
Singleton Z = Singleton::GetInstance(); /* Merge, insa nu e ok (!)*/
/*
Problema apare intrucat constructorul de copiere implicit este public
*/
cout << "Z.val = "<< Z.GetVal();
}

O soluţie corectată, ar putea fi următoarea:


/*************************************************************************/

class Singleton {
public:
static Singleton& GetInstance ();
//...

private:
Singleton(int v = 5) : val(v) { /* constructor private */
cout << "\nConstructor...";
}

/* constructor de copiere private */


Singleton(const Singleton &S) : val(S.val)
{
cout << "\nConstructor de copiere...";
}

int val;
//...
static Singleton *mpInstance;
};

/*************************************************************************/
Singleton* Singleton::mpInstance = NULL;

Singleton& Singleton::GetInstance()
{
if (mpInstance == NULL)
mpInstance = new Singleton;

return *mpInstance;
}

void main ()
{
Singleton Z = Singleton::GetInstance(); /* Eroare de compilare */
}

/************************************************************************/

7
Mihai TOGAN
Completăm şi mai mult exemplul:
/*****************************************************************************/
class Singleton {
public:
static Singleton& GetInstance ();
static Singleton* GetInstancePtr (int v);
int GetVal () { return val; }
//...

private:
Singleton(int v = 5) : val(v) { /* const. private */
cout << "\nConstructor...";
}

/* const. de copiere private */


Singleton(const Singleton &S) : val(S.val) {
cout << "\nConstructor de copiere...";
}

int val;
//...
static Singleton *mpInstance;
};
/******************************************************************************/
Singleton* Singleton::mpInstance = NULL;
Singleton& Singleton::GetInstance()
{
if (mpInstance == NULL)
mpInstance = new Singleton;

return *mpInstance;
}

Singleton* Singleton::GetInstancePtr(int v)
{
if (mpInstance == NULL)
mpInstance = new Singleton (v); /* atentie, aici:putem face new */
else
mpInstance->val = v;

return mpInstance;
}

/******************************************************************************/
void main ()
{
Singleton &Z = Singleton::GetInstance();
cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 5 */

Singleton *P = Singleton::GetInstancePtr (10); /* OK */


cout << "\n P.val = "<< P->GetVal(); /* afiseaza P.val = 10 */
cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 10 */
}

/******************************************************************************/

8
Mihai TOGAN
Întrebare: cum putem distruge instanța creată ?

void main ()
{
Singleton &Z = Singleton::GetInstance(); /* OK */
cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 5 */

Singleton* P = Singleton::GetInstancePtr (10); /* OK */


cout << "\n P.val = "<< P->GetVal(); /* afiseaza P.val = 10 */
cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 10 */

delete &Z; /* OK la compilare, OK la executie (runtime) */


delete P; /* OK la compilare, CRASH la runtime */
}

Observaţie: designul clasei nu este suficient de bun, permite build-uri generatoare de erori la
runtime. Dacă am proiectat clasa astfel încât construcţia instanţei unice să fie realizată într-
un mod controlat, la fel trebuie procedat și la distrugerea acelei instanţe.

/********************************************************************/

class Singleton {
public:
static Singleton& GetInstance ();
static Singleton* GetInstancePtr (int v);

static void DestroyInstance ();

int GetVal () { return val; }


//...

private:
Singleton(int v = 5) : val(v) {}
Singleton(const Singleton &S) : val(S.val) {}

~Singleton () {;} /* destructor private */

int val;
//...
static Singleton *mpInstance;
};

//...

void Singleton::DestroyInstance ()
{
if (mpInstance == NULL)
return;

delete mpInstance;
mpInstance = NULL;
}

/********************************************************************/

9
Mihai TOGAN

void main ()
{
// Singleton X; /* eroare (de compilare) */
// Signleton* Y = new Singleton /* eroare (de compilare) */

Singleton &Z = Singleton::GetInstance(); /* OK */


cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 5 */

Singleton* P = Singleton::GetInstancePtr (10); /* OK */


cout << "\n P.val = "<< P->GetVal(); /* afiseaza P.val = 10 */
cout << "\n Z.val = "<< Z.GetVal(); /* afiseaza Z.val = 10 */

// delete &Z; /* eroare (de compilare) */


// delete P; /* eroare (de compilare) */

Singleton::DestroyInstance (); /* OK, distruge instanța */


Singleton::DestroyInstance (); /* nu se intampla nimic deoarce mpInstance
= NULL */
Singleton::DestroyInstance (); /* nu se intampla nimic deoarce mpInstance
= NULL */
Singleton::DestroyInstance (); /* nu se intampla nimic deoarce mpInstance
= NULL */
}

/********************************************************************/

10
Mihai TOGAN
Funcții și clase friend
 Mecanism care poate fi folosit pentru a facilita accesul la datele și funcțiile membre
private ale unei clase.
 Mecanism nerecomandat, încalcă principiul OOP al încapsulării.

Exemplu de clasă friend:

class Point {
int x;
int y;
public:
//...
int getx() const {return x;}
int gety() const {return y;}

friend class Vector;


};

Acum ȋn clasa Vector putem accesa direct datele membre private ale clasei Point:

Vector::Vector (const char *nume, const Point& start, const Point& end)
{
cout << "\nInitializam vectorul '" << nume << "'...";
this->set (nume, start.x, start.y, end.x, end.y);
}

Observaţie: relaţia de tip friend nu este comutativă. Dacă Point este ȋn relaţie friend cu
Vector, nu înseamnă că implicit și Vector este ȋn relaţie friend cu Point.
Putem face o clasă de tip friend doar cu o singură funcție a unei clase sau cu o funcție
standalone.
class Vector {
//...
public:

friend void Point::print ();


friend void fc ();
};

Utilizarea uzuală a funcțiilor friend se face în situaţiile următoare:


 Dacă o clasă lucrează foarte mult/aproape cu altă clasă (una fără alta sunt useless), se
obişnuieşte să se realizeze o relaţie de clase prietene (cel puţin într-o direcţie).
 La supraîncărcarea de operatori, există situaţii când nu se poate face o supraîncărcare de
un anume tip fără funcții friend la nivelul clasei pentru care se face supraîncărcarea
operatorului.

11
Mihai TOGAN
Funcții inline
 Apelul se înlocuie cu codul obiect al funcției.
 Observaţie: este recomandat ca funcţiile inline să fie foarte scurte (creşte codul obiect al
programului).

Exemple de funcții inline:

inline int compare (int a,int b)


{
if (a>b) return 1;
if (a<b) return 0;
if (a==b) return -1
}

inline int max(int a, int b)


{
return (a > b) ? a : b;
}

12

You might also like