Klasa w klasie

0

Witam. Mam problem z działaniem pewnego programu w C++. Dopiero uczę się programowania obiektowego (właściwie to w tej chwili uczę się samych jego podstaw) i mam pewien problem, z którym sobie nie potrafię poradzić. Otóż mam podane dwie klasy: TString (zawierająca tablicę *char, zmienną int mówiącą o długości tablicy i metodę print(), drukującą ten string na ekranie konsoli) oraz Kolor (zmienne R, G i B, pole nazwa typu TString i metodę print(), wypisującą zawartość tej klasy na ekranie). Stworzenie konstruktorów, metody i destruktora dla TString nie było większym problemem i wszystko z tego co wiem działa jak należy, problem pojawia się przy konstruktorach dla klasy Kolor.

Otóż, jednym z poleceń jest napisanie konstruktora z parametrem TString. Zgodnie z logiką przypisuję jego wartość do pola nazwa i normalnie z niego korzystam. Problemów nie ma, metoda print() wypisuje całą zawartość klasy Kolor łącznie z polem typu TString i wszystko jest ok. Problem zaczyna się wtedy, gdy dochodzi do uruchamiania się destruktorów jeden po drugim - w pewnym momencie program próbuje usunąć o jedną klasę za dużo... ale najpierw dam kod, żeby było wiadomo o co chodzi. Pominę wszystkie include i using namespace std, szkoda się z tym powtarzać.

// PLIK TString.h
class TString
{
private:
	int len;
	char *str;
public:
	inline void print()
	{
    for (int i=0;i<len;i++)
	   cout<<*(str+i);
	cout<<endl;
    }
	TString();
	TString(char *ArgS);
	TString(char *ArgS, int ArgI);
	~TString();
};


// PLIK TString.cpp
TString::TString()
{
	cout<<"Konstruktor bezargumentowy TString"<<endl;
	len=0;
	str=0;
}
TString::TString(char *String)
{
	cout<<"Konstruktor z argumentem char* TString"<<endl;
	len=strlen(String);
	str=new char[len];
	for (int i=0;i<len;i++)
		*(str+i)=*(String+i); //strcpy(str,String);
}
TString::TString(char *String, int Dlug)
{
	cout<<"Konstruktor z dwoma argumentami char* i int TString"<<endl;
	len=Dlug;
	str=new char[len];
	for (int i=0;i<len;i++)
       *(str+i)=*(String+i);
}

TString::~TString()
{
	cout<<"Destruktor TString"<<endl;
	if (str)
	   delete []str;
}


// PLIK Kolor.h
class Kolor
{
private:
	int R,G,B;
	TString nazwa;
public:
	inline void print()
	{
		cout<<"R: "<<R<<endl;
		cout<<"G: "<<G<<endl;
		cout<<"B: "<<B<<endl;
		cout<<"Nazwa: ";
		nazwa.print();
	}
	Kolor();
	Kolor(TString *ArgT);
	~Kolor();
};


// PLIK Kolor.cpp
Kolor::Kolor()
{
	cout << "Konstruktor bezargumentowy Kolor" << endl;
	R=G=B=0;
}
Kolor::Kolor(TString *ArgT)
{
	cout << "Konstruktor z jednym argumentem TString Kolor" << endl;
	R=3; G=5; B=88;
	nazwa=*ArgT;
}

Kolor::~Kolor()
{
	cout << "Destruktor Kolor" << endl;
	//nazwa.~TString();
}


// PLIK main.cpp
int _tmain(int argc, _TCHAR* argv[])
{
	TString firstname("Imie");
	firstname.print();
	TString lastname("Nazwisko");
	lastname.print();
	Kolor COSTAM(&firstname);
	COSTAM.print();
	return 0;
}

To co się dzieje rozumiem tak: polu nazwa w klasie Kolor przypisany zostaje firstname. Problem w tym, że teraz obie nazwy wskazują na jedną klasę, więc przy próbie uruchomienia drugiego destruktora program wykrzacza się (bo nie ma już czego usunąć). Problem leży w tym, że kompletnie nie mam pojęcia, w jaki sposób napisać to tak, żeby program działał. Jesteście w stanie wytknąć mi wszystkie możliwe błędy w kodzie (przede wszystkim ten, który uniemożliwia mi zakończenie tego programu bez wykrzaczenia się)? Byłbym bardzo wdzięczny.

0

W destruktorze Kolor nie musisz wywoływać destruktora TString - sam się wywoła.
W konstruktorze Kolor nie musisz wywoływać konstruktora TString automatycznie zostanie użyty konstruktor domyślny.
Ale możesz to zrobić:

Kolor::Kolor(const TString &ArgT):nazwa(ArgT)
{
        cout << "Konstruktor z jednym argumentem TString Kolor" << endl;
        R=3; G=5; B=88;
}
0

jw, poza tym mylisz obiekty klas z klasami a to wielki blad!

0
_13th_Dragon napisał(a):

W destruktorze Kolor nie musisz wywoływać destruktora TString - sam się wywoła.
W konstruktorze Kolor nie musisz wywoływać konstruktora TString automatycznie zostanie użyty konstruktor domyślny.
Ale możesz to zrobić:

Kolor::Kolor(const TString &ArgT):nazwa(ArgT)
{
        cout << "Konstruktor z jednym argumentem TString Kolor" << endl;
        R=3; G=5; B=88;
}

Chyba nie do końca zrozumiałem. Znaczy się, zmieniłem dany konstruktor na taki jak tutaj. Prototyp w Kolor.h zmieniłem na Kolor(const TString &ArgT); (za pierwszym razem próbowałem do tego dodać :nazwa(ArgT), ale kompilator zaczął się pluć, a bez tego ruszył), natomiast wywołanie w main.cpp na Kolor COSTAM(firstname);. Efekt jest... co tu dużo mówić - taki sam: program nadal przy zakończeniu działania wykrzacza się w taki sam sposób jak poprzednio. Czy ja czegoś nie tutaj nie zrozumiałem? (Próbowałem co prawda trochę to ogarnąć ze slajdów z wykładu, ale niestety nie dało to na tyle dobrych efektów jak bym chciał.)

@Shalom: racja, chciałem napisać to tak szybko że kompletnie mi się to ze sobą wymieszało :) sorry za głupie błędy.

0

Jeżeli zrobiłeś:

Kolor(const TString &ArgT); // deklaracja
Kolor::Kolor(const TString &ArgT):nazwa(ArgT) // definicja
Kolor COSTAM(firstname); // użycie

to kompilator nie może się pluć, coś nie dorobiłeś.

Wywala się dla tego że klasa TString nie ma operatora przypisania i konstruktora kopiującego.

TString(const TString &S):len(S.len),str(new char[len]) } { memcpy(str,S.str,len); }
TString &operator=(const TString &S) { TString cp(S); swap(cp); return *this; }
void swap(TString &S) { swap(len,S.len); swap(str,S.str); }

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