Dyskusja:Konstruktor (programowanie obiektowe)
Z Wikipedii
[edytuj] Obliczanie rozmiaru obiektu przez konstruktor
rozmiar obiektu jest znany na etapie kompilacji, więc nie następuje jego obliczenie. jeśli się mylę to podajcie przykład kiedy tak nie jest. --zolv 09:18, 17 paź 2005 (CEST)
- Rozmiar obiektu każdej klasy jest znany w momencie kompilacji ale w wielu sytacjach nie wiadomo do jakiej klasy należeć będzie tworzony obiekt a informację o jego rozmiarze trzeba skądś wziąć i jest ona przyczepiona do tablicy VMT. W przypadku Turbo Pascala i Delphi, standardowo tworzone obiekty danej klasy mają taką samą wielkość. W Delphi można pokryć metody NewInstance - tworzące obiekt i Free - usuwające obiekt z pamieci i będziemy mogli tworzyć obiekty o tej samej klasie ale o różnej wielkości. W Turbo Pascalu można utworzyć obiekt, który nie jest potomkiem TObject, ale wówczas trzeba utworzyć procedurę tworzącą i niszczącą obiekt. StoK 14:46, 17 paź 2005 (CEST)
- a można prosić o jakiś minimalistyczny przykład który to potwierdzi? bo wydaje mi się, że wiem co masz na myśli, ale to nie jest odpowiedź na problem --zolv 21:47, 17 paź 2005 (CEST)
- Chciałbyś kod za darmo? A tak serio to obliczenie o które się dopytujesz w Delphi i chyba Borland C++ robi to obliczenie tak dl := VMT_tego_typu[-47], a wartość ta jest przekazywana do proccedur zarządzania pamięcią i jest to w zaprogramowane w assemblerze. StoK 10:09, 18 paź 2005 (CEST)
- Wporzo. ale chodzi mi o kod, który pokaże, że dwa obiekty tego samego typu (tej samej klasy) mają różne rozmiary, przez co konstruktor musi to obliczać. według mnie wie to już na poziomie kompilacji ZAWSZE. ale jak nie mam racji to zapodaj kod który pokaże różnice --zolv 16:41, 18 paź 2005 (CEST)
- Dodatkowo (abstrachując): możliwość tworzenia obiektów o różnych rozmiarach sugerowałoby polimorficzne zachowanie konstruktora, który niestety polimorficzny być nie może --zolv 19:54, 27 paź 2005 (CEST)
- Znasz tylko C++ i to tylko z jego wierzchu, w delphi konstruktor może być wirtualny. Pisałem już wcześniej, że rozmiar klasy jest znany w momencie kompilacji i jest umieszczany w tablicy metod wirtualnych, ale w trakcie pracy programu typu interpreter np. RTTI nie wiadomo w momencie tworzenia programu do jakiej klasy będzie należał tworzony obiekt i dlatego choć zmienna jest np. typu TObject, a tworzona jest typu TMojaFormatka o całkiem innym rozmiarze niż rozmiar TOject. Pisałem już wcześniej, że obliczenie jest bardzo proste, to po prostu pobranie liczby z odpowiedniego miejsca tablicy. StoK 08:23, 28 paź 2005 (CEST)
- co do pierwszego zdania to pozostawiam bez komentarza :) - poniżasz się chłopie. a co do reszty. prosze ci o podanie PROSTEGO kodu ktory udowodni twoje racje - nie potrafisz/nie chcesz (stawiam raczej na to pierwsze). a co do tekstu: (...)choć zmienna jest np. typu TObject, a tworzona jest typu TMojaFormatka o całkiem innym rozmiarze niż rozmiar TOject(...) to WIEDZIAŁEM, że o takie coś Ci będzie chodzić. ale to jest błędne rozumowanie!. stary zwróć uwagę czego dotyczy artykuł - podpowiem: konstruktora. i teraz pytanie: w którym momencie zadziała konstruktor? ano w momencie tworzenia obiektu
TMojaFormatka
, i kompilator WIE jakiego rozmiaru bedzie obiekt tej klasy. a to, że jest różnica w rozmiarze nie wynika z działania konstruktora!! to jest źródło działania obiektowości (coś a'la: każdy samochód jest pojazdem, ale nie każdy pojazd jest samochodem). ale przypominam - artykuł dotyczy KONSTRUKTORA a nie polimorfizmu. --zolv 10:20, 28 paź 2005 (CEST)
- co do pierwszego zdania to pozostawiam bez komentarza :) - poniżasz się chłopie. a co do reszty. prosze ci o podanie PROSTEGO kodu ktory udowodni twoje racje - nie potrafisz/nie chcesz (stawiam raczej na to pierwsze). a co do tekstu: (...)choć zmienna jest np. typu TObject, a tworzona jest typu TMojaFormatka o całkiem innym rozmiarze niż rozmiar TOject(...) to WIEDZIAŁEM, że o takie coś Ci będzie chodzić. ale to jest błędne rozumowanie!. stary zwróć uwagę czego dotyczy artykuł - podpowiem: konstruktora. i teraz pytanie: w którym momencie zadziała konstruktor? ano w momencie tworzenia obiektu
- Znasz tylko C++ i to tylko z jego wierzchu, w delphi konstruktor może być wirtualny. Pisałem już wcześniej, że rozmiar klasy jest znany w momencie kompilacji i jest umieszczany w tablicy metod wirtualnych, ale w trakcie pracy programu typu interpreter np. RTTI nie wiadomo w momencie tworzenia programu do jakiej klasy będzie należał tworzony obiekt i dlatego choć zmienna jest np. typu TObject, a tworzona jest typu TMojaFormatka o całkiem innym rozmiarze niż rozmiar TOject. Pisałem już wcześniej, że obliczenie jest bardzo proste, to po prostu pobranie liczby z odpowiedniego miejsca tablicy. StoK 08:23, 28 paź 2005 (CEST)
- Chciałbyś kod za darmo? A tak serio to obliczenie o które się dopytujesz w Delphi i chyba Borland C++ robi to obliczenie tak dl := VMT_tego_typu[-47], a wartość ta jest przekazywana do proccedur zarządzania pamięcią i jest to w zaprogramowane w assemblerze. StoK 10:09, 18 paź 2005 (CEST)
- a można prosić o jakiś minimalistyczny przykład który to potwierdzi? bo wydaje mi się, że wiem co masz na myśli, ale to nie jest odpowiedź na problem --zolv 21:47, 17 paź 2005 (CEST)
Dobra. Zmusiłem się, żeby wyjaśnić:
Napisałeś, że konstruktor w TurboPascalu (będę używał mimo to nazwy środowiska: Delphi) możę być wirtualny. Zgadza się, ale jego wirtualność (a więc to o co mógłbyś się oprzeć podczas swojej argumentacji) nie działa podczas tworzenia za pomocą klasy, natomiast działa podczas tworzenia innego obiektu przy pomocy już istniejącego obiektu, a więc coś takiego: nowy := istniejacyObiekt.Create();
. Wtedy zostanie wywołany odpowiedni konstruktor z dołu hierarchii obiektu istniejacyObiekt
(jeśli istniejacyObiekt
był na przykład tworzony w taki sposób: istniejacyObiekt := KlasaPochodna.Create();
). Ale ten sposób tworzenia nie udowadnia tego, że konstruktor (zwróć uwagę: chodzi o sam konstruktor! a nie polimorfizm) nie zna dokładnego rozmiaru obiektu - konstruktor go bardzo dobrze zna (łącznie z tablicą VMT ponieważ znana jest całą hierarchia tworzenia obiektu, a więc tablica również jest znana, jej rozmiar itp (swoją drogą ona i tak nie wchodzi w skład obiektu)). Tak więc ten argument odpada.
Z tego powodu spokojnie można stwierdzić, że konstruktor zawsze zna całkowity rozmiar obiektu (o to nam chodzi przecież). natomiast samo wywołanie konstruktora nie musi jednoznacznie wskazywać na daną klasę (i to tylko w momencie tworzenia obiektu poprzez inny obiekt), przez co w momencie wywołania tworzony obiekt nie musi być znany - ale nie o to nam chodzi, to przecież jest sprawa polimorfizmu. --zolv 12:29, 28 paź 2005 (CEST)
Twoja odpowiedź i edycje poczynione w artykułach potwierdzają moją opinię z pierwszego zdania, ale chłopie nie obrażaj się, nie każdy zna się na wszystkim, np. moja znajomość C++ jest powierzchowna i nie wstydzę się tego. Fragmenty które są nieprawdą: Kolejność wywołań konstruktorów klasy bazowej czy też obiektów składowych danej klasy jest określona kolejnością: 1. Konstruktory klas bazowych w kolejności w jakiej znajdują się w sekcji dziedziczenia w deklaracji klasy pochodnej. W niektórych językach jak Object Pascal nie jest konieczne wywoływanie konstruktora klasy bazowej. 2. Konstruktory obiektów składowych klasy w kolejności w jakiej obiekty te zostały zadeklarowane w ciele klasy. 3. Konstruktor klasy.
W powyższym fragmencie knocisz (zolv), bo z zapisu „nie jest konieczne wywoływanie konstruktora klasy bazowej” , to w pozostałych językach jest konieczne, a nie widzę tego w twoich przykładach. Pomieszałeś tu wywoływanie „automatyczne” generowane przez kompilator z koniecznością wywołania. „Natomiast W Object Pascalu może być wirtualny i jest traktowany jak zwykła metoda. Jego polimorficzne zachowanie ujawnia się podczas tworzenia obiektu korzystając z innego istniejącego obiektu. Takie zachowanie w innych językach również można uzyskać.”
Także powyższy fragment jest nieprawdą. Niżej przytaczam fragment z helpu Delphi
A constructor can be called using a variable of a class-reference type. This allows construction of objects whose type isn’t known at compile time. For example,
type TControlClass = class of TControl; function CreateControl(ControlClass: TControlClass;
const ControlName: string; X, Y, W, H: Integer): TControl;
begin
Result := ControlClass.Create(MainForm); with Result do begin Parent := MainForm; Name := ControlName; SetBounds(X, Y, W, H); Visible := True; end;
end;
The CreateControl function requires a class-reference parameter to tell it what kind of control to create. It uses this parameter to call the class’s constructor. Because class-type identifiers denote class-reference values, a call to CreateControl can specify the identifier of the class to create an instance of. For example,
CreateControl(TEdit, 'Edit1', 10, 10, 100, 20);
Constructors called using class references are usually virtual. The constructor implementation activated by the call depends on the runtime type of the class reference.
Jak widać wirtualny konstruktor służy do tworzenia obiektu o typie nie znanym w momencie kompilacji, a nie tak jak Ty (zolv) podajesz. W Delphi konstruktory są dziedziczone i mogą być wirtualne.
Kolejny knot w twoim wydaniu: „wykonanie kodu klasy bazowej (w niektórych językach nie wymagane)”
Nie wymagane, czy nie wykonywane? Znowu pomylenie tego co robi kompilator z tym co robi programista.
I jest tego więcej w innych tekstach o programowaniu obiektowym Twojego (zolv) autorstwa.
Wracając do obliczania rozmiaru, to sformułowanie to nie jest mojego pomysłu lecz przepisane ze strony angielskiej. Już Ci to pisałem, w przypadku Delphi rozmiar obiektu danej klasy jest stały, obliczony podczas kompilacji i umieszczony w tablicy metod wirtualnych. Ale jest możliwe zdefiniowanie klasy o zmiennej wielkości obiektu.
W naszej klasie pokrywamy metodę NewInstance np. tak: frozmiar:Integer; class function NewInstance: Tobject; override;
// Jej implementacja class function MojaKlasa.NewInstance: Tobject;
var p:pointer;
begin
p:= GetMem(Rozmiar_obiektu) InitInstance(p) fRozmiar = Rozmiar_obiektu;
end; Problemem jest brak możliwości przekazania rozmiaru obiektu do funkcji NewInstanece, no i trzeba jeszcze tą wartość zapamiętać w obiekcie gdzieś zapamiętać, tu w fRozmiar. Wartość ta jest potrzebna destruktorowi a właściwie metodzie FreeInstance, którą musimy też pokryć.
StoK 09:52, 29 paź 2005 (CEST)
Na początku: czepiasz się szczegółów wyrwanych z kontekstu. Według Ciebie w każdym momencie artykułu należałoby uwzględniać wszystkie możliwości dotyczące danego czegoś. Jednak takie tworzenie wyjaśnień w każdym miejscu gdzie niby powinno być, generuje ogrom powtórzeń. Treść artykułu liczy na dociekliwość i inteligencje czytelnika, a nie coś w stylu: wszedłem na stronke konstruktora , od razu w spisie klikam w konstruktor kopiujący i (według Ciebie) w tym miejscu powinna się znajdować cała składnia języka (no bo przecież przydałaby się w tym miejscu, bo a nuż wejdzie ktoś kto się nie zna na C++), z dokładnym wyjaśnieniem co to ten KK. Jeśli chcesz tworzyć takie artykuły - wporzo, ale sam z tego zrezygnujesz.
W powyższym fragmencie knocisz (zolv), bo z zapisu „nie jest konieczne wywoływanie konstruktora klasy bazowej” , to w pozostałych językach jest konieczne, a nie widzę tego w twoich przykładach. Pomieszałeś tu wywoływanie „automatyczne” generowane przez kompilator z koniecznością wywołania.
- Przykład nie musi przecież przedstawiać wszystkich możliwości, no przestań (czasem nie jest to przecież możliwe). To że czegoś brakuje, nie znaczy że ja tego nie wiem (Ty tak zakładasz, pozdrawiam) a negacja zdania jest prawdą (przykład: nie jestem bogaty nie oznacza, że wszyscy dookoła są). Zachęcam do rozszerzenia. A jeśli już brać tak dosłownie to zdanie które napisałem to w którym momencie jest ono nieprawdziwe? "nie jest konieczne wywoływanie konstruktora klasy bazowej". A jest konieczne w Object Pascalu? A w innych językach jest konieczne, a to, że nie trzeba tego pisać to już inna kwestia (brakuje tego? dopisz a nie opieprzaj innych - przecież ten artykuł się rozwija i nie jest to ostateczna wersja; wiesz wogóle o co chodzi z wiki?) Nie wiem czego się czepiasz. Łatwo jest powiedzieć knocisz.
„wykonanie kodu klasy bazowej (w niektórych językach nie wymagane)” Nie wymagane, czy nie wykonywane? Znowu pomylenie tego co robi kompilator z tym co robi programista.
- Przeczytaj to z 10 razy - może zrozumiesz. Przecież nie napiszę tak jak Ty tego chcesz: "wykonanie kodu klasy bazowej (w niektórych językach wykonanie kodu klasy bazowej nie jest wymagane)". To jest oczywiste, że słowanie wymagane tyczą się wymagania wykonania kodu klasy bazowej. Chyba przestałeś czytać książki, skoro takie zaawansowane konstrukcje w języku polskim Ci przeszkadzają.
I jest tego więcej w innych tekstach o programowaniu obiektowym Twojego (zolv) autorstwa.
- No to poprawiaj. 1. nie jestem doskonały/niewiem wszystkiego. 2. to jest wiki (podpowiadam) 3. nikt inny nie narzeka (jesteś chyba jedynym ultrazaawansowanym programistą-wikipedystą, któremu się coś nie podoba albo nikt z czytających nie jest skoro nikt nie poprawia).
A apropos twoich przykładów. Stary. Ja wiem jak to działa. Nie musisz mi mówić (ujmij to ładniej i wrzuć do artykułu, co się będzie marnować). ALE To nie jest artykuł o wywołaniach konstruktorów (stwórz nowy bo ta kwestia jest ważna i ciekawa - polecam). To jest artykuł o konstruktorze jako takim. I ty napisałeś: obliczenie rozmiaru obiektu. Sam kod konstruktora (stworzony przez użytkownika i dodana reszta niejawnie przez kompilator) nic nie oblicza!!! O to mi tylko chodzi. Kwestia tego o czym piszesz tkwi w polimorficznym zachowaniu się konstruktora na rzecz klasy. W porządku - typu tworzonego obiektu nie znamy na poziomie kompilacji , ale gdy zostanie on już de facto stwierdzony w momencie wykonania i zostanie wybrany odpowiedni konstruktor to następuje jego wykonanie (łączniez z odpowiednim przydziałem pamięci na nowy obiekt itp...).
To może inaczej: skoro konstruktor oblicza a nie zna rozmiaru obiektu to w kodzie samego konstruktora powinno się znaleźć coś co będzie go obliczało. Pokaż mi to (pokaż mi choćby kod asm'a).
Albo jeszcze inaczej to powiem: to, że w danym momencie nie wiemy jakiego typu będzie obiekt który zaraz stworzymy (o tym mówisz prze cały czas) nie oznacza wcale, że zadanie obliczenia tego zostaje przeniesione na barki konstruktora. To mechanizm wywołania o tym decyduje a nie konstruktor
Albo jeszcze inaczej: Konstruktor, żeby działać (ściślej: żeby mógł się wykonywać jego kod) musi mieć już pamięć na obiekt zarezerwowaną. Więc gdzie tu obliczanie rozmiar uobiektu? --zolv 12:51, 29 paź 2005 (CEST)
Koniec dyskusji o niczym czepiasz się dalej tego obliczania napisałem już Ci kilkakrotnie, a w delphi to "obliczenie" wyglada z grubsza tak dl := VMT_tego_typu[vmtRozmiarObiektu], oczywiście jest to zrobione w asm, jak chcesz to jest to tylko podstawienie i robi to kod w metodzie NewInstance, a wywołanei do niej generuje kompilator. StoK 13:08, 29 paź 2005 (CEST)
- a czy robi to konstruktor? (oświeć mnie - serio mówie - warto wiedzieć...) --zolv 13:13, 29 paź 2005 (CEST)
- A co do czepiania się to wydaje mi się, że wyczytałeś gdzieś po prostu regułke "rozmiar obiektu nie jest znany na etapie kompilacji..." i łączysz takie zdanie z tym, że na pewno kompilator musi ten rozmiar obliczac. Mówisz, mówisz, piszesz o wirtualnym zachowaniu się konstruktora, pokazujesz przykłady nieznajomości typu (nie związane z tematem) i nie potrafisz tego wyjaśnić a czepiasz się wszystkiego dookoła. Najlepiej zostawmy tobie kategorię "programowanie obiektowe" i niech się Stok bawi. Pozdro --zolv 13:29, 29 paź 2005 (CEST)
- A wracając do twojego tekstu:
„nie jest konieczne wywoływanie konstruktora klasy bazowej” , to w pozostałych językach jest konieczne, a nie widzę tego w twoich przykładach
to podpowiem Ci: wiesz czemu tego w moich przykładach nie widać? bo w moich przykładach nie ma dziedziczenia. Spoko. Wiem. Ciężko to zauważyć. --zolv 13:36, 29 paź 2005 (CEST)
"a czy robi to konstruktor? (oświeć mnie - serio mówie - warto wiedzieć...)" I tak i nie zależy co uważasz za konstruktor.
Jeśli w kodzie umieścimy konstrukcję tworzenia obiektu (wg Delphi) TList.Create, to w kodzie zobaczymy podstaawienie do rejestru dl wartosci 1, do EAX - adres tablicy VMT klasy tworzonej i wywołanie konstruktora. Oznacza to, że tworzenie obiektu robi konstruktor.
Co jest w konstruktorze. Na początku kodu każdego konstruktora jest kod, który nie wynika z kodu utworzonego przez programistę a jest dodany przez kompilator a wygląda on tak:
Jeśli dl<> 0 to twórz_obiekt; dl := 0 jesli adres obiektu = nil to exit i dopiero teraz kod odpowiadający temu co napisano w konstruktorze.
I tu możesz odpowiedzieć, że nie, bo robi to kod dołożony do konstruktora przez kompilator.
Nie mam zamiaru dyskutować, bo dyskusje te do niczego nie prowadzą, chyba że masz konstruktywne wnioski lub pytania. StoK 20:05, 29 paź 2005 (CEST)
[edytuj] Kolejność wywołań konstruktorów
W Object Pascalu konstruktor może być dziedziczony i virtualny i ze względu na brak dziedziczenia wielokrotnego oraz konieczność dziedziczenia od klasy bazowej (TObject) nie istnieje problem kolejności wywołań konstruktorów. - A co ma do tego to wszystko? Co jeśli w konstruktorze klasy znajdującej się na którymś z kolei poziomie dziedziczenia nie umieszcze klauzuli inherited;? Co rozumiesz pod pojęciem problem kolejności wywołań? bo jak dla mnie to problem nadal istnieje. --zolv 12:02, 1 lis 2005 (CET)
Nie ma żadnego problemu. W Turbo Pascalu jak i w Obiect Pascalu z Delphi wywoływany jest tylko konstruktor klasy odpowiadającej klasie tworzonego obiektu i to ten który podano. Nie ma pojęcia "konstruktor domyślny, kopiujący itd.".
Brak Inherited nic nie szkodzi jak nie ma potrzeby wywoływania konstruktora klasy z której dana klasa się wywodzi.
Wynika to z tego, że każdy konstruktor może utworzyć obiekt, o czym pisałem ostatnio.
StoK 13:21, 1 lis 2005 (CET)