Ĺadny brzuch
Programowanie to fajne hobby, no chyba że trafisz na jakiś błąd w programie, który spowoduje...
że zaczynasz walić z dyni w monitor :) Dobra, przejdę do rzeczy.
01: #include <iostream> 02: #include <string> 03: using namespace std; 04: 05: enum Typ{INT = 1, FLOAT, STRING}; 06: 07: template<typename T> 08: T Funkcja(Typ _rodzaj) 09: { 10: T wynik; 11: 12: switch(_rodzaj) 13: { 14: case INT: wynik = (T)10; break; 15: case FLOAT: wynik = (T)5.5; break; 16: case STRING: wynik = (T)"Napis"; break; 17: } 18: return wynik; 19: } 20: 21: int main() 22: { 23: float zmienna = Funkcja<float>(FLOAT); 24: cout << zmienna; 25: 26: int x; 27: cin >> x; 28: return 0; 29: }
Ten program się nie skompiluje. Taki błąd:
In function `T Funkcja(Typ) [with T = float]':
(wiersz 23)instantiated from here
(wiersz 16)pointer value used where a floating point value was expected
Jak wykomentuję linię 16. to błędu nie ma. Nie wiem, dlaczego ten string jest winny. Czy wynik = (T)"Napis" mimo rzutowania jest uważany za const char* ? Ja tego nie mogę rozgryźć, ale mam nadzieję że któryś(któraś) z was tak.
pzdr, Dante.
bo wynik jest u ciebie typem "float". Idzie sie poucz szablonow a nie bedziesz pisal ze string do niczego
Niby co ?
float zmienna = "lalalala"; <- no sam sopbie pomysl
A - i napisz jeszcze niby co to ma robic. Do zmiennej typu float zapisac zmiennoprzecinkowo adres stringa ?, czy to co w stringu ? co to w ogole ma robic ;)
Użytkownik st3tc edytował ten post 03 wrzesień 2005, 14:55
bo wynik jest u ciebie typem "float". Idzie sie poucz szablonow a nie bedziesz pisal ze string do niczego
Niby co ?
float zmienna = "lalalala"; <- no sam sopbie pomysl
A - i napisz jeszcze niby co to ma robic. Do zmiennej typu float zapisac zmiennoprzecinkowo adres stringa ?, czy to co w stringu ? co to w ogole ma robic ;)
nie bede tych glupot komentowal bo szkoda mojego czasu. Masz odpowiedz matolq:
case STRING: wynik = (T)"Napis"; break;
wynik jest typu float !!!!!!!!!!!!!!. Rozumiesz czy mam Ci to narysowac ?. To sie pytam co chcesz osiagnac przez zapis
float wynik = (T)"Napis";
I nie obrazaj mnie.
Napisales cos takiego :
float Funkcja(Typ _rodzaj) { float wynik; switch(_rodzaj) { case INT: wynik = (float)10; break; case FLOAT: wynik = (float)5.5; break; case STRING: wynik = (float)"Napis"; break; } return wynik; }
to sie pytam co ma byc wstawione do wynik jak sobie podasz swoj typ wyliczniowy (czek sprawde w ksiazce co to) jako STRING ?.
Użytkownik st3tc edytował ten post 03 wrzesień 2005, 20:01
nie bede tych glupot komentowal bo szkoda mojego czasu. Masz odpowiedz matolq:
case STRING: wynik = (T)"Napis"; break;
wynik jest typu float !!!!!!!!!!!!!!. Rozumiesz czy mam Ci to narysowac ?. To sie pytam lolq co chcesz osiagnac przez zapis
float wynik = (T)"Napis";
I nie obrazaj mnie.
Napisales cos takiego :
float Funkcja(Typ _rodzaj) {  float  wynik;   switch(_rodzaj)  {  case INT: wynik = (T)10; break;  case FLOAT: wynik = (T)5.5; break;  case STRING: wynik = (T)"Napis"; break;  }  return wynik; }
to sie pytam co ma byc wstawione do wynik jak sobie podasz swoj typ wyliczniowy (czek sprawde w ksiazce co to) jako STRING ?.
Kolego, to ja nie będę tego komentował. Otwórz sobie notatnik, wklej kod, który podałem, skompiluj, zlinkuj, uruchom program. Już?
Teraz wykomentuj sobie linię numer 16, skompiluj, zlinkuj, uruchom, użyj dev\brain i napisz, jak będziesz mądrzejszy(ale to chyba nieprędko będzie).
kolego dante7 - zaufaj mi ze st3tc nie jest jedenastolatkiem i ma mozg (btw jesli juz to dev/brain nie dev\brain) jak rowniez ogromna wiedze z zakresu c++ (ktorej to wiedzy jak widze brakuje Tobie). Apeluje abys przestal sie osmieszac i obrazac uzytkownikow tego forum.
To może ja to prościej od st3tc wyjaśnie:
pisząc takiego template, to tak jak byś napisał makro
#deifne aaa(a) (a)"aaaaaa"
A potem wstawiasz w a, float. Jak wiadomo preprocesor wkleja wszystko jak leci - i kompilator za chiny nie rozumie co ty wyprawiasz - robisz z float string'a?
Z template'ami jest podobnie - WSZYSTKIE case muszą być uodpornine na taką niespodzianke, bo inaczej kicha z funkcji.
BTW: jak kogoś nie znasz, to go nie bluzgaj - akurat st3tc, to ma chyba tu największą wiedze z nas wszystkich (imo, oczywiście :] )
Użytkownik Real_Noname edytował ten post 03 wrzesień 2005, 19:36
Hmm.. trochę przegiąłeś stary. Bluzgasz na st3tc'a, który od pierwszego posta stara Ci się pomóc. A przyczyną błędu jest od początku do końca Twoje niezrozumienie tematu.
Powtarzając już chyba po wszystkich - po prostu kompilator kompilując blok switch-case kompiluje WSZYSTKIE case'y - co jest dość logiczne - swoją funkcję możesz wywołać tak, jak sobie to Ty wyobrażasz:
float zmienna = Funkcja<float>(FLOAT);
ale też tak:
float zmienna = Funkcja<float>(STRING);
I co w drugim przypadku? Nikt nie zabrania takiego wywołania - kompilator musi to uwzględnić, a przy podstawianiu typu float pod szabon, zapis przy case STRING: staje się bez sensu z powodów, które st3tc opisał już conajmniej dwa razy.
A co do bluzgania na st3tc'a - n/c. Kiedyś się zorientujesz, jak głupio teraz postępowałeś.
Dobra, to będzie chyba ostatni(mój) post tego tematu. Po pierwsze st3tc zwracam Ci honor, raczej nie mam w zwyczaju nikogo obrażać, ale czasami niestety mi się zdarza, dlatego oczywiście przyznaję się do błędu i jeszcze raz przepraszam.
Po drugie, dlaczego od razu nie wytłumaczyłeś takiemu tępemu ludziowi jak ja tego, co napisałeś w poście #6, było by na pewno bardziej spokojnie.
Po trzecie... nie to już było by wszystko.
Wielki szacunek dla Ciebie i pozostałych, Dante.
Mam pewien pomysł, ale nie wiem, czy nie będzie tu wycieków pamieci
#include <iostream> #include <string> using namespace std; enum Typ{INT = 1, DOUBLE, STRING, TEST}; class Test { public: Test(){cout << "Konstruktor klasy test\n";}; ~Test(){cout << "Destruktor klasy test\n";}; }; void * Funkcja(Typ _rodzaj) { void * ptr = 0; switch(_rodzaj) { case INT: ptr = new int(1); break; case DOUBLE: ptr = new double(0.5); break; case STRING: ptr = new string("Lancuch znakow"); break; case TEST: ptr = new Test; break; } return ptr; } int main() { Test t = *(Test*)Funkcja(TEST); int x; cin >> x; return 0; }
Co o tym myślicie?
Tak sobie mysle ze zrobiles tuta taka mala fabryke abstrakcyjna:D
Kodu nie kompilowalem, ale wydaje mi sie, ze przydaloby sie kilka rzutowan, i bedzie OK. No i nie ma zwolnienia pamieci :D
Tak sobie mysle ze zrobiles tuta taka mala fabryke abstrakcyjna:D
To dobrze, czy źle? :)
Kodu nie kompilowalem, ale wydaje mi sie, ze przydaloby sie kilka rzutowan, i bedzie OK. No i nie ma zwolnienia pamieci :D
To dobrze, czy źle? :)
A możesz napisać, jak Ty byś to poprawił?
//Edit
To jest okno konsoli po uruchomieniu programu:
Jeden mały szczegół: Konstruktor i destruktor klasy Test są wywoływane tylko raz
O czym to świadczy?
Jak operujesz na wskaznikach(a dokladnie tworzysz obiekty wewnatrz funkcji), to musisz pamietac o zwalnianiu pamieci. Ty zamiast przechowywac wskaznik na obiekt, robisz kopie tego obiektu. W tym momencie sam sobiue szkodzisz:D
//============================================================================== // OneForAll, czyli funkcja po tuningu // pekore 2005 //============================================================================== #include <iostream> #include <string> using namespace std; enum Typ{INT = 1, DOUBLE, STRING, TEST}; class Test { public: Test(){cout << "Konstruktor klasy Test\n";}; Test(const Test & arg){cout << "Konstruktor kopiujacy klasy Test\n";}; ~Test(){cout << "Destruktor klasy Test\n";}; }; template <typename T> T Funkcja(Typ _rodzaj) { void * ptr = 0; switch(_rodzaj) { case INT: ptr = new int(10); break; case DOUBLE: ptr = new double(0.5); break; case STRING: ptr = new string("Lancuch"); break; case TEST: ptr = new Test; break; } T tmp = *(T*)ptr; delete (T*)ptr; return tmp; } int main() { Test t = Funkcja<Test>(TEST); return 0; }
Jakby komuś nie chciało się kompilować tego programu, to tu jest okno konsoli:
Microsoft Windows XP [Wersja 5.1.2600]
© Copyright 1985-2001 Microsoft Corp.
D:\Documents and Settings\Paweł>cd Moje dokumenty\Programowanie\OneForAll
D:\Documents and Settings\Paweł\Moje dokumenty\Programowanie\oneForAll>OneForAll.exe
Konstruktor klasy Test
Konstruktor kopiujacy klasy Test
Destruktor klasy Test
Destruktor klasy Test
D:\Documents and Settings\Paweł\Moje dokumenty\Programowanie\oneForAll>
Jak zwykle uwagi mile widziane.
Jak zwykle uwagi mile widziane. Jedna - jaki w ogole sens ma ten kod ktory piszecie ?
Jedna - jaki w ogole sens ma ten kod ktory piszecie ?
Ten kod (kod Funkcji, bo chyba o ten Ci chodzi) w tym programie nie ma większego sensu. Ale pomyśl sobie, że masz klasę, w niej ileś tam zmiennych, te zmienne jakichś tam (różnych) typów. No i masz napisać akcesory dla tej klasy. Zależy ile masz w tej klasie zmiennych, ale im więcej zmiennych, tym pewnie więcej akcesorów. A tu masz tylko jeden, uniwersalny akcesor.
Ten kod (kod Funkcji, bo chyba o ten Ci chodzi) w tym programie nie ma większego sensu. Ale pomyśl sobie, że masz klasę, w niej ileś tam zmiennych, te zmienne jakichś tam (różnych) typów. No i masz napisać akcesory dla tej klasy. Zależy ile masz w tej klasie zmiennych, ale im więcej zmiennych, tym pewnie więcej akcesorów. A tu masz tylko jeden, uniwersalny akcesor.
Czy ja juz przypadkiem nie napisalem, ze ty probujesz zrobic cos al'a fabryka abstrakcyjna :D
Tyle, ze zamiast na wskaznikach jedziesz teraz na obiektach, a po drugie program faktycznie nie ma zadnego sensu(a raczej nic tworczego nie wnosi :D).
I to jest wedlug Ciebie dobre ?
Dlatego chciałbym, żebyś na przykład Ty st3tc powiedział coś więcej na temat tego, co robię źle. Nie chodzi mi oczywiscie o bledy w kodzie - nie. Chodzi mi o strategie programowania.
Generalnie jezeli cos udostepniasz - powinno miec to cos wlasne metody dostepowe. Wiecej - jezeli masz jakas metoge Get<costam>Value, powinienes z miejsca napisac Set<costam>Value (o ile to nie const).
Po co ?. Zakladamy ze nasz kod bedzie uzywal inny programista. Nawet jezeli Set<costam> Ty nie uzywasz - ten ktos moze. Metody dla dostepu do zmiennych maja te zalete ze sa przejrzyste i ... bezpieczne.
BEZPIECZENSTWO
To jest dla mnie numer 1 w projektowaniu.
Idac Twoja strategia accesorow, dla klasy ktora posiada [n] zmiennych mozna napisac accesorka z switchem ktory pobiera adres tej zminnej i zwraca jako wskaznik typu void. Uzytkownik nastepnie castuje do wskaznika odpowiedniego typu i wykonuje dereferencje.
I tu jest olbrzymie miejsce na bledy:
int zmienna = *(int*)Funkcja(INT);
w tym przykladzie wszystko gra. Ale pozniej programista zmienil i zapomnial poprawic castowanie (napiety plan, deadline itp)
int zmienna = *(int*)Funkcja(FLOAT);
Ajajajaj - popatrzmy Funkcja pobiera adres zmiennej typu float. Programista sie pomylil i scastowal (void*) na (int*) zamiast (float*). Dereferencja potraktuje te pamiec nie jako 32-dwu bitowy zapis zmiennoprzecinkowy, ale jako 32-dwu bitowa liczbe calkowia ze znakiem (inna reprezentacja w pamieci).
Takie cos powoduje ciezkie do wykrycia bledy - wrecz potrafiace zniszczyc czlowieka :)
Ale idzmy dalej - A co jezeli jako parametr podajesz w accesorku nazwe zmiennej (jakis enumek ?). Tu sie zaczyna sajgon :
(void*)Funkcja( ZMIENNA_MENU );
I mamy problem - menu jest reprezentowane przez co ?. HMENU ?, CMenu ?, wxMenu ? Do czego mamy scastowac ?
Tutaj nie tylko mamy zalamane bezpieczenstwo, ale i wprowadzamy zament.
Co innego tutaj :
wxMenu *GetMenu();
-----------
PS - GetMenu to zwykly return, Twoj typ accesorkow to switch - jak myslisz ktore jest szybsze ? :)
PS2 - propo bezpieczenstwa:
int main() { Test t = Funkcja<Test>(TEST); return 0; }
A co jezeli Test bedzie mial jakies metody, a Ty przez pomylke podasz jako parametr np STRING ?.
To jest wlasnie to o czym mowie od poczatku. To jest blad programu ktory spowoduje kompletne posypanie sie aplikacji.
Pozdr
st3tc
Użytkownik st3tc edytował ten post 05 wrzesień 2005, 22:41
Aha, szkoda, że nie napisałem tego trochę wcześniej: chodziło mi o takie używanie akcesora: F<int>(MyAge), F<string>(MyName), itp. To tak na marginesie. F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE funkcje :). I nadal kazda z nich bedzie miala switcha, gdzie legalną pozycją bedzie TYLKO I WYLACZNIE jeden case z tego switcha.
To nic innego jak rozbudowane, przerosniete Get<costam>Value :) - bo kazda z wersji Twojej funkcji bedzie prawidlowa tylko i wylacznie dla jednego typu :)
No nie wiem czy to jest to co chciales ;)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 08:31
F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE klasy :).
Czyli, jeżeli jakas klasa posiada szablon funkcji, to kompilator tworzy różne wersje klasy, tak?
Nie wersje klasy - tylko wersje funkcji (w tym przykladzie) :). Szablon to wzor. Szablon funkcji to informacja jak wygenerowac funkcje, szablon klasy to informacja jak wygenerowac klase.
Pozwolilem sobie zmodyfikowac Twoj kod :
#include <string> class Osoba { public: Osoba(){} ~Osoba(){} template<typename T> T Pobierz(int _x) { return T(); } template<typename T> void Ustaw(int _x, T _wartosc) { } }; int main() { Osoba jakasOsoba; std::string imie = jakasOsoba.Pobierz<std::string>(0); int i = jakasOsoba.Pobierz<int>(1); float f = jakasOsoba.Pobierz<float>(2); char c = jakasOsoba.Pobierz<char>(3); return 0; }
A teraz popatrz na kod asm po kompilacji :
int main() { 00401000 push ebp 00401001 mov ebp,esp 00401003 push 0FFFFFFFFh 00401005 push offset $L16016 (407BA0h) 0040100A mov eax,dword ptr fs:[00000000h] 00401010 push eax 00401011 mov dword ptr fs:[0],esp 00401018 sub esp,20h Osoba jakasOsoba; 0040101B lea ecx,[jakasOsoba] 0040101E call _STL::allocator<char>::allocator<char> (4010A0h) 00401023 mov dword ptr [ebp-4],0 std::string imie = jakasOsoba.Pobierz<std::string>(0); 0040102A push 0 0040102C lea eax,[imie] 0040102F push eax 00401030 lea ecx,[jakasOsoba] 00401033 call ?Pobierz@?$@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@_STL@@@_STL@@@Osoba@@QAE? AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@_STL@@@_STL@@H@Z (401250h) 00401038 mov byte ptr [ebp-4],1 int i = jakasOsoba.Pobierz<int>(1); 0040103C push 1 0040103E lea ecx,[jakasOsoba] 00401041 call Osoba::Pobierz<int> (401280h) 00401046 mov dword ptr [i],eax float f = jakasOsoba.Pobierz<float>(2); 00401049 push 2 0040104B lea ecx,[jakasOsoba] 0040104E call Osoba::Pobierz<float> (401290h) 00401053 fstp dword ptr [f] char c = jakasOsoba.Pobierz<char>(3); 00401056 push 3 00401058 lea ecx,[jakasOsoba] 0040105B call Osoba::Pobierz<char> (4012B0h) 00401060 mov byte ptr [c],al i = i; 00401063 mov ecx,dword ptr [i] 00401066 mov dword ptr [i],ecx return 0; 00401069 mov dword ptr [ebp-2Ch],0 00401070 mov byte ptr [ebp-4],0 00401074 lea ecx,[imie] 00401077 call _STL::basic_string<char,std::char_traits<char>,_STL::allocator<char> >::~basic_string<char,std::char_traits<char>,_STL::allocator<char> > (4010B0h) 0040107C mov dword ptr [ebp-4],0FFFFFFFFh 00401083 lea ecx,[jakasOsoba] 00401086 call _STL::allocator<char>::~allocator<char> (401190h) 0040108B mov eax,dword ptr [ebp-2Ch] } 0040108E mov ecx,dword ptr [ebp-0Ch] 00401091 mov dword ptr fs:[0],ecx 00401098 mov esp,ebp 0040109A pop ebp 0040109B ret
Po uproszczeniu widzisz :
00401033 call ?Pobierz@?$@V?$basic_string@D (...) _STL@@H@Z (401250h) 00401041 call Osoba::Pobierz<int> (401280h) 0040104E call Osoba::Pobierz<float> (401290h) 0040105B call Osoba::Pobierz<char> (4012B0h)
Kazda metoda ma inny adres, poniewaz uzyles SZABLONU funkcji. Szablon to informacja dla kompilatora jaka funkcje ma WYGENEROWAC :).
Dlatego ciagle powtarzam, ze to co robicie nie ma zadnego sensu :)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 07:30
F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE klasy
Czyli czegoś się nauczyłem. I co nie ma to sensu? Mowilem ze metoda nie ma sensu (kod), a nie topick ;]
Co do pomylki z klasa/funkcja :) :
btw. Oczywiście Real Noname mówimy o akcesorach dla zmiennych, które klasa ma zamiar pokazać innym częściom kodu, przecież nie będę robił akcesorów dla zmiennych, które są tylko i wyłącznie na użytek klasy. Odpowiadalem wlasnie w kontekscie klas (i bylem swiecie przekonany ze mowimy o szablonach klas ... hm ... - racja moja pomylka :D). Takie rozwiazanie ma jeszcze jedna wade - pozwala innemu programiscie utworzyc metode klasy (jezeli szablon funkcji jest w klasie) o jakiej projektantowi klasy sie nawet nie snilo :). I mamy chaos :)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 08:36
Mowilem ze metoda nie ma sensu (kod), a nie topick ;]
A więc nie warto stosować szablonów funkcji jako metod klasy?
Warto stosować szablony klas tak jak to jest robione np. w STLu.
A jak inny programista będzie chciał coś zmienić w mojej klasie to dać mu metody wirtualne i niech sobie zmienia...
Chyba najlepiej stworzyć taką klase, której programista nie będzie musiał zmieniać bo i po co ma w nią ingerować?
Jak tworzysz klase, która ma spełniać jakieś określone zadanie, np. ma obsługiwać drukarkę to ma tylko to robić i nic więcej. Programista nie powinien jej zmieniać tak żeby zamiast drukarki obsługiwała skaner. Ona jest stworzona do jednego celu i to do czego została stworzona ma wykonywać możliwie jak najlepiej. Ktoś kto z niej korzysta nie powinien nic w niej zmieniać.
Jak ktoś będzie chciał obsłużyć skaner to poszuka sobie takiej klasy, która obsługuje skaner zamiast przerabiać klase od drukarki. Uniknie dzięki temu wielu błędów.
Ale jeśli już ktoś musi coś zmieniać to metody wirtualne będą chyba dobrym rozwiązaniem.
Uniwersalne metody czy klasy nie są dobre bo mogą bardzo komplikować kod i przez to prowadzić do wielu błędów. Dobrym tego przykładem jest kod dante7, którego chyba nikt nie rozumiał oprócz niego samego i zawierał wiele błędów.
Najważniejsze to tworzyć kod maksymalnie prosty, zrozumiały jak to tylko możliwe i żeby każda funkcja czy klasa wykonywała jak najmniej zadań.
Lepiej mieć 50 różnych klas czy funkcji, które wykonują po kawałeczku danego zadania niż jedną ogromną funkcje czy klase, która robi to wszystko.
Łatwiej wtedy wyeliminować błędy w kodzie.
Chodziło mi raczej o takie podejście:
programista ma moją klasę i na jej podstawie wyprowadza sobie klasę pochodną. Jeżeli uzna, że któraś metoda mojej klasy nie spełnia jego oczekiwań, to może ją sobie zmienić (oczywiście, jeżeli mu na to pozwolę, tzn. jeśli zrobię ją wirtualną).
btw. Oczywiście Real Noname mówimy o akcesorach dla zmiennych, które klasa ma zamiar pokazać innym częściom kodu, przecież nie będę robił akcesorów dla zmiennych, które są tylko i wyłącznie na użytek klasy. Odpowiadalem wlasnie w kontekscie klas (i bylem swiecie przekonany ze mowimy o szablonach klas ... hm ... - racja moja pomylka :D). Takie rozwiazanie ma jeszcze jedna wade - pozwala innemu programiscie utworzyc metode klasy (jezeli szablon funkcji jest w klasie) o jakiej projektantowi klasy sie nawet nie snilo :). I mamy chaos :)
eh... ja juz eotuje bo to przypomina walke z wiatrakami. Tak wiec wstrzymuje sie od odpowiedzi i mowie "pa"
eh... ja juz eotuje bo to przypomina walke z wiatrakami. Tak wiec wstrzymuje sie od odpowiedzi i mowie "pa"
zanotowane.pl doc.pisz.pl pdf.pisz.pl zsf.htw.pl
że zaczynasz walić z dyni w monitor :) Dobra, przejdę do rzeczy.
01: #include <iostream> 02: #include <string> 03: using namespace std; 04: 05: enum Typ{INT = 1, FLOAT, STRING}; 06: 07: template<typename T> 08: T Funkcja(Typ _rodzaj) 09: { 10: T wynik; 11: 12: switch(_rodzaj) 13: { 14: case INT: wynik = (T)10; break; 15: case FLOAT: wynik = (T)5.5; break; 16: case STRING: wynik = (T)"Napis"; break; 17: } 18: return wynik; 19: } 20: 21: int main() 22: { 23: float zmienna = Funkcja<float>(FLOAT); 24: cout << zmienna; 25: 26: int x; 27: cin >> x; 28: return 0; 29: }
Ten program się nie skompiluje. Taki błąd:
In function `T Funkcja(Typ) [with T = float]':
(wiersz 23)instantiated from here
(wiersz 16)pointer value used where a floating point value was expected
Jak wykomentuję linię 16. to błędu nie ma. Nie wiem, dlaczego ten string jest winny. Czy wynik = (T)"Napis" mimo rzutowania jest uważany za const char* ? Ja tego nie mogę rozgryźć, ale mam nadzieję że któryś(któraś) z was tak.
pzdr, Dante.
bo wynik jest u ciebie typem "float". Idzie sie poucz szablonow a nie bedziesz pisal ze string do niczego
Niby co ?
float zmienna = "lalalala"; <- no sam sopbie pomysl
A - i napisz jeszcze niby co to ma robic. Do zmiennej typu float zapisac zmiennoprzecinkowo adres stringa ?, czy to co w stringu ? co to w ogole ma robic ;)
Użytkownik st3tc edytował ten post 03 wrzesień 2005, 14:55
bo wynik jest u ciebie typem "float". Idzie sie poucz szablonow a nie bedziesz pisal ze string do niczego
Niby co ?
float zmienna = "lalalala"; <- no sam sopbie pomysl
A - i napisz jeszcze niby co to ma robic. Do zmiennej typu float zapisac zmiennoprzecinkowo adres stringa ?, czy to co w stringu ? co to w ogole ma robic ;)

nie bede tych glupot komentowal bo szkoda mojego czasu. Masz odpowiedz matolq:
case STRING: wynik = (T)"Napis"; break;
wynik jest typu float !!!!!!!!!!!!!!. Rozumiesz czy mam Ci to narysowac ?. To sie pytam co chcesz osiagnac przez zapis
float wynik = (T)"Napis";
I nie obrazaj mnie.
Napisales cos takiego :
float Funkcja(Typ _rodzaj) { float wynik; switch(_rodzaj) { case INT: wynik = (float)10; break; case FLOAT: wynik = (float)5.5; break; case STRING: wynik = (float)"Napis"; break; } return wynik; }
to sie pytam co ma byc wstawione do wynik jak sobie podasz swoj typ wyliczniowy (czek sprawde w ksiazce co to) jako STRING ?.
Użytkownik st3tc edytował ten post 03 wrzesień 2005, 20:01
nie bede tych glupot komentowal bo szkoda mojego czasu. Masz odpowiedz matolq:
case STRING: wynik = (T)"Napis"; break;
wynik jest typu float !!!!!!!!!!!!!!. Rozumiesz czy mam Ci to narysowac ?. To sie pytam lolq co chcesz osiagnac przez zapis
float wynik = (T)"Napis";
I nie obrazaj mnie.
Napisales cos takiego :
float Funkcja(Typ _rodzaj) {  float  wynik;   switch(_rodzaj)  {  case INT: wynik = (T)10; break;  case FLOAT: wynik = (T)5.5; break;  case STRING: wynik = (T)"Napis"; break;  }  return wynik; }
to sie pytam co ma byc wstawione do wynik jak sobie podasz swoj typ wyliczniowy (czek sprawde w ksiazce co to) jako STRING ?.

Kolego, to ja nie będę tego komentował. Otwórz sobie notatnik, wklej kod, który podałem, skompiluj, zlinkuj, uruchom program. Już?
Teraz wykomentuj sobie linię numer 16, skompiluj, zlinkuj, uruchom, użyj dev\brain i napisz, jak będziesz mądrzejszy(ale to chyba nieprędko będzie).

kolego dante7 - zaufaj mi ze st3tc nie jest jedenastolatkiem i ma mozg (btw jesli juz to dev/brain nie dev\brain) jak rowniez ogromna wiedze z zakresu c++ (ktorej to wiedzy jak widze brakuje Tobie). Apeluje abys przestal sie osmieszac i obrazac uzytkownikow tego forum.
To może ja to prościej od st3tc wyjaśnie:
pisząc takiego template, to tak jak byś napisał makro
#deifne aaa(a) (a)"aaaaaa"
A potem wstawiasz w a, float. Jak wiadomo preprocesor wkleja wszystko jak leci - i kompilator za chiny nie rozumie co ty wyprawiasz - robisz z float string'a?
Z template'ami jest podobnie - WSZYSTKIE case muszą być uodpornine na taką niespodzianke, bo inaczej kicha z funkcji.
BTW: jak kogoś nie znasz, to go nie bluzgaj - akurat st3tc, to ma chyba tu największą wiedze z nas wszystkich (imo, oczywiście :] )
Użytkownik Real_Noname edytował ten post 03 wrzesień 2005, 19:36
Hmm.. trochę przegiąłeś stary. Bluzgasz na st3tc'a, który od pierwszego posta stara Ci się pomóc. A przyczyną błędu jest od początku do końca Twoje niezrozumienie tematu.
Powtarzając już chyba po wszystkich - po prostu kompilator kompilując blok switch-case kompiluje WSZYSTKIE case'y - co jest dość logiczne - swoją funkcję możesz wywołać tak, jak sobie to Ty wyobrażasz:
float zmienna = Funkcja<float>(FLOAT);
ale też tak:
float zmienna = Funkcja<float>(STRING);
I co w drugim przypadku? Nikt nie zabrania takiego wywołania - kompilator musi to uwzględnić, a przy podstawianiu typu float pod szabon, zapis przy case STRING: staje się bez sensu z powodów, które st3tc opisał już conajmniej dwa razy.
A co do bluzgania na st3tc'a - n/c. Kiedyś się zorientujesz, jak głupio teraz postępowałeś.
Dobra, to będzie chyba ostatni(mój) post tego tematu. Po pierwsze st3tc zwracam Ci honor, raczej nie mam w zwyczaju nikogo obrażać, ale czasami niestety mi się zdarza, dlatego oczywiście przyznaję się do błędu i jeszcze raz przepraszam.
Po drugie, dlaczego od razu nie wytłumaczyłeś takiemu tępemu ludziowi jak ja tego, co napisałeś w poście #6, było by na pewno bardziej spokojnie.
Po trzecie... nie to już było by wszystko.
Wielki szacunek dla Ciebie i pozostałych, Dante.
Mam pewien pomysł, ale nie wiem, czy nie będzie tu wycieków pamieci
#include <iostream> #include <string> using namespace std; enum Typ{INT = 1, DOUBLE, STRING, TEST}; class Test { public: Test(){cout << "Konstruktor klasy test\n";}; ~Test(){cout << "Destruktor klasy test\n";}; }; void * Funkcja(Typ _rodzaj) { void * ptr = 0; switch(_rodzaj) { case INT: ptr = new int(1); break; case DOUBLE: ptr = new double(0.5); break; case STRING: ptr = new string("Lancuch znakow"); break; case TEST: ptr = new Test; break; } return ptr; } int main() { Test t = *(Test*)Funkcja(TEST); int x; cin >> x; return 0; }
Co o tym myślicie?
Tak sobie mysle ze zrobiles tuta taka mala fabryke abstrakcyjna:D
Kodu nie kompilowalem, ale wydaje mi sie, ze przydaloby sie kilka rzutowan, i bedzie OK. No i nie ma zwolnienia pamieci :D
Tak sobie mysle ze zrobiles tuta taka mala fabryke abstrakcyjna:D
To dobrze, czy źle? :)
Kodu nie kompilowalem, ale wydaje mi sie, ze przydaloby sie kilka rzutowan, i bedzie OK. No i nie ma zwolnienia pamieci :D

To dobrze, czy źle? :)
A możesz napisać, jak Ty byś to poprawił?
//Edit
To jest okno konsoli po uruchomieniu programu:
Jeden mały szczegół: Konstruktor i destruktor klasy Test są wywoływane tylko raz
O czym to świadczy?

Jak operujesz na wskaznikach(a dokladnie tworzysz obiekty wewnatrz funkcji), to musisz pamietac o zwalnianiu pamieci. Ty zamiast przechowywac wskaznik na obiekt, robisz kopie tego obiektu. W tym momencie sam sobiue szkodzisz:D
//============================================================================== // OneForAll, czyli funkcja po tuningu // pekore 2005 //============================================================================== #include <iostream> #include <string> using namespace std; enum Typ{INT = 1, DOUBLE, STRING, TEST}; class Test { public: Test(){cout << "Konstruktor klasy Test\n";}; Test(const Test & arg){cout << "Konstruktor kopiujacy klasy Test\n";}; ~Test(){cout << "Destruktor klasy Test\n";}; }; template <typename T> T Funkcja(Typ _rodzaj) { void * ptr = 0; switch(_rodzaj) { case INT: ptr = new int(10); break; case DOUBLE: ptr = new double(0.5); break; case STRING: ptr = new string("Lancuch"); break; case TEST: ptr = new Test; break; } T tmp = *(T*)ptr; delete (T*)ptr; return tmp; } int main() { Test t = Funkcja<Test>(TEST); return 0; }
Jakby komuś nie chciało się kompilować tego programu, to tu jest okno konsoli:
Microsoft Windows XP [Wersja 5.1.2600]
© Copyright 1985-2001 Microsoft Corp.
D:\Documents and Settings\Paweł>cd Moje dokumenty\Programowanie\OneForAll
D:\Documents and Settings\Paweł\Moje dokumenty\Programowanie\oneForAll>OneForAll.exe
Konstruktor klasy Test
Konstruktor kopiujacy klasy Test
Destruktor klasy Test
Destruktor klasy Test
D:\Documents and Settings\Paweł\Moje dokumenty\Programowanie\oneForAll>
Jak zwykle uwagi mile widziane.
Jak zwykle uwagi mile widziane. Jedna - jaki w ogole sens ma ten kod ktory piszecie ?
Jedna - jaki w ogole sens ma ten kod ktory piszecie ?

Ten kod (kod Funkcji, bo chyba o ten Ci chodzi) w tym programie nie ma większego sensu. Ale pomyśl sobie, że masz klasę, w niej ileś tam zmiennych, te zmienne jakichś tam (różnych) typów. No i masz napisać akcesory dla tej klasy. Zależy ile masz w tej klasie zmiennych, ale im więcej zmiennych, tym pewnie więcej akcesorów. A tu masz tylko jeden, uniwersalny akcesor.

Ten kod (kod Funkcji, bo chyba o ten Ci chodzi) w tym programie nie ma większego sensu. Ale pomyśl sobie, że masz klasę, w niej ileś tam zmiennych, te zmienne jakichś tam (różnych) typów. No i masz napisać akcesory dla tej klasy. Zależy ile masz w tej klasie zmiennych, ale im więcej zmiennych, tym pewnie więcej akcesorów. A tu masz tylko jeden, uniwersalny akcesor.

Czy ja juz przypadkiem nie napisalem, ze ty probujesz zrobic cos al'a fabryka abstrakcyjna :D
Tyle, ze zamiast na wskaznikach jedziesz teraz na obiektach, a po drugie program faktycznie nie ma zadnego sensu(a raczej nic tworczego nie wnosi :D).
I to jest wedlug Ciebie dobre ?

Dlatego chciałbym, żebyś na przykład Ty st3tc powiedział coś więcej na temat tego, co robię źle. Nie chodzi mi oczywiscie o bledy w kodzie - nie. Chodzi mi o strategie programowania.
Generalnie jezeli cos udostepniasz - powinno miec to cos wlasne metody dostepowe. Wiecej - jezeli masz jakas metoge Get<costam>Value, powinienes z miejsca napisac Set<costam>Value (o ile to nie const).
Po co ?. Zakladamy ze nasz kod bedzie uzywal inny programista. Nawet jezeli Set<costam> Ty nie uzywasz - ten ktos moze. Metody dla dostepu do zmiennych maja te zalete ze sa przejrzyste i ... bezpieczne.
BEZPIECZENSTWO
To jest dla mnie numer 1 w projektowaniu.
Idac Twoja strategia accesorow, dla klasy ktora posiada [n] zmiennych mozna napisac accesorka z switchem ktory pobiera adres tej zminnej i zwraca jako wskaznik typu void. Uzytkownik nastepnie castuje do wskaznika odpowiedniego typu i wykonuje dereferencje.
I tu jest olbrzymie miejsce na bledy:
int zmienna = *(int*)Funkcja(INT);
w tym przykladzie wszystko gra. Ale pozniej programista zmienil i zapomnial poprawic castowanie (napiety plan, deadline itp)
int zmienna = *(int*)Funkcja(FLOAT);
Ajajajaj - popatrzmy Funkcja pobiera adres zmiennej typu float. Programista sie pomylil i scastowal (void*) na (int*) zamiast (float*). Dereferencja potraktuje te pamiec nie jako 32-dwu bitowy zapis zmiennoprzecinkowy, ale jako 32-dwu bitowa liczbe calkowia ze znakiem (inna reprezentacja w pamieci).
Takie cos powoduje ciezkie do wykrycia bledy - wrecz potrafiace zniszczyc czlowieka :)
Ale idzmy dalej - A co jezeli jako parametr podajesz w accesorku nazwe zmiennej (jakis enumek ?). Tu sie zaczyna sajgon :
(void*)Funkcja( ZMIENNA_MENU );
I mamy problem - menu jest reprezentowane przez co ?. HMENU ?, CMenu ?, wxMenu ? Do czego mamy scastowac ?
Tutaj nie tylko mamy zalamane bezpieczenstwo, ale i wprowadzamy zament.
Co innego tutaj :
wxMenu *GetMenu();
-----------
PS - GetMenu to zwykly return, Twoj typ accesorkow to switch - jak myslisz ktore jest szybsze ? :)
PS2 - propo bezpieczenstwa:
int main() { Test t = Funkcja<Test>(TEST); return 0; }
A co jezeli Test bedzie mial jakies metody, a Ty przez pomylke podasz jako parametr np STRING ?.
To jest wlasnie to o czym mowie od poczatku. To jest blad programu ktory spowoduje kompletne posypanie sie aplikacji.
Pozdr
st3tc
Użytkownik st3tc edytował ten post 05 wrzesień 2005, 22:41

Aha, szkoda, że nie napisałem tego trochę wcześniej: chodziło mi o takie używanie akcesora: F<int>(MyAge), F<string>(MyName), itp. To tak na marginesie. F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE funkcje :). I nadal kazda z nich bedzie miala switcha, gdzie legalną pozycją bedzie TYLKO I WYLACZNIE jeden case z tego switcha.
To nic innego jak rozbudowane, przerosniete Get<costam>Value :) - bo kazda z wersji Twojej funkcji bedzie prawidlowa tylko i wylacznie dla jednego typu :)
No nie wiem czy to jest to co chciales ;)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 08:31
F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE klasy :).

Czyli, jeżeli jakas klasa posiada szablon funkcji, to kompilator tworzy różne wersje klasy, tak?
Nie wersje klasy - tylko wersje funkcji (w tym przykladzie) :). Szablon to wzor. Szablon funkcji to informacja jak wygenerowac funkcje, szablon klasy to informacja jak wygenerowac klase.
Pozwolilem sobie zmodyfikowac Twoj kod :
#include <string> class Osoba { public: Osoba(){} ~Osoba(){} template<typename T> T Pobierz(int _x) { return T(); } template<typename T> void Ustaw(int _x, T _wartosc) { } }; int main() { Osoba jakasOsoba; std::string imie = jakasOsoba.Pobierz<std::string>(0); int i = jakasOsoba.Pobierz<int>(1); float f = jakasOsoba.Pobierz<float>(2); char c = jakasOsoba.Pobierz<char>(3); return 0; }
A teraz popatrz na kod asm po kompilacji :
int main() { 00401000 push ebp 00401001 mov ebp,esp 00401003 push 0FFFFFFFFh 00401005 push offset $L16016 (407BA0h) 0040100A mov eax,dword ptr fs:[00000000h] 00401010 push eax 00401011 mov dword ptr fs:[0],esp 00401018 sub esp,20h Osoba jakasOsoba; 0040101B lea ecx,[jakasOsoba] 0040101E call _STL::allocator<char>::allocator<char> (4010A0h) 00401023 mov dword ptr [ebp-4],0 std::string imie = jakasOsoba.Pobierz<std::string>(0); 0040102A push 0 0040102C lea eax,[imie] 0040102F push eax 00401030 lea ecx,[jakasOsoba] 00401033 call ?Pobierz@?$@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@_STL@@@_STL@@@Osoba@@QAE? AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@_STL@@@_STL@@H@Z (401250h) 00401038 mov byte ptr [ebp-4],1 int i = jakasOsoba.Pobierz<int>(1); 0040103C push 1 0040103E lea ecx,[jakasOsoba] 00401041 call Osoba::Pobierz<int> (401280h) 00401046 mov dword ptr [i],eax float f = jakasOsoba.Pobierz<float>(2); 00401049 push 2 0040104B lea ecx,[jakasOsoba] 0040104E call Osoba::Pobierz<float> (401290h) 00401053 fstp dword ptr [f] char c = jakasOsoba.Pobierz<char>(3); 00401056 push 3 00401058 lea ecx,[jakasOsoba] 0040105B call Osoba::Pobierz<char> (4012B0h) 00401060 mov byte ptr [c],al i = i; 00401063 mov ecx,dword ptr [i] 00401066 mov dword ptr [i],ecx return 0; 00401069 mov dword ptr [ebp-2Ch],0 00401070 mov byte ptr [ebp-4],0 00401074 lea ecx,[imie] 00401077 call _STL::basic_string<char,std::char_traits<char>,_STL::allocator<char> >::~basic_string<char,std::char_traits<char>,_STL::allocator<char> > (4010B0h) 0040107C mov dword ptr [ebp-4],0FFFFFFFFh 00401083 lea ecx,[jakasOsoba] 00401086 call _STL::allocator<char>::~allocator<char> (401190h) 0040108B mov eax,dword ptr [ebp-2Ch] } 0040108E mov ecx,dword ptr [ebp-0Ch] 00401091 mov dword ptr fs:[0],ecx 00401098 mov esp,ebp 0040109A pop ebp 0040109B ret
Po uproszczeniu widzisz :
00401033 call ?Pobierz@?$@V?$basic_string@D (...) _STL@@H@Z (401250h) 00401041 call Osoba::Pobierz<int> (401280h) 0040104E call Osoba::Pobierz<float> (401290h) 0040105B call Osoba::Pobierz<char> (4012B0h)
Kazda metoda ma inny adres, poniewaz uzyles SZABLONU funkcji. Szablon to informacja dla kompilatora jaka funkcje ma WYGENEROWAC :).
Dlatego ciagle powtarzam, ze to co robicie nie ma zadnego sensu :)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 07:30
F<int>(MyAge), F<string>(MyName) utworzy w procesie kompilacji dwie ROZNE klasy

Czyli czegoś się nauczyłem. I co nie ma to sensu? Mowilem ze metoda nie ma sensu (kod), a nie topick ;]
Co do pomylki z klasa/funkcja :) :
btw. Oczywiście Real Noname mówimy o akcesorach dla zmiennych, które klasa ma zamiar pokazać innym częściom kodu, przecież nie będę robił akcesorów dla zmiennych, które są tylko i wyłącznie na użytek klasy. Odpowiadalem wlasnie w kontekscie klas (i bylem swiecie przekonany ze mowimy o szablonach klas ... hm ... - racja moja pomylka :D). Takie rozwiazanie ma jeszcze jedna wade - pozwala innemu programiscie utworzyc metode klasy (jezeli szablon funkcji jest w klasie) o jakiej projektantowi klasy sie nawet nie snilo :). I mamy chaos :)
Użytkownik st3tc edytował ten post 06 wrzesień 2005, 08:36
Mowilem ze metoda nie ma sensu (kod), a nie topick ;]

A więc nie warto stosować szablonów funkcji jako metod klasy?
Warto stosować szablony klas tak jak to jest robione np. w STLu.
A jak inny programista będzie chciał coś zmienić w mojej klasie to dać mu metody wirtualne i niech sobie zmienia...
Chyba najlepiej stworzyć taką klase, której programista nie będzie musiał zmieniać bo i po co ma w nią ingerować?
Jak tworzysz klase, która ma spełniać jakieś określone zadanie, np. ma obsługiwać drukarkę to ma tylko to robić i nic więcej. Programista nie powinien jej zmieniać tak żeby zamiast drukarki obsługiwała skaner. Ona jest stworzona do jednego celu i to do czego została stworzona ma wykonywać możliwie jak najlepiej. Ktoś kto z niej korzysta nie powinien nic w niej zmieniać.
Jak ktoś będzie chciał obsłużyć skaner to poszuka sobie takiej klasy, która obsługuje skaner zamiast przerabiać klase od drukarki. Uniknie dzięki temu wielu błędów.
Ale jeśli już ktoś musi coś zmieniać to metody wirtualne będą chyba dobrym rozwiązaniem.
Uniwersalne metody czy klasy nie są dobre bo mogą bardzo komplikować kod i przez to prowadzić do wielu błędów. Dobrym tego przykładem jest kod dante7, którego chyba nikt nie rozumiał oprócz niego samego i zawierał wiele błędów.
Najważniejsze to tworzyć kod maksymalnie prosty, zrozumiały jak to tylko możliwe i żeby każda funkcja czy klasa wykonywała jak najmniej zadań.
Lepiej mieć 50 różnych klas czy funkcji, które wykonują po kawałeczku danego zadania niż jedną ogromną funkcje czy klase, która robi to wszystko.
Łatwiej wtedy wyeliminować błędy w kodzie.

Chodziło mi raczej o takie podejście:
programista ma moją klasę i na jej podstawie wyprowadza sobie klasę pochodną. Jeżeli uzna, że któraś metoda mojej klasy nie spełnia jego oczekiwań, to może ją sobie zmienić (oczywiście, jeżeli mu na to pozwolę, tzn. jeśli zrobię ją wirtualną).

btw. Oczywiście Real Noname mówimy o akcesorach dla zmiennych, które klasa ma zamiar pokazać innym częściom kodu, przecież nie będę robił akcesorów dla zmiennych, które są tylko i wyłącznie na użytek klasy. Odpowiadalem wlasnie w kontekscie klas (i bylem swiecie przekonany ze mowimy o szablonach klas ... hm ... - racja moja pomylka :D). Takie rozwiazanie ma jeszcze jedna wade - pozwala innemu programiscie utworzyc metode klasy (jezeli szablon funkcji jest w klasie) o jakiej projektantowi klasy sie nawet nie snilo :). I mamy chaos :)

eh... ja juz eotuje bo to przypomina walke z wiatrakami. Tak wiec wstrzymuje sie od odpowiedzi i mowie "pa"
eh... ja juz eotuje bo to przypomina walke z wiatrakami. Tak wiec wstrzymuje sie od odpowiedzi i mowie "pa"
