resueltos programacion orientada objetos metodos ejercicios ejemplos codigo clases atributos c++ virtual-functions

programacion - codigo de clases en c++



¿Simulando un miembro estático virtual de una clase en c++? (5)

@Hershi: el problema con ese enfoque es que cada instancia de cada clase derivada tiene una copia de los datos, lo que puede ser costoso de alguna manera.

Tal vez podrías probar algo como esto (estoy gritando sin un ejemplo de compilación, pero la idea debería ser clara).

#include <iostream> #include <string> using namespace std; struct DerivedData { DerivedData(const string & word, const int number) : my_word(word), my_number(number) {} const string my_word; const int my_number; }; class Base { public: Base() : m_data(0) {} string getWord() const { return m_data->my_word; } int getNumber() const { return m_data->my_number; } protected: DerivedData * m_data; }; class Derived : public Base { public: Derived() : Base() { if(Derived::s_data == 0) { Derived::s_data = new DerivedData("abc", 1); } m_data = s_data; } private: static DerivedData * s_data; }; DerivedData * Derived::s_data = 0; int main() { Base * p_b = new Derived(); cout getWord() << endl; }

En cuanto a la pregunta de seguimiento sobre la eliminación del objeto estático: la única solución que se me ocurre es usar un puntero inteligente, algo así como el puntero compartido de Boost .

¿Hay alguna forma de tener una especie de miembro estático virtual en C ++?

Por ejemplo:

class BaseClass { public: BaseClass(const string& name) : _name(name) {} string GetName() const { return _name; } virtual void UseClass() = 0; private: const string _name; }; class DerivedClass : public BaseClass { public: DerivedClass() : BaseClass("DerivedClass") {} virtual void UseClass() { /* do something */ } };

Sé que este ejemplo es trivial, pero si tengo un vector de datos complejos que va a ser siempre el mismo para todas las clases derivadas, pero se necesita acceder desde los métodos de la clase base.

class BaseClass { public: BaseClass() {} virtual string GetName() const = 0; virtual void UseClass() = 0; }; class DerivedClass : public BaseClass { public: DerivedClass() {} virtual string GetName() const { return _name; } virtual void UseClass() { /* do something */ } private: static const string _name; }; string DerivedClass::_name = "DerivedClass";

Esta solución no me satisface porque necesito volver a implementar el miembro _name y su acceso GetName () en cada clase. En mi caso, tengo varios miembros que siguen el comportamiento _name y décimas de clases derivadas.

¿Alguna idea?


Aquí hay una solución:

struct BaseData { const string my_word; const int my_number; }; class Base { public: Base(const BaseData* apBaseData) { mpBaseData = apBaseData; } const string getMyWord() { return mpBaseData->my_word; } int getMyNumber() { return mpBaseData->my_number; } private: const BaseData* mpBaseData; }; class Derived : public Base { public: Derived() : Base(&sBaseData) { } private: static BaseData sBaseData; } BaseData Derived::BaseData = { "Foo", 42 };


Estoy de acuerdo con la sugerencia de Hershi de usar una plantilla como la "clase base". Según lo que describes, suena más como un uso para plantillas en lugar de subclases.

Puede crear una plantilla de la siguiente manera (no ha intentado compilar esto):

template <typename T> class Object { public: Object( const T& newObject ) : yourObject(newObject) {} ; T GetObject() const { return yourObject } ; void SetObject( const T& newObject ) { yourObject = newObject } ; protected: const T yourObject ; } ; class SomeClassOne { public: SomeClassOne( const std::vector& someData ) { yourData.SetObject( someData ) ; } private: Object<std::vector<int>> yourData ; } ;

Esto le permitirá usar los métodos de clase de plantilla para modificar los datos según sea necesario desde dentro de sus clases personalizadas que usan los datos y comparten los diversos aspectos de la clase de plantilla.

Si tiene la intención de usar la herencia, puede que tenga que recurrir a las "alegrías" de usar un puntero void * en su BaseClass y tratar con el casting, etc.

Sin embargo, según su explicación, parece que necesita plantillas y no herencia.


Parece que la respuesta está en la pregunta: el método que sugeriste parece ser la dirección correcta, excepto que si tienes una gran cantidad de miembros compartidos, es posible que desees agruparlos en una estructura o clase y superar eso como el argumento para el constructor de la clase base.

Si insistes en que los miembros "compartidos" se implementen como miembros estáticos de la clase derivada, es posible que puedas generar automáticamente el código de las clases derivadas. XSLT es una gran herramienta para autogenerar clases simples.

En general, el ejemplo no muestra la necesidad de miembros "estáticos virtuales", porque para propósitos como estos no se necesita herencia, sino que se debe usar la clase base y hacer que acepte los valores apropiados en el constructor. crear una instancia única de los argumentos para cada "subtipo" y pasarle un puntero para evitar la duplicación de los datos compartidos. Otro enfoque similar es usar plantillas y pasar como el argumento de la plantilla una clase que proporciona todos los valores relevantes (esto se conoce comúnmente como el patrón de "Política").

Para concluir: a los fines del ejemplo original, no hay necesidad de tales miembros "estáticos virtuales". Si aún cree que se necesitan para el código que está escribiendo, intente elaborar y agregar más contexto.

Ejemplo de lo que describí arriba:

class BaseClass { public: BaseClass(const Descriptor& desc) : _desc(desc) {} string GetName() const { return _desc.name; } int GetId() const { return _desc.Id; } X GetX() connst { return _desc.X; } virtual void UseClass() = 0; private: const Descriptor _desc; }; class DerivedClass : public BaseClass { public: DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {} virtual void UseClass() { /* do something */ } }; class DerDerClass : public BaseClass { public: DerivedClass() : BaseClass("Wowzer", 843,...) {} virtual void UseClass() { /* do something */ } };

Me gustaría profundizar en esta solución, y quizás dar una solución al problema de la inicialización:

Con un pequeño cambio, puede implementar el diseño descrito anteriormente sin crear necesariamente una nueva instancia del "descriptor" para cada instancia de una clase derivada.

Puede crear un objeto singleton, DescriptorMap, que contendrá la instancia única de cada descriptor y usarlo al construir los objetos derivados de esta manera:

enum InstanceType { Yellow, Big, BananaHammoc } class DescriptorsMap{ public: static Descriptor* GetDescriptor(InstanceType type) { if ( _instance.Get() == null) { _instance.reset(new DescriptorsMap()); } return _instance.Get()-> _descriptors[type]; } private: DescriptorsMap() { descriptors[Yellow] = new Descriptor("Yellow", 42, ...); descriptors[Big] = new Descriptor("InJapan", 17, ...) ... } ~DescriptorsMap() { /*Delete all the descriptors from the map*/ } static autoptr<DescriptorsMap> _instance; map<InstanceType, Descriptor*> _descriptors; }

Ahora podemos hacer esto:

class DerivedClass : public BaseClass { public: DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {} virtual void UseClass() { /* do something */ } }; class DerDerClass : public BaseClass { public: DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {} virtual void UseClass() { /* do something */ } };

Al final de la ejecución, cuando el tiempo de ejecución de C realiza desinicializaciones, también llama al destructor de objetos estáticos, incluido nuestro autoptr, que elimina nuestra instancia de DescriptorsMap.

Entonces ahora tenemos una sola instancia de cada descriptor que también se borra al final de la ejecución.

Tenga en cuenta que si el único propósito de la clase derivada es proporcionar los datos "descriptores" relevantes (es decir, en lugar de implementar funciones virtuales), entonces debe conformarse con hacer que la clase base no sea abstracta, y simplemente crear una instancia con el apropiado descriptor cada vez.


Suena como si estuviera tratando de evitar tener que duplicar el código en las clases de hojas, entonces ¿por qué no solo derivar una clase base intermedia de la clase base? esta clase intermedia puede contener los datos estáticos y todas sus clases de hojas derivan de la clase base intermedia. Esto presupone que se desea una pieza estática de datos mantenida sobre todas las clases derivadas, que parece serlo a partir de su ejemplo.