Destruktor - samoczynne odpalanie

Destruktor - samoczynne odpalanie
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Mam pewien problem.
Nie wiem dlaczego destruktor macierzy sam się odpala ?

Klasa macierz:

Kopiuj
#include "stdafx.h"
#include <iostream>
#include "macierz.h"
#include <cstdlib>

Macierz::Macierz(int Wiersz, int Kolumna)
{
	this->Wiersz = Wiersz;
	this->Kolumna = Kolumna;

	tablica = new int *[Wiersz];

	for (int i = 0; i < Wiersz; i++)
	{
		tablica[i] = new int[Kolumna];
	}
}

Macierz::Macierz(int Wiersz, int Kolumna, Macierz *macierz)
{
	Macierz(Wiersz, Kolumna);

	if ((macierz->Wiersz == Wiersz) && (macierz->Kolumna == Kolumna))
	{
		for (int i = 0; i < Wiersz; i++)
			for (int j = 0; j < Kolumna; j++)
				tablica[i][j] = macierz->tablica[i][j];
	}
	else
	{
		std::cout << "Nie mozna zainicjalizowac macierzy macierzą o innych rozmiarach" << std::endl;
	}
}

Macierz::Macierz(const Macierz & macierz)
{
	Wiersz = macierz.Wiersz;
	Kolumna = macierz.Kolumna;
	tablica = macierz.tablica;
}

int Macierz::get_value(int x, int y)
{
	int temp;

	if ((Wiersz > x) && (Kolumna > y))
	{
		temp = tablica[x][y];
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie w tej macierzy" << std::endl;
		return 0;
	}
	return temp;
}

void Macierz::set_value(int x, int y, int value)
{
	if ((Wiersz > x) && (Kolumna > y))
	{
		tablica[x][y] = value;
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie - nie mozna tam zapisac wartosci." << std::endl;
	}
}


int Macierz::wyznacznik()
{
	return det(*this);
}

int Macierz::det(Macierz x)
{
	int value = 0;

	if (x.Wiersz != x.Kolumna)
	{
		std::cout << "Nie mozna obliczyc wyznacznika macierzy niekwadratowej!" << std::endl;
		return 0;
	}

	// poniższy kod bedzie procedowany tylko wtedy jak mamy do czynienia z macierza kwadratowa

	if ((x.Wiersz == 1) && (x.Kolumna == 1))
	{
		return x.tablica[0][0];
	}
	else
	{
		for (int k = 0; k < x.Kolumna; k++)
		{
			Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

			for (int i = 1; i < x.Wiersz; i++)
			{
				for (int j = 0; j < x.Kolumna; j++)
				{
					if (j < k)
					{
						temp->set_value(i - 1, j, x.tablica[i][j]);
					}
					if (j > k)
					{
						temp->set_value(i - 1, j - 1, x.tablica[i][j]);
					}
				}
			}

			if ((k % 2) == 0)
			{
				value += x.tablica[0][k] * det(*temp);
			}
			else
			{
				value -= x.tablica[0][k] * det(*temp);
			}
		}
		return value;
	}
}

Macierz::~Macierz()
{
	for (int i = 0; i < Wiersz; i++)
	{
		delete (tablica[i]);
	}
	delete (tablica);
}

Funkcja główna programu

Kopiuj
// MacierzLab.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <cstdlib>
#include "macierz.h"
#include "wektor.h"

using namespace std;

int main()
{
	// tworzymy macierz o wymiarach 0..2 i 0..3 (czyli macierz 3x4 elementową)
	Macierz * x = new Macierz(3, 3);
	x->set_value(0, 0, 1);
	x->set_value(0, 1, 2);
	x->set_value(0, 2, 3);

	x->set_value(1, 0, 2);
	x->set_value(1, 1, 3);
	x->set_value(1, 2, 4);

	x->set_value(2, 0, 1);
	x->set_value(2, 1, 0);
	x->set_value(2, 2, 1);


	cout << "Wyznacznik macierzy x wynosi : " << x->wyznacznik() << endl;

	system("PAUSE");
	return 0;
}

Cały kod generuje poprawne wyniki w sensie obliczenia wyznacznika macierzy jednakże nie wiem dlaczego ale nie mam żadnej możliwości kontroli wywołania destruktora.
Przez debugowanie ustaliłem że odpala mi się on samoczynnie i to w chwilach gdy odpalić się powinien... ;) jednak proszę mi wyjaśnić dlaczego się tak dzieje jeśli przecież nie używam do tego celu składni :

Kopiuj
delete(temp);
edytowany 1x, ostatnio: flowCRANE
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
3
Adamos19 napisał(a):

Mam pewien problem.
Nie wiem dlaczego destruktor macierzy sam się odpala ?

Klasa macierz:

Kopiuj
#include "stdafx.h"
#include <iostream>
#include "macierz.h"
#include <cstdlib>
Macierz::Macierz(int Wiersz, int Kolumna, Macierz *macierz)
{
	Macierz(Wiersz, Kolumna);

Wygląda na stworzenie obiektu Macierz na stosie. Powinieneś wywołać ten konstruktor z listy inicjalizacyjnej.

edytowany 1x, ostatnio: nalik
several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 20 godzin
4

Zacznijmy od podstaw, Twój desktruktor jest źle napisany, w ten sposób powinieneś zwalniać ręcznie zaolokowaną drwuwymiarową tablicę:

Kopiuj
Macierz::~Macierz()
{
    for (int i = 0; i < Wiersz; i++)
    {
        delete[] tablica[i];
    }
    delete[] tablica;
}

A co do pytania:

Nie wiem dlaczego destruktor macierzy sam się odpala ?

Bo masz w jendym miejscu inicjalizacje na stosie:

Kopiuj
Macierz::Macierz(int Wiersz, int Kolumna, Macierz *macierz)
{
    Macierz(Wiersz, Kolumna);

(edit) @nalik byłeś trochę szybszy ;)


edytowany 2x, ostatnio: several
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Twierdzisz że instrukcja która w swojej składni ma "new" tworzy obiekt na stosie? Zgodnie z moją najlepszą wiedzą tak nie jest bo tworzy go na stercie zarządzalnej.
Zauważ ponadto że w kodzie w klasie macierz mamy w metodzie det coś takiego:

Kopiuj
Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

Ja nie twierdzę że nie masz racji, wszystko wskazuje właśnie na to że MASZ ale JAKIM CUDEM????

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0
several napisał(a):

Zacznijmy od podstaw, Twój desktruktor jest źle napisany, w ten sposób powinieneś zwalniać ręcznie zaolokowaną drwuwymiarową tablicę:

Kopiuj
Macierz::~Macierz()
{
    for (int i = 0; i < Wiersz; i++)
    {
        delete[] tablica[i];
    }
    delete[] tablica;
}

A co do pytania:

Nie wiem dlaczego destruktor macierzy sam się odpala ?

Bo masz w jendym miejscu inicjalizacje na stosie:

Kopiuj
Macierz::Macierz(int Wiersz, int Kolumna, Macierz *macierz)
{
    Macierz(Wiersz, Kolumna);

Przepraszam, rzeczywiście. Pomyłka. Poprawiłem to ale to nie ma wpływu na "samoczynne" odpalanie się destruktora.

several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 20 godzin
3
Adamos19 napisał(a):

Twierdzisz że instrukcja która w swojej składni ma "new" tworzy obiekt na stosie?

Nikt tak nie napisał, także nie wiem skąd to sobie wziąłeś. W miejscu które wskazał @nalik i ja masz stworzoną tymczasową zmienną na stosie. Jeśli chcesz zawołać konstruktor z innego konstruktora to TUTAJ masz przykład jak to zrobić.


nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
0
Adamos19 napisał(a):

Twierdzisz że instrukcja która w swojej składni ma "new" tworzy obiekt na stosie? Zgodnie z moją najlepszą wiedzą tak nie jest bo tworzy go na stercie zarządzalnej.

Oj, Oj, podstawy :) new wywoła konstruktor klasy Macierz. Wywołanie tego konstruktora stworzy na stosie wywołań funkcji nowy rekord aktywacji (ramkę stosu). Konstruktor jest źle napisany i tworzy obiekt tej samej klasy na stosie, w ramce wywołanej funkcji. Podczas wychodzenia z konstruktora (wywołanego przez new), obiekty stworzone na stosie zostaną zniszczone.

several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 20 godzin
0

Przepraszam, rzeczywiście. Pomyłka. Poprawiłem to ale to nie ma wpływu na "samoczynne" odpalanie się destruktora.

Nic się samoczynnie nie odpala :D @nalik w pierwszej odpowiedzi napisał Ci co musisz zrobić, żeby zrobić poprawną delegację do innego konstruktora. Ja podałem Ci źródło, które trochę rozszerza temat. Nie wiem co możemy dla Ciebie więcej zrobić.


edytowany 1x, ostatnio: several
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ale konstruktor o którym Panowie piszecie nie może się odpalić bo nigdzie w kodzie nie wywołuję go (z trzema argumentami) a jedynie wywołuję konstruktor z dwoma argumentami który nie odpala już konstruktora żadnego wewnątrz siebie.
Innymi słowy, Panowie skróćmy ten kod o to o czym nie dyskutujemy, teraz wygląda to tak, a pytanie pozostaje ciągle AKTUALNE:

Kopiuj
#include "stdafx.h"
#include <iostream>
#include "macierz.h"
#include <cstdlib>

Macierz::Macierz(int Wiersz, int Kolumna)
{
	this->Wiersz = Wiersz;
	this->Kolumna = Kolumna;

	tablica = new int *[Wiersz];

	for (int i = 0; i < Wiersz; i++)
	{
		tablica[i] = new int[Kolumna];
	}
}

int Macierz::get_value(int x, int y)
{
	int temp;

	if ((Wiersz > x) && (Kolumna > y))
	{
		temp = tablica[x][y];
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie w tej macierzy" << std::endl;
		return 0;
	}
	return temp;
}

void Macierz::set_value(int x, int y, int value)
{
	if ((Wiersz > x) && (Kolumna > y))
	{
		tablica[x][y] = value;
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie - nie mozna tam zapisac wartosci." << std::endl;
	}
}


int Macierz::wyznacznik()
{
	return det(*this);
}

int Macierz::det(Macierz x)
{
	int value = 0;

	if (x.Wiersz != x.Kolumna)
	{
		std::cout << "Nie mozna obliczyc wyznacznika macierzy niekwadratowej!" << std::endl;
		return 0;
	}

	// poniższy kod bedzie procedowany tylko wtedy jak mamy do czynienia z macierza kwadratowa

	if ((x.Wiersz == 1) && (x.Kolumna == 1))
	{
		return x.tablica[0][0];
	}
	else
	{
		for (int k = 0; k < x.Kolumna; k++)
		{
			Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

			for (int i = 1; i < x.Wiersz; i++)
			{
				for (int j = 0; j < x.Kolumna; j++)
				{
					if (j < k)
					{
						temp->set_value(i - 1, j, x.tablica[i][j]);
					}
					if (j > k)
					{
						temp->set_value(i - 1, j - 1, x.tablica[i][j]);
					}
				}
			}

			if ((k % 2) == 0)
			{
				value += x.tablica[0][k] * det(*temp);
			}
			else
			{
				value -= x.tablica[0][k] * det(*temp);
			}
			int p = 0;
			delete temp;
		}
		return value;
	}
}

Macierz::~Macierz()
{
	for (int i = 0; i < Wiersz; i++)
	{
		delete[] tablica[i];
	}
	delete[] tablica;
}


Natomiast za informacje o teorii dot. odpalania konstruktora z innego konstruktora serdecznie dziękuję, przydadzą się jeszcze w tym projekcie. ALE NIE TUTAJ!

edytowany 1x, ostatnio: Adamos19
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
2

A tutaj masz kolejny błąd:

Kopiuj
int Macierz::wyznacznik()
{
    return det(*this);
}

int Macierz::det(Macierz x)
{

Jak już przekazywać to referencję albo wskaźnik. Klasa problemu ta sama. x zostanie zniszczone podczas wychodzenia z metody det. Niestety nie masz poprawnego konstruktora kopiującego, więc x przepisało sobie wskaźniki z this i w efekcie skasowało oryginalne dane.

edytowany 3x, ostatnio: nalik
several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 20 godzin
0

Jesteś pewien, że odpala się znikąd?

Kopiuj
Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);
// ... 
int p = 0;
delete temp;

Dodałeś delete temp więc desktruktor powinieneś się odpalać.


edytowany 1x, ostatnio: several
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No dobrze ale przecież w metodzie "det" mam tworzone słówkiem "new" kolejny obiekt który również jest niszczony ale nie bardzo wiem dlaczego.
Znaczy że co, że jeśli w ramach dowolnej funkcji wywołam komendę tworzącą obiekt opatrzoną słówkiem "new" to po zakończeniu tej funkcji obiekt ten istnieć nie będzie??? Bezsensu.

nalik
Przeczytaj poprzedni post, edytowałem go. Wyjaśnia co robi det z argumentem w połączeniu z takim, a nie innym, konstruktorem kopiującym :)
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Tak, bo dodałem to dopiero potem. Miało tego nie być , ma być tak:

Kopiuj
#include "stdafx.h"
#include <iostream>
#include "macierz.h"
#include <cstdlib>

Macierz::Macierz(int Wiersz, int Kolumna)
{
	this->Wiersz = Wiersz;
	this->Kolumna = Kolumna;

	tablica = new int *[Wiersz];

	for (int i = 0; i < Wiersz; i++)
	{
		tablica[i] = new int[Kolumna];
	}
}

int Macierz::get_value(int x, int y)
{
	int temp;

	if ((Wiersz > x) && (Kolumna > y))
	{
		temp = tablica[x][y];
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie w tej macierzy" << std::endl;
		return 0;
	}
	return temp;
}

void Macierz::set_value(int x, int y, int value)
{
	if ((Wiersz > x) && (Kolumna > y))
	{
		tablica[x][y] = value;
	}
	else
	{
		std::cout << "Nie ma elementu o tym indeksie - nie mozna tam zapisac wartosci." << std::endl;
	}
}


int Macierz::wyznacznik()
{
	return det(*this);
}

int Macierz::det(Macierz x)
{
	int value = 0;

	if (x.Wiersz != x.Kolumna)
	{
		std::cout << "Nie mozna obliczyc wyznacznika macierzy niekwadratowej!" << std::endl;
		return 0;
	}

	// poniższy kod bedzie procedowany tylko wtedy jak mamy do czynienia z macierza kwadratowa

	if ((x.Wiersz == 1) && (x.Kolumna == 1))
	{
		return x.tablica[0][0];
	}
	else
	{
		for (int k = 0; k < x.Kolumna; k++)
		{
			Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

			for (int i = 1; i < x.Wiersz; i++)
			{
				for (int j = 0; j < x.Kolumna; j++)
				{
					if (j < k)
					{
						temp->set_value(i - 1, j, x.tablica[i][j]);
					}
					if (j > k)
					{
						temp->set_value(i - 1, j - 1, x.tablica[i][j]);
					}
				}
			}

			if ((k % 2) == 0)
			{
				value += x.tablica[0][k] * det(*temp);
			}
			else
			{
				value -= x.tablica[0][k] * det(*temp);
			}
		}
		return value;
	}
}

Macierz::~Macierz()
{
	for (int i = 0; i < Wiersz; i++)
	{
		delete[] tablica[i];
	}
	delete[] tablica;
}

a i tak się odpala!
Gdy dodam tą instrukcję "delete" to odpala się niejako ponownie na tym samym wskaźniku co już powoduje błąd w samym działaniu destruktora a to pewnie dlatego że odpalony raz modyfikuje sobie wskaźnik który mu pokazywał na obszar który miał zwolnić. A jak odpala się na zmodyfikowanym już wskaźniku to chce pisać po pamięci na co mu nie pozwala system wyrzucając właśnie taki wyjątek.
Pytanie pozostaje ciągle aktualne Panowie, z jakiej paki odpala mi się destruktor obiektu macierz jeśli go jawnie nie wywołuję instrukcją "delete" ???

edytowany 1x, ostatnio: Adamos19
several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 20 godzin
0

a i tak się odpala!

Odpala się, z powodu kopiowania do argumentu w metodzie det, tak jak Ci @nalik napisał wcześniej.


edytowany 1x, ostatnio: several
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
1

Jeżeli mogę coś zasugerować, to wywal Pan w piz** to new i użyj std::vector.

Kopiuj
tablica = vector<vector<int>>(wiersze, vector<int>(kolumny, 0));

Problem z niepoprawnym konstruktorem kopiującym zniknie. Co najwyżej będzie kopiował dane w tą i z powrotem, gdy nie użyjesz referencji.

edytowany 3x, ostatnio: nalik
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ok, jestem w stanie to przyjąć do wiadomości, dzięki!
Ale to dlaczego w takim razie powstaje błąd jeśli w tej metodzie chcę zwolnić to co przed momentem stworzyłem instrukcją:

Kopiuj
Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

pisząc tą metodę tak:

Kopiuj
int Macierz::det(Macierz x)
{
	int value = 0;

	if (x.Wiersz != x.Kolumna)
	{
		std::cout << "Nie mozna obliczyc wyznacznika macierzy niekwadratowej!" << std::endl;
		return 0;
	}

	// poniższy kod bedzie procedowany tylko wtedy jak mamy do czynienia z macierza kwadratowa

	if ((x.Wiersz == 1) && (x.Kolumna == 1))
	{
		return x.tablica[0][0];
	}
	else
	{
		for (int k = 0; k < x.Kolumna; k++)
		{
			Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

			for (int i = 1; i < x.Wiersz; i++)
			{
				for (int j = 0; j < x.Kolumna; j++)
				{
					if (j < k)
					{
						temp->set_value(i - 1, j, x.tablica[i][j]);
					}
					if (j > k)
					{
						temp->set_value(i - 1, j - 1, x.tablica[i][j]);
					}
				}
			}

			if ((k % 2) == 0)
			{
				value += x.tablica[0][k] * det(*temp);
			}
			else
			{
				value -= x.tablica[0][k] * det(*temp);
			}

                       delete temp;

		}
		return value;
	}
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
0
Adamos19 napisał(a):

Ok, jestem w stanie to przyjąć do wiadomości, dzięki!
Ale to dlaczego w takim razie powstaje błąd jeśli w tej metodzie chcę zwolnić to co przed momentem stworzyłem instrukcją:

Bo już zwolniłeś te dane wywołując det(*tmp). Wskaźniki są już niepoprawne. Prawdopodobnie tam gdzie wskazują nadal są stare dane, jeszcze nie nadpisane, więc odczyt się uda, ale kolejne wywołanie zwalniania pamięci nie.

edytowany 2x, ostatnio: nalik
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Niemożliwe moim zdaniem ponieważ metodę det wywołuję właśnie tak:

Kopiuj
det(*temp)

a zatem przekazuję jej obiekt i ona robi se z niego kopię, czyż nie?

edytowany 1x, ostatnio: Adamos19
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
1
Adamos19 napisał(a):

Niemożliwe moim zdaniem ponieważ metodę det wywołuję właśnie tak:

Kopiuj
det(*temp)

a zatem przekazuję jej obiekt i ona robi se z niego kopię, czyż nie?

Tak. Ale robi kopię wywołując Twój konstruktor kopiujący. Twój konstruktor kopiujący to tak zwana płytka kopia, gdyż nie skopiował buforów tablicy utworzonych na stercie (nie alokował ich od nowa). W efekcie oba obiekty wskazują na to samo i oba próbują zwolnić to samo.

edytowany 3x, ostatnio: nalik
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:minuta
0
Adamos19 napisał(a):

tworzy go na stercie zarządzalnej.

Nie na „stercie zarządzalnej” tylko na po prostu stercie.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Dobra, już rozumiem!!! :)))
@Azarien a czym to się różni dokładnie?
@nalik bardzo dziękuję za wyjaśnienia, pomogłeś mi, do tej pory nie myślałem w ogóle nad tym w jaki sposób obiekt trafia poprzez argument do funkcji, kurcze a tu proszę.
W przypadku przekazywania przez wartość ODPALA się konstruktor kopiujący który robi "płytką" kopię obiektu (bez alokowania i przepisywania wartości elementów macierzy) a kopię tą tworzy na stosie, a zatem w przypadku zakończenia działania funkcji stos się zwija o ten zapis co powoduje obiekt zostaje niszczony a destruktor zajmuje się usunięciem tego obszaru. Zatem nie muszę już wywoływać delete i żadnych wycieków pamięci nie będzie! Czy tak ? :D:D

nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
1
Adamos19 napisał(a):

@nalik bardzo dziękuję za wyjaśnienia, pomogłeś mi, do tej pory nie myślałem w ogóle nad tym w jaki sposób obiekt trafia poprzez argument do funkcji, kurcze a tu proszę.
W przypadku przekazywania przez wartość ODPALA się konstruktor kopiujący który robi "płytką" kopię obiektu (bez alokowania i przepisywania wartości elementów macierzy) a kopię tą tworzy na stosie, a zatem w przypadku zakończenia działania funkcji stos się zwija o ten zapis co powoduje obiekt zostaje niszczony a destruktor zajmuje się usunięciem tego obszaru. Zatem nie muszę już wywoływać delete i żadnych wycieków pamięci nie będzie! Czy tak ? :D:D

Tak, wszystko poza przedostatnim zdaniem. To jest niepoprawny kod i metodą poprawienia go nie jest dodanie kolejnego niepoprawnego kodu, który sprawi, że przypadkiem będzie działać, dopóki znowu nie wywołasz det albo innej niepoprawnej metody. Zmień parametry do metod, by przyjmowały referencję const Macierz& i zdecydowanie użyj std::vector lub (nie polecam, std::vector, już to robi) zaimplementuj konstruktor kopiujący i operator przypisania, które wykonają głęboką kopię buforów.

edytowany 3x, ostatnio: nalik
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Jak napisać tą metodę:

Kopiuj
int Macierz::wyznacznik()
{
	return det(*this);
}

aby współdziałała poprawnie z tą:

Kopiuj
int Macierz::det(const Macierz & x)
{
	int value = 0;

	if (x.Wiersz != x.Kolumna)
	{
		std::cout << "Nie mozna obliczyc wyznacznika macierzy niekwadratowej!" << std::endl;
		return 0;
	}

	// poniższy kod bedzie procedowany tylko wtedy jak mamy do czynienia z macierza kwadratowa

	if ((x.Wiersz == 1) && (x.Kolumna == 1))
	{
		return x.tablica[0][0];
	}
	else
	{
		for (int k = 0; k < x.Kolumna; k++)
		{
			Macierz *temp = new Macierz(x.Wiersz - 1, x.Kolumna - 1);

			for (int i = 1; i < x.Wiersz; i++)
			{
				for (int j = 0; j < x.Kolumna; j++)
				{
					if (j < k)
					{
						temp->set_value(i - 1, j, x.tablica[i][j]);
					}
					if (j > k)
					{
						temp->set_value(i - 1, j - 1, x.tablica[i][j]);
					}
				}
			}

			if ((k % 2) == 0)
			{
				value += x.tablica[0][k] * det(*temp);
			}
			else
			{
				value -= x.tablica[0][k] * det(*temp);
			}
			delete temp;
		}
		return value;
	}
}

Przecież jeśli argumentem metody jest referencja to można ją przekazać za pomocą wskaźnika , czyż nie?

Jeśli nie, to jakiej składni powinienem użyć żeby przekazać referencje do obiektu "bieżącego", bo próbowałem tak: &this ale to generuje błąd ???

I ostatnie jeszcze tylko pytanie na koniec:
Nie rozumiem sensu zastosowania w tym wypadku predefiniowanej klasy vector ponieważ ja wykonując liczenie wyznacznika w żaden sposób nie mogę i nie chcę modyfikować bieżącego obiektu macierzy a to z kolei pociąga za sobą fakt że nie chcę nic "realokować" (ani obcinać macierzy źródłowej ani jej rozszerzać), a robić to muszę ponieważ tak wygląda to zgodnie ze wzorem na rozwinięcie metodą Laplace'a wyznacznika względem danego wiersza... jaką to niby przewagę będzie miało zastosowanie "vector" zamiast tego co ja zrobiłem i jak to użyć w moim kodzie? Chętnie się dowiem jeśli koledzy z łaski swojej napiszą.....

edytowany 4x, ostatnio: Adamos19
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
1

A co tym razem nie działa?

  1. Po pierwsze, nie musisz tworzyć macierzy temp przez new. Dlaczego? Bufory, których używa i tak istnieją na stosie, a przekazujesz ją do innych metod przez referencję, a wychodząc z metody, gdzie została utworzona i tak chcesz się jej pozbyć. Ergo, nie potrzebujesz tworzyć jej na stercie, może istnieć na stosie, w zakresie tej funkcji.
  2. &this to wskaźnik na wskaźnik
  3. Poprawne wywołanie to det(*this), gdyż chcesz mieć referencję do obiektu wskazywanego przez this.
Adamos19 napisał(a):

I ostatnie jeszcze tylko pytanie na koniec:
Nie rozumiem sensu zastosowania w tym wypadku predefiniowanej klasy vector ponieważ ja wykonując liczenie wyznacznika w żaden sposób nie mogę i nie chcę modyfikować bieżącego obiektu macierzy a to z kolei pociąga za sobą fakt że nie chcę nic "realokować" (ani obcinać macierzy źródłowej ani jej rozszerzać), a robić to muszę ponieważ tak wygląda to zgodnie ze wzorem na rozwinięcie metodą Laplace'a wyznacznika względem danego wiersza... jaką to niby przewagę będzie miało zastosowanie "vector" zamiast tego co ja zrobiłem i jak to użyć w moim kodzie? Chętnie się dowiem jeśli koledzy z łaski swojej napiszą.....

To jest bardziej pytanie o samo projektowanie. std::vector zapewnia Ci poprawną zarządzanie buforem - inizjalizacje, sprzątanie i kopiowanie. Jak sam widzisz pisanie tego z ręki to strata czasu i łatwo popełnić błąd. To samo co robisz przy użyciu własnych buforów możesz uzyskać z wykorzystaniem std::vector (przykładowo, referencja na vector nie skopiuje Ci buforów).
Jeżeli nie chcesz realokować danych, to masz takie opcje:
a) Twórz widok na obecną macierz, który powinien być osobną klasę. Widok nie będzie sprzątał, jedynie udawał macierz o mniejszym rozmiarze. Zamiast własnego buforu będzie trzymał odnośnik od prawdziwego buforu (np. przez referencję na std::vector). Oczywiście czas życia widoku nie może być większy niż oryginalnej macierzy, chyba, że zastosujesz zliczanie referencji do posprzątania.
b) Zmodyfikuj metody, by działały na oryginalnej macierzy, ale przyjmowały jako parametry zakres.
c) Używaj macierzy, które współdzielą bufory, ale taką płytką kopię twórz przy użyciu dedykowanej metody, a same bufory zapakuj w wskaźnik, który będzie zliczał referencję. Zakres będzie parametrem macierzy, nie buforu, który wykorzystujesz. I nie ma tutaj znaczenia, czy skorzystasz z vector czy z własnych buforów, logika będzie taka sama.

edytowany 5x, ostatnio: nalik
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No dobra dobra, zgadzam się z Tobą ale przecież ja słówkiem new tworzę całkiem inną macierz, nie taką jak obecnie utworzona na stosie (rozmiarowo np. o jeden niżej niż bieżąca)....
Chyba jeszcze czegoś nie rozumiem... możesz powiedzieć która macierz została utworzona na stosie w chwili wywołania metody z przekazaną do niej przez referencję macierzą ?
Innymi słowy: co tworzy się na stosie i czy coś się tworzy jeśli metodę wywołuję i przekazuję do niej obiekt przez referencję...?

nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
0
Adamos19 napisał(a):

No dobra dobra, zgadzam się z Tobą ale przecież ja słówkiem new tworzę całkiem inną macierz, nie taką jak obecnie utworzona na stosie (rozmiarowo np. o jeden niżej niż bieżąca)....

Tak, a zaraz potem ją kasujesz. Może lepiej utworzyć ją na stosie (przypominam, bufory i tak są na stercie), przekazywać przez referencję i pozwolić kompilatorowi posprzątać automatycznie?
I tak swoją drogą, w tym przypadku alokacja pamięci i tak się odbywa, więc równie dobrze mógł ją załatwić std::vector.

Chyba jeszcze czegoś nie rozumiem... możesz powiedzieć która macierz została utworzona na stosie w chwili wywołania metody z przekazaną do niej przez referencję macierzą ?

Jak przekazujesz *temp do det i nie używasz referencji, to tworzysz nową macierz tymczasową, która współdzieli bufor z temp.

Innymi słowy: co tworzy się na stosie i czy coś się tworzy jeśli metodę wywołuję i przekazuję do niej obiekt przez referencję...?

Zakładając przekazywanie przez referencję, to nic się nie utworzy od nowa.

Pomijam już fakt, że tworzenie nowych macierzy o takich samych rozmiarach (i to w dodatku przez new) jest wywoływane w pętli. Efektywniej byłoby stworzyć macierz raz i tylko ją czyścić, by przygotować do ponownego użycia.

edytowany 5x, ostatnio: nalik
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Wszystko to co napisałeś w ostatnim poście jest dla mnie zrozumiałe poza jedną rzeczą:
Nie rozumiem pierwszego zdania:

"(...) Tak, a zaraz potem ją kasujesz. Może lepiej utworzyć ją na stosie (przypominam, bufory i tak są na stercie), przekazywać przez referencję i pozwolić kompilatorowi posprzątać automatycznie?
I tak swoją drogą, w tym przypadku alokacja pamięci i tak się odbywa, więc równie dobrze mógł ją załatwić std::vector."

Jak by to miało wyglądać, możesz mi wysłać te kilka linijek kodu do analizy ale najprościej jak to możliwe...
chciałbym zobaczyć bardzo o czym piszesz...

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:5 dni
  • Lokalizacja:Szczecin
2

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Panowie, cieniak ze mnie i nie potrafię zrozumieć działania <vectora>.
Mam kod:

Kopiuj
	std::vector<Punkt3D *> v;

	while (1)
	{
		v.push_back(new Punkt3D(1, 2, 3));
		v.pop_back();
	}

lub inaczej:

Kopiuj
	std::vector<Punkt3D *> v;

	while (1)
	{
		v.push_back(new Punkt3D(1, 2, 3));
		v.clear();
	}

Pytanie dlaczego on powoduje wyciek pamięci jeśli przecież pisaliście że vector powinien działać tak że daje sobie świetnie radę z zarządzaniem pamięcią...
Jeśli dokładam nowo tworzony obiekt do vector a potem ściągam ten obiekt z niego to dlaczego on się nie kasuje???

edytowany 3x, ostatnio: Adamos19
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:5 dni
  • Lokalizacja:Szczecin
0

Bo wektor radzi sobie z tym, czym zarządza. W tym przypadku jest to wskaźnik na Punkt3D, a nie obiekt Punkt3D. Możesz uznać, że wywoływany destruktor wskaźnika nie różni się od destruktora inta - czyli nic nie robi.


Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)