2011-10-27 1 views
9

Stavo leggendo la documentazione di std::sub_match<BidirectionalIterator> e ho visto che eredita pubblicamente da std::pair<BidirectionalIterator, BidirectionalIterator>. Poiché uno sub_match è semplicemente una coppia di iteratori in una sequenza di caratteri, con alcune funzioni aggiuntive, posso capire che è implementato con uno pair, ma perché utilizzare l'ereditarietà pubblica?Perché lo std :: sub_match <T> eredita pubblicamente da std :: pair <T, T>?

Il problema con l'ereditare pubblicamente da std::pair<T,U> equivale a ereditare pubblicamente dalla maggior parte delle altre classi standard: non si intende che vengano manipolate in modo polimorfico (in particolare non definiscono un distruttore virtuale). Anche gli altri membri non funzioneranno correttamente, ovvero l'operatore di assegnazione e la funzione membro di scambio (non copieranno il membro matched di sub_match).

Perché ha Boost sviluppatori e quindi la commissione ha deciso di implementare sub_match ereditando pubblicamente da pair invece di usare la composizione (o ereditarietà privata con l'utilizzo di dichiarazioni se volevano mantenere l'accesso utente tramite first e second)?

risposta

5

È una domanda interessante. Presumibilmente, lo hanno considerato sicuro perché nessuno lo allocerebbe mai in modo dinamico. Circa l' unico modo che si vuole ottenere sub_match oggetti è come valore di ritorno da alcune delle funzioni di basic_regex, o come copie di altri sub_match, e tutti questi sarà o provvisori o variabili locali.

Nota che non è sicuro per mantenere sub_match oggetti intorno in ogni caso, dal momento che essi contengono iteratori la cui vita ... non sembra essere specificato nel standard. Fino a quando l'oggetto match_results viene riutilizzato? Fino a quando l'operando string alla funzione che ha riempito l'oggetto match_results è stato distrutto? O?

Avrei comunque evitato l'eredità pubblica. Ma in questo caso, è non così pericoloso come sembra, perché non c'è davvero alcun motivo per cui tu voglia allocare dinamicamente uno sub_match.

+0

Concordo sull'allocazione dinamica che probabilmente non dovrebbe mai accadere. Tuttavia, i problemi potrebbero ancora apparire con '=' e 'swap'. Ad esempio, stavo pensando a Boost.Range, ma non richiede che gli intervalli siano assegnabili o scambiabili. Tuttavia, è interessante notare che gli algoritmi Boost.Range non accettano 'sub_match'es come argomenti, ma lo fanno se vengono manipolati attraverso un riferimento a una' coppia' (problemi delle classi di tratto). –

0

Perché non hanno bisogno di un distruttore virtuale? ;-)

+0

I distruttori non sono gli unici membri che non funzioneranno correttamente: l'operatore di assegnazione e lo scambio copieranno solo i membri 'pair' e non quelli contenuti da' sub_match' (il valore booleano 'matched' in particolare). –

+0

Gli autori sentivano chiaramente che non avevano bisogno di un distruttore virtuale, o che ne avrebbero fornito uno. Ma perché, poiché nella maggior parte dei casi, l'ereditarietà pubblica implica la necessità di un distruttore virtuale nella classe base. –

+0

@LucTouraille Buon punto. Abbastanza chiaramente, non c'è intenzione che 'sub_match' siaA' pair'. –

0

Se std::sub_match<BidirectionalIterator> non ha uno stato a sé stante, allora è corretto che erediti da std::pair. Non farlo a casa però.

+0

Lo stato non ha niente a che fare con questo. Se elimini un oggetto 'sub_match' attraverso un puntatore' pair <> ', è un comportamento indefinito. –

+1

Inoltre, ha effettivamente uno stato a parte (un booleano che indica se la partita ha avuto successo). –

3

Ecco ciò che l'autore regex s' ha da dire in proposito: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1429.htm#matches_discussion

Nulla di molto specifico alla tua domanda, ho paura.

Direi che questa decisione è stata un compromesso tra la reinvenzione della ruota e l'introduzione di un piccolo rischio di uso improprio. Si noti che in generale non è necessario costruire un sub_match, vengono restituiti dalla funzione regex. Inoltre coppie di iteratori sono un modo molto pratico di implementare le gamme.

3

Perché C++ non ha modo di ereditare un'interfaccia senza ereditarietà pubblica. È possibile ereditare un'implementazione con ereditarietà privata, ma tutto è privato.Se si desidera la stessa interfaccia come std::pair, è necessario essere a std::pair.

Inoltre, considera questo. Questo è ovviamente un comportamento indefinito:

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

Ma lo è anche questo:

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

Perché è il primo molto di più di una preoccupazione rispetto al secondo?

sub_match e pair sono oggetti leggeri (a seconda del loro contenuto, ovviamente). Sono pensati per essere copiati o passati per riferimento, tutti al 100% sicuri. C'è poco o nessun motivo per allocarli sullo heap e usarli tramite i puntatori. Quindi, mentre comprendo la tua preoccupazione, penso che sia improbabile che accada in un codice reale.

+0

In effetti, il tuo secondo esempio è (quasi) difettoso come il primo, ma questo perché hai considerato che il membro 'pair' sarebbe pubblico: perché dovresti assumere un incapsulamento così cattivo? E mentre sono d'accordo sul fatto che la cancellazione probabilmente non sarà mai un problema, non sono d'accordo sul fatto che passare per riferimento sia sicuro al 100%, a causa delle altre funzioni dei membri non virtuali. –

+0

È possibile * ereditare (parti) un'interfaccia pubblica utilizzando l'ereditarietà privata: 'class sub_match: pair {using pair.primo; utilizzando pair.second; bool abbinato; } ' – JohannesD

+0

Se si desidera la stessa interfaccia di' std :: pair' senza esserne uno, è possibile utilizzare l'ereditarietà privata insieme all'utilizzo di dichiarazioni e/o delega. Naturalmente, questo è molto più lavoro per lo sviluppatore della sottoclasse. –