Sunteți pe pagina 1din 47

Programare orientata pe obiecte

Curs 13 – functor, move constructor, move assignment

C
++{};
C++ Acest curs
› Pointer la functii
› Functori (obiecte functii)
› Lvalue, rvalue
› Constructor de mutare
› Operator de asignare de mutare
› I/O
C++ Pointeri la functii (PF)
› Contine adresa de memorie a unei secvente de instructiuni
aflate in segmentul de cod.
› Dereferentierea unui astfel de pointer permite executia
secventei respective de instructiuni
› Dereferentierea permite si furnizarea de argumente daca
pointerul a fost declarat astfel
› Beneficiu: permite implementarea unui mechanism de
selectie a secventei de cod ce urmeaza a fi executate la
run-time.
type (*FunctionPointerName)(type param1, type param2, ...)
C++ Pointer la functii
#include <iostream>
using namespace std;

double Add(double lt, double rt)


{
return lt + rt;
}

double Multiply(double lt, double rt)


{
return lt * rt;
}

double BinaryOperation(double lt, double rt,


double(*Operation)(double lt, double rt))
{
return Operation(lt, rt);
}
C++ Pointer la functii
int main(void)
{
double a = 5.4;
double b = 6.7;

cout<<"Add: "<<a<<", "<<b<<" = "<<BinaryOperation(a, b, Add)<<endl;


cout<<"Multiply: "<<a<<", "<<b<<" = "<<BinaryOperation(a, b, Multiply)<<endl;

return 0;
}

Add: 5.4, 6.7 = 12.1


Multiply: 5.4, 6.7 = 36.18
C++ Probleme ridicate de utilizarea pointerilor la functii
› Eficienta: PF sunt ineficienti comparativ cu functorii (urmeaza
a fi explicat). Compilatorul, in mod frecvent, ii va considera ca
fiind raw pointers si va incerca sa inlocuiasca apelul cu
secventa de cod pointata.
› Stare: pointeri la functii nu pot retine/stoca starea variabilelor
interne in mod implicit. Se poate totusi, daca sunt declarate ca
fiind statice, dar aceste variabile nu sunt thread-safe. Acest
lucru poate fi rezolvat prin adaugarea unui mecanism de
sincronizare dar pot sa apara bottlenecks sau race
conditions in programele multithread.
C++ Probleme ridicate de utilizarea pointerilor la functii
› Template: PF nu prea merg bine cu functiile template daca
sunt mai multe semnaturi ale functiei in cod. Solutia este de a
utiliza function pointer casting ceea ce duce la un cod
dificil si sintaxa fara valoare adaugata.
› Adaptare: PF au un parametri ficși (ca numar si tip), motiv
pentru care nu sunt flexibili in ceea ce priveste adresa unor
functii cu parametri diferiti (ca numar si tip). Impachetarea
acestor functii in altele care sa corespunda, cu hard-code-
area unor parametri de apel, duce la un cod mai putin flexibil
si expandat.
C++ Obiecte functii
› Un obiect functie permite obiectului instantei unei clase de
a fi apelat sau invocat ca si cum ar fi o functie.
› Un obiect functie are functia operator() supraincarcata
› Avantajul consta in faptul ca fiind obiecte pot contine stare,
individual sau static (pentru toate instantele)
C++ Functori
class BinaryFunction
{
public:
BinaryFunction() {}
virtual double operator()(double lt, double rt) = 0;
};

class Add : public BinaryFunction


{
public:
Add() {}
double operator()(double lt, double rt) { return lt + rt; }
};

class Multiply : public BinaryFunction


{
public:
Multiply() {}
double operator()(double lt, double rt) { return lt * rt; }
C++ Functori
double BinaryOperation(double lt, double rt, BinaryFunction* operation)
{
return (*operation)(lt, rt);
}

int main(void)
{
double a = 5.4;
double b = 6.7;
BinaryFunction *add = new Add;
BinaryFunction *multiply = new Multiply;

cout<<"Add: "<<a<<", "<<b<<" = "<<BinaryOperation(a, b, add)<<endl;


cout<<"Multiply: "<<a<<", "<<b<<" = "<<BinaryOperation(a, b, multiply)<<endl;

delete add;
delete multiply; Add: 5.4, 6.7 = 12.1
return 0; Multiply: 5.4, 6.7 = 36.18
}
C++ Lvalues and Rvalues
C++
› glvalue – “generalized” lvalue – expresie a carei evaluare
determina identitatea unui obiect, bit-field, sau functie
› prvalue – “pure” rvalue – expresie a carei evaluare:
– calculeaza valoarea operandului unui operator (nu are ca rezultat
un obiect).
– Initializeaza un obiect sau bit field
› xvalue – “eXpiring” value – gvalue ce indica un obiect sau
bit field ale carei resurse pot fi reutilizate
› lvalue – gvalue care nu este xvalue si apare in partea
stanga a operatorului de asignare
› rvalue – este prvalue sau xvalue
C++ lvalue
• a function call or an overloaded operator expression, whose return type is lvalue reference, such
as std::getline(std::cin, str), std::cout << 1, str1 = str2, or ++it;
• a = b, a += b, a %= b, and all other built-in assignment and compound assignment expressions;
• ++a and --a, the built-in pre-increment and pre-decrement expressions;
• *p, the built-in indirection expression;
• a[n] and p[n], the built-in subscript expressions, where one operand in a[n] is an array lvalue
(since C++11);
• a.m, the member of object expression, except where m is a member enumerator or a non-static
member function, or where a is an rvalue and m is a non-static data member of non-reference
type;
• p->m, the built-in member of pointer expression, except where m is a member enumerator or a
non-static member function;
• a.*mp, the pointer to member of object expression, where a is an lvalue and mp is a pointer to
data member;
• p->*mp, the built-in pointer to member of pointer expression, where mp is a pointer to data
member;
• a, b, the built-in comma expression, where b is an lvalue;
• a ? b : c, the ternary conditional expression for some b and c (e.g., when both are lvalues of the
same type, but see definition for detail);
C++ prvalue
• a literal (except for string literal), such as 42, true or nullptr;
• a function call or an overloaded operator expression, whose return type is non-reference, such
as str.substr(1, 2), str1 + str2, or it++;
• a++ and a--, the built-in post-increment and post-decrement expressions;
• a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
• a && b, a || b, !a, the built-in logical expressions;
• a < b, a == b, a >= b, and all other built-in comparison expressions;
• &a, the built-in address-of expression;
• a.m, the member of object expression, where m is a member enumerator or a non-static member
function[2], or where a is an rvalue and m is a non-static data member of non-reference type
(until C++11);
• p->m, the built-in member of pointer expression, where m is a member enumerator or a non-
static member function[2];
• a.*mp, the pointer to member of object expression, where mp is a pointer to member function[2],
or where a is an rvalue and mp is a pointer to data member (until C++11);
• p->*mp, the built-in pointer to member of pointer expression, where mp is a pointer to member
function[2];
• a, b, the built-in comma expression, where b is an rvalue;
• a ? b : c, the ternary conditional expression for some b and c (see definition for detail);
C++ xvalue
• a function call or an overloaded operator expression, whose return type is rvalue reference to
object, such as std::move(x);
• a[n], the built-in subscript expression, where one operand is an array rvalue;
• a.m, the member of object expression, where a is an rvalue and m is a non-static data member
of non-reference type;
• a.*mp, the pointer to member of object expression, where a is an rvalue and mp is a pointer to
data member;
• a ? b : c, the ternary conditional expression for some b and c (see definition for detail);
C++ gvalue
A glvalue expression is either lvalue or xvalue.

rvalue
An rvalue expression is either prvalue or xvalue.

https://en.cppreference.com/w/cpp/language/value_category
C++ Lvalue Reference Declarator: &
type-id & cast-expression
› Fie urmatoarea secventa
int x = 5, y;
y = x + 2;
y este lvalue
x + 2 este rvalue

› Nu este legal/corect x + 2 = y; deoarece x + 2 reprezinta o


valoare temporara (este stocata temporar in memorie)
› In mod obisnuit zona de memorie respectiva nu poate fi
accesata (referentiata)
› Temporara inseamna ca zona respectiva de memorie poate fi
reutilizata de catre program
C++
› Fie urmatoarea secventa
int x = 5;
int& r = x + 3; // Ilegal, nu este permisa o referinta catre o zona de memorie
temporara deoarece zona de memorie nu va contine o valoare/cantitate valida

› C++ permite totusi o referinta constanta catre o valoarea


temporara (pe scurt catre temporar)
int x = 5;
const int& cr = x + 3; // Legal
› Modificatorul const va plasa valoarea temporarului intr-o
zona de memorie referita de cr.
› Comportamentul va fi de const int iar zona de memorie
va fi pastrata pe intreaga durata de viata a cr.
C++
› Fie secventa
int f(int n)
{
return 10 * n;
}
› Urmatorul apel este corect
std::cout << f(x + 2) << '\n';

› deoarece un temporar este furnizat functiei. Valoarea


temporarului va fi copiata in parametrul formal n.
C++
› Fie secventa:
int g(int& n)
{
return 10 * n;
}

› Urmatorul apel nu este corect:


std::cout << g(x + 2) << '\n';

› deoarece functia accepta o referinta catre o variabila insa ii


este furnizata un temporar. Indata ce functia este
executata zona de memorie referita devine invalida iar
compilatorul nu va permite acest lucru
C++
› Fie secventa:
int h(const int& n)
{
return 10 * n;
}

› urmatorul apel este corect:


std::cout << h(x + 2) << '\n';

› Deoarece n fiind const int, programul va crea o copie a


temporarului, o va plasa intr-o zona de memorie si va
furniza functiei h o referinta catre zona respectiva.
C++ Rvalue Reference Declarator: &&
type-id && cast-expression
› C++ permite un tip special de referinta pentru rvalues
cunoscuta sub denumirea de rvalue reference.
› Fie secventa:
int x = 5;
int&& r = x + 3; // Legal, de notat existenta a doua ampersand-uri &&
std::cout << "x = " << x << " r = " << r << '\n';

› Referinta r furnizeaza acces la valoarea temporarului


C++
› Fie secventa:
int h(int&& n)
{
return 10 * n;
}
› urmatorul apel este corect:
std::cout << h(x + 3) << '\n';

› Functiile pot fi supraincarcate pentru a accepta ambele


tipuri de referinte lvalue si rvalue.
#include <iostream>

C++ int F(const int& n)


{
std::cout << “Apel cu parametru referinta, resultaul este: ";
return 2 * n;
}

int F(int&& n)
{
std::cout << " Apel cu parametru referinta rvalue, resultaul este: ";
return 2 * n;
}

int main()
{
int x = 6;
std::cout << F(x) << '\n';
std::cout << F(x + 2) << '\n';
std::cout << F(6) << '\n';

return 0; Apel cu parametru referinta, resultaul este: 12


} Apel cu parametru referinta lvalue, resultaul este: 16
Apel cu parametru referinta lvalue, resultaul este: 12
C++
› Fie secventa: › Apelul:
#include <iostream>
#include "intlist.h“ auto myList = MakeList(4);
› Invoca constructorul de copiere al
IntList MakeList(int n) IntList. Este echivalent cu:
{
IntList result; auto myList { MakeList(4) };
for (int i = 0; i < n; i++)
result.Insert(i); › Expresia MakeList(4) reprezinta un
return result; obiect IntList.
}
› MakeList(4) este rvalue, un
int main() temporar, deoarece nu poate sa apara
{ in partea stanga a operatorului de
auto myList = MakeList(4); asignare.
myList.Print();
return 0;
}
C++ Creare temporar MakeList(4)

Constructor de copiere myList din temporar

Distrugere temporar

Distrugere myList
C++
› Avand in vedere ca in declararea constructorului de copiere
regasim parametrul const IntList&, va accepta obiectul
temporar. Acest obiect temporar este este returnat de functia
MakeList si este o copie a obiectului local result de tip
IntList returnat de functie.
› Obiectul result este un obiect tranzitoriu si nu se recomanda
a se crea o copie a unui astfel de obiect deoarece construirea
si distrugerea obiectului temporar sunt consumatoare de timp.
class IntList
{
class Node
C++ {
public:
int data; // elementrul data al listei
Node *next; // adresa urmatorului nod din lista
Node(int d); // Constructor
~Node(); // Destructor
};
Node *head; // adresa primului alement din lista
Node *tail; // adresa ultimului element din lista
int len; // numarul elementelor din lista
public:
IntList(); // Constructor, creaza o lista goala
~IntList(); // Destructor, dealoca spatial de memorie ocupat de lista
IntList(const IntList& c); // Constructor de copiere
IntList& operator=(const IntList& other); // operator de asignare de copiere
IntList(IntList&& c); // Constructor de mutare
IntList& operator=(IntList&& other); // operator de asignare de mutare
void Insert(int n); // inserare la sfarsitul listei
void Print() const; // afiseaza elementele listei
int Length() const; // returneaza numerul de elemente din lista
void Clear(); // elimina toate elementele din lista.
};
C++
› Resolvarea ambiguitatii:
– Daca apelantul furnizeaza lvalue, compilatorul genereaza cod ce
invoca constructorul de copiere sau operatorul de asignare de copiere
– Daca apelantul furnizeaza rvalue, compilatorul genereaza cod ce
invoca constructorul de mutare sau operatorul de asignare de mutare
› Scopul constructorul de mutare este de a muta resursele unui
obiect temporar intr-un obiect nou creat (pentru a evita
crearea unei copii a unui temporar).
› Similar, scopul operatorului de asignare de mutare este de a
muta resursele unui obiect temporar intr-un obiect existent
fara a crea un nou obiect.
C++
› Este in regula sa “”furam” resursele unui obiect temporar
deoarece acel obiect este transitoriu si nu mai poate fi folosit
mai tarziu in program.
› Atunci cand viata “efemera” a obiectului tranzitoriu se
“termina” va fi apelat apelat destructorul aferent. Din acest
motiv atat constructorul de mutare cat si operatorul de
asignare de mutare trebuie sa lase respectivul obiect
temporar intr-o stare bine definita. Acesta este si motivul
pentru care parametrul ambelor functii nu este const.
// exemplu de implementare constructor de mutare
IntList::IntList(IntList&& temp): IntList()
C++ {
// Swap contents with the temporary
std::swap(head, temp.head);
std::swap(tail, temp.tail);
std::swap(len, temp.len);
}

› Este apelat constructorul vid pentru a construi o lista valida,


initalizand head, tail si len.
› Sunt interschimbate valorile membrilor listei vide cu cele ale
obiectului temporar.
› De notat ca lista nu a fost parcursa pentru a fi copiata (ce ar fi
insemnat 1.000.000 noduri) iar obiectul temporar nu contine
dangling pointers.
// exemplu de implementare operator de asignare de mutare
IntList& IntList::operator=(IntList&& temp)
C++ {
std::swap(head, temp.head);
std::swap(tail, temp.tail);
std::swap(len, temp.len);
return *this;
}

› Diferenta consta in faptul ca operatorul de asignare de mutare


de ofera obiectului temporar resursele obiectului concret spre
a fi distruse comparative cu constructorul de mutare care
ofera o lista vida.
C++
#include <iostream>
#include "intlist.h“

IntList MakeList(int n)
{
IntList result;
for (int i = 0; i < n; i++)
result.Insert(i); Creating node 0 (21169512)
return result; Creating node 1 (21214272)
} Creating node 2 (21214512)
Creating node 3 (21214304)
int main() Creating node 4 (21214208)
{ 0 1 2 3 4
auto myList = MakeList(5); Destroying node 4 (21214208)
myList.Print(); Destroying node 3 (21214304)
} Destroying node 2 (21214512)
Destroying node 1 (21214272)
Destroying node 0 (21169512)
C++ I/O
› Limbajul C++ nu are instructiuni specifice operațiilor de intrare/ieșire
› La baza operațiilor I/O se află conceptul de stream (flux). Prin stream se
intelege un flux de date de la o sursă la o destinație sau consumator
– sursa – tastatura, fisier pe disc, zona de memorie
– destinatie – monitor, fisier pe disc, zona de memorie
› C++ conține două ierarhii de clase: una are ca radacina clasa streambuf,
iar cealaltă, clasa ios
› streambuf – furnizează funcții generale pentru lucrul cu zonele tampon
(buffers) și permite tratarea operațiilor de intrare/ieșire fără formatări
complexe. Clase derivate – strstreambuf și filebuf
C++
C++
› Clasa ios are un pointer spre streambuf. Contine membri pentru a
controla interfața streambuf și pentru tratarea erorilor. Clase derivate:
istream pentru gestiunea intrărilor și ostream pentru gestiunea iesirilor
class istream : virtual public ios
class ostream : virtual public ios

› O a treia clasa, numita iostream deriva din ambele clase:


class iostream : public istream, public ostream

› Clasele istream, ostream, și iostream sunt, fiecare, clase de bază


pentru alte trei derivari:
class istream_withassign : public istream
class ostream_withassign : public ostream
class iostream_withassign : public iostream
C++
› Clasa istream – permite realizarea de conversii formatate sau
neformatate ale caracterelor încărcate din obiecte de tip streambuf
› Clasa ostream permite conversii formatate sau neformatate în caractere
care se pastreaza în obiecte de tip streambuf
› Clasa iostream mosteneste facilitatile ambelor clase de baza
permitand operatii în ambele directii
› Clasele cu sufixul withassign furnizeaza streamuri standard: cin,
cout, cerr, clog
› Obiectul cin (console input) este o instantiere a clasei
istream_withassign si el corespunde fisierului standard de intrare
definit de pointerul stdin
C++
› Analog, obiectul cout (console output) este o instantiere a clasei
ostream_withassign și el corespunde fisierului standard de iesire definit
de stdout
› Obiectele cerr și clog sunt si ele instantieri ale clasei
ostream_withassign și corespund fisierului standard de iesire definit de
pointerul stderr.
› Clasele cu sufixul withassign se deosebesc de claselor lor de baza prin
faptul că supraincarca operatorul de atribuire.
class istream_withassign : public istream
{
istream_withassign();
istream& operator = (istream&);
istream& operator = (streambuf&);
}
› Obiectele cin, cout, cerr, clog, se instantieza o singura data, chiar daca
fisierul respectiv se include de mai multe ori.
C++ Iesire standard
› Iesirile standard se pot realiza folosind operatorul “<<” supraincarcat pentru
operatii de iesire, numit si operator de inserare
› Operandul stang este un obiect al clasei ostream
› Operandul din dreapta are un tip pentru care a fost supraincarcat operatorul
“<<”. Acesta poate fi un obiect predefinit sau un tip abstract.
ostream& operator << (tip);

› Implicit operatorul de inserare este supraincarcat pentru tipurile predefinite:


char, short, int, long, unsigned, unsigned long, char *, float,
double, long double, void *
› Regulile de prioritate se pastreaza
› Deoarece la supraincarcarea operatorului de inserare se returneaza o
referinta la obiectul curent, operatorii de inserare se pot aplica inlantuiti
C++ Exemple
› 1. cout << i; › 1. printf(“%d”, i);

› 2. cout << “abc”; › 2. printf(“%s”, “abc”);

› 3. cout << “\n”; › 3. printf(“\n”);

› 4. char c[] = “abc”; › 4. char c[] = “abc”;


cout << “c”; printf(“%s”, c);

› 5. cout <<"i=“<<i+10<<endl; › 5. printf("i=%d\n", i+10)


C++ Functii membre
› int width(); //returneaza dimensiunea curenta a campului
› int width(int w); //seteaza dimensiunea campului
› char fill(); //returneaza caracterul de umplere
› char fill(char c); //seteaza caracterul de umplere

cout.width(5); printf("%5d", i);


cout << i;

cout.width(5);
printf("%05d", i);
cout.fill(‘0’);
cout << i;
C++ Indicatori de format
› Sunt definiti in clasa ios si reprezinta biti din dublul cuvant x_flag,
membru al clasei
public enum
{
skipws=0x0001, //Skip white space on input.
left=0x0002, //Left-align values; pad on the right with the fill character.
right=0x0004, //Right-align values; pad on the left with the fill character (default alignment).
internal=0x0008, //Add fill characters after any leading sign or base indication, but before the
value.
dec=0x0010, //Format numeric values as base 10 (decimal) (default radix).
oct=0x0020, //Format numeric values as base 8 (octal).
hex=0x0040, //Format numeric values as base 16 (hexadecimal).
showbase=0x0080, //Display numeric constants in a format that can be read by the C++ compiler.
showpoint=0x0100, //Show decimal point and trailing zeros for floating-point values.
uppercase=0x0200, //Display uppercase A through F for hexadecimal values and E for scientific
values.
showpos=0x0400, //Show plus signs (+) for positive values.
scientific=0x0800, //Display floating-point numbers in scientific format.
fixed=0x1000, //Display floating-point numbers in fixed format.
unitbuf=0x2000, //Cause ostream::osfx to flush the stream after each insertion. By default, cerr
is unit buffered.
stdio=0x4000, //Cause ostream::osfx to flush stdout and stderr after each insertion.
};
C++
› Indicatorii mai sus enumerati sunt grupati in trei grupe:
– adjustfiled – right, left si internal – defineste pozitiile caracterelor de
umplere
– basefield – dec, oct si hex – defineste baza de numeratie
– floatfield – scientific, fixed – definesc formatul de afisare
› In cadrul unei grupe numai un bit poate fi setat
› Biti indicatori de format pot fi setati prin intermediul functiei setf membra a
clasei ios
long setf(long f);
long setf(long bit, long grupa);

› Ambele returneaza valoarea precedenta a x_flag.


› Prima functie modifica formatul curent setand bitii corespunzatori bitilor de 1
din f. Ceilalti corespunzator bitilor de 0 din f raman nemodificati
C++
› Biti indicatori de format pot fi resetati prin intermediul functiei unsetf
membra a clasei ios.

void unsetf(long mask);

int i = 123;

cout.width(5);
cout.fill(' ');
cout.setf(ios::left, ios:adjustfield);
cout.setf(ios::showpos);
cout << i;
C++ Manipulatori
› Permit definirea formatelor pentru operatiile de intrare/iesire.
› Sunt functii membru speciale ce returneaza o referinta la un stream. In
acest fel apelurile manipulatorilor se pot inlantui.
› Practic, facilitatile oferite de functiile width, setf, fill etc. pot fi realizate
cu ajutorul manipulatorilor
Manipulator Actiune
dec Seteaza indicatorul de conversie in zecimal
oct Seteaza indicatorul de conversie in octal
hex Seteaza indicatorul de conversie in hexazecimal
ws Seteaza indicatorul skipws de salt peste caracterele
albe
endl Insereaza newline si videaza zona tampon a streamului
ends Insereaza caracterul nul
flush Videaza zona tampon a unui obiect al clasei ostream
C++
Manipulator Actiune
setbase(int n) Seteaza baza dfe conversie egala cu n;//unde n poate avea una
din valorile 0, 8, 10 sau 16. Valoarea 0 inseamna conversie
implicita, adica in zecimal
resetiosflags(long f) Sterge biti de format specificati de parametrul f
setiosflags(long f) Seteaza bitii de format specificati de parametrul f
setfill(int n) Seteaza caracterul de umplere la valoarea lui n
setprecision(int n) Seteaza precizia la valoarea lui n
setw(int n) Seteaza dimensiunea campului la valoarea lui n

int i = 123;

cout << setw(5) << resetiosflags(ios::internal|ios::right) <<


setiosflags(ios::left) << setfill('0') << i;
C++ Iesire neformatata
ostream& ostream::put(char c);
ostream& ostream::write(const signed char c, int n);
ostream& ostream::write(const unsigned char c, int n);

› Asupra acestor functii bitii de format nu au nici un efect

S-ar putea să vă placă și