Idiotyczna niejawna konwersja w obie strony...

0

Witam,

Mam klasę macierzy (Mac), która posiada:

Konstruktor kopiujący treści:

Mac::Mac(Mac& m, char* t)
{
	if (t) strcpy(Naz, t);
	else strcpy(Naz, m.Naz);

	this->n = m.n;
	P = m.P;
	Q = m.Q;

	A = new double[n];
	for (int i = 0; i < n; i++)
	{
		A[i] = m.A[i];
	}

	hasA = m.hasA;
	hasN = m.hasN;
}

Poza tym operator rzutowania do double (liczący wyznacznik):

Mac::operator double()const
{
	if (P != Q) return 0;
	if (P == 1) return A[0];
	if (P == 2) return A[0] * A[3] - A[1] * A[2];

	double multiplier;
	double** minorA = new double*[P - 1];
	for (int i = 0; i < P - 1; i++)
	{
		minorA[i] = new double[Q - 1];
		
	}

	double* dividends = new double[Q - 1];
	int l; 
	for (l = 0; l < Q + 1; l++) //l - wybrana kolumna według której rozwijamy
	{
		if (l == Q) //jeżeli tak jest to znaczy że mamy cały wiersz zer
		{
			return 0;
		}
		if (A[l] != 0) //żeby nie dzielić przez 0
		{
			for (int j = 0; j < l; j++)//przepisanie wszystkich elementów od 0 do kolumny do minora, i do dzielnych
			{
				dividends[j] = A[j];
				for (int k = 0; k < P - 1; k++)
				{
					minorA[k][j] = A[(k + 1) * P + j];
				}
			}
			for (int j = l; j < Q - 1; j++)//i od kolumny do końca
			{
				dividends[j] = A[j + 1];
				for (int k = 0; k < P - 1; k++)
				{
					minorA[k][j] = A[(k + 1) * P + j + 1];
				}
			}

			for (int j = 0; j < Q - 1; j++)//robimy zera
			{
				multiplier = - dividends[j]/ A[l];

				for (int k = 0; k < P - 1; k++)
				{
					minorA[k][j] = minorA[k][j] + A[(k + 1) * P] * multiplier;
				}
			}

			break;
		}
	}

	Mac minor(P - 1, Q - 1, minorA); 
	return (l % 2 == 0 ? 1 : -1)*(double)minor; //liczymy wyznacznik minora
}

oraz konstruktor konwertujący z double:

Mac::Mac(double a, char* t):Vec(a, t)
{
	P = 1;
	Q = 1;
}

Jak widać, tak, Mac dziedziczy po klasie Vec (czyli macierz po wektorze). Proszę tego nie komentować, nie ja wymyśliłem tą konstrukcję, jest ona narzucona przez prowadzącego zajęcia. Konstruktor konwertujący bazowy (czyli w Vec) ma taką postać:

Vec::Vec(double a, char *t)
{
	//cout << "\nwywolano konstruktor - konwerter\n";

	if (t)
	{
		strcpy(Naz, t);
		hasNaz = true;
	}
	n = 1;
	A = new double[n];
	A[0] = a;

	hasN = true;
	hasA = true;
}

Naz - char*, nazwa
A - double*, elementy wektora/macierzy
n - int, liczba elementów wektora/macierzy
P - liczba wierszy macierzy (tylko macierz)
Q - liczba kolumn macierzy (tylko macierz)

hasNaz/N/A - boole, które mi były potrzebne. Nieistotne.

Pytanie: dlaczego, jak mam linijkę:

Mac m4 = Mac(m3, newName);

(m3 już istnieje, jest macierzą, newName - char*, też już istnieje, oba są wypełnione prawidłowo)

Dlaczego, na taką instrukcję kompilator działa w ten sposób że:
a) tworzy mi nową macierz na podstawie innej macierzy, tylko że z inną nazwą ---> to jest w porządku, ale:
b) następnie konwertuje mi tą macierz do double, czyli liczy wyznacznik ---> ?!!!
c) tylko po to, by następnie skonwertować konstruktorem z powrotem do macierzy - macierzy 1x1 o elemencie będącej wyznacznikiem?!

Nie rozumiem. I nie wiem jak zmusić toto do prawidłowego działania.

1

krótko: operator rzutowania na double NIE POWINIEN ISTNIEĆ )
ani byc jakims "zamierzonym" dzialaniem.. a jesli juz naprawde bardzo chcesz aby to tak bylo, powinien miec on przylepione do siebie Kropelką frazę EXPLICIT
), która uprzykrzy Ci jego używanie, ale za to zabezpieczy Cie przed przypadkowym odpaleniem się konwersji..

a teraz pozwól że przeczytam resztę Twojego postu..

*) wlasnie z powodu automatycznych konwersji. jesli chcesz, moge to opisac pozniej, jak z pracy wyjde.. ale to jest niekrotki temat

edit;
ok, sorry za nerwa, wlasnie doczytalem do konca.. jesli jest to wymysl prowadzacego - no to trudno. jednak jesli Twoj, to uzyj przynajmniej explicit..

a odpowiedz na Twoje pytane brzmi:
kompilator za pewne robi tak, poniewaz nie znalazl lepszej sciezki konwersji pomiedzy lewa i prawa strona przypisania.. stac sie tak moglo na przyklad w momencie gdy ktos zapomnial napisac konstruktora kopiujacego, ale rowniez na przyklad gdy jego konstruktor kopiujacy wymaga parametru "macierz &" zamiast "macierz const&" !!

zeby dokladnie Ci odpowiedziec jaka sciezka padla i czemu, musialbys niestety zalaczyc komplet kodu:/

0

Explicit próbowałem, ale generuje błędy, treści:

'Mac::operator double' : illegal storage class

Dokładnie 3, takie same, w pliku nagłówkowym, przy deklaracji, wszystkie.
Nie do końca je rozumiem, zwłaszcza zważywszy na to, iż słówko explicit znam od niecałego pół miesiąca.

I moim skromnym zdaniem jak dla mnie nie powinno być w tym wypadku żadnej konwersji - skoro typem zwracanym przez konstruktor kopiujący jest, a jakże macierz.

I jaka właściwie w tym wypadku jest różnica pomiędzy Mac& a Mac const&? Znaczy - rozumiem, że to drugie obiecuje, że owa macierz nie będzie zmieniana wewnątrz konstruktora. No ale co to właściwie zmienia?

PS.
Dzięki - to działa. Choć wciąż nie wiem co to zmienia.

0
  1. no tak. Zapomniałem nadmienić - ten konstruktor kopiujący JEST jednoargumentowy. Ponieważ drugi argument jest nieobowiązkowy, ale to widać tylko w definicji, której nie zmieściłem - sory.
Mac::Mac(double a, char*t = 0);
  1. Owszem. Ale to już powiedz prowadzącemu zajęcia, jak Ci się chce. W poleceniu jest, żeby zdefiniować konwersję macierzy do double. I ma toto działać. Jedyna sensowna interpretacja macierzy jako double, na którą wpadłem (to już nie było wyspecyfikowane) - to jej wyznacznik.

Edit. Eeee? znikł post, na który odpowiadałem?... mhm.

1

I jaka właściwie w tym wypadku jest różnica pomiędzy Mac& a Mac const&?

To zmienia niesamowicie wiele!!! W wielkim skrocie, to oznacza, ze "prawa strona" nie tylko nie bedzie zmieniana, ale takze ze w zwiazku z tym, ze ma prawo byc obiektem stałym w tym np. stalym tymczasowym, ktore sa o wiele czestsze niz zmienialne-tymczasowe, porownaj banalne:

char const * const jeden = "mama";
char dwa[] = "tata";

string _jeden = string(jeden);  --- wymaga istnienia ctor string(char const *     albo ctor string(char *
string _dwa = string(dwa); --- wymaga istnienia ctor string(char * 

string _trzy = string( (dwa + jeden).c_str() );  -- a tutaj jaki jest mozliwy, hm?
string _trzy2 = string( (dwa + jeden) );  -- a tutaj jaki jest mozliwy, hm? 

const & są wbrew pozorom luzniejsze! tzn. w uzyciu, nie w napisaniu :)

ten konstruktor kopiujący JEST jednoargumentowy. Ponieważ drugi argument jest nieobowiązkowy (...) Mac::Mac(double a, char*t = 0);

nienienie.. on jest DWUargumentowy. domyslne wartosci argumentow, widzisz, są w C++ bardzo mylaca rzecza.. pewne zdziwi Cie, że możesz je w locie przedefiniowywac PÓŹNIEJ, por.:

int funkcja(int a, int b, int c, int d = 0) {return a+b+c+d;}
int main()
{
    int A = funkcja(1,1,1,1);
    int B = funkcja(1,1,1);
    
    {   //jakis nowy scope, ot tak sobie

        int funkcja(int a, int b = 3, int c = 3, int d = 3);  // inline local forward declaration! with NEW default values..

        int C = funkcja(1,1,1,1);
        int D = funkcja(1);  // poprawne!! D===10
    }
}

widzisz.. fakt ze istnieje cos takiego jak "wartosc domyslna" nie jest cecha definicji funkcji, tylko jej deklaracji oraz miejsca wywolania.. jesli masz konstruktor dwuargumentowy z drugim argumentem domyslnym, to jest on nadal DWUarguemntowy, ot jedynie kazde miejsce jego wywolania po prostu automatycznie dopisuje do wywolania drugi parametr. To NIE powoduje ze ta funkcja ma o parametr mniej.

... a to powoduje, ze w Twoim kodzie nie istnieje konstruktor kopiujący !!
konstruktor kopiujacy ma scisle okreslona sygnature, brzmiąca tak, albo tak:
T(T const & rhs)
T(T & rhs)

i nie ma wiecej opcji! konstruktor () jest domyslny, konstruktory wszystkie pozostałe JEDNOargumentowe to konwersje, a wszystkie pozostale, to po prostu, "konstruktory", ogólne, zwykłe.

edit:
ookej, do copyctorów dochodza jeszcze sygnatury
T(T const volatile & rhs)
T(T volatile & rhs)
ale to już się czepiam..

0

ookej.. a teraz sie zdziwilem.. otóż standard z 2003 (a takze draft2008..) definiuje:

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&,
volatile X& or const volatile X&, and either there are no other parameters or else all other
parameters have default arguments
(8.3.6).106) [Example: X::X(const X&) and X::X(X&, int=1)
are copy constructors.

ale ale.. wartosci domyslne sa aspektem scope-lokalnym :( Szczerze mowiac, odkad odkrylem ze defaultvalues sa lokalne, nie bawilem sie tym tematem i nieprobowalem jakos nigdy 'exploitowac' mozliwosci wartosci domyslnych, i raczej uwazam zeby nie dolaczac ich w deklaracjach moich klas/funkcji. Jak komus potrzebne, to sobie machnie lokalne swoje wlasne i tyle...

W takim razie trafiles na kruczek, ze akurat Twoj kompilator nie trzyma sie tego małego kawałka standardu. Trochę mu się nie dziwię, i trochę sie dziwie standardowi, że ma aż tak szeroką definicje copyctora. No, a tutaj wychodzi w takim razie, ze w sensie tekstu standardu, Twoj kompilator rozróżnia konstruktory kopiujące bardziejsze i mniejbardziejsze :)

którystam edit: zauwazylem tag "visual" - ano, mozliwe.. ms dopiero dociaga do standardu, zreszta przeciez chyba tylko comeau sie faktycznie standardu trzyma:P moze sie potem tym tematem pobawie, ale nie obiecuje, z czasem cienko..

0

Spoko...

A myślałem że jest to pełnoprawny konstruktor kopiujący - przynajmniej tak twierdzi wykładowca. I tak każe pisać konstruktor kopiujący, więc... :/

No cóż. Ale z człowiekiem, wg którego macierz dziedziczy po wektorze, to wszystko jest możliwe. Dzięki wielkie :D skończyłem ten projekt finally!! <jupi> Tera mam inny problem, z figurami, ale to już jest w innym temacie. Przynajmniej konstrukcja jest prawidłowsza...

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