2011-10-27 16 views
9

Czytałem dokumentację std::sub_match<BidirectionalIterator> i widziałem, że publicznie dziedziczy po std::pair<BidirectionalIterator, BidirectionalIterator>. Ponieważ sub_match jest po prostu parą iteratorów w sekwencji znaków, z kilkoma dodatkowymi funkcjami, mogę zrozumieć, że jest on implementowany z pair, ale po co używać publicznego dziedziczenia?Dlaczego std :: sub_match <T> publicznie dziedziczy ze std :: pair <T, T>?

Problem z dziedziczeniem publicznie z std::pair<T,U> jest taki sam, jak dziedziczenie publicznie z większości innych klas standardowych: nie mają one na celu zmanipulowania polimorficznego (w szczególności nie definiują wirtualnego destruktora). Inni członkowie również nie będą działać poprawnie, a mianowicie operator przypisania i funkcja członka zamiany (nie będą kopiować członka matched z sub_match).

Dlaczego Zwiększ deweloperów, a następnie komisja zdecydowała się na wdrożenie sub_match przez dziedziczenie publicznie od pair zamiast stosowania kompozycji (lub prywatnym dziedziczenie z użyciem deklaracji, jeśli chcieli zachować dostęp użytkownika poprzez first i second)?

Odpowiedz

5

To interesujące pytanie. Prawdopodobnie uważali, że jest bezpieczna, ponieważ nikt i tak nie mógł go dynamicznie przydzielić. O jedynym sposobem masz zamiar dostać sub_match obiektów jest jako wartość zwracana z niektórych funkcji basic_regex lub jako kopie innych sub_match, a wszystkie z nich będzie albo temporaries lub lokalnych zmienne.

Należy pamiętać, że to nie jest bezpieczne utrzymanie sub_match obiektów wokół tak, ponieważ zawierają one iteratory których żywotność ... nie wydaje się być określona w średnia. Do czasu ponownego użycia obiektu match_results? Dopóki operand do funkcji, która wypełniła obiekt, nie zostanie zniszczony? Lub?

Wciąż unikałbym publicznego dziedziczenia. Ale w tym przypadku jest to nie tak niebezpieczne, jak się wydaje, ponieważ nie ma powodu, dla którego miałbyś chcieć dynamicznie przydzielić sub_match.

+0

Zgadzam się na przydzielanie dynamiczne, które prawdopodobnie nigdy nie powinno się zdarzyć. Jednak problemy mogą nadal występować z '=' i 'swap'. Myślałem o Boost.Range na przykład, ale nie wymaga zakresów do przypisania lub zamiany. Warto jednak zauważyć, że algorytmy Boost.Range nie akceptują argumentów "sub_match'es", ale robią to, jeśli są manipulowane przez odniesienie do 'pair' (problemów z klasami cech). –

0

Ponieważ nie potrzebowały wirtualnego destruktora? ;-)

+0

Destruktory nie są jedynymi członkami, które nie będą działać poprawnie: operator przypisania i swap będą kopiować tylko elementy 'pair', a nie te zawarte w' sub_match' (w szczególności 'dopasowane 'boolean). –

+0

Autorzy najwyraźniej uważali, że nie potrzebowali wirtualnego destruktora, inaczej go dostarczyli. Ale dlaczego, ponieważ w większości przypadków dziedziczenie publiczne implikuje potrzebę wirtualnego destruktora w klasie bazowej. –

+0

@LucTouraille Dobra uwaga. Całkiem wyraźnie nie ma zamiaru, aby 'sub_match' był parą'A. –

0

Jeśli std::sub_match<BidirectionalIterator> nie ma własnego stanu, to może się odziedziczyć po std::pair. Nie rób tego jednak w domu.

+0

Stan nie ma z tym nic wspólnego. Jeśli usuniesz obiekt 'sub_match' za pomocą wskaźnika' pair <> ', jest to niezdefiniowane zachowanie. –

+1

Co więcej, rzeczywiście ma swój własny stan (wartość boolowska wskazująca, czy mecz się powiódł). –

3

Oto co autor regex „s ma do powiedzenia na ten temat: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1429.htm#matches_discussion

Nic bardzo specyficzne pytania, obawiam się.

Przypuszczam, że ta decyzja była kompromisem między wymyślaniem koła a wprowadzaniem małego ryzyka niewłaściwego użycia. Zauważ, że generalnie nie ma potrzeby konstruowania sub_match, są one zwracane z funkcji regex. Ponadto pary iteratorów są bardzo praktycznym sposobem wprowadzania zakresów.

3

Ponieważ C++ nie ma sposobu na dziedziczenie interfejsu bez publicznego dziedziczenia. Możesz dziedziczyć implementację z prywatnym dziedziczeniem, ale wtedy wszystko jest prywatne.Jeśli chcesz mieć ten sam interfejs co std::pair, musisz być a std::pair.

Rozważ to również. Jest to oczywiście niezdefiniowane zachowanie:

std::sub_match<BidirectionalIterator> theMatch = ...; 
std::pair<BidirectionalIterator> *pMatch = &theMatch; 
delete pMatch; 

Ale tak to jest:

std::sub_match<BidirectionalIterator> theMatch = ...; 
std::pair<BidirectionalIterator> *pMatch = &theMatch.pair; 
delete pMatch; 

Dlaczego jest pierwszym tak dużo więcej troski niż sekundę?

sub_match i pair są obiektami lekkimi (w zależności od ich zawartości oczywiście). Powinny być kopiowane lub przekazywane przez odniesienie, z których wszystkie są w 100% bezpieczne. Nie ma najmniejszego powodu, aby przydzielić je do sterty i używać ich za pomocą wskaźników. Więc chociaż rozumiem twoją troskę, myślę, że to mało prawdopodobne, aby stało się to w jakimkolwiek prawdziwym kodzie.

+0

Rzeczywiście, twój drugi przykład jest (prawie) błędny jak pierwszy, ale to dlatego, że uważałeś, że członek "pary" byłby publiczny: dlaczego założyłbyś, że takie złe enkapsulowanie zostanie użyte? I chociaż zgadzam się, że usunięcie prawdopodobnie nigdy nie byłoby problemem, nie zgadzam się, że przekazywanie przez odniesienie jest w 100% bezpieczne, z powodu innych nie-wirtualnych funkcji członkowskich. –

+0

Użytkownik * może * dziedziczyć (części) publicznego interfejsu przy użyciu prywatnego dziedziczenia: 'class sub_match: pair {using pair.pierwszy; za pomocą pair.second; bool dopasowany; } ' – JohannesD

+0

Jeśli chcesz mieć ten sam interfejs co' std :: pair' bez tego, możesz używać prywatnego dziedziczenia wraz z deklaracjami i/lub delegowaniem. Oczywiście jest to znacznie więcej pracy dla twórcy podklasy. –

Powiązane problemy