Wiem, że dla klas szablonowych nie ma czegoś takiego jak szablonowy konstruktor kopiujący
eee... co? vector<T> ma konstrutor kopiujacy, brzmiacy vector<T>::vector(vector<T>const&) i nie ma z tym problemu.. jesli zas chcesz konstruktor-nie-kopiujacy, tylko 'konwertujacy', postaci a'la: template<X> vector<T>::vector(X const&) no to przeciez sam juz go napisales pod postacia template<STL_Con> Zrodlo<T>::Zrodlo(Stl_container&) ?? kompletnie nie rozumiem tego zdania co tutaj napisales
Na typedefach nie zachodzi polimorfizm, prawda?
eee.. co?s wybacz, malo to inteligentnie brzmi z mojej strony, a brzmisz mądrze, ale ni w ząb nie rozumiem:( typedef jest cechą statyczną kodu, jest staly w czasie kompilacji i dobrze znany. zero dynamizmu, wiec jako polimorfizm? vector<double>::iterator jest iteratorem<double>, vector<asddfg>::iterator jest iteratorem<asddfg> i to wiadomo tak samo dobrze jak to ze vector<double> jest vectorem<doublem>. chodzi Ci o takie pojecie 'polimorfizmu' gdzie vector<int> i vector<double> bylyby oba vectorem<cokolwiek>? jesli tak, to nie, nie ma.
Inną możliwością jest skonstruowanie ZrodloDanych::iterator, ale bez polimorfizmu znowu się tutaj nie obejdzie
ale przeciez polimorfizm juz masz uzyty w klasie bazowej basic_iter, wiec w czymże problem?? skoro juz go uzywasz, to sobie uzywaj, na zdrowie! a jak go nie chcesz bo cos tam, to i tak ten kod juz wymaga solidnej przebudowy, wiec dziura w postaci test7 to szczegol skoro trzeba zaczac prawie od nowa:)
--- a teraz do rzeczy..
bezposredni problem "niekompilowania się testu numer 7" w Twoim kodzie jest taki, ze linia
ZrodloDanych<double> zrodlo_b(zrodlo_a);
odpala ctor
template < typename STL_Container > ZrodloDanych(STL_Container &_orig) {
poniewaz on "najlepiej pasuje". zreszta, nawet innego nie ma ktory moglby.
ten ctor nie moze sie skompilowac, poniewaz uzywa on STL_Container::iterator i STL_Container::value_type, ktore nie istnieja, poniewaz w tym przypadku STL_Container==ZrodloDanych<double>, a przeciez Ty w ZrodloDanych NIE ZDEFINIOWALES nazw ani 'iterator' ani 'valuetype' !! co wiecej, nie ma tam tez metod begin ani end, ktorych ten ctor sie spodziewa.
albo wiec zdefiniuj dobry ctor ktory zostanie lepiej dopasowany (1), albo dostarcz te metody (2).
na opcje (2):
juz dopisanie w ZrodloDanych linijki
template < typename T >
class ZrodloDanych {
private:
typedef __M_basic_it< T >* __T_iterator;
typedef T value_type; /// <----- tej tutaj
spowoduje ze liczba bledow kompilacji spada z 7 do 6. ot, skoro zakladasz ze kontenery maja takie skladowe, to Twoj kontener tez musi je miec! czeka Cie wiec napisanie typedefa dla 'iterator' oraz dostarczenie begin i end.. no, chyba ze nie chcesz 'byc kontenerem stl', w takim razie przechodzimy do...
...do opcji (1), czyli zadbanie o to, zeby copy-ctor nie probowal sie wiazac z ctor(STL_Container&)
primo: jako, ze operujesz na "basic iter", nie wiesz jaka jest jego implementacja! to znaczy, ze w zasadzie, ctor Twojego ZrodloDanych nie ma zielonego pojecia jaka jest faktyczna implementacja iteratora, wie tylko ze to-oryginalne-zrodlo ma w sobie __M_basic_it< T >*. jednak aby w ogole mowic o tworzeniu kopii zrodla danych, musisz jakos skonstruowac temu nowemu zrodlu calkiem nowy iter ktory bedzie odpowiadal iter'owi oryginalnego...
albo wiec redukujesz ten problem do (2) i do "pelnego udawania STL'a" i niech ZrodloDanych ma metody begin, end, i nazwy iterator i value_type, i na podstawie ich tworzysz calkiem nowy iter o znanym typie, zupelnie jak w ctorze stlcontainer - i w zasadzie nic nie robisz i uzywasz tego ctora.. albo tez (skoro juz wybrales opcje (1) czyli nie robimy tak jak napisalem przed tym nawiasem:))).. albo tez wiec trzeba jakos zapewnic mozliwosc "skopiowania" basic itera.
efektywny kod jest prostszy niz opisanie go:
ZrodloDanych(ZrodloDanych<T> &_orig) {
__M_iterator = _orig.__M_iterator->make_new_copy(); // <---- !
}
fakt, nie wiesz czym jest faktycznie owo __M_basic_it< T >*, ale przeciez ONO WIE. niech wiec wykona kopie siebie samego, dynamiczna, stad wirtualna metoda 'new_copy' zeby zaznaczyc owo 'new', a wirtualna... no coz, przeciez cale __M_basic_it< T > jest po to, aby ustalic wirtualny interfejs, wiec chyba nie problem dorzucic metode w dodatku dosc poreczna skoro cctor'a nijak tam nie dolozysz..
no i oczywiscie więc również trzeba bedzie popelnic:
template < typename _TbasicType >
class __M_basic_it {
public:
virtual _TbasicType operator *() = 0;
virtual bool operator++() = 0;
virtual __M_basic_it<_TbasicType>* make_new_copy() = 0; // <----- dodane
};
a i oraz faktyczna implementacja pozniej:
template < typename _Titerator, typename _Ttype, typename _TbasicType = _Ttype >
class __M_it : public __M_basic_it< _TbasicType > {
.....
....
public:
__M_basic_it<_TbasicType>* make_new_copy() { ///<----- dodane
return new __M_it<_Titerator,_Ttype,_TbasicType>(__M_iterator, __M_end); //<---- uwaga na pewien maly problem z BEGIN.. opis nizej
}
}
po takich poprawkach (tzn. zaprezentowalem opcje (1) nie (2)!!), kod testu 7 sie skompiluje.
a czy bedzie dzialal pooprawnie? oj, niekoniecznie.
linijki:
(kod7 oryginalny)
ZrodloDanych<double> zrodlo_a(liczby);
while(zrodlo_a.nastepnyElement()) // <--- zuzycie iteratora w 100%
cout << zrodlo_a.aktualnaWartosc() << " ";
ZrodloDanych<double> zrodlo_b(zrodlo_a); // <--- kopia pustki!!!
while(zrodlo_b.nastepnyElement())
cout << zrodlo_b.aktualnaWartosc() << " ";
porownaj to z:
(kod7 inny)
ZrodloDanych<double> zrodlo_a(liczby);
ZrodloDanych<double> zrodlo_b(zrodlo_a); // <--- kopia przed zuzyciem
while(zrodlo_a.nastepnyElement())
cout << zrodlo_a.aktualnaWartosc() << " ";
while(zrodlo_b.nastepnyElement())
cout << zrodlo_b.aktualnaWartosc() << " ";
kod7oryginalny sugeruje, ze po 'kopii' zrodla danych, chcialbys aby ta kopia powtarzala oryginalna sekwencje. ale Twoje zrodlo pamieta jedynie dwa iteratory: aktualny oraz koncowy. to znaczy, ze jezeli ze zrodla odczytasz N elementow, i potem skopiujesz zrodlo, to tak napisany cctor skpoiuje 'aktualny' iterator a nie startowy! kopia zrodla bedzie powtarzac sekwencje poczawszy od aktualnego stanu zastanego w momencie kopiowania i dlatego kod7oryginalny wypisze jeden zestaw, a kod7inny wypisze dwa zestawy. co wiecej, z racji tego ze zrodlodanych pamieta iteratory aktualny-oraz-koniec, nie masz szans sprawic aby to zaczelo zachowywac sie inaczej. musialbys do zrodladanych (albo do ktoregos m_iteratora) dostrzelic jeszcze zapamietywanie punktu startu.. no, chyba ze test7 faktycznie mial wypisywac wszystko-a-potem-nic i zle odebralem intecje.
edit: pufff.. troche tego wyszlo. ale mam nadzieje ze zrozumiale. jesli cos przeoczylem np. jakies zalozenia albo ograniczenia w postaci rozwiazania to przepraszam, pozno jest:/ opcje (2) przetestowalem u siebie na VS2008 i przynajmniej test7 dziala poprawnie. opcji (1) szczerze, nie chcialo mi sie troche, poniewaz przy wirutalnej bazie iteratora i nieujawnianiu jego konkretnego typu implementacji, napisanie begin/end jest troche wywrotowe -- one musza zwracac cos kopiowalnego, czyli trzebaby bylo dolozyc kolejna klaske-opakowanie, nie wirtualna, majaca copyctor, zawierajaca basic-iter i blablabla... i szybciej chyba byloby zrezygnowac z basiciter i sprobowac zrobic je typowane statycznie bez wirtualizmu.. dobra, zmeczylem sie, nie jestem w stanie ocenic sensownosci trzech ostatnich zdan ktore napisalem przed chwila. czas spac. ciao.