Saptamana 05 / 14 Programare Orientata Obiect
Tipuri de membri, operatorul = si relatia "has a"
Membri constanti si statici, metode statice, ce cer membrii pointeri de la constructori si destructor, operatorul de atribuire, compunerea claselor si impartirea unei clase in fisier header si fisier sursa.
- Sa declari membri constanti si sa ii initializezi in lista de initializatori
- Sa folosesti membri si metode statice si sa stii la ce nu au acces
- Sa explici de ce o clasa cu membru pointer are nevoie de constructor de copiere si destructor proprii
- Sa scrii operator= cu verificarea auto-atribuirii, care intoarce *this
- Sa modelezi relatii "has a" 1:1 si 1:M prin compunere
- Sa imparti o clasa in fisierele .h si .cpp, protejate cu include guards
Membri constanti
Un membru const primeste valoarea o singura data, la crearea obiectului, si nu se mai schimba dupa aceea. Exemple bune: numarul matricol, seria unui produs.
class Produs {
const int id;
double pret;
public:
Produs(int i, double p) : id(i), pret(p) {} // corect: lista de initializatori
// Produs(int i, double p) { id = i; } // eroare: atribuire catre membru const
};
Lista de initializatori se executa inainte de corpul constructorului. Cand incepe corpul, id exista deja, deci id = i; acolo este o atribuire, nu o initializare. Compilatorul o refuza.
Membri statici si metode statice
Un membru static apartine clasei, nu unui obiect. Exista o singura copie, comuna tuturor obiectelor, si exista chiar daca nu ai creat niciun obiect.
class Produs {
static int urmatorulId; // declaratie, in clasa
// ...
};
int Produs::urmatorulId = 1; // definitie, in afara clasei, o singura data
O metoda statica se apeleaza prin numele clasei: Produs::create(). Nu are this, deci poate folosi direct doar membrii statici.
Constructorul creste contorul, destructorul il scade. Ce afiseaza programul?
#include <iostream>
using namespace std;
class Contor {
public:
static int numar;
Contor() { numar++; }
~Contor() { numar--; }
};
int Contor::numar = 0;
int main() {
Contor a, b;
{
Contor c;
cout << Contor::numar << " ";
}
cout << Contor::numar;
} Modifica programul de mai jos: adauga o a doua metoda statica, care intoarce pretul celui mai scump produs creat pana acum.
#include <iostream>
using namespace std;
class Produs {
const int id; // fix pe toata durata de viata a obiectului
double pret;
static int urmatorulId; // un singur contor pentru toata clasa
public:
Produs(double p) : id(urmatorulId++), pret(p) {}
int getId() const { return id; }
static int create() { return urmatorulId - 1; }
};
int Produs::urmatorulId = 1;
int main() {
Produs a(10), b(25.5);
Produs c(7);
cout << a.getId() << " " << b.getId() << " " << c.getId() << endl;
cout << "create: " << Produs::create() << endl;
}Intrare (stdin)
Membri pointeri
Cand o clasa detine memorie printr-un pointer, functiile generate de compilator gresesc:
- constructorul de copiere generat copiaza adresa, nu datele;
- destructorul generat nu elibereaza nimic;
- operator= generat copiaza adresa si pierde vechiul bloc.
Deci clasa are nevoie de toate trei scrise de mana: destructor (delete[]), constructor de copiere (aloca si copiaza) si operator=. Aceasta este Regula celor trei: daca ai nevoie de una dintre ele, aproape sigur ai nevoie de toate trei.
Operatorul de atribuire
Cutie b = a; creeaza un obiect nou, deci apeleaza constructorul de copiere. c = a; modifica un obiect care exista deja, deci apeleaza operator=.
C este constructorul obisnuit, K constructorul de copiere, A operatorul de atribuire.
#include <iostream>
using namespace std;
class Cutie {
public:
int v;
Cutie(int x) : v(x) { cout << "C"; }
Cutie(const Cutie& c) : v(c.v) { cout << "K"; }
Cutie& operator=(const Cutie& c) { v = c.v; cout << "A"; return *this; }
};
int main() {
Cutie a(1);
Cutie b = a;
Cutie c(2);
c = a;
cout << " " << b.v << " " << c.v;
} Pentru o clasa cu membru pointer, operator= are patru pasi: verifici auto-atribuirea, eliberezi memoria veche, copiezi celalalt obiect, intorci *this.
Tablou& Tablou::operator=(const Tablou& t) {
if (this == &t) return *this; // a = a; nu are voie sa isi stearga datele
delete[] date;
dim = t.dim;
date = new int[dim];
for (int i = 0; i < dim; i++) date[i] = t.date[i];
return *this; // ca sa mearga a = b = c;
}
Tablou a(2), b(3); - fiecare obiect are blocul lui.
Pasul 1/3
Compunerea claselor: “has a”
O clasa poate avea ca membri obiecte din alte clase. O masina are un motor (1:1). O grupa are mai multi studenti (1:M).
class Motor { int putere; /* ... */ };
class Masina {
Motor motor; // 1:1
};
class Grupa {
Student studenti[30]; // 1:M, capacitate fixa
int nr;
};
Membrii sunt construiti inainte de corpul constructorului clasei care ii contine, in ordinea declararii. Distrugerea are loc in ordine inversa.
#include <iostream>
using namespace std;
class Motor {
public:
Motor() { cout << "M"; }
~Motor() { cout << "m"; }
};
class Masina {
Motor motor;
public:
Masina() { cout << "C"; }
~Masina() { cout << "c"; }
};
int main() {
Masina x;
} Daca membrul nu are constructor fara parametri, clasa care il contine trebuie sa apeleze unul dintre constructorii lui in lista de initializatori: Masina(int cp) : motor(cp) {}.
Fisier header si fisier sursa
In programele mai mari, fiecare clasa sta in doua fisiere. Header-ul spune ce este clasa, fisierul sursa spune cum functioneaza.
// Produs.h
#pragma once // sau varianta clasica de mai jos
// #ifndef PRODUS_H
// #define PRODUS_H
class Produs {
const int id;
double pret;
static int urmatorulId;
public:
Produs(double p);
int getId() const;
static int create();
};
// #endif
// Produs.cpp
#include "Produs.h"
int Produs::urmatorulId = 1;
Produs::Produs(double p) : id(urmatorulId++), pret(p) {}
int Produs::getId() const { return id; }
int Produs::create() { return urmatorulId - 1; }
main.cpp include doar "Produs.h". Garda impiedica definirea clasei de doua ori cand doua headere includ acelasi fisier. Membrul static se defineste in .cpp, niciodata in header, altfel primesti eroare de link-editare pentru definitii multiple.
Verifica-te
Scor: 0/8