Ho un problema, credo di sapere dove ma non so come fare ad evitarlo.
Per prima cosa il codice:
#ifndef STRINGA_H
#define STRINGA_H
#pragma once
#include <iostream>
using std::ostream;
using std::istream;
class Stringa
{
friend ostream &operator<<(ostream &uscita, const Stringa &s);
friend istream &operator>>(istream &ingresso, Stringa &s);
public:
Stringa(const char *s = "");// costruttore di defoult, assegna per defoult una stringa vuota
Stringa(const Stringa &s);// costruttore di copia
~Stringa();//distruttore
// riassegnazione di operatori
const Stringa &operator=(const Stringa &s);//operatore di assegnamento
const Stringa &operator+=(const Stringa &s);//operatore di concatenamento
const Stringa &operator+(const Stringa &s);//operatore di concatenamento in stringa nuova
bool operator!() const;// risponde alla domanda !s -> "la stringa this è vuota?" Questa è una ridefinizione non in linea con il significato dell'operatore !
bool operator==(const Stringa &s) const;// verifica se this == s
bool operator<(const Stringa &s) const;// verifica se this < s
char &operator[](int n);//operatore di indicizzazione lvalue
char operator[](int n) const;//operatore di indicizzazione rvalue
Stringa operator()(int n, int l = 0);//estrae una sottostringa dalla posizione n, lunga l, l per default può valere 0 lo considererò "finoi alla fine"
int getLunghezza() const;//restituisce la lunghezza della stringa
// riassegnazione di operatori in line
bool operator!=(const Stringa &s) const {
return !(*this == s);//!(==)
}// verifica se this != s e permette la chiamata in line della funzione serve la classe proxi
bool operator>(const Stringa &s) const {// verifica se this > s
return (s < *this); // this > s -> s < this
}// verifica se this > s e permette la chiamata in line della funzione serve la classe proxi
bool operator<=(const Stringa &s) const {// verifica se this <= s
return !(s < *this); // this <= s -> s >= this -> !(s < this)
}// verifica se this <= s e permette la chiamata in line della funzione serve la classe proxi
bool operator>=(const Stringa &s) const {// verifica se this >= s
return !(*this < s); //this >= s -> !(this < s)
}// verifica se this >= s e permette la chiamata in line della funzione serve la classe proxi
private:
//membri dato
int lunghezza;//lunghezza della stringa senza il carattere terminatore
char *ptrStringa;//puntatore al primo carattere della stringa
//funzioni
void setStringa(const char *s);//funzione che imposta la stringa
};
#endif // !STRINGA_H
#include "stdafx.h"
#include "Stringa.h"
#include <iostream>
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
#include <iomanip>
using std::setw;
#include <cstring>
using std::strcpy;
using std::strcat;
using std::strcmp;
using std::strlen;
#include <cstdlib>
using std::exit;
Stringa::Stringa(const char *s)
:lunghezza(s != "" ? strlen(s) : 0)// lunghezza o 0 oppure lunghezza di s
{
cout << "costruttore di default e di conversione: " << s << endl;
setStringa(s);// utilizzata per creare l'istanza dell'oggetto
}
Stringa::Stringa(const Stringa & s)//costruttore di copia
: lunghezza(s.lunghezza)
{
cout << "costruttore di copia: " << s.ptrStringa << endl;
setStringa(s.ptrStringa);// utilizzata ancora per creare l'istanza dell'oggetto
}// la corretta costruzione dell'oggetto è sempre gestita dalla funzione setStringa(s)
Stringa::~Stringa()
{
cout << "distruttore: " << ptrStringa << endl;
delete [] ptrStringa;
}
const Stringa & Stringa::operator=(const Stringa & s)
{
cout << "chiamata all'operatore =" << endl;
// evitiamo l'autoassegnamento
if (&s != this) {
delete[] ptrStringa;//cancello la vecchia stringa
lunghezza = s.lunghezza;
setStringa(s.ptrStringa);// utilizzata ancora per creare l'istanza dell'oggetto
}
else {// se cerco di copiare su me stesso, avviso
cout << "sto cercando di copiare una stringa su se stessa" << endl;
}// in entrambi i casi ritorno this
return *this;
}
const Stringa & Stringa::operator+=(const Stringa & s)
{
int newLunghezza = this->lunghezza + s.lunghezza;
char *ptrTmp = new char[newLunghezza + 1];//mi creo il nuovo contenuto dell'oggetto
strcpy(ptrTmp, ptrStringa);//copio nella nuova stringa la prima parte, quella dell'oggetto "chiamante"
strcpy(ptrTmp + lunghezza, s.ptrStringa);//(ptrTmp + lunghezza) è il puntatore alla posizione dove inserire la stringa successiva da concatenare
delete [] ptrStringa;// cancello lo spazio di memoria occupato precedentemente
ptrStringa = ptrTmp; // associo il nuovo puntatore al puntatore dell'oggetto
lunghezza = newLunghezza;// il valore della nuova lunghezza
return *this;
}
const Stringa & Stringa::operator+(const Stringa & s)
{
Stringa *ptrTmpStringa = new Stringa("");//allochiamno la memoria per la stringa temporanea
ptrTmpStringa->lunghezza = this->lunghezza + s.lunghezza; //assegno la lunghezza della nuova stringa
strcpy(ptrTmpStringa->ptrStringa, this->ptrStringa);//copio nella nuova stringa la prima parte, quella dell'oggetto "chiamante"
strcpy(ptrTmpStringa->ptrStringa + lunghezza, s.ptrStringa);//(ptrTmp + lunghezza) è il puntatore alla posizione dove inserire la stringa successiva da concatenare
return *ptrTmpStringa;
}
bool Stringa::operator!() const
{
return (lunghezza == 0);// le lunghezza è uguale a 0
}
bool Stringa::operator==(const Stringa & s) const
{
return (strcmp(ptrStringa, s.ptrStringa) == 0);//strcmp ritorna 0 se le due stringhe sono uguali
}
bool Stringa::operator<(const Stringa & s) const
{
return (strcmp(ptrStringa, s.ptrStringa) < 0);//strcmp ritorna un numero negativo se la prima stringa è minore della seconda
}
char & Stringa::operator[](int n)
{
if ((n < 0) || (n >= lunghezza)) {//controllo che l'indice richiesto appartenga al range ammesso
cerr << "Errore: indice = " << n << " out of range (" << lunghezza << ")" << endl;
return ptrStringa[lunghezza];
//exit(1);//fine del programma per errore lo maschero
}
return ptrStringa[n];//se tutto OK ritorno il carattere richiesto modificabile
}
char Stringa::operator[](int n) const
{
if ((n < 0) || (n >= lunghezza)) {//controllo che l'indice richiesto appartenga al range ammesso
cerr << "Errore: indice = " << n << " out of range (" << lunghezza << ")" << endl;
return '\0';
//exit(1);//fine del programma per errore
}
return ptrStringa[n];//se tutto OK ritorno il carattere richiesto costante
}
int Stringa::getLunghezza() const
{
return lunghezza;
}
void Stringa::setStringa(const char * s)
{// funzione alla base dell'inizializzazione della classe
ptrStringa = new char[lunghezza + 1];//allochiamno la memoria necessaria (lunghezza + 1) per il carattere di terminazione
if (s != 0) {//se s punta ad una stringa la copio
strcpy(ptrStringa, s);//lunghezza + 1 è richiesto da strcpy_s per evitare errori di overflow buffer
}
else {//altrimenti copio una stringa vuota
ptrStringa[0] = '\0';
}
}
ostream & operator<<(ostream & uscita, const Stringa & s)
{
uscita << s.ptrStringa;
return uscita;
}
istream & operator>>(istream & ingresso, Stringa & s)
{
char temp[100]; //mi creo un buffer per memorizzare l'ingresso -> 99 caratteri c'è anche il terminatore
ingresso >> setw(100) >> temp;//memorizzo l'input nel buffer temporaneo
s = temp; // utilizzo la ridefinizione dell'operatore = "const Stringa & Stringa::operator=(const Stringa & s)"
return ingresso;
}
Stringa Stringa::operator()(int n, int l)
{//estrae una sottostringa dalla posizione n, lunga l
if ((n < 0) || (n >= lunghezza) || (l < 0)) {//controllo la correttezza dell'indice n e controllo che l sia maggiore di 0
return "";// viene restituita una stringa vuota, viene automaticamente convertita nell'oggetto Stringa
}
int newLunghezza;//si determina la lunghezza della sottostringa
if ((l == 0) || (n + l > lunghezza)) {//se l è uguale a 0 oppure se n+l è maggiore della lunghezza totale della stringa
newLunghezza = lunghezza - n;//allora la lunghezza della nuova stringa è dal'indice n fino alla fine
}
else {
newLunghezza = l;//altrimenti è uguale a l
}
char *ptrTmpStringa = new char[newLunghezza + 1];//c'è sempre il carattere di terminazione
//copio la sottostringa nell'area di memoria a lei dedicata
strncpy(ptrTmpStringa, &ptrStringa[n], newLunghezza); //copio in ptrTempStringa la sottostringa di ptrStringa lunga newLunghezza partendo dalla posizione n
ptrTmpStringa[newLunghezza] = '\0';//aggiungo il carattere di terminazione
//creo per copia l'oggetto Tmpstringa
Stringa tmpStringa(ptrTmpStringa);//richiamo il costruttore
delete[] ptrTmpStringa;//cancello l'array temporaneo (la sottostringa è oramai salva in tmpStringa
return tmpStringa; //ritorno una copia della stringa temporanea
}
#include "stdafx.h"
#include <iostream>
using std::cout;
using std::cin;
..
int main()
{
Stringa prima("questa e' la prima");
cout << "----1" << endl;
Stringa seconda("questa e' la seconda");
cout << "----2" << endl;
Stringa terza(" ");
cout << "----3" << endl;
Stringa quarta;
cout << "----4" << endl;
quarta = prima + terza;
cout << "----5" << endl;
quarta += seconda;
cout << "----6" << endl;
cout << quarta << endl;
cout << "----7" << endl;
cout << "\n\n\nProgramma terminato, premi return per continuare" << endl;
{ int a = 0;
while (getchar() != '\n'); // svuoto il buffer di ingresso per evitare la chiusura immediata
cout << "premi un tasto e return per continuare "; // serve per bloccare la finestra Consol
cin >> a;
return 0; // terminazione programma corretta
}
}
Per evitarvi tutta la lettura del codice è una "scrittura" scolastica della classe striga "scimiottando" la classe string del c++
tutto è funzionato correttamente fino a quando ho provato ad implementare l'overload dell'operatore "+"
Il problema è che quando chiudo il programma genero un errore per "heap danneggiato"
Credo che il problema (ovviamente) sia la funzione aggiunta
const Stringa &operator+(const Stringa &s);//operatore di concatenamento in stringa nuova
Al suo interno alloco la memoria per la nuova stringa
quando ritorno (se ho capito bene la teoria) cancello tutto, ma mi salvo il puntatore che assegno ad una nuova Stringa, ma poi quando vado a concludere il programma questo cerca di cancellare qualcosa che è già stato cancellato, e qui a mio parere nascono i problemi.
Ho questa idea, perché ho nella testa una lacuna che non mi riesco a chiarire:
supponiamo di avere una funzione che ritorna una stringa, la stringa la creo all'interno della funzione e la ritorno con il puntatore al primo carattere.
Quando libero lo spazio creato nella funzione?
in modo automatico quando la funzione ritorna? e come faccio allora ad averla disponibile nel main?
devo preoccuparmene io nel main? ma se ho solo un puntatore che mi ritorna come faccio?.
Ok, poche idee ma ben confuse, riuscite ad aiutarmi?