PARTICULARITĂȚILE ELABORĂRII APLICAȚIILOR C++
Croitor Mihail
Cuprins
Pointer la funcție Moștenire Funcții virtuale
Polimorfism în baza funcțiilor virtuale Clase abstracte
Redefinire operatorilor Obiecte funcționale Șabloane
Polimorfism static Specializare șabloanelor
Unele particularități ale standardului C++11
Pointer la funcție
Pointer la funcție este un pointer, care conține adresa punctului de întrare în această funcție.
Declararea variabilei-pointer la funcție:void (*pfunc)(double*, const size_t);
Declararea tipului pointer la funcție:typedef void (*pfunc_t)(double*, const size_t);
Pointer la funcție: exemplu
#include <stdio.h>typedef void (*event_t)(void*, void*);
struct Button{ size_t x, y, width, height; char text[255]; event_t OnClick;};
void ButtonPressed(void* /*sender*/, void* /*arg*/){ puts("button pressed!");}
int main(){ Button btn = {5, 5, 75, 30, "Press Me!", ButtonPressed}; btn.OnClick(); return 0;}
Moștenire
Moștenirea este un mecanism, care permite descrierea clasei noi (clasa copil, clasa derivată) în baza unei clase existente (clasa de bază, clasa părinte) cu păstrarea funcționalității clasei de bază.
Moștenirea permite manipularea cu obiecte de clasa derivata ca cu obiecte clasei de bază.
Pointer la obiectul clasei de bază poate referi la obiectul clasei derivate. Afirmație inversă este greșită.
class Base {int value;
public:Base(): value(0){}int getValue() { return value; }
};
class Derived: public Base{public:
Derived() : Base() {}void setValue(int data) { value = data; }
};
Exemplu moștenirii:
Moștenire: exemplu
class Entity{protected:
std::string name;Entity* parent;
public:virtual std::string ToString() const = 0;Entity* GetParent() const{ return parent; }
};
class Folder: public Entity{std::list<Entity*> childs;
public:Folder(std::string n, Entity* p): name(n), parent(p){}std::string ToString() const { return name; }
};
Funcții virtuale
Funcție virtuală a clasei este o metodă, care poate fi redefinită în clasa copil și în dependența de situație se determină în timpul executării aplicației care realizarea concretă a funcției va fi apelată.
Polimorfismul în baza funcţiilor virtuale se numeste polimorfism dinamic. Această proprietate printre altele permite invocarea unei şi aceleiaşi funcţiei (fără redeclararea/redefinirea) cu parametrii actuali de diferite tipuri.
class Base{public: Base(): value(0){} virtual void show() { std::cout << "Base" << std::endl; }}; class Derived: public Base{public: Derived() : Base() {} virtual void show() { std::cout << «Derived" << std::endl; }};
Exemplu:
Funcții virtuale:exemplu de polimorfism
class Base{public: Base(): value(0){} virtual void show() { std::cout << "Base" << std::endl; }}; class Derived: public Base{public: Derived() : Base() {} virtual void show() { std::cout << "Derived" << std::endl; }}; void print(Base* p) { p->show(); } int main(){ Base* b = new Base(); Derived* d = new Derived(); print(b); print(d); return 0;}
Funcții virtuale:clase abstracte
O funcție virtuala poate fi declarata in clasa fără definirea corpului ei. In acest caz funcția se egalează cu zero si se numește funcția virtuală pură sau funcția abstractă. Daca o clasa conține cel puțin o funcție virtuala pura, atunci aceasta clasa se numește clasa abstracta.Orice funcție abstractă se redefinește în clasa-copil.Clasele abstracte se utilizează în cazuri când programator are nevoie de o mulțime de clase cu comportament comun.
struct Shape{virtual void Draw() = 0;virtual void Hide() = 0;
};struct Point: Shape{
virtual void Draw(){ // draw this point
}virtual void Hide(){ // hide this point}
};Clase abstracte sunt asemănătoareinterfețelor din Java și C#
Redefinirea operatorilor
pentru comoditate, este utila supraîncărcarea (redefinirea) unui operator standard pentru o clasa. Programul in acest caz devine mai compact si mai clar. Forma comuna pentru supraincarcare este urmatoarea:<return_type> operator <operator_sign> (<operator_parametres>);
сlass int_pair{ int first, second;public: int_pair(int f = 0, int s = 0): first(f), second(s) {} bool operator == (const int_pair& p) const { return (first == p.first) && (second == p.second); }};
bool operator != (const int_pair& p1, const int_pair& p2){ return !(p1 == p2);}
pentru a determina locul de declarare a operatorului - in cadrul clasei, sau in afara ei - întrebați-va, daca acest operator schimba parametrii de intrare. Daca da, atunci se recomanda declararea acestui operator in clasa, dacă nu, atunci mai bine declarați-l in afara clasei.
Obiecte funcționale
In declarația clasei poate fi supraîncărcat operatorul (). Daca acest operator este supraîncărcat, atunci obiectele acestei clase obțin unele proprietăți ale funcțiilor (ele pot fi utilizate in același mod ca si funcții). Aceste obiecte se numesc obiecte funcționale sau functori. Utilizarea functorilor este comoda atunci când funcția trebuie sa aibă "memorie", sau ca o alternativa pointerilor la funcție.
// functor care socoate numărul de apeluriclass _swap{ static size_t counter = 0; static void increment() { ++counter; }public: _swap(){} void operator ()(int& a, int& b){ int tmp = a; a = b; b = tmp; inc(); } int getNrCalls() {return count; }};_swap swap;int a = 3, b = 5;swap(a, b);
Șabloane
Șablonul clasei reprezintă un mecanism de generare a familiei de clase cu comportament comun
Șablonul funcției reprezintă un mecanism de generare a familiei de funcții cu comportament comun
template<class TYPE>void swap(TYPE& p1, TYPE& p2){ TYPE tmp = p1; p1 = p2; p2 = tmp;}
Noțiunea șablonul funcției deseori se asociază cu noțiunea de algoritm.
Șabloane: polimorfism static
int main(){ int i1 = 3, i2 = 5; float f1 = 1.2, f2 = 2.3; double d1 = 1.003, d2 = 10; swap(i1, i2); swap(f1, f2); swap(d1, d2); return 0;}
În exemplu dat, una si aceeași denumirea funcției este utilizata cu tipuri de date diferite. Aceasta proprietate, realizată cu ajutorul șabloanelor, se numește polimorfism static, deoarece determinarea comportamentului funcției are loc la etapa de compilare a programului.
Șabloane:specializarea șabloanelor
Pentru un tip de date concret poate fi definită o versiune specială a șablonului.
template<>void swap(int* a, int* b){
int tmp = *a; *a = *b;
*b = tmp;}
Dacă un șablon (și/sau specializarea lui) nu se utilizează în codul sursă atunci la etapa de compilare el nu se include în codul binar!
С++11: tipuri automate
Cuvântul cheie auto spune compilatorului să determine tipul variabilei in mod automat, după valoare
auto val = 5;
С++11: inițializatori
A apărut posibilitatea inițializării claselor containeri cu ajutorul listelor de inițializare.
class Array10{ int _val[10];public: Array10(std::initializer_list<int> list);};
Array10 a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
С++11: lambda-funcții
Expresii lambda permit programatorilor să accelereze scrierea codului. Sub expresii lambda înțeleg un mod special de definirea functorilor.
[](int x, int y) { return x + y; }
// то же, что и [](int x, int y) -> int{ int result = x + y; return result; }
struct sum{ int opertator()(int x, int y){ return x+y;}
};
C++11: pointer nul
Utilizarea valorii 0 ca pointer nul în unele cazuri aducea la cod periculos. Se recomandă utilizarea cuvântului cheie nullptr.
void test(int);void test(char*);int main(){ test(0); // ??? test(nullptr); // !!! return true;}
С++11: buclă «pentru fiecare»
int a[5] = {1, 2, 3, 4, 5};
for(auto& el: a){++el;}
for(auto el: a){std::cout << el << " ";}
Standard nou С++ propune o formă nouă de bucla for: for( type element: collection){}
Mulțumesc de atenție!
Recommended