Pergunta

Existe uma maneira de ter uma espécie de virtual de membro estático em C++?

Por exemplo:

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 */ }
};

Eu sei que este exemplo é trivial, mas se eu tiver um vetor de dados complexos que vai ser sempre a mesma para todas as classe derivada, mas é necessário para ser acessado a partir da base de dados de métodos de classe?

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 solução não satify mim, porque eu preciso de reimplementar o membro _name e seu acessor GetName() em cada classe.No meu caso eu tenho vários membros que se segue _name comportamento e décimos de classes derivadas.

Alguma idéia?

Foi útil?

Solução

Aqui está uma solução:

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 };

Outras dicas

Parece que a resposta está na pergunta - o método que você sugeriu que parece ser a direção certa para ir, exceto que se você tiver um grande número dos membros compartilhados convém reunir-se em uma classe ou struct e passado como argumento para o construtor da classe base.

Se você insiste em ter o "compartilhada" membros implementado como membros estáticos da classe derivada, você pode ser capaz de gerar automaticamente o código das classes derivadas.O XSLT é uma grande ferramenta para a auto-geração de classes simples.

Em geral, o exemplo não mostrar a necessidade de "virtual" e "estático" membros, para fins de como essas, você não precisa da herança, em vez disso, você deve usar a classe base e que ele aceite os valores apropriados no construtor - talvez a criação de uma única instância de argumentos para cada "sub-tipo" e passar um ponteiro para ele para evitar a duplicação de dados compartilhados.Outra abordagem é utilizar os modelos e passar como argumento de modelo de uma classe que fornece todos os valores relevantes (isso é comumente referido como a "Política" ou padrão).

Para concluir - para efeitos do exemplo original, não há nenhuma necessidade para tal "virtual" e "estático" de membros.Se você ainda acha que eles são necessários para o código que você está escrevendo, por favor, tente elaborar e adicionar mais contexto.

Exemplo do que eu descrevi acima:

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 */ }
};

Eu gostaria de desenvolver esta solução, e, talvez, dar uma solução para o problema de inicialização:

Com uma pequena alteração, você pode implementar o projeto descrito acima, sem necessariamente criar uma nova instância do "descritor" para cada instância de uma classe derivada.

Você pode criar um objeto singleton, DescriptorMap, que irá conter a única instância de cada descritor, e usá-lo quando a construção de objetos derivados da seguinte forma:

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;
}

Agora, podemos fazer isso:

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 */ }
};

No final da execução, quando o tempo de execução C executa uninitializations, ele também chama o destruidor de objetos estáticos, incluindo a nossa autoptr, que exclui a instância do DescriptorsMap.

Então, agora temos uma única instância de cada descritor que também está sendo excluído no final de execução.

Observe que, se o único propósito da classe derivada é a oferta de relevantes "" descritor de dados (i.e.em oposição à implementação de funções virtuais), então você deve fazer a fazer a fazer a classe base não-abstrata, e apenas a criação de uma instância com o descritor apropriado a cada vez.

Concordo com Hershi sugestão para usar um modelo como a "classe base".Do que você está descrevendo, soa mais como uma utilização de modelos em vez de subclassificação.

Você pode criar um modelo como se segue ( não tentei compilar este ):


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 ;
} ;

Isso permitirá que você usar o modelo de classe de métodos para modificar os dados, conforme necessário, de dentro de suas classes personalizadas que usam os dados e compartilhar os vários aspectos do modelo de classe.

Se você estiver com a intenção de usar a herança, então você pode ter que recorrer ao "alegrias" da utilização de um void* ponteiro na sua classe base e lidar com fundição, etc.

No entanto, com base na sua explicação, parece que você precisa de modelos e não a herança.

@Hershi:o problema com essa abordagem é a de que cada instância de cada classe derivada tem uma cópia dos dados, que pode ser caro, de alguma forma.

Talvez você pode tentar algo como isto (estou cuspir-balling sem uma compilação de exemplo, mas a idéia deve ser claro).


#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;
}

Em relação a pergunta sobre como excluir o objeto estático:a única solução que vem à mente é usar um ponteiro inteligente, algo como o Aumentar compartilhada ponteiro.

Soa como se você está tentando evitar ter que duplicar o código na folha de classes, então por que não derive de uma base intermédia de classe da classe base.este intermediário classe pode conter dados estáticos, e ter todas as suas folhas classes derivam do intermediário classe base.Isto pressupõe que um estática pedaço de dados realizada ao longo de todas as classes derivadas é desejado, o que parece então, a partir de seu exemplo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top