Dziedziczenie: sprawdzenie jaki to naprawdę typ; Tablica wskaźników a konstruktor kopiujący;

0

Witam.

Mam napisać na zajęcia program z okropną konstrukcją - macierz dziedziczy po wektorze...

no ale do rzeczy.

Klasa Vec ma:
int n - ile współrzędnych
double* A - współrzędne

i inne mniej ważne bzdury

Klasa Mac ma:
int n - ile elementów - odziedziczone po Vecu
double* A - elementy (jednowymiarowe!) - po Vecu
poza tym co ma swoje to:
int P, Q - ile rzędów i wierszy - czyli taki "sztuczny" podział tablicy jednowymiarowej na dwa wymiary.

I teraz żeby móc trzymać wszystkie obiekty w jednej tablicy, mam klasę TableManager, która ma:
int k - ilość wektorów/macierzy (dla uproszczenia nazywam je potem tablicami (table))
Vec** tables - dynamiczna tablica wskaźników na wektory.

Właśnie to, że to tablica wskaźników, daje mi możliwość wsadzania tam też macierzy. Czyli de facto jest to tablica na moje 'tables'.

Jako że Vec** tables (razem z int k) jest ukryta w sekcji prywatnej (i ma być), dostaję się do niej za pomocą operatora indeksu.

Vec& TableManager::operator[](int i)const   //na razie bez wstawiania do tablicy
{
.
.
.
}

W środku potrzebuję sprawdzić, czy element z i-tego indeksu tables jest tak naprawdę wektorem, czy też macierzą. Wiem że na strumieniach dało się zrobić coś takiego:

istream& input;
.
.
.
if (&input == &cin)
{
...
}

I to działa. Nie wiem jak to zrobić na wskaźnikach, w mojej sytuacji. Da się? Jeżeli tak to jak? Jeżeli nie, to jak to inaczej zrobić?

To była pierwsza rzecz.

Druga:

TableManager(TableManager &tableManager)
{
	k = tableManager.k;

	for (int i = 0; i < k; i++)
	{
		*tables[i] = *tableManager.tables[i];
	}
}

To jest konstruktor kopiujący TableManagera.
Nie jestem pewien, ale mam wątpliwości co do tego czy działa tak, jakbym chciał. Naturalnie ma on skopiować, czyli przepisać każdą 'table' z jednej tablicy do drugiej, a nie ustawiać wskaźniki na to samo. Jeżeli się nie mylę, operator * stojący przed czymś, co jest wskaźnikiem to operator wyłuskania. Czyli zwraca obiekt/zmienną, na którą pokazuje wskaźnik. jako że tables jest tablicą wskaźników, to oczekuję, że po tej instrukcji w forze właśnie obiekty zostaną przekopiowane. "Do tego na co pokazuje wskaźnik tables[i] (*tables[i]) skopiuj to, na co pokazuje wskaźnik tableManager.tables[i] (*tableManager.tables[i])". Niestety, o ile dobrze pamiętam (a nie jestem w stanie teraz tego sprawdzić), to działało dokładnie tak, jak właśnie nie chciałem, aby działało. Jak temu zaradzić?

Teraz tak: mam w Vecu mieć operator indeksacji - zwraca on i-tą współrzędną wektora.
W Mac też ma być taki operator - tyle że zwraca on cały i-ty wiersz jako dynamiczną tablicę doubli (dzięki temu można np. odnieść się do macierzy m[i][j], pierwsze to indekser, drugie jest już na tablicy doubli)

0

Przecież to wektor jest szczególnym przypadkiem macierzy, a nie macierz wektora.

Wg mnie operator [] powinien zwracać widok na dany wiersz, a nie tworzyć nową tablicę, bo gdy stworzy nową tablicę to zmiany w niej nie będą odzwierciedlone w macierzy. Widokiem może być np obiekt z polem offset, wskaźnikiem do wewnętrznej tablicy doubli z macierzy, operatorem [] no i czym tam jeszcze chcesz.

1

Użyj Bracie rzutowania wskaźnika poprzez dynamic_cast

0
Wibowit napisał(a)

Przecież to wektor jest szczególnym przypadkiem macierzy, a nie macierz wektora.

Powiedz to prowadzącemu. Właśnie 90% kłopotów mam przez tą idiotyczną konstrukcję.

Wibowit napisał(a)

Wg mnie operator [] powinien zwracać widok na dany wiersz, a nie tworzyć nową tablicę, bo gdy stworzy nową tablicę to zmiany w niej nie będą odzwierciedlone w macierzy. Widokiem może być np obiekt z polem offset, wskaźnikiem do wewnętrznej tablicy doubli z macierzy, operatorem [] no i czym tam jeszcze chcesz.

Będą dwa operatory. Jeden na rzecz obiektu stałego będzie kopiować wiersz. Drugi, do zmian będzie faktycznie zwracał widok na nowy wiersz.

Wciąż jednak brak odpowiedzi na moje pytania :(

0

Z tym konstruktorem kopiującym to jakoś nie bardzo Cie rozumiem, ale jeżeli chodzi o to dziedziczenie, to jest to błąd projektowy - tutaj dziedziczenie nie jest na miejscu. Macierz to nie jest szczególny typ wektora, ale macierz składa się z wektorów. Czyli zamiast dziedziczenia powinna być kompozycja.

0

dynamic_cast. Muszę pogrzebać co to dokładnie jest, a potem spróbuję.

0

@MasterBLB

Hmmm... pomogło. Jeśli chodzi o kompilację. Zobaczymy co mi będzie z mojej klasy na ekran wypisywało, gdy skończę jej konsolową obsługę... ale wielkie dzięki :)

@byku_guzio

Z konstruktorem kopiującym chodzi o to, że ma on kopiować obiekty, a nie ustawiać wskaźniki na jedno i to samo. Bo w efekcie potem zmienię w nowoutworzonym Managerze jedną 'table', i zmieni się też w tym starym - efekt wielce niepożądany. I teraz jak skopiować obiekty z jednej (dynamicznej) tablicy wskaźników na te obiekty, do drugiej dynamicznej tablicy wskaźników na obiekty? Nie jestem właśnie pewien, czy to co napisałem kopiuje, czy tylko przestawia wskaźniki na te same obiekty.

0
Sushi napisał(a)

Teraz tak: mam w Vecu mieć operator indeksacji - zwraca on i-tą współrzędną wektora.
W Mac też ma być taki operator - tyle że zwraca on cały i-ty wiersz jako dynamiczną tablicę doubli (dzięki temu można np. odnieść się do macierzy m[i][j], pierwsze to indekser, drugie jest już na tablicy doubli)

Aaaaa, wgl tego ostatniego akapitu miało nie być ^^ lol. Wybaczcie za to. I za triple-posting.

0

Dodatkowy problem.

Fragment polecenia brzmi:

Nie definiować dla klasy macierzy operatorów wejścia i wyjścia, lecz wykorzystać
istniejące operatory zdefiniowane dla klasy wektorów. W tym celu należy w obu
klasach zdefiniować funkcje wirtualne, które będą realizować te fragmenty operacji
we/wy, które są indywidualne w tych klasach. Np. w operacji wejścia indywidualne
będą zaproszenia do podania wartości elementu wektora lub tablicy, natomiast
wprowadzone wartości są w obu przypadkach wpisywane kolejno do tablicy A.

istniejące operatory zdefiniowane dla klasy wektorów

  • dotyczy poprzedniego zadania. To zadanie jest rozszerzeniem poprzedniego, gdzie już były takie operatory.

No więc mam operator wejścia (w pliku TableManager.cpp):

istream &operator>>(istream& wej, Vec* vec)
{
	if (&wej == &cin)
	{
		if (dynamic_cast<Mac*>(vec) != NULL)
		{
			Mac* m = dynamic_cast<Mac*>(vec);
			m->Wczytaj(m);
		}
		else
		{
			vec->Wczytaj(vec);
		}	
	}
	else 
	{
		if (dynamic_cast<Mac*>(vec) != NULL)
		{
			Mac* m = dynamic_cast<Mac*>(vec);
			m->Wczytaj(wej, m);
		}
		else
		{
			vec->Wczytaj(wej, vec);
		}
	}

	return wej;
}

Wczytaj() - to ta metoda wirtualna, a dokładniej dwie dla każdej klasy (w sumie 4) - jedna z klawiatury (dla usera, żeby o wszystko ładnie się pytało), druga z pliku (który ma postać raczej csv, niezbyt czytelną).

Problem pojawia się już na startupie, gdzie chcę wczytać wszystkie dane z pliku do mojej tablicy w Managerze. W tym celu mam w metodzie Open() Managera fragment kodu, który już bezpośrednio wywołuje te operatory:

	tables = new Vec*[k];

	for (i = 0; i < k; i++)
	{
		c = wej.get();
		if (c == 'M')
		{
			wej >> dynamic_cast<Mac*>(tables[i]);
		}
		else if (c == 'V')
		{
			wej >> tables[i];
		}
	}

tables jest już zaalokowana (k zostało wczytane z pliku odrobinę wcześniej). Niestety, jest jakby pusta. I teraz jak wchodzi w tym drugim warunku (bo pierwszy w pliku okazuje się być wektor, oznaczony w pliku jako 'V') w operator, to próbuje w nim castować **nic **na Mac, by sprawdzić, czy jest macierzą. I niestety rzuca mi toto zachowanie wyjątkiem. Podejrzewam, że gdyby pierwsze było 'M', nie 'V', to by walnęło już w Open(). Hmm... tylko że *jakoś *muszę wiedzieć, czy to jest wektor, czy macierz, to co chcę wczytać, bo od tego zależy sposób wczytywania. Na nic jednak nie mogę wpaść...

PS:

Już sobie z tym jakoś poradziłem. Jakoś takoś. Teraz jeszcze jeden problem. Dynamiczna tablica dwuwymiarowa mi nie działa:

		double** elements = new double*[P];
		for (int j = 0; j < P; j++)
		{
			elements[j] = new double[Q];
			for (int l = 0; l < Q; l++)
			{
				elements[j][l] = (*m)[j][l];
			}
		}

Na podstawie wielu różnych źródeł jestem przekonany że tak toto właśnie powinno wyglądać. P - liczba wierszy. Q - liczba kolumn. Najpierw robimy tablicę P wskaźników na wiersze (też wskaźniki), by potem dla każdego wiersza (wskaźnika) zrobić Q kolumn (doubli). Potem od razu chcę w locie do tego wiersza i kolumny w locie przepisać element z j-tego wiersza i l-tej kolumny macierzy pokazywanej przez wskaźnik m (który jest trochę wcześniej).
W efekcie takiego kodu okazuje się, że mam tablicę P wskaźników, z których każdy pokazuje na jeden i ten sam wiersz macierzy, i wstawiając do j-tego wiersza coś, w efekcie wstawiam do wszystkich P. Dlaczego?

0

Szczerze powiedziawszy nie za bardzo rozumiem dlaczego brniesz w to zadanie wiedząc, że jest ono postawione trochę na głowie. Jeżeli ktoś ma powiedzieć wykładowcy, że jest z nim jakiś problem, to właśnie Ty, a nie nikt z nas. ;)

Z powyższego powodu nie będę się raczej zagłębiał w kod, ale powiem Ci, że po przeczytaniu tytułu tego tematu wydawało mi się, ze potrzebujesz Visitor Pattern. http://en.wikipedia.org/wiki/Visitor_pattern

0

Wiesz, chodzenie pod prąd zazwyczaj oznacza dwóję na koniec semestru. Bo przecież wykładowca wie lepiej... Dlatego wolę zrobić co mi każą. Dobre programy będę pisał dla siebie, nie dla uczelni.

I plis, odpowiedz chociaż na fragment "PS" powyższego posta. Wydaje mi się, że mam jakieś zaćmienie umysłowe i nie widzę jakiejś ważnej rzeczy. Pewno gdybym wstał z ranka świeży od razu bym to zobaczył, ale potrzebuję tego teraz...

PS2.

Dobra. Zapomnieć. Zostałem oszukany przez debugger i jego watche, które pokazywały mi właśnie to co opisałem w pierwszym PSie. Cout wypisuje na konsolę samą prawdę. A nędzny VC++ sowy watch nie potrafi podejrzeć tablicy dwuwymiarowej :/

0

Dobra, następny problem: przy próbie wypisania wszystkiego na ekran za pierwszym razem, zaraz po wczytaniu:

 
TableManager tableManager("tables.txt");

cout << "TableManager utworzony. Zawartosc:\n";

for (int i = 0; i < tableManager.GetK(); i++)
{
	cout << tableManager[i] << "\n";
}

getch();

... w linijce cout << tableManager[i] wywala mi dwa errory Linkera: unresolved token i unresolved external symbol, w obu przypadkach chodzi mu o ostream. Nie wiem czemu. TableManager[i] zwraca Vec&, jak już wiadomo z poprzednich postów. Istnieje przeciążony operator wyjścia:

ostream &operator<<(ostream& wyj, Vec* vec)

przyjmuje wskaźnik na wektor. Musi tak być, gdyż potem mam warunek: if (dynamic_cast<Mac*>(vec) != NULL). Gdybym to robił na referencjach, to dynamic_cast na referencję (jeżeli byłoby niewłaściwe castowanie) walnąłby wyjątkiem, a nie zwrócił NULLa. A jestem zwolennikiem, że zawsze lepiej jest unikać łapania wyjątków, a rozwiązać problem w jakiś lepsiejszy sposób. Jeżeli linijkę cout << tableManager[i] << "\n"; zamieniałem na cout << &(tableManager[i]) << "\n";, żeby do operatora przekazać adres w pamięci, który to byłby adresem wskaźnika, to wtedy nawet nie raczył mi wchodzić do mojej przeciążonej funkcji, tylko po swojemu: wypisywał adresy na ekranie. Wrong. :/ Nie za bardzo wiem jak sobie tutaj poradzić.

Edit:
Zrobiłem taki szpas, że zamiast zwracać referencje, teraz mój operator indeksowania zwraca wskaźnik do Veca. Niestety, tak jak w poprzednim przypadku również tu cout wypluwa mi tylko adresy na ekran... :(

0

Czary-mary:

cout << *tableManager[i] << "\n";
0

To też już próbowałem, ale kompilator twierdzi, iż jest to
error C2100: illegal indirection

0

Wstaw aktualny,kompletny kod programu

0

program.cpp:

#pragma once

#include <conio.h>

#include "TableManager.h"

void main()
{
	TableManager tableManager("tables.txt");

	cout << "TableManager utworzony. Zawartosc:\n";

	for (int i = 0; i < tableManager.GetK(); i++)
	{
		cout << *tableManager[i] << "\n";
	}




	getch();
}

TableManager.h:

#pragma once

#include "Vec.h"
#include "Mac.h"

#include <fstream>
#include <iostream>
#include <iomanip>


class TableManager
{
	int k;
	Vec** tables;

public:

	TableManager(char* filename);
	TableManager(TableManager &tableManager);

	int GetK();

	void Open(char* filename);
	void Save(char* filename);

	void Add(Vec v);
	void Insert(int index, Vec& v);
	void Remove(int i);

	void Reallocate(int newK, Vec** newVectors);

	Vec& operator[](int i)const;
};

TableManager.cpp:

#include "TableManager.h"

using namespace std;

bool isMatricesNow;

ostream &operator<<(ostream& wyj, Vec* vec)
{
	if (&wyj == &cout)
	{
		if (dynamic_cast<Mac*>(vec) != NULL)
		{
			Mac* m = dynamic_cast<Mac*>(vec);
			m->Wypisz(m);
		}
		else
		{
			vec->Wypisz(vec);
		}
	}
	else
	{		
		if (dynamic_cast<Mac*>(vec) != NULL)
		{
			Mac* m = dynamic_cast<Mac*>(vec);
			m->Wypisz(wyj, m);
		}
		else
		{
			vec->Wypisz(wyj, vec);
		}
		vec->Wypisz(wyj, vec);
	}

	return wyj;
}

istream &operator>>(istream& wej, Vec* vec)
{
	if (&wej == &cin)
	{
		if (isMatricesNow)
		{
			Mac* m = (Mac*)vec;
			m->Wczytaj(m);
		}
		else
		{
			vec->Wczytaj(vec);
		}	
	}
	else 
	{
		if (isMatricesNow)
		{
			Mac* m = (Mac*)vec;
			m->Wczytaj(wej, m);
		}
		else
		{
			vec->Wczytaj(wej, vec);
		}
	}

	return wej;
}



TableManager::TableManager(char* filename)
{	
	k = 0;
	tables = 0;

	if (filename)
	{
		Open(filename);
	}
	else
	{
		tables = new Vec*[k];
	}
}

TableManager::TableManager(TableManager &tableManager)
{
	k = tableManager.k;

	for (int i = 0; i < k; i++)
	{
		*tables[i] = *tableManager.tables[i]; //TODO: UWAAAGAAA.
	}
}

void TableManager::Open(char* filename)
{
	filebuf plik;
	plik.open(filename, ios::in);

	istream wej(&plik);

	char* s = new char[3];
	s[0] = 0;
	char c = wej.get();
	if (c == -1) 
	{
		tables = new Vec*[0];
		return;
	}
	int i = 0;
	while(c != '_')
	{
		s[i] = c;
		i++;
		s[i] = 0;
		c = wej.get();
	}
	Vec::SetP(atoi(s));

	if (k > 0)
	{
		for (int j = 0; j < k; j++)
		{
			if (tables[j]) delete tables[j];
		}
	}
	if (tables) delete[] tables;

	delete[] s;
	s = new char[5];
	s[0] = 0;
	c = wej.get();
	i = 0;
	while(c != '_')
	{
		s[i] = c;
		i++;
		s[i] = 0;
		c = wej.get();
	}
	k = atoi(s);

	tables = new Vec*[k];	

	for (i = 0; i < k; i++)
	{
		c = wej.get();
		if (c == 'M')
		{
			isMatricesNow = true;
		}
		if (isMatricesNow)
		{
			tables[i] = new Mac();
			Mac* m = (Mac*)tables[i];
			wej >> m;
		}
		else
		{
			tables[i] = new Vec();
			wej >> tables[i];
		}
	}

	isMatricesNow = false;

	delete[] s;
	plik.close();
}

void TableManager::Save(char* filename)
{
	filebuf plik;
	plik.open(filename, ios::out);

	ostream wyj(&plik);

	wyj << Vec::GetP() << "_" << k << "_";

	for (int i = 0; i < k; i++)
	{
		if (dynamic_cast<Mac*>(tables[i]) != NULL)
		{
			Mac* m = dynamic_cast<Mac*>(tables[i]);
			wyj << "M" << m;
		}
		else
		{
			wyj << "V" << tables[i];
		}
	}

	plik.close();
}

void TableManager::Add(Vec v)
{	
	Insert(k, v);
}

void TableManager::Insert(int index, Vec& v)
{
	Vec** newTables = new Vec*[k + 1];

	int i = 0;
	for (; i < index; i++)
	{
		*newTables[i] = *tables[i]; //TODO: UWAGA 2
	}
	*newTables[++i] = v; //TODO: UWAGA 3

	for (++i; i < k + 1; i++)
	{
		*newTables[i] = *tables[i - 1]; //TODO: UWAGA 4
	}

	Reallocate(k + 1, newTables);
}

void TableManager::Reallocate(int newK, Vec** newTables)
{
	if (k > 0)
	{
		for (int j = 0; j < k; j++)
		{
			if (tables[j]) delete tables[j];
		}
	}
	if (tables) delete[] tables;

	tables = newTables;
	k = newK;
}


Vec& TableManager::operator[](int i)const
{

	char name[4];
	for (int j = 0; j < 4; j++)
	{
		name[j] = tables[i]->GetNaz()[j];
	}

	int n = tables[i]->GetN();

	if (dynamic_cast<Mac*>(tables[i]) != NULL)
	{
		Mac* m = dynamic_cast<Mac*>(tables[i]);
		int P = m->GetP();	
		int Q = m->GetQ();
		double** elements = new double*[P];
		for (int j = 0; j < P; j++)
		{
			elements[j] = new double[Q];
		}

		for (int j = 0; j < P; j++)
		{
			for (int l = 0; l < Q; l++)
			{
				elements[j][l] = (*m)[j][l];
			}
		}
		Mac m3(P, Q, elements, name);
		Mac& m2 = m3;
		return (Vec&) m2;
	} 
	else
	{
		double* elements = new double[n];
		for (int j = 0; j < n; j++)
		{
			elements[j] = (*tables[i])[j];
		}
		Vec v3(n, elements, name);
		Vec& v2 = v3;
		return v2;
	}
}

int TableManager::GetK()
{
	return k;
}


void TableManager::Remove(int i)
{
	Vec** newTables = new Vec*[k - 1];

	for (int j = 0; j < i; j++)
	{
		newTables[j] = tables[j];
	}
	for (int j = i; j < k - 1; j++)
	{
		newTables[j] = tables[j + 1];
	}

	Reallocate(k - 1, newTables);
}

Mam wkleić jeszcze? tego jest 2x tyle (cała klasa Mac i Vec). właśnie dlatego wklejałem po części... Traci na czytelności. I rad byłbym jeszcze niejakie wyjaśnienie, a nie samo czary-mary. Czyli dlaczego działa tak a nie inaczej? Chciałbym się też coś-niecoś nauczyć ;)

0

Tak,wrzuć te klasy bo innaczej nie mogę zrobić sobie projektu u siebie.
A tak na oko to popraw:
1.w funkcji manager::operator[] widzę pewien błąd wewnątrz ifów zwracających referencję do macierzy/wektora.Popatrz uważnie i spróbuj zgadnąć,co jest nie teges :P.To tak à propos naumienia się czegoś,w tym wypadku wykrywania bugów ;)
2.Niech operator<< przyjmuje referencję na wektor,nie wskaźnik
Z tego co widzę po definicjach operator<< oczekiwał,że dostanie wskaźnik na wektor,a dostawał referencję/obiekt
Acha,jeszcze apropos używania dynamic_cast-wpierw próbuj rzutować na macierz,bo na wektor,jako klasę bazową,to imo da się zawsze.

0
MasterBLB napisał(a)

Tak,wrzuć te klasy bo innaczej nie mogę zrobić sobie projektu u siebie.
A tak na oko to popraw:
1.w funkcji manager::operator[] widzę pewien błąd wewnątrz ifów zwracających referencję do macierzy/wektora.Popatrz uważnie i spróbuj zgadnąć,co jest nie teges :P.To tak à propos naumienia się czegoś,w tym wypadku wykrywania bugów ;)
2.Niech operator<< przyjmuje referencję na wektor,nie wskaźnik
Z tego co widzę po definicjach operator<< oczekiwał,że dostanie wskaźnik na wektor,a dostawał referencję/obiekt
Acha,jeszcze apropos używania dynamic_cast-wpierw próbuj rzutować na macierz,bo na wektor,jako klasę bazową,to imo da się zawsze.

Hmm... trochę się pozmieniało od czasu mojego ostatniego posta. Dobra, wrzucę wszystko jeszcze raz, ale po prostu na jakiś hosting. Nie będę forum zaśmiecał.

    • znalazłem jakiegoś buga w tym operatorze, który mnie denerwuje - mój wektor, który chcę zwrócić konstruuję sobie (v3), potem tworzę do niego referencję, i chcę ją zwrócić, ale w tym momencie wołany jest destruktor do mojego wektora, w efekcie potem w operatorze wejścia >> próbuję wywołać metodę na 'niczym' i dostaję nullReferenceEx. Nie wiem jak utrzymać ten obiekt przy życiu. Nie wiem po kiego grzyba on to usuwa, przecież jeszcze tego używam...
    • no, właśnie w nowej wersji operator << i >> przyjmuje (znów) referencję. Wskaźnik był po temu, że jeżeli używa się dynamic_cast<T*> to zwraca NULLa jakby co, i jestem w stanie to sprawdzić if'em. Jak używam dynamic_cast<T&> to jak coś jest nie tak - wali wyjątkiem :/ a jak już pisałem - wolałbym uniknąć zbędnego łapania wyjątków.

i a propoS dynamic_cast - przecież rzutuję na <Mac*> wszędzie (chyba? chyba że ślepy jestem od za długiego nerdzenia... :D )

Projekt, który jest up-to-date:
http://www.sendspace.pl/file/bdbf64482c978be6090be0b
<--- przestarzały. Post niżej nowszy.
PS. Cytuj fragment kodu, o którym będziesz pisał, pls.

PS2. Najbardziej interesuje mnie to, czemu, jak mam funkcję TableManager::Open(), i w niej fragment kodu:

 		if (isMatricesNow)
		{
			tables[i] = new Mac();
			wej >> *(tables[i]);
		}
		else
		{
			tables[i] = new Vec();
			wej >> *(tables[i]);
		}

...to nawet, jak wejdzie w tego pierwszego if'a i wywoła operator wejścia przekazując w nim Mac, nie Vec, a operator >> otrzymuje referencję na Vec, jak widać tu:

istream& operator>>(istream& wej, Vec& vec)
{
	if (&wej == &cin)
	{
		vec.Wczytaj(vec);
	}
	else 
	{
		vec.Wczytaj(wej, vec);
	}

	return wej;
}

...to tak czy siak wywołuje mi funkcję Wczytaj() na rzecz wektora, nie macierzy, mimo że jest to funkcja wirtualna, co widać tutaj:

class Vec
{
protected:
	.
	.
	//deklaracje
	.
	.
public:
	//więcej deklaracji innych rzeczy
	.
	.
	virtual void Wczytaj(Vec& vec);
	virtual void Wczytaj(istream& wej, Vec& vec);
	virtual void Wypisz(Vec& vec);
	virtual void Wypisz(ostream& wyj, Vec& vec);
};
class Mac : public Vec
{
	.
	.
	//deklaracje
	.
	.
public:
	//więcej deklaracji innych rzeczy
	.
	.
	virtual void Wczytaj(Mac& mac);
	virtual void Wczytaj(istream& wej, Mac& mac);
	virtual void Wypisz(Mac& mac);
	virtual void Wypisz(ostream& wyj, Mac& mac);
};

?

Tak właśnie mi się przypomniało, że nie wiem w sumie po co mi dynamic_cast. Skoro funkcje są wirtualne, i mamy referencję na Vec, to powinno wykonać się to, czym ten obiekt naprawdę jest. Chyba.

0

@Azrael_Valedhel

  1. Fakt. Zwracam referencję do lokalnego. Skapłem się. Ale już nie. Teraz operator[] zwraca przez wartość (skoro i tak zwracany obiekt w zamierzeniu nie jest zmieniany to chyba mogę), a jako to co zwraca podaje to, na co pokazuje wskaźnik utworzony przez new:
		Vec* v2 = new Vec(n, elements, name);
		return *v2;
  1. Nie ogarniam visitor pattern. Czy ten wzorzec nie wymagałby przerobienia całej konstrukcji? Jeśli tak, to nie mam na to czasu, bo jutro (właściwie to dzisiaj, koło 15tej) powinienem to oddać... (teoretycznie za tydzień też mogę, ale wtedy ocena idzie o 1 w dół).

PS.
Najbardziej aktualna wersja projektu:
http://www.sendspace.pl/file/49e12b45c289e6018a99bce

PS2. @Azrael_Valedhel
Dobra, wgłębiłem się i mniej więcej na czuja ogarniam visitor. Ale nie mam bladego pojęcia o interfejsach w C++. Wgl nie wiedziałem że jest coś takiego w C++. Znam je z C# do tej pory. Chyba że to nie to samo? I nie za bardzo wiem jak ten wzorzec przystaje do mojego przykładu.

1
  1. A zwalniasz kiedyś tę pamięć? Bo teraz to mi wygląda na wyciek pamięci przy kazdym wołaniu tej funkcji/metody (chociaż kodu nie przeglądałem). Nie możesz po prostu return Vec(n, elements, name);?
  2. dynamic_cast używasz tutaj aby dowiedzieć się jakiego konkretnie typu jest obiekt, na który wskazuje wskaźnik Bazowa*, prawda? Zamiast tego możesz utworzyć obiekt Visitor visitor i wywołać bazowa->accept(visitor) - i w metodzie klasy Visitor zapisać kod, który ma się wykonać na rzecz tego obiektu.
    Przykład może to jaśniej pokaże.
#include <iostream>

class Base;
class Derived;

class  Visitor {
public:
    void visit(Base& base) const;
    void visit(Derived& derived) const;
};

class Base {
public:
    virtual ~Base() {}
    virtual void accept(const Visitor& visitor) {
        visitor.visit(*this);
    }
    void do_something() {
        std::cout << "Wooot!\n";
    }
};

class Derived : public Base {
public:
    virtual ~Derived() {}
    virtual void accept(const Visitor& visitor) {
        visitor.visit(*this);
    }
    void do_something_else() {
        std::cout << "What? 'Woot' is so 1990...\n";
    }
};

void Visitor::visit(Base& base) const {
    // masz referencję do obiektu - wiesz jakiego jest typu. ;)
    std::cout << "Base behaviour\n";
    base.do_something();
}
void Visitor::visit(Derived& derived) const {
    // masz referencję do obiektu - wiesz jakiego jest typu. ;)
    std::cout << "Derived behaviour\n";
    derived.do_something_else();
}

int main() {
    using namespace std;
    
    Base* base = new Base;
    Base* derived = new Derived;;
    Visitor visitor;
    base->accept(visitor);
    derived->accept(visitor);
    
    delete derived;
    delete base;
    
    return 0;
}

Wynik:

Base behaviour
Wooot!
Derived behaviour
What? 'Woot' is so 1990...

Jeden Visitor powinien zajmować się jednym konkretnym działaniem, czyli jeśli potrzebujesz więcej akcji - tworzysz nowe Visitory. Dla każdego nowego musisz dodać nowe przeciążenie metody accept w klasach Base i dziedziczących. Nie jest to może najwygodniejsze, ale mnie bardziej przekonuje niż dynamic_cast, jeżeli byłby często używany. http://www.swe.uni-linz.ac.at/research/deco/designPatterns/Visitor/visitor.defaultroles.c++.html
Szukaj C++ Visitor Pattern żeby znaleźć więcej info. ;)

Chociaż po spojrzeniu na kod metody Vec TableManager::operator[](int i)const nie jestem przekonany czy akurat tutaj Ci się aż tak bardzo przyda ten wzorzec, przynajmniej w takiej wersji kodu... Mam wrażenie, że zabrnąłeś w to wszystko głównie przez to początkowe "głupie" założenie. Jesteś na 100% pewny, że profesor jest betonowy i lepiej oddać głupią pracę niż próbować go poprawiać odnośnie założeń? :P Czy tylko ja miałem (no może nie zawsze, ale zdażało się) do czynienia z wykładowcami, którzy jeśli udało się wykazać błąd w ich rozumowaniu to raczej to chwalili zamiast ganić? ;)

0
  1. O. Whoa. Można tak. No to się faktycznie nowego czegoś nauczyłem. No dobra, masz racjętu jest wyciek...

  2. Fajny ten pattern. Aczkolwiek i tak nie mam już na niego dość czasu :( (zwłaszcza uwzględniając fakt że pierwszy raz się z nim spotykam...). Deadline == 13.15
    I: tak, wykładowca jest betonem, a prowadzący laborki robi 100% rzeczy pod jego dyktando i nie chce słyszeć o żadnym odejściu od wzoru. Raz spróbowałem zrobić coś odrobinę w bok to mi poszła ocena w dół. :/

I rad byłbym znać szybko odpowiedź na pytanie zawarte w PS2 posta z 00:56. Jest kluczowa w tym momencie.

PS. Ok, odpowiedź już poznana... i zrozumiana. Wszystko dlatego że wewnątrz funkcji Vec::Wejście argumentem była Vec&, podczas gdy w funkcji Mac::Wejście - Mac&. A powinien być też Vec&.

Tylko że teraz pojawiło się nowe pytanie innej natury:

Dlaczego, do jasnej ciasnej, jak mam kod główny programu:

#pragma once

#include <conio.h>

#include "TableManager.h"

void main()
{
	TableManager tableManager("tables.txt");

	cout << "TableManager utworzony. Zawartosc:\n";

	for (int i = 0; i < tableManager.GetK(); i++)
	{
		cout << tableManager[i] << "\n";
	}




	getch();
}

to w linijce cout << tableManager[i] owszem, wyciąga mi wektor/macierz z tablicy, tylko że następnie zamiast użyć przeciążonego operatora, stosuje wbudowaną w wektor/macierz konwersję do double'a (która w pierwszym przypadku jest długością, w drugim wyznacznikiem (nie pytać się co z macierzami prostokątnymi, to nie jest w moim pytaniu istotne :P )), i wypisuje liczbę. Grr..

1 użytkowników online, w tym zalogowanych: 0, gości: 1