Discussion:
Prywatne dziedziczenie vs zawieranie w C++ FAQ
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
SasQ
2007-09-04 22:22:18 UTC
Permalink
Siema.

Przeglądając sobie C++ FAQ natknąłem się na taki motyw:
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
i oczywiście pierwszą myślą, gdy zobaczyłem to:

class Car : private Engine

było: "WTF??! o_O"
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki?
Piszą tam, że "private inheritance is a syntactic variant of
composition" ["Dziedziczenie prywatne jest składniową
odmianą zawierania"]. Jak dla mnie nie jest to prawdą, bo
dany zestaw funkcji/danych można odziedziczyć tylko raz,
a w przypadku zawierania można je mieć wielokrotnie
powtórzone w obiekcie. Kolejną różnicą jest fakt, że przy
zawieraniu nie można nadpisać metod wirtualnych, co jest
oczywiście możliwe gdy dziedziczymy.

To, że "z zewnątrz" dziedziczenie prywatne i zawieranie
wydają się wyglądać tak samo, nie oznacza jeszcze, że
to JEST to samo ;P Błędność podanego tam przykładu
narzuca się sama, gdy sobie uświadomić, że implementacja
klasy Car widzi tą zależność dziedziczenia i może z niej
korzystać. A jaki sens ma stwierdzenie, że samochód JEST
szczególnym przypadkiem silnika? :PPP Jest to idiotyzmem
nawet, jeśli reszta świata o tym nie wie ;J

Co o tym myślicie?
Mam rację, czy może coś przeoczyłem?
--
SasQ
Seweryn Habdank-Wojewódzki
2007-09-04 23:29:19 UTC
Permalink
Witam
Post by SasQ
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
class Car : private Engine
było: "WTF??! o_O"
Dobrze, że "WTF".
Post by SasQ
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki?
Nie całkiem.
Post by SasQ
Piszą tam, że "private inheritance is a syntactic variant of
composition" ["Dziedziczenie prywatne jest składniową
odmianą zawierania"].
Mają rację w dużej mierze. Co może też być ciekawe guru Meyers w jednej ze
swoich książek (55 sposobów) pisze podobnie, ale ...
Post by SasQ
Jak dla mnie nie jest to prawdą, bo
dany zestaw funkcji/danych można odziedziczyć tylko raz,
a w przypadku zawierania można je mieć wielokrotnie
powtórzone w obiekcie. Kolejną różnicą jest fakt, że przy
zawieraniu nie można nadpisać metod wirtualnych, co jest
oczywiście możliwe gdy dziedziczymy.
... właśnie to też podkreśla. Co nie zmienia, że jest to syntaktyczny
wariant. Jednak Meyers podkreśla różnice kiedy należy stosować prywatne
składniki (zawieranie) a kiedy dziedziczenie.
Post by SasQ
To, że "z zewnątrz" dziedziczenie prywatne i zawieranie
wydają się wyglądać tak samo
właśnie, ale można to tak określić bo z zewnątrz to jest podobne.
Post by SasQ
, nie oznacza jeszcze, że
to JEST to samo ;P
Nie jest.
Post by SasQ
Błędność podanego tam przykładu
narzuca się sama, gdy sobie uświadomić, że implementacja
klasy Car widzi tą zależność dziedziczenia i może z niej
korzystać. A jaki sens ma stwierdzenie, że samochód JEST
szczególnym przypadkiem silnika?
A nie jest? Samochód to taki silnik co ma 4 koła. Rozumiem, że to jest tylko
krótkie FAQ, dlatego jest mało czytelne.
Post by SasQ
:PPP Jest to idiotyzmem
nawet, jeśli reszta świata o tym nie wie ;J
E tam. Raczej spojrzał bym na to pozytywnie. Poczytaj w Meyersie. On
dokładnie podaje różnice zastosowania wskazując na "syntaktyczną"
(zewnętrzną) równoważność pokazuje kiedy się one rożnią i jak je stosować.
Post by SasQ
Co o tym myślicie?
Od dziedziczenia prywatnego lepsze jest zawieranie klasy, która jest
prywatna i dziedziczy publicznie, popatrz na różnice pomiędzy:

class bar{
public:
virtual void on_bar();
}

class foo : private bar {
virtual void on_bar();
};

vs.

class foo
{
private:
class barable : public bar {
virtual void on_bar();
};
barable bar_field;
};
Post by SasQ
Mam rację, czy może coś przeoczyłem?
Masz rację, ale pewne sprawy przeoczyłeś -- z zewnątrz to jest podobne, a
różnice są subtelne.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Rafal Dabrowa
2007-09-05 00:13:49 UTC
Permalink
Post by SasQ
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
class Car : private Engine
było: "WTF??! o_O"
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki?
Piszą tam, że "private inheritance is a syntactic variant of
composition" ["Dziedziczenie prywatne jest składniową
odmianą zawierania"]. Jak dla mnie nie jest to prawdą, bo
dany zestaw funkcji/danych można odziedziczyć tylko raz,
a w przypadku zawierania można je mieć wielokrotnie
powtórzone w obiekcie.
Bo dziedziczenie prywatne to jest pewien szczególny przypadek
zawierania. Tak samo jak kwadrat jest odmianą prostokąta.
Post by SasQ
Kolejną różnicą jest fakt, że przy
zawieraniu nie można nadpisać metod wirtualnych, co jest
oczywiście możliwe gdy dziedziczymy.
Są tam wypisane różnice pomiędzy dziedziczeniem prywatnym i
zawieraniem, i ta różnica jest tam też wypisana.
Post by SasQ
To, że "z zewnątrz" dziedziczenie prywatne i zawieranie
wydają się wyglądać tak samo, nie oznacza jeszcze, że
to JEST to samo ;P
Tam nikt nie twierdzi że to jest to samo.
--
Rafal
Mateusz Loskot
2007-09-05 01:23:57 UTC
Permalink
Post by SasQ
Siema.
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
class Car : private Engine
było: "WTF??! o_O"
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki? Piszą
tam, że "private inheritance is a syntactic variant of composition"
["Dziedziczenie prywatne jest składniową odmianą zawierania"].
Z jedną uwagą, jest to kompozycja, czyli przypadek agregacji z
wymagalnością *posiadania* agregowanego typu (założenie: samochód nie
może istnieć bez silnika).

I użycie prywatnego dziedziczenia jest tu jak najbardziej poprawne,
nie jest bajką.
Post by SasQ
Jak dla mnie nie jest to prawdą, bo dany zestaw funkcji/danych można
odziedziczyć tylko raz, a w przypadku zawierania można je mieć
wielokrotnie powtórzone w obiekcie.
Prywatne dziedziczenie to złożenie typów 1:1, co jest jak najbardziej
poprawną agregacją. Oczywiście nie jest możliwe osiągnięcie 1:N
z użyciem dziedziczenia, ale to nie zmienia poprawności rozwiązania.
Post by SasQ
Kolejną różnicą jest fakt, że przy zawieraniu nie można nadpisać
metod wirtualnych, co jest oczywiście możliwe gdy dziedziczymy.
Różnica między tymi rozwiązaniami leży gdzie indziej.
Dziedziczenie klasycznie rozumiane nie tworzy nowego, innego typu,
ale podtyp. A więc podtyp oferuje te same usługi (interfejs) co typ
bazowy, plus dodaje coś od siebie.

Agregacja zaś tworzy nowy typ.

Zgodnie z tym, prywatne dziedziczenie jest tu jak najbardziej
odpowienikiem agregacji, a dokładniej kompozycji, bo:

- tworzy nowy typ

- ów nowy typ nie oferuje interfejsu typu z którego dziedziczy
prywatnie, czyli dzięki właściwościom prywatnego dziedziczenia,
Car nie będzie udostępniał interfejsu typu Engine. Engine jest więc
elementem wewnętrznej implementacji Car, a nie częścią interfejsu.

I to w zasadzie wystarczy, aby poprawnie uznać private inheritance jako
composition 1:1.

Możliwość przesłonięcia funkcji typu bazowego nic tu nie zmienia.
Post by SasQ
To, że "z zewnątrz" dziedziczenie prywatne i zawieranie wydają się
wyglądać tak samo, nie oznacza jeszcze, że to JEST to samo ;P
Ale obie formy równoznacznie reprezentują sens agregacji jaką jest
kompozycja.
Post by SasQ
Błędność podanego tam przykładu narzuca się sama, gdy sobie
uświadomić, że implementacja klasy Car widzi tą zależność
dziedziczenia i może z niej korzystać. A jaki sens ma stwierdzenie,
że samochód JEST szczególnym przypadkiem silnika? :PPP Jest to
idiotyzmem nawet, jeśli reszta świata o tym nie wie ;J
Masz rację, że byłby to błąd, jednak nie jest, ponieważ taka sytuacja
tutaj *nie* zachodzi.
W przykładzie "class Car : private Engine", nie jest tworzony podtyp
ale *nowy* typ i tym samym nie zachodzi tu relacja IS-A, ale HAS-A.
I to wszystko zgodnie z ideą dziedziczenia i kompozycji.
Post by SasQ
Co o tym myślicie? Mam rację, czy może coś przeoczyłem?
IMO, przeoczyłeś prawdziwą istotę dziedziczenia i kompozycji oraz
różnice między tymi koncepcjami.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Mateusz Loskot
2007-09-05 01:55:53 UTC
Permalink
Przypomniał mi się jeszcze jeden argument, który świadczy o poprawności:

class Car : private Engine

Jednym z podstawowych zasad dziedziczenia jest "podstawialność typów",
która to zasada została sformułowana w postaci Liskov Substitution
Principle (LSP) [1]. Z grubsza, każda hierarchia typów jest (projektowo)
poprawna jeśli typy w niej uczestniczące zgodne są z LSP.

Aby sprawdzić, czy dziedziczenie przykład z FAQ tworzy "klasyczną"
hierarchię podtypów wystarczy sprawdzić podstawialność typów
występujących w drzewie dziedziczenia.

Jest funkcja:

void foo(Engine* p)
{
}

Test #1: Czy poprawne jest:

class Car : public Engine {};
Car* p new = Car();
foo(p);

Tak, ponieważ Car jest typu Engine, podtypem.

Test #2: Czy poprawne jest:

class Car : private Engine {};
Car* p new = Car();
foo(p);

Nie, ponieważ Car *nie* jest typu Engine i ta sama funkcja foo() nie
może operować na obiekcie typu Car i typu Engine, bez odpowiedniej
zmiany prototypu. Wniosek jest taki, że prywatne dziedziczenie nie jest
zgodne z LSP, a więc nie nie tworzy podtypu, a nowy niezależny typ.

Formułując definicję LSP dla Car i Engine, wygląda to tak:

"Typ Car nie jest podtypem Engine, ponieważ dowolna operująca na typie
Engine nie może operować na typie Car nie zdając sobie z tego sprawy."

[1] http://www.objectmentor.com/resources/articles/lsp.pdf

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
s***@gmail.com
2007-09-05 08:42:16 UTC
Permalink
Post by Mateusz Loskot
I użycie prywatnego dziedziczenia jest tu jak najbardziej poprawne,
nie jest bajką.
Poprawne, bo?
Post by Mateusz Loskot
Dziedziczenie klasycznie rozumiane nie tworzy nowego, innego typu,
ale podtyp. A więc podtyp oferuje te same usługi (interfejs) co typ
bazowy, plus dodaje coś od siebie.
Agregacja zaś tworzy nowy typ.
No przeciez to wlasnie caly czas mowie - jako glowny argument za
niestosowaniem dziedziczenia w miejsce zawierania.
Post by Mateusz Loskot
Zgodnie z tym, prywatne dziedziczenie jest tu jak najbardziej
- tworzy nowy typ
- ów nowy typ nie oferuje interfejsu typu z którego dziedziczy
prywatnie, czyli dzięki właściwościom prywatnego dziedziczenia,
Dla swiata zewnetrznego tworzy nowy typ. Dla klasy pochodnej wiezy
krwi sa jednak wyraznie widoczne i moze z nich skorzystac. Dla niej
jest ona podtypem.
Post by Mateusz Loskot
Car nie będzie udostępniał interfejsu typu Engine.
Swiatu zewnetrznemu - nie. Ale sobie jak najbardziej. Jesli bylby to
inny przypadek dziedziczenia niepublicznego - dziedziczenie chronione
- interfejs ten bylby dodatkowo widoczny dla klas pochodnych. Mimo ze
dla swiata zewnetrznego typy te nadal nie wygladaja na spokrewnione,
to dla klasy pochodnej i jej klas pochodnych to pokrewienstwo jest
widoczne.
Post by Mateusz Loskot
I to w zasadzie wystarczy, aby poprawnie uznać private inheritance jako
composition 1:1.
...
Ale obie formy równoznacznie reprezentują sens agregacji jaką jest
kompozycja.
Jesli sie skupic wylacznie na agregacji, to mozna rownie dobrze uznac,
ze kazde dziedziczenie, nawet publiczne, jest rownoznaczne z
kompozycja, bo takze powoduje, ze obiekt klasy bazowej staje sie
czescia obiektu pochodnego ;-P
Post by Mateusz Loskot
W przykładzie "class Car : private Engine", nie jest tworzony podtyp
ale *nowy* typ i tym samym nie zachodzi tu relacja IS-A, ale HAS-A.
Z zewnatrz jest to widziane jako HAS-A [albo raczej nie jest widziane,
bo jest prywatne ;-P]. Jednak od wewnatrz jest to widziane jako
relacja IS-A. Klasa pochodna doskonale wie, ze jest potomkiem klasy
bazowej i moze w pelni korzystac z tego faktu.
Post by Mateusz Loskot
I to wszystko zgodnie z ideą dziedziczenia i kompozycji.
Nie z moja ;-J
Ja jestem raczej za jasnym okreslaniem, co programista ma na mysli.
Uzywanie prywatnego dziedziczenia jako agregacji jest IMO naduzyciem i
moze powodowac niejasnosci - ktos moze sie nabrac, ze rzeczywiscie
zachodzi tu jakas zaleznosc dziedziczenia i zastanawiac sie nad jego
sensem, podczas gdy takiego sensu nie ma i jest to zwyczajny "hack" ;-
P
Post by Mateusz Loskot
IMO, przeoczyłeś prawdziwą istotę dziedziczenia i kompozycji oraz
różnice między tymi koncepcjami.
Jesli ja przeoczylem, to chetnie ja zobacze ;-J
Post by Mateusz Loskot
Jednym z podstawowych zasad dziedziczenia jest "podstawialność typów",
która to zasada została sformułowana w postaci Liskov Substitution
Principle (LSP) [1]. Z grubsza, każda hierarchia typów jest (projektowo)
poprawna jeśli typy w niej uczestniczące zgodne są z LSP.
"Oni to wiedza, Kocie" ;-J
Post by Mateusz Loskot
Aby sprawdzić, czy dziedziczenie przykład z FAQ tworzy "klasyczną"
hierarchię podtypów wystarczy sprawdzić podstawialność typów
występujących w drzewie dziedziczenia.
I wtedy okaze sie, ze tworzy ;-) jednak ta hierarchia nie jest
publicznie dostepna. O wiezach rodzinnych wiedza tylko i wylacznie
potomkowie ;-J I dla nich podstawialnosc typow jak najbardziej
zachodzi, gdyz doskonale wiedza, od jakiej klasy bazowej pochodza. We
wlasnej implementacji moga sobie bez obaw podstawiac siebie do
wskazników/referencji na klase bazowa.
Post by Mateusz Loskot
class Car : private Engine {};
Car* p new = Car();
foo(p);
Nie, ponieważ Car *nie* jest typu Engine i ta sama funkcja foo() nie
może operować na obiekcie typu Car i typu Engine, bez odpowiedniej
zmiany prototypu.
Alez publiczne podstawianie w oczywisty sposob nie moze zachodzic i
temu nikt nie przeczy! ;-) Podstawianie zachodzi jednak prywatnie
wewnatrz klasy Car. Jesli chodziloby o dziedziczenie chronione, z
takiego podstawiania moglyby rowniez korzystac klasy pochodne od Car.
Dlatego własnie uwazam ten przyklad za bledny.

Bardziej adekwatny bylby pewnie przyklad taki:

class PracownikNaCzarno : private Pracownik

Dla ogolnej publiki PracownikNaCzarno nie przyznaje sie, że jest
czyims Pracownikiem ;-J Jednak on sam dobrze o tym wie, ze dla kogos
pracuje, i potrafi wykonywac wszystkie te same czynnosci, co normalny
pracownik [dziedziczenie jest dla neigo widoczne, wie, ze jest
szczegolnym przypadkiem Pracownika]. Niektore czynnosci Pracownika
moze nawet wykonywac po swojemu ;-J [virtual].

Nasunal mi sie jeszcze jeden argument. O tej zaleznosci dziedziczenia
moglby rowniez wiedziec jakis przyjaciel [friend] owego
PracownikaNaCzarno i on takze widzialby ta zaleznosc dziedziczenia.
Dla niego taki PracownikNaCzarno jest normalnym Pracownikiem
[podstawialnosc zachodzi] i przyjaciel moze go zatrudnic ;-J
Post by Mateusz Loskot
Wniosek jest taki, że prywatne dziedziczenie nie jest
zgodne z LSP, a więc nie nie tworzy podtypu, a nowy niezależny typ.
Zalezy z jakiej perspektywy na to patrzymy ;-J

--
SasQ
Rafal Dabrowa
2007-09-05 10:06:32 UTC
Permalink
Post by s***@gmail.com
Swiatu zewnetrznemu - nie. Ale sobie jak najbardziej. Jesli bylby to
inny przypadek dziedziczenia niepublicznego - dziedziczenie chronione
- interfejs ten bylby dodatkowo widoczny dla klas pochodnych. Mimo ze
dla swiata zewnetrznego typy te nadal nie wygladaja na spokrewnione,
to dla klasy pochodnej i jej klas pochodnych to pokrewienstwo jest
widoczne.
Dla jej klas pochodnych - nie.

Zakładasz że klasa bazowa jest widoczna dla świata zewnętrznego. Ale
może to być jakiś prywatny typ, pomocniczy dla implementacji danej
klasy. Jaki sens ma publiczne dziedziczenie z klasy o której
istnieniu świat w ogóle nie wie ?
Post by s***@gmail.com
Z zewnatrz jest to widziane jako HAS-A [albo raczej nie jest widziane,
bo jest prywatne ;-P]. Jednak od wewnatrz jest to widziane jako
relacja IS-A. Klasa pochodna doskonale wie, ze jest potomkiem klasy
bazowej i moze w pelni korzystac z tego faktu.
Prywatne dziedziczenie jest częścią implementacji klasy. Ktoś może
chcieć zaimplementować jakąś klasę jako pochodną jakiejś innej.
I to ma sens, tak samo jak czynienie pewnych składowych prywatnymi.
--
Rafal
Seweryn Habdank-Wojewódzki
2007-09-05 11:06:35 UTC
Permalink
Witam
Post by s***@gmail.com
Post by Mateusz Loskot
I to wszystko zgodnie z ideą dziedziczenia i kompozycji.
Nie z moja ;-J
Ja jestem raczej za jasnym okreslaniem, co programista ma na mysli.
Uzywanie prywatnego dziedziczenia jako agregacji jest IMO naduzyciem i
moze powodowac niejasnosci - ktos moze sie nabrac, ze rzeczywiscie
zachodzi tu jakas zaleznosc dziedziczenia i zastanawiac sie nad jego
sensem, podczas gdy takiego sensu nie ma i jest to zwyczajny "hack" ;-
P
Zapominasz, że możesz chcieć przeładować/przysłonić funkcje wirtualne na
swój sposób, tego nie możesz zrobić bez dziedziczenia.
Post by s***@gmail.com
Niektore czynnosci Pracownika
moze nawet wykonywac po swojemu ;-J [virtual].
No właśnie.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
s***@gmail.com
2007-09-05 11:22:00 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Zapominasz, że możesz chcieć przeładować/przysłonić funkcje wirtualne na
swój sposób, tego nie możesz zrobić bez dziedziczenia.
Nie zapominam.
Post by Seweryn Habdank-Wojewódzki
Kolejną różnicą jest fakt, że przy zawieraniu nie można nadpisać metod
wirtualnych, co jest oczywiście możliwe gdy dziedziczymy.
Oraz fragment zacytowany przez Ciebie ponizej.

Jesli zachodzi potrzeba zachowania polimorficznego, to wtedy
dziedziczenie prywatne jak najbardziej jest na miejscu. Bo wtedy jest
jasne, ze klasy sa ze soba powiazane zwiazkiem dziedziczenia, a nie po
prostu zawierania.

Zastanawiam sie, dlaczego nikt nie odniosl sie do tego przykladu,
ktory jasno ukazuje roznice miedzy zwyklym zawieraniem, a
Post by Seweryn Habdank-Wojewódzki
Nasunal mi sie jeszcze jeden argument. O tej zaleznosci
dziedziczenia moglby rowniez wiedziec jakis przyjaciel [friend] owego
PracownikaNaCzarno i on takze widzialby ta zaleznosc dziedziczenia.
Dla niego taki PracownikNaCzarno jest normalnym Pracownikiem
[podstawialnosc zachodzi] i przyjaciel moze go zatrudnic ;-J
W powyzszym przykladzie klasa lub funkcja zaprzyjazniona jest
kolejna ... osoba :P, ktora jest w stanie wyraznie dostrzec zaleznosc
dziedziczenia miedzy klasa bazowa i pochodna, oraz wykorzystac ta
zaleznosc poslugujac sie zasada podstawialnosci Liskova. Moze nawet
byc zaprzyjazniony z wieloma takimi klasami dziedziczacymi prywatnie
jakis wspolny interfejs, dzieki czemu moze sie [caly czas prywatnie
przed swiatem zewnetrznym] odwolywac do tych klas poprzez ogolny
interfejs klasy bazowej.
Jesli ktos chce wykazac rownowaznosc prywatnego dziedziczenia i
zawierania, powinien byc w stanie uzyc zawierania takze w tym
przypadku. Oczywiscie jest to niemozliwe, co ewidentnie dowodzi ze te
dwie koncepcje nie sa tym samym i nie mozna ich stosowac zamiennie.

--
SasQ
Seweryn Habdank-Wojewódzki
2007-09-05 11:33:33 UTC
Permalink
Witam
Post by s***@gmail.com
Jesli ktos chce wykazac rownowaznosc prywatnego dziedziczenia i
zawierania, powinien byc w stanie uzyc zawierania takze w tym
przypadku. Oczywiscie jest to niemozliwe, co ewidentnie dowodzi ze te
dwie koncepcje nie sa tym samym i nie mozna ich stosowac zamiennie.
A kto wykazuje, że to jest to samo? FAQ jest za krótkie aby wykazać niuanse.
A ten wątek właśnie pokazuje różnice, które jak widzisz są dość delikatne.
A tak na marginesie, Twój przykład z friendem jest celny, a friendów należy
unikać, więc może się okazać że taką konstrukcję należy zbudować całkiem
inaczej.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
s***@gmail.com
2007-09-05 12:08:59 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
A kto wykazuje, że to jest to samo?
C++ FAQ:
"private inheritance is a syntactic variant of composition (AKA
aggregation and/or has-a).
(...)
The "Car has-a Engine" relationship can also be expressed using
private inheritance"

Mateusz Loskot:
"prywatne dziedziczenie jest tu jak najbardziej odpowienikiem
agregacji, a dokładniej kompozycji
(...)
to w zasadzie wystarczy, aby poprawnie uznać private inheritance jako
composition 1:1. Możliwość przesłonięcia funkcji typu bazowego nic tu
nie zmienia.
Ale obie formy równoznacznie reprezentują sens agregacji jaką jest
kompozycja."
Post by Seweryn Habdank-Wojewódzki
FAQ jest za krótkie aby wykazać niuanse.
Bledny przyklad jednak sie tam zmiescil ;-) Zamiast niego mogliby dac
jakis bardziej poprawny. Podobnie jak zmiescilyby sie tam teksty o
roznicy miedzy zawieraniem a prywatnym dziedziczeniem, w miejscu tych,
w ktorych teraz pisze, ze to jest to samo ;-J
Osobiscie sam moglbym napisac taki punkt FAQ, jesli tylko by to to ode
mnie zalezalo.

Zgadzam sie co do tego, ze pozniej tam pokazali roznice, wymienili je
na liscie [dosc nieporadnie, bo punkty z listy wyjatkow neguja punkty
z listy podobienstw, ale lepszy rydz niz nic ;-P]. Glownie razi mnie
tam ten przykladowy kod, ktory nie jest najlepszym przykladem
zastosowania prywatnego dziedziczenia, a wrecz jest niepoprawnym jego
uzyciem i powinien byc podpisany "Kids, don't try this at home" ;-)
Post by Seweryn Habdank-Wojewódzki
A ten wątek właśnie pokazuje różnice, które jak widzisz są dość delikatne.
A tak na marginesie, Twój przykład z friendem jest celny
Milo slyszec, ze przynajmniej w tym sie ze mna zgadzacie ;-)
Post by Seweryn Habdank-Wojewódzki
a friendów należy unikać, więc może się okazać że taką konstrukcję
należy zbudować całkiem inaczej.
I pewnie tak byloby najrozsadniej, tu tez sie zgadzam. Choc takie
konstrukcje się zdarzaja i czasami [w rzadkich przypadkach] sa
przydatne. Jako przyklad pokazujacy roznice miedzy dziedziczeniem
prywatnym a zawieraniem to juz na pewno sie przydaje jak widac ;-D

--
SasQ
Seweryn Habdank-Wojewódzki
2007-09-05 12:43:18 UTC
Permalink
Witam
Post by s***@gmail.com
"private inheritance is a syntactic variant of composition (AKA
aggregation and/or has-a).
(...)
The "Car has-a Engine" relationship can also be expressed using
private inheritance"
Tak. Bo to jest prawda. Tak możesz napisać Przecież samochód zawiera silnik.
i to realizujesz za pomocą dziedziczenia jak i zawierania. Kiedy zachodzą
różnice? A no w tedy gdy samochód ma dwa silniki jeden elektryczny a drugi
spalinowy. To już nie jest wesoło, ale to już inny przykład.
Post by s***@gmail.com
"prywatne dziedziczenie jest tu jak najbardziej odpowienikiem
agregacji, a dokładniej kompozycji
(...)
to w zasadzie wystarczy, aby poprawnie uznać private inheritance jako
composition 1:1. Możliwość przesłonięcia funkcji typu bazowego nic tu
nie zmienia.
Ale obie formy równoznacznie reprezentują sens agregacji jaką jest
kompozycja."
To jest prawda. Różnice pojawiają się dalej kiedy wychodzisz poza
kompozycję, bo przesłanianie funkcji wirtualnych jest wychodzeniem poza to.

A zatem masz twierdzenie takie. Jeżeli chcesz zrobić agregację możesz użyć
dziedziczenia prywatnego lub kompozycji. Twierdzenie odwrotne nie jest
prawdziwe, czyli mając kompozycję lub dziedziczenie to wcale nie musi to
być agregacja. Czyli uzyskujemy fakt, że nie ma równoważności, ale jest
wynikanie w jedną stronę.
Post by s***@gmail.com
Bledny przyklad jednak sie tam zmiescil ;-)
Nie taki błędny.
Post by s***@gmail.com
Zamiast niego mogliby dac jakis bardziej poprawny.
Zapodaj temat autorowi.
Post by s***@gmail.com
Osobiscie sam moglbym napisac taki punkt FAQ, jesli tylko by to to ode
mnie zalezalo.
To popraw autora.
Post by s***@gmail.com
Post by Seweryn Habdank-Wojewódzki
A ten wątek właśnie pokazuje różnice, które jak widzisz są dość
delikatne. A tak na marginesie, Twój przykład z friendem jest celny
Milo slyszec, ze przynajmniej w tym sie ze mna zgadzacie ;-)
Mówię tylko za siebie.
Post by s***@gmail.com
Post by Seweryn Habdank-Wojewódzki
a friendów należy unikać, więc może się okazać że taką konstrukcję
należy zbudować całkiem inaczej.
I pewnie tak byloby najrozsadniej, tu tez sie zgadzam. Choc takie
konstrukcje się zdarzaja i czasami [w rzadkich przypadkach] sa
przydatne. Jako przyklad pokazujacy roznice miedzy dziedziczeniem
prywatnym a zawieraniem to juz na pewno sie przydaje jak widac ;-D
Zrób pożyteczny przykład i zapodaj na grupę dla postronnych czytających ten
wątek.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Paweł Kierski
2007-09-05 13:22:01 UTC
Permalink
***@gmail.com w wiadomości <***@y42g2000hsy.googlegroups.com> pisze:
[...]
Post by s***@gmail.com
Post by Seweryn Habdank-Wojewódzki
FAQ jest za krótkie aby wykazać niuanse.
[...]
Post by s***@gmail.com
Osobiscie sam moglbym napisac taki punkt FAQ, jesli tylko by to to ode
mnie zalezalo.
Jest polska strona ( http://pl.cpp.wikia.com/ ), która zawiera
jednocześnie FAQ grupy ( http://pl.cpp.wikia.com/wiki/FAQ ).

Ten temat bardziej nadawałby się do kategorii zaawansowanych
zagadnień: http://pl.cpp.wikia.com/wiki/Niuanse . FAQ w założeniu
miało być odpowiedziami na faktycznie częste pytania na grupie.
--
Paweł Kierski
***@pkierski.net
dodaj "[nomorespam]" w temacie jeśli piszesz z domeny innej niż .pl,
albo koniecznie chcesz obejść moje filtry 8-)
Mateusz Loskot
2007-09-06 00:53:07 UTC
Permalink
Post by s***@gmail.com
Post by Seweryn Habdank-Wojewódzki
Kolejną różnicą jest fakt, że przy zawieraniu nie można nadpisać metod
wirtualnych, co jest oczywiście możliwe gdy dziedziczymy.
Oraz fragment zacytowany przez Ciebie ponizej.
Jesli zachodzi potrzeba zachowania polimorficznego, to wtedy
dziedziczenie prywatne jak najbardziej jest na miejscu. Bo wtedy jest
jasne, ze klasy sa ze soba powiazane zwiazkiem dziedziczenia, a nie po
prostu zawierania.
Ale i to nie zmieni faktu, że taki Car nie jest typem Engine, ponieważ
nie będzie można skręcać kołami poprzez wskaźnik do klasy
bazowej - Engine*. W dyskutowanym przykładzie z FAQ, Car nie jest
specjalizacją typu Engine, a Engine nie jest generalizacją Car. Kropka.
Post by s***@gmail.com
Zastanawiam sie, dlaczego nikt nie odniosl sie do tego przykladu,
ktory jasno ukazuje roznice miedzy zwyklym zawieraniem, a
Chwila moment, niektórzy pracują w innej strefie czasowej ;-)
Ja się odniosłem.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Mateusz Loskot
2007-09-06 00:47:16 UTC
Permalink
Post by s***@gmail.com
Post by Mateusz Loskot
I użycie prywatnego dziedziczenia jest tu jak najbardziej poprawne,
nie jest bajką.
Poprawne, bo?
Post by Mateusz Loskot
Dziedziczenie klasycznie rozumiane nie tworzy nowego, innego typu,
ale podtyp. A więc podtyp oferuje te same usługi (interfejs) co typ
bazowy, plus dodaje coś od siebie.
Agregacja zaś tworzy nowy typ.
No przeciez to wlasnie caly czas mowie - jako glowny argument za
niestosowaniem dziedziczenia w miejsce zawierania.
Czyli wychodzi na "Kwestia Gustu (tm)" :-)
Post by s***@gmail.com
Post by Mateusz Loskot
Zgodnie z tym, prywatne dziedziczenie jest tu jak najbardziej
- tworzy nowy typ
- ów nowy typ nie oferuje interfejsu typu z którego dziedziczy
prywatnie, czyli dzięki właściwościom prywatnego dziedziczenia,
Dla swiata zewnetrznego tworzy nowy typ. Dla klasy pochodnej wiezy
krwi sa jednak wyraznie widoczne i moze z nich skorzystac. Dla niej
jest ona podtypem.
Ale to jest bez znaczenia dla tego jak nowy typ jest widoczny przez
klienta, a więc pozostaje to szczegółem implementacji, nie interfejsu.
Post by s***@gmail.com
Post by Mateusz Loskot
Car nie będzie udostępniał interfejsu typu Engine.
Swiatu zewnetrznemu - nie. Ale sobie jak najbardziej.
...a więc szczegół implementacji typu Car.
Post by s***@gmail.com
Jesli bylby to
inny przypadek dziedziczenia niepublicznego - dziedziczenie chronione
- interfejs ten bylby dodatkowo widoczny dla klas pochodnych.
Ale nie jest to taki przypadek.
Post by s***@gmail.com
Mimo ze dla swiata zewnetrznego typy te nadal nie wygladaja na
spokrewnione,
Post by s***@gmail.com
to dla klasy pochodnej i jej klas pochodnych to pokrewienstwo jest
widoczne.
IMO, dywagacja bez znaczenia.
Post by s***@gmail.com
Post by Mateusz Loskot
I to w zasadzie wystarczy, aby poprawnie uznać private inheritance jako
composition 1:1.
...
Ale obie formy równoznacznie reprezentują sens agregacji jaką jest
kompozycja.
Jesli sie skupic wylacznie na agregacji, to mozna rownie dobrze uznac,
ze kazde dziedziczenie, nawet publiczne, jest rownoznaczne z
kompozycja, bo takze powoduje, ze obiekt klasy bazowej staje sie
czescia obiektu pochodnego ;-P
I takie myślenie sprawia, że programiści nadużywają dziedziczenia.
Post by s***@gmail.com
Post by Mateusz Loskot
W przykładzie "class Car : private Engine", nie jest tworzony podtyp
ale *nowy* typ i tym samym nie zachodzi tu relacja IS-A, ale HAS-A.
Z zewnatrz jest to widziane jako HAS-A [albo raczej nie jest widziane,
bo jest prywatne ;-P].
Kompozycja nie wpływa na interfejs typu, więc prywatność/publiczność
komponentów nie ma tu nic do rzeczy, poza dobrym i złym stylem
programowania, etc.
Post by s***@gmail.com
Jednak od wewnatrz jest to widziane jako
relacja IS-A. Klasa pochodna doskonale wie, ze jest potomkiem klasy
bazowej i moze w pelni korzystac z tego faktu.
Post by Mateusz Loskot
I to wszystko zgodnie z ideą dziedziczenia i kompozycji.
Nie z moja ;-J
"Ale co pan poradzisz? Nic pan nie poradzisz!" :-)
Post by s***@gmail.com
Ja jestem raczej za jasnym okreslaniem, co programista ma na mysli.
Uzywanie prywatnego dziedziczenia jako agregacji jest IMO naduzyciem i
moze powodowac niejasnosci - ktos moze sie nabrac, ze rzeczywiscie
zachodzi tu jakas zaleznosc dziedziczenia i zastanawiac sie nad jego
sensem, podczas gdy takiego sensu nie ma i jest to zwyczajny "hack" ;-
P
O nabieraniu nie ma tu mowy, o ile wiemy co jest co.
Post by s***@gmail.com
Post by Mateusz Loskot
IMO, przeoczyłeś prawdziwą istotę dziedziczenia i kompozycji oraz
różnice między tymi koncepcjami.
Jesli ja przeoczylem, to chetnie ja zobacze ;-J
Jaśniej nie potrafię :-(
Post by s***@gmail.com
Post by Mateusz Loskot
Aby sprawdzić, czy dziedziczenie przykład z FAQ tworzy "klasyczną"
hierarchię podtypów wystarczy sprawdzić podstawialność typów
występujących w drzewie dziedziczenia.
I wtedy okaze sie, ze tworzy ;-) jednak ta hierarchia nie jest
publicznie dostepna.
[...]
I to właśnie jest owo przeoczenie, czytasz kod z mikroskopowej
odległości. Są takie grafiki, na których z bliskiej odległości widać
szum, a z dalszej po wpatrzeniu się, widać np. skaczące delfiny... :-)
Post by s***@gmail.com
Post by Mateusz Loskot
class Car : private Engine {};
Car* p new = Car();
foo(p);
Nie, ponieważ Car *nie* jest typu Engine i ta sama funkcja foo() nie
może operować na obiekcie typu Car i typu Engine, bez odpowiedniej
zmiany prototypu.
Alez publiczne podstawianie w oczywisty sposob nie moze zachodzic i
temu nikt nie przeczy! ;-) Podstawianie zachodzi jednak prywatnie
wewnatrz klasy Car.
I jest ono całkowicie bez znaczenia.
Prywatna implementacja typu jest poza zakresem, bez znacznenia, dla OOP.
Atomowym elementem na jakim operuje OOP jest klasa.
Post by s***@gmail.com
Jesli chodziloby o dziedziczenie chronione, z
takiego podstawiania moglyby rowniez korzystac klasy pochodne od Car.
Dlatego własnie uwazam ten przyklad za bledny.
Wolnoć Tomku...
Post by s***@gmail.com
class PracownikNaCzarno : private Pracownik
Dla ogolnej publiki PracownikNaCzarno nie przyznaje sie, że jest
czyims Pracownikiem ;-J Jednak on sam dobrze o tym wie, ze dla kogos
pracuje, i potrafi wykonywac wszystkie te same czynnosci, co normalny
pracownik [dziedziczenie jest dla neigo widoczne, wie, ze jest
szczegolnym przypadkiem Pracownika]. Niektore czynnosci Pracownika
moze nawet wykonywac po swojemu ;-J [virtual].
Tak, on *sam*, wewnętrznie jest pełnowartościowym pracownikiem - potrafi
wykonać to i owo, ale dla świata zewnętrznego nie jest pracownikiem,
ponieważ...Szef nawet jakby chciał, to nie może (legalnie)
wystawić (pod)obiektowi Pracownik zaświadczenia o pracę bo
PracownikNaCzarno sam nie obsługuje operacji przyjmij_zaświadczenie(),
a Pracownik::przyjmij_zaświadczenie() jest niedostępne dla Szef'a.

Prywatne dziedziczenie to taki "Syntax Pepper" (tm):
Semantyka kompozycji, ale składnia dziedziczenia,
więc trochę daje po oczach. Ale wciąż jest to poprawna kompozycja.
Post by s***@gmail.com
Nasunal mi sie jeszcze jeden argument. O tej zaleznosci dziedziczenia
moglby rowniez wiedziec jakis przyjaciel [friend] owego
PracownikaNaCzarno i on takze widzialby ta zaleznosc dziedziczenia.
Dla niego taki PracownikNaCzarno jest normalnym Pracownikiem
[podstawialnosc zachodzi] i przyjaciel moze go zatrudnic ;-J
Nie, bo...zabrania tego ustawa o ochronie danych osobowych :-)
Post by s***@gmail.com
Post by Mateusz Loskot
Wniosek jest taki, że prywatne dziedziczenie nie jest
zgodne z LSP, a więc nie nie tworzy podtypu, a nowy niezależny typ.
Zalezy z jakiej perspektywy na to patrzymy ;-J
Ja patrzę z sensu zastosowania i użyteczności.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Paweł Kierski
2007-09-05 07:20:15 UTC
Permalink
Mateusz Loskot w wiadomości <fbl0fe$pdv$***@inews.gazeta.pl> pisze:
[...]
Post by Mateusz Loskot
Post by SasQ
Co o tym myślicie? Mam rację, czy może coś przeoczyłem?
IMO, przeoczyłeś prawdziwą istotę dziedziczenia i kompozycji oraz
różnice między tymi koncepcjami.
To ja może jeszcze tak dodam: dziedziczenie w sensie C++ może
wyrażać zarówno dziedziczeni w sensie abstrakcyjnych typów danych
(publiczne), jak i zawieranie (prywatne). Oczywiście "C++-owe"
zawieranie daje nieco inne możliwości wyrażania zawierania niż
prywatne dziedziczenie (jest to inna składniowo realizacja).

Podobnie, jak posiadanie wskaźnika do obiektu innej klasy może
wyrażać różne zależności między obiektami.
--
Paweł Kierski
***@pkierski.net
dodaj "[nomorespam]" w temacie jeśli piszesz z domeny innej niż .pl,
albo koniecznie chcesz obejść moje filtry 8-)
Seweryn Habdank-Wojewódzki
2007-09-05 11:13:34 UTC
Permalink
Witam
Post by Mateusz Loskot
Prywatne dziedziczenie to złożenie typów 1:1, co jest jak najbardziej
poprawną agregacją. Oczywiście nie jest możliwe osiągnięcie 1:N
z użyciem dziedziczenia, ale to nie zmienia poprawności rozwiązania.
BTW zobacz na przykład który podałem z zawieraniem prywatnej klasy
publicznie dziedziczącej inną. Przy takim podejściu możesz zarówno
realizować 1:N jak i fakt, że czasem masz metody wirtualne i chcesz je
przeładować. Oczywiście pomysł nie jest mój tylko Meyersa.

Pozdrawiam
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Mateusz Loskot
2007-09-06 00:56:56 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Witam
Post by Mateusz Loskot
Prywatne dziedziczenie to złożenie typów 1:1, co jest jak
najbardziej poprawną agregacją. Oczywiście nie jest możliwe
osiągnięcie 1:N z użyciem dziedziczenia, ale to nie zmienia
poprawności rozwiązania.
BTW zobacz na przykład który podałem z zawieraniem prywatnej klasy
publicznie dziedziczącej inną. Przy takim podejściu możesz zarówno
realizować 1:N jak i fakt, że czasem masz metody wirtualne i chcesz
je przeładować. Oczywiście pomysł nie jest mój tylko Meyersa.
Racja, ten przykład dobry jest.
W sumie wersja z typem prywatnym jest bardzo dobrą prezentacją idei o
którą toczy się bój zaciekły, ale wiadomo, krótsza wersja (choć mniej
doskonała) szerzej się przyjmie.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Maciej Piechotka
2007-09-05 15:50:04 UTC
Permalink
Post by Mateusz Loskot
Post by SasQ
Siema.
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
class Car : private Engine
było: "WTF??! o_O"
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki? Piszą tam,
że "private inheritance is a syntactic variant of composition"
["Dziedziczenie prywatne jest składniową odmianą zawierania"].
Z jedną uwagą, jest to kompozycja, czyli przypadek agregacji z
wymagalnością *posiadania* agregowanego typu (założenie: samochód nie
może istnieć bez silnika).
class engine {
unsigned int m_hp;
public:
engine(unsigned int hp) : m_hp(hp) {}
unsigned int get_hp() {
return m_hp;
}
};

class car {
engine m_engine;
public:
car(unsigned int hp) : m_engine(hp) {}
engine *get_engine() {
return &m_engine;
}
};

Robi to samo a poprawnie twierdzi, że samochów ma silnik a nie jest
silnikiem.

Pozdrawiam
--
I've probably left my head... somewhere. Please wait untill I find it.
Homepage (pl_PL): http://uzytkownik.jogger.pl/
(GNU/)Linux User: #425935 (see http://counter.li.org/)
Mateusz Loskot
2007-09-06 00:57:43 UTC
Permalink
Post by Maciej Piechotka
Post by Mateusz Loskot
Post by SasQ
Siema.
http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.2
class Car : private Engine
było: "WTF??! o_O"
Czy tylko mi się zdaje, czy oni w tym FAQ opowiadają bajki? Piszą tam,
że "private inheritance is a syntactic variant of composition"
["Dziedziczenie prywatne jest składniową odmianą zawierania"].
Z jedną uwagą, jest to kompozycja, czyli przypadek agregacji z
wymagalnością *posiadania* agregowanego typu (założenie: samochód nie
może istnieć bez silnika).
class engine {
unsigned int m_hp;
engine(unsigned int hp) : m_hp(hp) {}
unsigned int get_hp() {
return m_hp;
}
};
class car {
engine m_engine;
car(unsigned int hp) : m_engine(hp) {}
engine *get_engine() {
return &m_engine;
}
};
Robi to samo a poprawnie twierdzi, że samochów ma silnik a nie jest
silnikiem.
Tak.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Maciej Sobczak
2007-09-05 07:50:20 UTC
Permalink
Post by SasQ
class Car : private Engine
Co o tym myślicie?
Oprócz tego co już zostało napisane, dodam drobiazg: prywatne
dziedziczenie w C++ ma dodatkowy sens związany z kolejnością
inicjalizacji składowych i można je wykorzystać do wymuszenia tej
kolejności pomiędzy daną składową a normalnymi klasami bazowymi.

--
Maciej Sobczak
http://www.msobczak.com/
aberazioon
2007-09-05 08:27:19 UTC
Permalink
Oprócz tego co już zostało napisane, dodam drobiazg: (...)
To ja jeszcze dodam też troche OT.

Sewerynie, Mateuszu, Pawle i Maćku! Złączcie siły w jakimś wspólnym
projekcie, z taką wiedzą informatyczną skondensowaną do teamu 4 osób
można zawojować niejedno!..

pzdr
mk
Seweryn Habdank-Wojewódzki
2007-09-05 11:08:53 UTC
Permalink
Witam
Post by aberazioon
Oprócz tego co już zostało napisane, dodam drobiazg: (...)
To ja jeszcze dodam też troche OT.
Sewerynie, Mateuszu, Pawle i Maćku! Złączcie siły w jakimś wspólnym
projekcie, z taką wiedzą informatyczną skondensowaną do teamu 4 osób
można zawojować niejedno!..
Dzięki za pozytywną ocenę :-).

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Maciej Sobczak
2007-09-05 21:11:00 UTC
Permalink
Post by aberazioon
Sewerynie, Mateuszu, Pawle i Maćku! Złączcie siły w jakimś wspólnym
projekcie, z taką wiedzą informatyczną skondensowaną do teamu 4 osób
można zawojować niejedno!..
Kiedyś na pewnym placu zabaw widziałem atrapę samochodu dla dzieci
(szkielet z rurek + siedzenia), w której były 4 kierownice - po jednej
kierownicy przy każdym siedzeniu. Można wojować, że hej!

Tak jakoś mi się skojarzyło... :-)

--
Maciej Sobczak
http://www.msobczak.com/
Seweryn Habdank-Wojewódzki
2007-09-05 21:21:20 UTC
Permalink
Witam
Post by Maciej Sobczak
Kiedyś na pewnym placu zabaw widziałem atrapę samochodu dla dzieci
(szkielet z rurek + siedzenia), w której były 4 kierownice - po jednej
kierownicy przy każdym siedzeniu. Można wojować, że hej!
Tak jakoś mi się skojarzyło... :-)
Szkoda, że dużo projektów informatycznych tak właśnie dziś wygląda -- każdy
dba o to, aby mieć kierownicę, a nikt nie chce robić jako silnik, czy koła.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Mateusz Loskot
2007-09-05 22:12:08 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Witam
Post by Maciej Sobczak
Kiedyś na pewnym placu zabaw widziałem atrapę samochodu dla dzieci
(szkielet z rurek + siedzenia), w której były 4 kierownice - po jednej
kierownicy przy każdym siedzeniu. Można wojować, że hej!
Tak jakoś mi się skojarzyło... :-)
Szkoda, że dużo projektów informatycznych tak właśnie dziś wygląda -- każdy
dba o to, aby mieć kierownicę, a nikt nie chce robić jako silnik, czy koła.
A czy znajdzie się wolny wakat na wlew do paliwa ,-) ?

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
SasQ
2007-09-05 23:58:34 UTC
Permalink
On Wed, 05 Sep 2007 00:22:18 +0200, SasQ wrote:

Znalazłem coś takiego:

http://www.gotw.ca/publications/mill06.htm

Wygląda na to, że Sutter jest ze mną ;)
Na wjazd zauważył on, że:

"Uses and Abuses of Inheritance, Part 1
Inheritance is often overused, even by experienced developers."

Pierwszy przykład, jaki podaje, to właśnie użycie prywatnego
dziedziczenia do osiągnięcia zawierania. Podał podobny przykład,
jak w C++ FAQ Lite, tylko zamiast Engine jest MyList, a zamiast
Car jest MySet1. Na tym przykładzie stwierdza on:

"2. More generally, what is the difference between nonpublic
inheritance and containment?
(...)
Question 2 gets us right down to business:
* Nonpublic inheritance should always express
IS-IMPLEMENTED-IN-TERMS-OF (with only one rare exception,
which I'll cover shortly). It makes the using class depend
upon the public and protected parts of the used class.
* Containment always expresses HAS-A and, therefore,
IS-IMPLEMENTED-IN-TERMS-OF. It makes the using class depend
upon only the public parts of the used class."

a więc nie ma on wątpliwości, że zależność posiadania, "HAS-A",
odnosi się tylko do zawierania [kompozycji]. Później próbuje on
odpowiedzieć na pytane, jakie najczęściej mogą być powody do tego,
by stosować prywatne dziedziczenie zamiast zawierania. Wymienia
wszystkie te rzeczy, które już tutaj przegadaliśmy ;J Oddaje mi
jednak rację w sprawie podstawialności Liskova ;J że nie jest
ona ograniczona wyłącznie do publicznego dziedziczenia:

"* We need 'controlled polymorphism' LSP IS-A, but in certain
code only. Public inheritance should always model IS-A as
per the Liskov Substitution Principle (LSP).[3] Nonpublic
inheritance can express a restricted form of IS-A, even though
most people identify IS-A with public inheritance alone.
Given class Derived : private Base, from the point of view of
outside code, a Derived object IS-NOT-A Base, and so of course
can't be used polymorphically as a Base because of the access
restrictions imposed by private inheritance. However, inside
Derived's own member functions and friends only, a Derived object
can indeed be used polymorphically as a Base (you can supply a
pointer or reference to a Derived object where a Base object is
expected), because members and friends have the necessary access.
If instead of private inheritance you use protected inheritance,
then the IS-A relationship is additionally visible to
further-derived classes, which means subclasses can also make
use of the polymorphism."

Następnie próbuje rozwikłać kwestię 3: która wersja kodu jest
bardziej odpowiednia - ta z prywatnym dziedziczeniem, czy z zawieraniem.
Najważniejsze jest IMO to stwierdzenie:

"* MySet IS-NOT-A MyList, not even within MySet's
member functions and friends."

Podobnie jak Samochód NIE JEST Silnikiem, ani dla świata zewnętrznego,
ani dla funkcji składowych i przyjaciół klasy Samochód :P Zdaniem
Suttera nawet jeśli ze względu na pozostałe możliwości chcielibyśmy
dla własnej wygody użyć dziedziczenia prywatnego, ten ostatni punkt
daje nam wyraźnie do zrozumienia, że jesteśmy w błędzie i próbujemy
jedynie "wyhaczyć" [nagiąć] właściwości dziedziczenia do naszych
niecnych celów, mimo że związek dziedziczenia nie zachodzi dla tych
dwóch klas ;P

"This last point is interesting, because it points out a (minor)
disadvantage of inheritance: Even had one of the other criteria
been true, so that we would use inheritance, we would have to be
careful that members and friends of MySet wouldn't accidentally
use a MySet polymorphically as a MyList -- a remote possibility,
maybe, but sufficiently subtle that if it did ever happen it
would probably keep the poor programmer who encountered it
confused for hours."

Ja też byłbym "confused" widząc prywatne dziedziczenie zamiast
zawierania ;J Czasem to, co uważamy za zaletę, może okazać się
wadą i zemścić w przyszłości:

"In short, MySet should not inherit from MyList. Using inheritance
where containment is just as effective only introduces gratuitous
coupling and needless dependencies, and that's never a good idea.
Unfortunately, in the real world I still see programmers -- even
experienced ones -- who implement relationships like MySet's using
inheritance."

:P
Przykład takiego kodu podaje pod sam koniec artykułu [przykład 2a].
Próbuje użyć prywatnego dziedziczenia po to, by móc nadpisać jedną z
funkcji klasy bazowej. Okazuje się, że to z pozoru "ułatwienie"
niesie ze sobą pewne konsekwencje - możemy odziedziczyć coś, czego
nie chcemy ;-J Rozwiązaniem tego problemu okazuje się po raz kolejny
użycie zawierania obiektu odziedziczonej klasy, zamiast bezpośregniego
prywatnego dziedziczenia [przykład 2b]. Takie rozwiązanie pozwala
ponadto zawrzeć więcej egzemplarzy obiektu takiej odziedziczonej
klasy, mimo to pozwalając korzystać z zalet dziedziczenia ;)
--
SasQ
Mateusz Loskot
2007-09-06 01:27:33 UTC
Permalink
On Wed, 05 Sep 2007 00:22:18 +0200, SasQ wrote: Pierwszy przykład,
jaki podaje, to właśnie użycie prywatnego dziedziczenia do
osiągnięcia zawierania. Podał podobny przykład, jak w C++ FAQ Lite,
tylko zamiast Engine jest MyList, a zamiast Car jest MySet1. Na tym
"2. More generally, what is the difference between nonpublic
inheritance and containment? (...) Question 2 gets us right down to
business: * Nonpublic inheritance should always express
IS-IMPLEMENTED-IN-TERMS-OF (with only one rare exception, which I'll
cover shortly). It makes the using class depend upon the public and
protected parts of the used class. * Containment always expresses
HAS-A and, therefore, IS-IMPLEMENTED-IN-TERMS-OF. It makes the using
class depend upon only the public parts of the used class."
a więc nie ma on wątpliwości, że zależność posiadania, "HAS-A",
odnosi się tylko do zawierania [kompozycji].
Moim zdaniem błędnie rozumiesz co Sutter pisze.

Te dwa terminy, HAS-A oraz IS-IMPLEMENTED-IN-TERMS-OF (IIITO), należą do
dwóch różnych światów, ale się nie wykluczają. I to jest to o czym
próbowałem Ci wyjaśnić sugerując że zbyt dosłownie czytasz kod.

Meyers w "Effective C++" wyjaśnia dokładnie, a tutaj jest sam miód tematu:

Bill Venners:
Please differentiate HAS-A and IS- IMPLEMENTED-IN-TERMS-OF.

Scott Meyers:
When you write software, you deal with two worlds. You deal with the
world you want to model, the outside world. You also deal with a world
that exists only inside the software, which involves just getting the
code to work. HAS-A corresponds to something in the real world. A Car
HAS Wheels or a Person HAS Friends. HAS-A corresponds to the application
domain. IS-IMPLEMENTED-IN-TERMS-OF never exists in the real world; it is
part of the implementation domain. So you couldn't say a Car
IS-IMPLEMENTED-IN-TERMS-OF Wheels. A Car HAS Wheels. But you could say
the ParkingLot IS-IMPLEMENTED-IN-TERMS-OF a List. There's no List in the
real world. The List only exists inside the software. So HAS-A is a
relationship between classes that exists in the application domain.
IS-IMPLEMENTED-IN-TERMS-OF is a relationship between classes that exists
in the implementation domain.


Mam nadzieję, że podzielasz opinię Venner'sa na udzielenie odpowiedzi
przez Meyers'a :-)

Nie bardzo widzę sens kontynuacji debaty, bo wsio powinno być jasne.

Pozdrawiam
--
Mateusz Loskot
http://mateusz.loskot.net
Seweryn Habdank-Wojewódzki
2007-09-06 11:35:09 UTC
Permalink
Witam
Post by Mateusz Loskot
Nie bardzo widzę sens kontynuacji debaty, bo wsio powinno być jasne.
Pozwolę sobie dolać oliwy do ognia.

Meyers podaje jeszcze jeden przykład, który jest dość abstrakcyjny, ale np.
w C++ pozwala na pewne sztuczki programowania.

Poniżej jest nieco zmodyfikowany kod Meyersa:

#include <iostream>

class Empty {}; // has no data, so objects should
// use no memory

struct Int { int x; };
struct Foo { void foo() {} };
struct VFoo { virtual void foo() {} };

template <typename T>
class HoldsAnInt_1 // should need only space for an int
{
private:
int x;
T e; // should require no memory
};

template <typename T>
class HoldsAnInt_2: private T
{
private:
int x;
};

int main()
{
using ::std::cout;

cout << sizeof ( HoldsAnInt_1<Empty>) << '\n';
cout << sizeof ( HoldsAnInt_2<Empty>) << '\n';
cout << sizeof ( HoldsAnInt_1<Int>) << '\n';
cout << sizeof ( HoldsAnInt_2<Int>) << '\n';
cout << sizeof ( HoldsAnInt_1<Foo>) << '\n';
cout << sizeof ( HoldsAnInt_2<Foo>) << '\n';
cout << sizeof ( HoldsAnInt_1<VFoo>) << '\n';
cout << sizeof ( HoldsAnInt_2<VFoo>) << '\n';
}


I teraz pytanie. Jaki jest w każdym przypadku sizeof()?

Jesli komuś zalezy na tym, aby rozmiar klasy był mały, to zawieranie może
nie być dobre.

Jeśli ktoś chce mieć odpowiedzi na pewne pytania nt. kształtu klasy to może
dostać porównując stosowne rozmiary.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Seweryn Habdank-Wojewódzki
2007-09-06 11:57:19 UTC
Permalink
Witam
Post by Seweryn Habdank-Wojewódzki
I teraz pytanie. Jaki jest w każdym przypadku sizeof()?
Jesli komuś zalezy na tym, aby rozmiar klasy był mały, to zawieranie może
nie być dobre.
Jeśli ktoś chce mieć odpowiedzi na pewne pytania nt. kształtu klasy to
może dostać porównując stosowne rozmiary.
Zapomniałem dodać, że jest to kod pokazujący, że nie każde zawieranie czy
dziedziczenie po stronie języków programowania ma na celu stworzyć
agregację w nomenklaturze OOP.

Sutter też to podkreśla mówiąc, że to są różne światy i że nie
należy "automatycznie na pałę" wstawiać pewnych równoważności oraz nie
należy z góry pewnych podejść negować, bo może okazać się, że podejście
jedyne słuszne jest do bani w jakimś konkretnym przypadku.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Loading...