This is an old revision of the document!


Laboratorul 02: Elemente Specifice C++

Laboratorul de azi are ca scop acomodarea voastra cu limbajul C++. Asta presupune tranzitia de la limbajul C, dar si intelegerea conceptelor elementare, noi, ce tin de C++. Asadar, materialul de astazi poate fi vazut ca un fel de 'Crash Course' pentru C++. El reprezinta minimul necesar care trebuie stiut pentru a intelege continutul laboratoarelor urmatoare, dar nu elimina necesitatea voastra de a exersa cu limbajul pentru a deveni familiari cu acesta.

Inainte de fiecare laborator, vom prezenta surse aditionale de informare, unde veti putea gasi explicatii suplimentare si exemple concrete.

In cazul laboratorului curent, recomandam urmatoarele capitole din cartea Absolute C++

  • Capitolul 1 (Chapter 1. pag 1-45): C++ Basics
  • Capitolul 2 (Chapter 2. pag 45-99): Flow of Control (if-else, for etc)
  • Capitolul 10 (Chapter 10. pag 419-471): Pointers and Dynamic Arrays

1. Comentarii "stil" C++

Putem scrie comentarii pe o linie folosind simbolurile

//

Ce este intre 'forward-slash-uri' si sfarsitul liniei se ignora.

// Comentariu C++
 
/* Comentariu C */
 
/*
 * Comentariu Stil Documentatie (Asemanator C)
 */

2. Struct si Union

Structurile si uniunile pot fi referite doar prin nume, deci fara a fi nevoiti sa utilizam cuvintele cheie struct sau union de fiecare data. Asta arata ca struct-urile si union-urile sunt in C++ tipuri de date recunoscute automat.

struct str
{
    // Cod aferent structurii 
};
 
union uni
{
    // Cod aferent union
};
 
str a, b;
uni c, d;
 
void f(str x) {/* cod functie */}

3. Functii membre ale unei structuri

Pentru a modifica campurile unei variabile de tip struct, putem utiliza functiile membre (metode). Aceste functii au urmatoarele caracteristici:

  • variabila struct asupra careia se lucreaza nu este data ca parametru
  • nu cresc dimensiunea tipului de date
  • au acces direct la campurile structurii, inclusiv cele declarate private

Vom prezenta un exemplu de utilizare al acestor functii. Utilitatea lor se va vedea in cazul programarii obiectuale.
Exemplul poate fi rulat aici

#include <iostream>
#include <cstdlib>
 
using namespace std;
 
struct point{
  private: //altfel, sunt implicit public
    int x;
    int y;
  public: //pentru a fi vizibile de oriunde
    void modificare(int, int); //implementarile functiilor se face
    void afisare();            //dupa definirea structurii
};
 
//Implementarea metodelor din struct
void point::modificare(int xx, int yy)
{
  x = xx; //functiile membre au acces la campurile private
  y = yy;
}
 
void point::afisare()
{
  cout << x << " " << y << endl;
}
 
 
int main() 
{
  point p; //declararea unei variabile de tip struct point
 
  p.afisare(); //va afisa ce se afla in memorie, la adresele campurilor x si y
 
  p.modificare(3, 3); //modificam valorile lui x si y folosind functia membra
 
  p.afisare(); //va afisa noile valori 3 si 3
 
  return 0;
}

4. Functii Inline

O functie inline e definita in felul urmator:

inline TIP_RETURNAT NUME_FUNCTIE(LISTA_ARGUMENTE) {/* cod functie */}

De ce sunt utile functiile inline? La apelul unei functii inline, in loc de apelarea propriu-zisa a functiei, se substituie codul efectiv al functiei la compilare. Avantajul este in termeni de viteza. Nu se va mai 'cauta' definitia functiei, va fi 'direct' unde trebuie. Dezavantajul este cresterea dimensiunii fisierului binar generat. Asadar, functiile inline reprezinta un trade-off intre viteza si spatiu.

Pentru a rula exemplul de mai jos, click aici.

#include <iostream>
using namespace std;
 
// Declaram functia 'max' drept inline
inline int max(int a, int b)
{
  return (a > b) ? a : b;
}
 
/* Echivalent in C */
#define max(a,b) (((a) > (b)) ? (a) : (b))
 
int main() {
 
  int nr1 = 2, nr2 = 6;
  cout << max(nr1, nr2) << endl;
 
  return 0;
}

5. Supradefinirea functiilor

In C++ este permisa supradefinirea functiilor, adica existenta mai multor functii cu acelasi nume, dar care difera prin tipul si/sau numarul parametrilor. Decizia de a apela o functie sau alta se face dupa tipul sau numarul parametrilor.

Aceasta inseamna ca o functie este recunoscuta nu numai prin numele sau, ci prin “semnatura”, adica nume, tip si lista de parametri.

In exemplul de fata, se considera patru functii cu acelasi nume (cub). Trei dintre ele au un unic parametru (int, float si double), iar a patra are doi parametri. Ultima functie apeleaza functia cub specifica tipului float. Pentru a urmari apelurile, fiecare functie este insotita de un mesaj de identificare la consola.

Pentru a experimenta cu codul de mai jos, click aici.

#include <iostream>
using namespace std;
 
int cub (int n)
{    cout << "Apel functie cub int\n"; return n*n*n; }
 
double cub (double n)
{    cout << "Apel functie cub double\n"; return n*n*n; }
 
float cub (float n) 
{    cout << "Apel functie cub float\n"; return n*n*n; }
 
float cub (float x, float a)
{  cout << "Apel functie cub diferenta\n";
     return cub (x-a);  // Aici se va apela cub cu float  
}
 
int main() {
 
  cout << cub(3) << endl << endl;
 
  double var = 3.33335678976;
  cout << cub(var) << endl << endl;
 
  float var2 = 3.2;
  cout << cub(var2) << endl << endl;
 
  cout << cub(9.9, 3.3) << endl << endl;
 
  return 0;
}

6. Operatorii de scriere (>>) si citire (<<)

Prin acesti operatori se pot face scrieri si citiri la consola in formate predefinite. Aceasta ne scuteste de folosirea functiilor de tip printf/scanf etc. si ofera o forma comoda de I/O.

Codul de mai jos poate fi executat de aici.

#include <iostream>
using namespace std;
 
int main() 
{
  int i, j, k;
 
  cout << "Introduceti 3 intregi:\n";
  cin >> i >> j >> k;
  cout << "Intregii sunt:\n";
  cout << i << " " << j << " " << k << "\n";
 
  return 0;
}

7. Tipul referinta

O declaratie de forma

int x;
int &y = x;

precizeaza ca y este o referinta catre un intreg (catre x). Din punct de vedere sintactic, y este tot un intreg, care nu “exista” insa (nu are suport propriu de memorie) ci este o alta referire la variabila x. Utilitatea principala a referintelor este impunerea transferului prin referinta a parametrilor la o functie.

Acest fapt este ilustrat prin functia schimba de mai jos.

void schimba (int & a,int & b)
{    int temp = a; a = b; b = temp; }

Declaratiile de parametri spun ca a si b sunt referinte catre intregi, deci la un apel de forma:

int x = 7, y = 3;
schimba(x, y);

in variabilele a si b din functie se vor gasi referinte catre intregii x si y din programul apelant. Aceasta face ca functia sa poata modifica efectiv aceste variabile.

De comparat cu metoda de implementare a transferului prin referinta la C standard, unde trebuiau utilizati explicit pointeri

void schimba_C (int * a, int * b)
{    int temp = *a; *a = *b; *b = temp; }

cu apelul

int x = 7, y = 3;
schimba_C (&x, &y);

Codul complet il puteti rula de aici.

#include <iostream>
using namespace std;
 
void schimba (int & a,int & b)
{    int temp = a; a = b; b = temp; }
 
int main() {
 
  int a = 4, b = 20;
  cout << "Inainte de interschimbare: " << endl;
  cout << a << " " << b << endl;
 
  // Apelam interschimbarea
  schimba(a,b);
 
  cout << "Dupa interschimbare:" << endl;
 
  cout << a << " " << b << endl;
 
  return 0;
}

8. Alocari dinamice de memorie

Alocarea de memorie se face in C++ cu operatorul new (care face parte din limbaj). Enumeram urmatoarele forme de alocare:

  • Alocare simpla
// Variabila
int var = new int;
  • Alocarea unui tablou
int dimensiune = 10;
int *vect = new int[dimensiune];
  • Alocarea cu initializare
// Varianta C++
int *vect = new int[20]();
 
/* Echivalent, in C, ati fi procedat astfel: */
int i;
int *vect = malloc(sizeof(int) * 20);
for (i = 0; i < 20; i++) {
    vect[i] = 0;
}

Atentie la utilizazarea pointerilor adecvati.

Daca se doreste o alocare oarecare (fara a avea un tip precizat), se poate folosi tipul char (adica alocare la nivel de octet), sau unsigned char, “botezat” eventual cu typedef.

// vrem sa alocam 20 de bytes, pentru un sir de caractere
// 1 byte == sizeof(char)
char *s1 = new char[sizeof(char) * 20 + 1];
// sau
char *s2 = new char[20*sizeof(unsigned char) + 1]

Metoda prezentata mai sus este nefolosita in industrie, tipul char* fiind inlocuit de tipul C++ string. Pentru mai multe detalii asupra clasei string, click aici. De asemenea, puteti inspecta Capitolul 9 (Chapter 9, pag 367-) : Strings, din cartea Absolute C++ pentru explicatii suplimentare, dar si exemple concrete.

9. Functii cu parametri impliciti

Functia print_tab de mai jos are trei parametri, dintre care ultimul este de tip pointer la char, care este initializat implicit pe sirul constant “Mesaj implicit”. Functia se poate apela cu un parametru explicit, sau ca si cand ar fi doar cu doi parametri, caz in care se va utiliza al treilea parametru implicit.

// Definitie
void print_tab (int *a, int n, char *mesaj = "Mesaj implicit\n")
{
    for (int i = 0; i < n; i++)
        cout << a[i] << " ";
    cout << "\n" << mesaj;
}
 
// Apeluri
print_tab (q, 10, "Mesaj explicit\n");
print_tab (q, 10);

10. Experiment

Ca sa testati toate conceptele prezentate in acest laborator, rulati codul de mai jos apasand aici.

#include <cstdlib>
#include <iostream>
 
using namespace std;
 
 
int cub (int n)
{    cout << "Apel functie cub int\n"; return n*n*n; }
 
double cub (double n)
{    cout << "Apel functie cub double\n"; return n*n*n; }
 
float cub (float n) 
{    cout << "Apel functie cub float\n"; return n*n*n; }
 
float cub (float x, float a)
{    
  float m; 
  cout << "Apel functie cub diferenta\n";
  return cub (x-a);  // Aici se va apela cub cu float  
}
 
void schimba (int & a,int & b)
{    int temp = a; a = b; b = temp; }
 
void print_tab (int *a, int n, char *mesaj = "Mesaj implicit\n")
{
  for (int i = 0; i < n; i++)
      cout << a[i] << " ";
  cout << "\n" << mesaj;
}
 
int main(int argc, char *argv[])
{
   int i = 12; float f = 12.5F; double d = 12.5;
    int j = 21, k; int *p, *q;
//
//   Test functii cub
//
    cout << cub(i) << "  " << cub (f) << "  " << cub (d) << "\n";
    cout << cub (f, 2.5) << "\n";
    cout << "i = " << i << " j = " << j << " inainte de schimba(i,j)\n";
    schimba (i, j);
    cout << "i = " << i << " j = " << j << " dupa schimba(i.j)\n";
//
//   Test << si >>
//
    cout << "Introduceti 3 intregi:\n";
    cin >> i >> j >> k;
    cout << "Intregii sunt:\n";
    cout << i << " " << j << " " << k << "\n";
//
//   Test operatori new si delete
//
    p = new int; *p = 30;          // Alocare simpla
    cout << *p << "\n"; delete p;
    p  =  new  int (100);          //  Alocare cu initializare
    cout << *p << "\n";  delete  p;  
    q = new int [10];              // Alocare tablou de 10 int
    for (i = 0; i < 10; i++)
        *(q + i) = i + 1; //q[i]=i+1;
    for (i = 0; i < 10; i++)
    	cout << q[i] << " ";
    cout << "\n";
//
//   Test functie cu argument implicit
//
    print_tab (q, 10, "Mesaj explicit\n");
    print_tab (q, 10);
    delete [] q;	 	// Eliberare tablou
//
//	La compilatoarele mai vechi, eliberarea de tablouri (i.e. operatorul delete []) nu este posibila.
//	Se foloseste in acest caz delete simplu.
//
 
 
    return 0;
}
poo-is/laboratoare/02.1601543953.txt.gz · Last modified: 2020/10/01 12:19 by alexandru.ionita99
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0