This is an old revision of the document!


Laboratorul 02: Elemente specifice C++

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)

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 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;
}

4. 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;
}

5. 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;
}

6. 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;
}

7. 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, inspectati resursele utile pentru explicatii suplimentare, dar si exemple concrete.

8. 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);

9. 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.1599482095.txt.gz · Last modified: 2020/09/07 15:34 by sergiu.craioveanu
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