Siruri (de caractere) uni-multidimensionale - Pointeri si siruri dinamice
Siruri si clase
Mai intai prezentam siruri de clase. Dorim ca fiecare element sa contina articole de tipuri diferite.
Exemplu
Avem acel exemplu cu bani U.S.D. Vom scrie un program C++ (de fapt un fisier de aplicatie, adica doar functia main()) care citeste o lista (sir a carui tip de baza este clasa Bani) si calculeaza cat de mult difera fiecare suma de cel mai mare element.
// Fisierul "fis4-1.cpp".
//
Acest program contine un vector de elemente de tip "class Bani"
definita in programul "curs3-5.h" si "curs3-5.cpp".
// Prezentam doar partea care este in plus.
// Fisierul "curs4-1.h" contine in plus prototipul:
friend int operator <(const Bani& suma1, const Bani& suma2);
// Preconditie: "suma1" si "suma2" au deja valori
// Returneaza true daca "suma1" este mai mica decat "suma2" altfel false.
// Fisierul "curs4-1.cpp" contine in plus definitia:
int operator <(const Bani& suma1, const Bani& suma2)
// Fisierul "main4-1.cpp" este:
// Fisier de aplicatii care contine functia main().
#include <iostream.h>
#include "curs4-1.h"
int main()
Bani diferenta[5];
for (i = 0; i < 5; i++)
diferenta[i] = max - suma[i];
cout << "Cea mai mare suma este " << max << endl;
cout << "Sumele si diferentele fata de cea mai mare suma sunt:\n";
for (i = 0; i < 5; i++)
cout << suma[i] << " difera prin " << diferenta[i] << endl;
return 0;
}
In continuare studiem posibilitatea definirii intr-o clasa a unui sir ca o variabila membru.
Exemplu
Dorim sa scriem un program C++ care sa memoreze durata probelor unui innotator (alergator) pe distante diferite. Dorim sa obtinem timpul cel mai bun.
class Proba
;
Declaram o variabila ce apartine clasei Proba.
Proba timp;
Mai intai citim distanta, apoi cele 10 durate. La sfarsit, proba care are timpul cel mai mic.
// Fisierul "curs4-2.h"
// Header ce descrie clasa Proba, prototipurile functiilor si operatorilor.
// Acest header se mai numeste si fisier de interfata.
#include <iostream.h>
#include <stdlib.h>
#define NMAX 10
class Proba
;
// Fisierul "curs4-2.cpp"
// Fisier de implementare care contine definitiile tuturor functiilor si operatorilor de supraincarcare.
#include "curs4-2.h"
Proba :: Proba(int distanta1, int durata1[])
Proba :: Proba(int distanta1)
Proba :: Proba()
int min(const Proba& obiect1)
return index;
}
void Proba :: citire()
}
void Proba :: afisare()
void Proba :: afisare_min(int i)
// Fisierul "main4-4.cpp"
// Program care demostreaza folosirea clasei StringVar
#include <iostream.h>
#include "curs4-4.h"
void conversatie(int lung_max);
int main()
void conversatie(int lung_max)
string1 are 14 caractere si string2 are loc pentru 11+ sfarsitul '\0'. In unele implementari C++ aceasta va implica plasarea ultimelor 3 caractere undeva in memorie.
Pentru evitarea acestor probleme, vom defini propria noastra clasa String ce va contine doua variabile membre:
Un sir de caractere numit caracter (nu plasam '\0' in sirul de caractere)
O variabila de tip int care memoreaza numarul de caractere memorate in sirul de caractere. Aceasta variabila va fi numita lungime_curenta.
Clasa String va contine operatiile uzuale pentru sirurile de caractere -concatenarea lor- prin supraincarcarea operatorului +
Exemplu
// Fisierul "curs4-3.h"
// Header ce descrie clasa String, prototipurile functiilor si operatorilor.
// Acest header se mai numeste si fisier de interfata.
#include <iostream.h>
const int MAX_STRING_LENGTH = 100;
class String
;
// Fisierul "curs4-3.cpp"
// Fisier de implementare care contine definitiile tuturor functiilor si operatorilor de supraincarcare.
#include "curs4-3.h"
#include <iostream.h>
#include <stdlib.h>
#include <ctype.h>
const int FALSE = 0;
const int TRUE = 1;
String operator +(const String& sir1, const String& sir2)
// foloseste stdlib.h
void copie_bucata(String& destinatie, const String& sursa, int numar)
if (numar > sursa.lungime)
numar = sursa.lungime;
for (int i = 0; i < numar; i++)
destinatie.caracter[i] = sursa.caracter[i];
destinatie.lungime = numar;
}
int operator ==(const String& sir1, const String& sir2)
else
return (! nepotrivire);
}
}
String :: String()
String :: String(const char a[])
int String :: length() const
// foloseste stdlib.h
char String :: un_caracter(int pozitie) const
return (caracter[pozitie]);
}
void String :: seteaza_caracter(int pozitie, char noul_caracter)
caracter[pozitie] = noul_caracter;
if (pozitie == lungime)
lungime++;
}
// foloseste iostream.h si ctype.h
istream& operator >>(istream& intrare, String& sir)
sir.lungime = i;
// daca cuvantul nu incape, atunci elimina partea ce depaseste
while (! isspace(next))
intrare.get(next);
return intrare;
}
// foloseste iostream.h
ostream& operator <<(ostream& iesire, const String& sir)
void String :: citeste_linia(istream& intrare)
caracter[i] = '\0';
lungime = i;
}
// Fisierul "main4-3.cpp"
// Fisier de aplicatii care contine functia main().
#include <iostream.h>
#include "curs4-3.h"
int main()
Clase si siruri dinamice
Am vazut in exemplul precedent o posibilitate de a defini vectori pentru simularea sirurilor de caractere. Acea definitie a clasei necesita o dimensiune maxima apriorica sirului de caractere. Vom defini o clasa numita StringVar, a carei obiecte sunt variabile sir. Un obiect al acestei clase va fi implementat folosind un vector dinamic a carui dimensiune este determinata cand programul este pus in executie.
Deci obiectul tipului StringVar va avea toate avantajele sirurilor dinamice plus trasaturi suplimentare.
Exemplu:
// Fisierul "curs4-4.h"
// Header ce descrie clasa StringVar, prototipurile functiilor si operatorilor.
// Acest header se mai numeste si fisier de interfata.
// Un obiect al acestei clase va fi definit astfel
StringVar obiect(lungime) si nu obiect[lungime]
#include <iostream.h>
const int MAX_STRING_LENGTH = 100;
class StringVar
;
// Fisierul "curs4-4.cpp"
// Fisier de implementare care contine definitiile tuturor functiilor si operatorilor de supraincarcare.
#include <iostream.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "curs4-4.h"
// foloseste stddef.h si stdlib.h
StringVar :: StringVar(int lungime)
valoare[0] = '\0';
}
// foloseste stddef.h si stdlib.h
StringVar :: StringVar()
valoare[0] = '\0';
}
// foloseste string.h, stddef.h si stdlib.h
StringVar :: StringVar(const char a[])
strcpy(valoare, a);
}
// foloseste string.h, stddef.h si stdlib.h
StringVar :: StringVar(const StringVar& obiect_sir)
strcpy(valoare, obiect_sir.valoare);
}
StringVar :: ~StringVar()
// foloseste string.h
int StringVar :: length() const
// foloseste iostream.h
void StringVar :: citeste_linie(istream& intrare)
// foloseste iostream.h
ostream& operator <<(ostream& iesire, const StringVar& sir)
// Fisierul "main4-4.cpp"
// Program care demostreaza folosirea clasei StringVar
#include <iostream.h>
#include "curs4-4.h"
void conversatie(int lung_max);
int main()
void conversatie(int lung_max)
Destructori
Un destructor este o functie membru a unei clase care este apelat automat cand un obiect al clasei nu mai este in domeniu. Asta insemna ca daca un obiect al clasei este o variabila locala a unei functii atunci destructorul este automat apelat ca o ultima actiune inaintea sfarsitului apelului functiei.
Destructorii sunt folositi pentru a elimina orice variabila dinamica care a fost creata de catre obiect astfel incat memoria ocupata de aceste variabile dinamice se returneaza catre heap. Numele unui destructor este format din ~
urmata de numele clasei.
Constructorul de copiere
Cand un parametru al unui apel prin valoare este de tip pointer comportarea acestuia poate crea probleme.
Un constructor de copiere este un constructor care are un parametru ce este de acelasi tip ca si clasa. Acest paramentru trebuie sa fie parametrul apel prin referinta (normal acesta este precedat de modificatorul const).
Constructorul de copiere pentru o clasa este apelat automat cand o functie returneaza o valoare de tipul clasei. Constructorul de copiere este de asemeni apelat automat cand un argument este parametru apel prin valoare al unui tip
ce clasa. Un constructor de copiere poate fi de asemeni folosit in acelasi mod ca alti construtori.
Orice clasa care foloseste pointeri si operatorul new ar trebui sa aiba un constructor de copiere.
Exemplu
void afisare (StringVar sir)
Consideram codul, care include un apel al functiei de mai sus:
StringVar mesaj("Ce mai faci?");
afisare(mesaj);
cout << "Dupa apel:" << mesaj << endl;
I. Presupunem ca nu avem constructori de copiere. Se observa imediat ca dupa apelul functiei afisare se apeleaza automat destructorul clasei care va returna memoria folosita de sir folosita in heap. Dar sir si mesaj sunt variabile pointer care au aceeasi adresa, deci practic dupa apel variabila mesaj nu va mai fi accesibila.
II.Presupunem ca avem constructor de copiere. Atunci in momentul apelului
afisare(mesaj);
constructorul de copiere este automat apelat si se va crea o copie a argumentului de apel astfel incat parametrul formal sa va unifica cu copia parametrului actual.
In cazul exemplului nostru, modificarea variabilei sir nu va implica si modificarea variabilei mesaj. Cu alte cuvinte, variabila mesaj va avea aceeasi valoare.
Supraincarcarea operatorului de asignare
Exemplu
StringVar sir1(10),sir2(10);
sir1=sir2;
De obicei, versiunea predefinita a intructiunii de asignare (a doua obiecte) va copia valorile variabilelor membre ale lui sir2 in variabilele membre ale lui sir1. Aceasta poate cauza probleme, deoarece variabila membru valoare este
un pointer. Deci sir1.valoare si sir2.valoare puncteaza la aceleasi adrese de memorie.
Modificarea lui sir1 (sau a lui sir2) va cauza si modificarea (fara voie) a lui sir2 (sau a lui sir1).
Asadar vom supraincarca operatorul de asignare. Acesta nu se poate supraincarca ca ceilalti operatori, cum ar fi << si + (= este de fapt o instructiune de asignare) cand supraincarcam operatorul de asignare acesta trebuie sa fie un membru al clasei, nu poate fi friend al clasei.
Vom adauga in sectiunea public a clasei:
void operator = (const StringVar & parte_dreapta);
Deci un apel de genul (instructiune)
sir1 = sir2;
va insemna ca sir1 este obiectul apelat si sir2 este argumentul operatorului membru = .
Definitia operatorului poate fi:
StringVar::operator = ( const StringVar& parte_dreapta )
}
for(int i=0;i<noua_lungime;i++)
valoare[i]=parte_dreapta.valoare[i];
valoare[noua_lungime]='\0';
}
Exercitii propuse spre implementare
Scrieti un program C++ in care sa descrieti operatiile fundamentale cu numere intregi foarte mari memorate ca vectori. Numarul maxim de cifre se fixeaza aprioric. Clasa definita trebuie sa contina supraincarcarea operatorilor +,-,/,*,%
Scrieti un program C++ care citeste o linie de text si afiseaza numarul de cuvinte din linie, respectiv numarul de aparitii a fiecarei litere din linia de text (puteti presupune ca nu are importanta e majuscula sau nu !).
Scrieti un program C++ care foloseste clasa StringVar si defineste functiile si operatorii supraincarcati in clasa String.
|