c++ - new - No heredaras de std:: vector
vector of vectors c++ (11)
¿Qué esperas lograr? ¿Solo brindando alguna funcionalidad?
La forma idiomática de C ++ para hacer esto es simplemente escribir algunas funciones gratuitas que implementan la funcionalidad. Lo más probable es que realmente no necesite un std :: vector, específicamente para la funcionalidad que está implementando, lo que significa que en realidad está perdiendo reusabilidad al tratar de heredar de std :: vector.
Le recomendaría encarecidamente que observe la biblioteca estándar y los encabezados, y medite sobre cómo funcionan.
Ok, esto es realmente difícil de confesar, pero tengo una gran tentación en este momento de heredar de std::vector
.
Necesito unos 10 algoritmos personalizados para vector y quiero que sean miembros directamente del vector. Pero, naturalmente, también quiero tener el resto de la interfaz std::vector
. Bueno, mi primera idea, como un ciudadano respetuoso de la ley, era tener un miembro de std::vector
en la clase MyVector
. Pero luego tendría que reprogramar manualmente toda la interfaz de std :: vector. Demasiado para escribir A continuación, pensé en la herencia privada, de modo que en lugar de volver a proporcionar métodos escribiría un montón sobre el using std::vector::member
en la sección pública. Esto es tedioso también en realidad.
Y aquí estoy, realmente creo que simplemente puedo heredar públicamente de std::vector
, pero advierto en la documentación que esta clase no debe usarse de forma polimórfica. Creo que la mayoría de los desarrolladores son lo suficientemente competentes como para entender que esto no debería usarse polimórficamente de todos modos.
¿Mi decisión es absolutamente injustificable? Si es así, ¿por qué? ¿Puede proporcionar una alternativa que tendría los miembros adicionales en realidad miembros pero no implicaría volver a escribir toda la interfaz del vector? Lo dudo, pero si puedes, seré feliz.
Además, aparte del hecho de que un idiota puede escribir algo así como
std::vector<int>* p = new MyVector
¿Hay algún otro peligro real en el uso de MyVector? Al decir realista, descarto cosas como imaginar una función que toma un puntero al vector ...
Bien, he declarado mi caso. He pecado. Ahora depende de ti que me perdones o no :)
Creo que se deben seguir muy pocas reglas ciegamente el 100% del tiempo. Parece que lo has pensado mucho y estás convencido de que este es el camino a seguir. Entonces, a menos que alguien presente buenas razones específicas para no hacerlo, creo que debe seguir adelante con su plan.
En realidad, no hay nada de malo con la herencia pública de std::vector
. Si necesita esto, solo haga eso.
Sugeriría hacer eso solo si es realmente necesario. Solo si no puede hacer lo que desea con funciones gratuitas (por ejemplo, debe mantener cierto estado).
El problema es que MyVector
es una nueva entidad. Significa que un nuevo desarrollador de C ++ debe saber qué diablos es antes de usarlo. ¿Cuál es la diferencia entre std::vector
y MyVector
? ¿Cuál es mejor usar aquí y allá? ¿Qué pasa si necesito mover std::vector
a MyVector
? ¿Puedo usar swap()
o no?
No produzca nuevas entidades solo para hacer que algo se vea mejor. Estas entidades (especialmente, tales comunes) no van a vivir en el vacío. Vivirán en un ambiente mixto con una entropía constantemente creciente.
En términos prácticos: si no tiene ningún miembro de datos en su clase derivada, no tiene ningún problema, ni siquiera en el uso polimórfico. Solo necesita un destructor virtual si los tamaños de la clase base y la clase derivada son diferentes y / o tiene funciones virtuales (lo que significa una tabla v).
PERO en teoría: desde [expr.delete] en C ++ 0x FCD: en la primera alternativa (eliminar objeto), si el tipo estático del objeto a eliminar es diferente de su tipo dinámico, el tipo estático será un clase base del tipo dinámico del objeto a eliminar y el tipo estático tendrá un destructor virtual o el comportamiento no está definido.
Pero puede derivar privadamente de std :: vector sin problemas. He usado el siguiente patrón:
class PointVector : private std::vector<PointType>
{
typedef std::vector<PointType> Vector;
...
using Vector::at;
using Vector::clear;
using Vector::iterator;
using Vector::const_iterator;
using Vector::begin;
using Vector::end;
using Vector::cbegin;
using Vector::cend;
using Vector::crbegin;
using Vector::crend;
using Vector::empty;
using Vector::size;
using Vector::reserve;
using Vector::operator[];
using Vector::assign;
using Vector::insert;
using Vector::erase;
using Vector::front;
using Vector::back;
using Vector::push_back;
using Vector::pop_back;
using Vector::resize;
...
La razón principal para no heredar de std :: vector públicamente es la ausencia de un destructor virtual que efectivamente evita el uso polimórfico de descendientes. En particular, no tiene permitido delete
un std::vector<T>*
que apunta a un objeto derivado (incluso si la clase derivada no agrega ningún miembro), pero el compilador en general no puede advertirle al respecto.
La herencia privada está permitida bajo estas condiciones. Por lo tanto, recomiendo usar la herencia privada y reenviar los métodos requeridos del padre como se muestra a continuación.
class AdVector: private std::vector<double>
{
typedef double T;
typedef std::vector<double> vector;
public:
using vector::push_back;
using vector::operator[];
using vector::begin;
using vector::end;
AdVector operator*(const AdVector & ) const;
AdVector operator+(const AdVector & ) const;
AdVector();
virtual ~AdVector();
};
En primer lugar, debe considerar refactorizar sus algoritmos para abstraerlos del tipo de contenedor en el que están operando y dejarlos como funciones de plantillas libres como lo señalan la mayoría de los respondedores. Esto generalmente se hace haciendo que un algoritmo acepte un par de iteradores en lugar de contenedores como argumentos.
No hay ninguna razón para heredar de std::vector
menos que se quiera hacer una clase que funcione de manera diferente que std::vector
, porque maneja a su manera los detalles ocultos de la definición de std::vector
, o a menos que uno tenga razones ideológicas para usar los objetos de dicha clase en lugar de los de std::vector
. Sin embargo, los creadores del estándar en C ++ no proporcionaron std::vector
con ninguna interfaz (en forma de miembros protegidos) que dicha clase heredada pudiera aprovechar para mejorar el vector de una manera específica. De hecho, no tenían manera de pensar en ningún aspecto específico que pudiera necesitar extensión o ajuste de implementación adicional, por lo que no tenían que pensar en proporcionar dicha interfaz para ningún propósito.
Las razones para la segunda opción pueden ser solo ideológicas, porque std::vector
s no son polimórficas, y de lo contrario no hay diferencia si expone la interfaz pública de std::vector
través de herencia pública o membresía pública. (Supongamos que necesita mantener algún estado en su objeto para que no pueda salirse con las funciones gratuitas). En una nota menos sonora y desde el punto de vista ideológico, parece que std::vector
s es una especie de "idea simple", por lo que cualquier complejidad en forma de objetos de diferentes clases posibles en su lugar no tiene ningún valor ideológico.
Sí, es seguro siempre que tenga cuidado de no hacer cosas que no son seguras ... No creo que alguna vez haya visto a alguien usar un vector con nuevo, así que en la práctica probablemente todo salga bien. Sin embargo, no es la expresión común en c ++ ...
¿Puedes dar más información sobre qué son los algoritmos?
A veces terminas yendo por un camino con un diseño y luego no puedes ver los otros caminos que podrías haber tomado; el hecho de que reclames que necesitas vector con 10 algoritmos nuevos me suena la alarma, ¿hay realmente 10 propósitos generales? Algoritmos que un vector puede implementar, o ¿está tratando de hacer un objeto que sea un vector de propósito general Y que contenga funciones específicas de la aplicación?
Ciertamente no estoy diciendo que no deberías hacer esto, es solo que con la información que has dado, suenan las alarmas que me hacen pensar que tal vez algo esté mal con tus abstracciones y que hay una manera mejor de lograr lo que querer.
Si está considerando esto, ya ha matado a los pedantes de idiomas en su oficina. Con ellos fuera del camino, ¿por qué no simplemente hacer
struct MyVector
{
std::vector<Thingy> v; // public!
void func1( ... ) ; // and so on
}
Eso esquivará todos los posibles errores que podrían surgir al subir accidentalmente su clase MyVector, y aún puede acceder a todos los vectores con solo agregar un poco de .v
.
Si sigue un buen estilo de C ++, la ausencia de una función virtual no es el problema, sino el corte (consulte https://.com/a/14461532/877329 ).
¿Por qué la ausencia de funciones virtuales no es el problema? Porque una función no debe intentar delete
ningún puntero que reciba, ya que no tiene una propiedad de él. Por lo tanto, si se siguen políticas de propiedad estrictas, los destructores virtuales no deberían ser necesarios. Por ejemplo, esto siempre es incorrecto (con o sin destructor virtual):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
delete obj;
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj); //Will crash here. But caller does not know that
// ...
}
Por el contrario, esto siempre funcionará (con o sin destructor virtual):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj);
// The correct destructor *will* be called here.
}
Si el objeto es creado por una fábrica, la fábrica también debe devolver un puntero a un eliminador activo, que se debe usar en lugar de delete
, ya que la fábrica puede usar su propio montón. La persona que llama puede obtenerlo en forma de share_ptr
o unique_ptr
. En resumen, no delete
nada que no haya recibido directamente de uno new
.
También heredé de std::vector
recientemente, y encontré que es muy útil y hasta ahora no he tenido ningún problema.
Mi clase es una clase de matriz dispersa, lo que significa que necesito almacenar mis elementos de matriz en algún lugar, es decir, en un std::vector
. Mi razón para heredar era que era un poco perezoso para escribir interfaces para todos los métodos y también estoy interconectando la clase con Python a través de SWIG, donde ya hay un buen código de interfaz para std::vector
. Me resultó mucho más fácil extender este código de interfaz a mi clase en lugar de escribir uno nuevo desde cero.
El único problema que puedo ver con el enfoque no es tanto con el destructor no virtual, sino más bien con algunos otros métodos, que me gustaría sobrecargar, como push_back()
, push_back()
resize()
, insert()
etc. Private inheritance podría ser una buena opción.
¡Gracias!
Todo el STL fue diseñado de tal manera que los algoritmos y los contenedores están separados .
Esto condujo a un concepto de diferentes tipos de iteradores: const iterators, iterators de acceso aleatorio, etc.
Por lo tanto, le recomiendo que acepte esta convención y diseñe sus algoritmos de tal manera que no se preocupen por cuál es el contenedor en el que están trabajando , y solo requerirían un tipo específico de iterador que necesitarían para realizar su operaciones.
Además, déjame redirigirte a algunos buenos comentarios de Jeff Attwood .