2011-10-27 17 views
9

Estaba leyendo la documentación de std::sub_match<BidirectionalIterator> y vi que hereda públicamente desde std::pair<BidirectionalIterator, BidirectionalIterator>. Dado que sub_match es simplemente un par de iteradores en una secuencia de caracteres, con algunas funciones adicionales, puedo entender que está implementado con un pair, pero ¿por qué usar herencia pública?¿Por qué std :: sub_match <T> hereda públicamente de std :: pair <T, T>?

El problema con heredar públicamente desde std::pair<T,U> es lo mismo que heredar públicamente de la mayoría de las otras clases estándar: no están destinados a ser manipulados polimórficamente (en particular, no definen un destructor virtual). Otros miembros tampoco funcionarán correctamente, es decir, el operador de asignación y la función de miembro de intercambio (no copiarán el miembro matched de sub_match).

¿Por qué impulsar a los desarrolladores y luego el comité decidió implementar sub_match heredando públicamente desde pair en lugar de utilizar la composición (o herencia privada con el uso de declaraciones si querían mantener a sus miembros acceso a través first y second)?

Respuesta

5

Es una pregunta interesante. Presumiblemente, lo consideraron seguro porque nadie asignaría dinámicamente uno de todos modos. Acerca de la única manera que va a conseguir es sub_match objetos como valor de retorno de algunas de las funciones de basic_regex, o como copias de sí sub_match, y todos ellos serán o temporales o variables locales.

Tenga en cuenta que no es seguro para guardar objetos sub_match alrededor de todos modos, ya que contienen iteradores cuya vida útil ... no parece que se especificarán en la norma. Hasta que se vuelva a utilizar el objeto match_results? Hasta el string operando a la función que rellena el objeto match_results se destruye? ¿O?

Todavía habría evitado la herencia pública. Pero en este caso, es no tan peligroso como parece, porque realmente no hay ninguna razón por la que quiera asignar dinámicamente un sub_match.

+0

Estoy de acuerdo con la asignación dinámica que probablemente nunca debería suceder. Sin embargo, los problemas podrían aparecer con '=' y 'swap'. Estaba pensando en Boost.Range, por ejemplo, pero no requiere que los rangos sean Asignables o Swappable. Sin embargo, es interesante observar que los algoritmos de Boost.Range no aceptan 'sub_match'es como argumentos, pero lo hacen si se manipulan a través de una referencia a un' par' (problemas de clases de rasgos). –

0

¿Porque no necesitaban el destructor virtual? ;-)

+0

Los destructores no son los únicos miembros que no funcionarán correctamente: el operador de asignación y el intercambio copiarán solo los miembros 'pair' y no los contenidos por' sub_match' (el booleano 'emparejado' notablemente). –

+0

Los autores obviamente sintieron que no necesitaban un destructor virtual, o lo habrían proporcionado. Pero por qué, dado que en la mayoría de los casos, la herencia pública implica la necesidad de un destructor virtual en la clase base. –

+0

@LucTouraille Buen punto. Muy claramente, no hay intención de que 'sub_match' sea un' par'. –

0

Si std::sub_match<BidirectionalIterator> no tiene un estado propio, entonces está bien que herede de std::pair. No lo hagas en casa sin embargo.

+0

El estado no tiene nada que ver con eso. Si elimina un objeto 'sub_match' mediante un puntero' pair <> ', se trata de un comportamiento no definido. –

+1

Además, de hecho tiene un estado propio (un booleano que indica si la coincidencia fue exitosa). –

3

Esto es lo que el autor regex 's tiene que decir al respecto: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1429.htm#matches_discussion

Nada muy específica a su pregunta, me temo.

Supongo que esta decisión fue una compensación entre reinventar la rueda e introducir un pequeño riesgo de uso indebido. Tenga en cuenta que, en general, no es necesario construir un sub_match, se devuelven desde la función regex. Además, los pares de iteradores son una forma muy práctica de implementar rangos.

3

Porque C++ no tiene forma de heredar una interfaz sin herencia pública. Puede heredar una implementación con herencia privada, pero todo es privado.Si desea la misma interfaz que std::pair, tiene que ser a std::pair.

Además, considere esto. Esto es obviamente un comportamiento indefinido:

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

Pero también lo es la siguiente:

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

¿Por qué el primer tanto más preocupante que el segundo?

sub_match y pair son objetos livianos (según su contenido, por supuesto). Están destinados a ser copiados o aprobados por referencia, todo lo cual es 100% seguro. Hay poco o ningún motivo para asignarlos en el montón y usarlos a través de punteros. Entonces, aunque entiendo su preocupación, creo que es poco probable que suceda en un código real.

+0

De hecho, su segundo ejemplo es (casi) tan defectuoso como el primero, pero esto se debe a que considera que el miembro 'pair' sería público: ¿por qué asumiría que se usaría una encapsulación tan mala? Y aunque estoy de acuerdo en que la eliminación probablemente nunca sea un problema, no estoy de acuerdo en que la aprobación por referencia sea 100% segura, debido a las otras funciones de miembros no virtuales. –

+0

Usted * puede * heredar (partes) de una interfaz pública usando herencia privada: 'clase sub_match: pair {using pair.primero; usando pair.second; bool emparejado; } ' – JohannesD

+0

Si quiere la misma interfaz que' std :: pair' sin serlo, puede usar la herencia privada junto con el uso de declaraciones y/o delegación. Por supuesto, esto es mucho más trabajo para el desarrollador de la subclase. –

Cuestiones relacionadas