C++-alocarea dinamica a unor membri/supraincarcare operatori


(Stefan Davidoaia) #1

salut,am o bucățică de program ce nu mi afișează un obiect construit prin supraîncărcarea cu + a altor doua obiecte ,unul dintre membri clasei este o matrice alocată dinamic
aici e header-ul

#pragma once
class matrice
{private:
	int nrlinie;
	int nrcoloana;
	int **mat2;
public:
	
	
	matrice();
	matrice(matrice&);
    matrice operator+( matrice&);
	~matrice();
	void afisare();
	




};

respectiv functii.c

#include <iostream>
#include "Header.h"
using namespace std;

matrice::matrice()
{
	
	cin >> nrlinie;
	cin >> nrcoloana;
	mat2=new int*[nrlinie];
	for (int i = 0; i<nrlinie; i++)
		mat2[i] = new int[nrcoloana];
	for (int i = 0; i < nrlinie; i++)
		for (int j = 0; j < nrcoloana; j++)
			cin>>mat2[i][j];

	
}
matrice::~matrice()
{
	for (int i = 0; i < nrlinie; ++i)
		delete[] mat2[i];
	delete[] mat2;

}
void matrice::afisare()
{
	for (int i = 0; i < nrlinie; i++)
		for (int j = 0; j < nrcoloana; j++)
			cout << mat2[i][j] << " ";
	cout << endl;


}
matrice matrice::operator+( matrice &a)
{
	matrice r;

	r.nrcoloana = nrcoloana;
	r.nrlinie = nrlinie;

	for (int i = 0; i < r.nrlinie; i++)
		for (int j = 0; j < r.nrcoloana; j++)
		{
			r.mat2[i][j] = mat2[i][j] + a.mat2[i][j];
			
		}
	return r;


}

si main

#include <iostream>
#include "Header.h"
using namespace std;
int main()
{
	
	matrice a;
	matrice b;

	matrice c;

	
	c = a.operator+(b);//incercat si c=a+b;
	c.afisare();
	return 0;
}

ideea e ca nu-mi afișează nimic,ce trebuie modificat la codul meu?respectiv ce ar trebui adaugat?


(Serghei Amelian) #2

Eu aş începe cu int **mat2, am neplăcuta senzaţie că scrii date într-o zonă de memorie nealocată :slight_smile: De fapt mă mir că te plângi că nu afişează nimic, când mai probabil ar fi fost să te plângi că programul crapă cu “segmentation fault”.


De fapt aduni şi afişezi matrici goale, dacă programul n-ar crăpa, ar fi normal să nu afişeze nimic.


(Stefan Davidoaia) #3

Nu,matricile nu sunt goale,sunt citite de la tastatura am probat ,îmi sunt afișate matricile dar nu și matricea creată cu ajutorul supraîncărcări operatorului + ,nu sunt decât un începător,poate greșesc cel mai probabil,dar măcar îmi poți arăta ce ar trebui înlocuit măcar să învăț ceva din toată treaba asta.PS :nu mă plâng ,doar caut sa învăț .


(Stefan Davidoaia) #4


după cum observi îmi alocă ,dar după acel c=a+b ,se oprește execuția nu se mai apelează afișarea respectiv dealocarea obiectelor,ce se întâmplă ?sau măcar cum ar arăta programul corect.


(Serghei Amelian) #5

Ai dreptate, nu am citit codul atent, nu mă aşteptam să ai input-ul în constructor. Nu e bine să ai inputul în constructor, pentru că atunci când instanţiezi obiectul temporar “r”, îţi cere în mod automat din nou input, ceea ce nu cred că vrei. Asta una.

A doua, ai un soi de conflict între “r” şi “c”, pentru că primul alocă memorie în constructor şi o distruge imediat în destructor, când părăseşte funcţia operator+(const matrice &a), iar la “c” ajunge doar gunoi (pointer la memorie nealocată). Mai grav, “c” are şi el “delete” în destructor, deci va încerca şi el să elibereze memorie, pe care nici măcar nu o are de fapt (asta se cheamă double-free şi de regulă duce la crăparea instantenee a programului).

Ideea e să faci overload şi la operatorul “=” (egal) şi acolo să faci copierea din memoria obiectului temporar “r”. Aici se poate implementa în două feluri, ori faci deep-copy (adică aloci memorie pentru “c” şi copiezi manual conţinutul din “r”), ori implementezi ceva cu operatorul “move” (adică “furi” pointer-ul mt2 de la “r” şi îl laşi să creadă că are zero linii şi nu are ce memorie să elibereze).

Probabil e complicat ce zic eu aici, dar s-o luăm pas cu pas şi îi dai de cap până la urmă.

Ca să înţelegi mai bine, am conceput un mic test, ca să vezi ordinea alocării şi dealocării.

http://cpp.sh/2isz

#include <stdio.h>

class MyClass {
public:
    int val;

    MyClass()
    {
        fprintf(stdout, "MyClass()\n");
    }

    ~MyClass()
    {
        fprintf(stdout, "~MyClass(val=%d)\n", val);
    }

    MyClass(const MyClass&)
    {
        fprintf(stdout, "MyClass(const MyClass&)\n");
    }

    MyClass operator+(const MyClass &other)
    {
        MyClass result;

        result.val = val + other.val;

        return result;
    }

    MyClass &operator=(MyClass &&other)
    {
        val = other.val;

        // ii spunem obiectului temporar ca nu mai are memorie
        other.val = -1;

        fprintf(stdout, "&operator=(MyClass&&) const\n");
        return *this;
    }

    MyClass &operator=(const MyClass &other)
    {
        val = other.val;
        fprintf(stdout, "&operator=(const MyClass&) const\n");
        return *this;
    }
};


int main(int argc, char *argv[])
{
    MyClass class1;
    MyClass class2;
    MyClass class3;

    class1.val = 1;
    class2.val = 2;

    class3 = class1 + class2;

    fprintf(stdout, "result=%d\n", class3.val);

    return 0;
}


(Stefan Davidoaia) #6

Foarte de ajutor testul și explicațiile !Am ajuns la partea cu operatorul = ,îmi e putin neclar

 MyClass &operator=(MyClass &&other)
    {
        val = other.val;

        // ii spunem obiectului temporar ca nu mai are memorie
        other.val = -1;

        fprintf(stdout, "&operator=(MyClass&&) const\n");
        return *this;
    }

bucata de mai sus reprezinta deep-copy
respectiv

MyClass &operator=(const MyClass &other)
    {
        val = other.val;
        fprintf(stdout, "&operator=(const MyClass&) const\n");
        return *this;
    }
```operatorul move
sau reprezinta aceeași chestie ambele?

(Serghei Amelian) #7

E invers, deep-copy înseamnă să duplici memoria şi să nu te atingi de cea a lui “r”. În bucata a doua, preiau valoarea de la el şi i-o setez la “-1”. În cazul tău, i-aş “fura” pointerul “mat2” şi l-aş seta la NULL, pentru ca destructorul lui să nu aibă ce să şteargă (trebuie prevăzut şi în destructor să nu intre în delete daca “mat2” e NULL, de genul ăsta:

matrice::~matrice()
{
    if(mat2)
    {
        for (int i = 0; i < nrlinie; ++i)
	        delete[] mat2[i];
	    delete[] mat2;
    }
}

În orice caz, primul lucru pe care trebuie să-l faci e să muţi input-ul din contructor, acolo face numai belele. Fă o funcţie separată, cum ai făcut pentru afişare.


Apropo, pune şi denumiri coerente la variabile. De exemplu, nr_linii e mai sugestiv decât nr_linie.


(Stefan Davidoaia) #8

am făcut câteva modificări pe unele parți după recomandările date,dar nu am reușit să duc până la capăt programul în mod corect :

în main

#include <iostream>
#include "Header.h"
using namespace std;
int main()
{
	int n; int m;
	cin >> n;
	cin >> m;

	matrice a(n,m);
	matrice b(n,m);

	matrice c(n,m);

	a.afisare();
	b.afisare();
	
	c = a+b;
	c.afisare();
	return 0;
}

în funcții

#include <iostream>
#include "Header.h"
using namespace std;

matrice::matrice(int n,int m)
{
	nr_linii = n;
	nr_coloane = m;

	mat2=new int*[nr_linii];
	for (int i = 0; i<nr_linii; i++)
		mat2[i] = new int[nr_coloane];
	cout << "citire elemente matrice ";
	cout << endl;
	citire();

	
}

matrice::~matrice()
{
	cout << "element sters"<<endl;

	
		if (mat2)
		{
			for (int i = 0; i < nr_linii; ++i)
				delete[] mat2[i];
			delete[] mat2;
		}

}
void matrice::afisare()
{
	cout << "afisare matrice:";
	cout << endl;
	for (int i = 0; i < nr_linii; i++)
		for (int j = 0; j < nr_coloane; j++)
			cout << mat2[i][j] << " ";
	cout << endl;


}
void matrice::citire()
{
	cout << " " << "functie citire" << endl;
	for (int i = 0; i < nr_linii; i++)
		for (int j = 0; j < nr_coloane; j++)
			cin >> mat2[i][j];

}
matrice matrice::operator+( matrice &a)
{
	matrice r(nr_linii,nr_coloane);

	r.nr_coloane = nr_coloane;
	r.nr_linii = nr_linii;

	for (int i = 0; i < r.nr_linii; i++)
		for (int j = 0; j < r.nr_coloane; j++)
		{    
			r.mat2[i][j] = mat2[i][j] + a.mat2[i][j];
			
		}
	return r;


}

matrice matrice::operator=(matrice &other)
{

	nr_linii = other.nr_linii;
	other.nr_linii = 0;


	return *this;
}

și header

#pragma once
class matrice
{private:
	int nr_linii;
	int nr_coloane;
	int **mat2;
public:
	

	matrice(int n,int m);
	void citire();
    matrice operator+( matrice&);
	matrice operator=(matrice &other);
	~matrice();
	void afisare();
	




};

(Stefan Davidoaia) #9

îmi distruge obiectul c,dar nu-mi afișează,cel mai probabil am înțeles greșit


(cosmos) #10

Poate zic o prostie, dar nu ar trebui ca mai intai sa afisezi matricea si dupaia sa invoci destructorul ?


(Stefan Davidoaia) #11

Se face în mod automat din câte citisem,problema e alta,el îmi șterge obiectul înainte să-l returneze în supraîncărcarea operatorului +,soluția e dată mai sus ,numai ca nu știu exact cum să o pun pe exemplul meu.


(Serghei Amelian) #12

La prima vedere, am identificat două greşeli:

  1. Faci citirea în constructor, ceea ce înseamnă că îţi va cere input de fiecare dată când instanţiezi obiectul, adică inclusiv când instanţiezi obiectul temporar “r”, ceea ce nu cred că vrei.

  2. În funcţia de asignare nu ai copiat tot, ai copiat doar “nr_linii”. Trebuie să copiezi şi “nr_coloane” şi “mat2”. Şi tot acolo, trebuie să setezi la NULL variabila “mat2” a obiectului de la care “furi” pointerul “mat2”, altfel vei avea double-free.


(Stefan Davidoaia) #14

1)am rezolvat prima parte
2)la returnare la supraîncărcarea egalului tot *this simplu voi returna?și cum setez mat2 la nul?
mat2=NULL; sau altfel?


(Serghei Amelian) #15

Da, *this. Dar cred că operatorul egal trebuie definit in genul asta:

matrice &operator=(matrice &&other);

Mai trebuie sa pui acolo other.mat2 = NULL sau other.mat2 = nullptr, e acelaşi lucru.


(Stefan Davidoaia) #16

tot nu-l ia ,oricum mulțumesc enorm de ajutor,am învățat o grozăvie de lucruri noi ! :slight_smile:


(Serghei Amelian) #17

Pai de ce îţi cere de 4 ori elementele matricii?


(Stefan Davidoaia) #18

de fapt o face de 2 ori pentru obiectul a și obiectul b ,restul sunt mesaje sa-mi arate ca e apelat constructorul


(Serghei Amelian) #19

Sunt confuz, ceva mai inainte scria “citire elemente matrice” de 4 ori. Mă rog. Şi acum ce anume nu funcţionează?


(Stefan Davidoaia) #20

am modificat sa nu-mi mai apară acel mesaj,de 4 ori scuze de confuzie ,sunt bătut eu în cap.Păi nu mi afișează obiectul c,după toate modificările făcute…cred că l-am stricat eu mai tare…


(Serghei Amelian) #21

În prima fază, verifică dacă intra în operator=, posibil să nu facă asignarea acolo.