2008-08-29 2 views
10

Есть ли какой-нибудь виртуальный статический член в C++?Имитация виртуального статического члена класса в C++?

Например:

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

Я знаю, что этот пример тривиален, но если у меня есть вектор комплексных данных, который собирается быть всегда одинаковой для всех производного класса, но нужно быть доступен из базы методы класса?

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

Это решение не отвечает соответствующим требованиям меня, потому что мне нужно переопределить _name члена и его аксессор GetName() в каждом классе. В моем случае у меня есть несколько членов, которые следуют за поведением _name и десятыми производными классами.

Любая идея?

ответ

8

Вот одно решение:

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

Кажется, что ответ в вопросе - метод, который вы предложили, кажется правильным направлением, за исключением того, что если у вас есть большое количество этих общих членов, вы можете захотеть их собрать в структуру или класс и минус это как аргумент конструктору базового класса.

Если вы настаиваете на том, чтобы «общие» члены реализовывались как статические члены производного класса, вы могли бы автоматически генерировать код производных классов. XSLT - отличный инструмент для автоматического создания простых классов.

В целом, пример не показывает необходимости для «виртуальных статических» членов, потому что для таких целей вам действительно не нужно наследование - вместо этого вы должны использовать базовый класс и принять его соответствующие значения в конструктор - возможно, создав один экземпляр аргументов для каждого «подтипа» и передав ему указатель, чтобы избежать дублирования общих данных. Другой подобный подход заключается в использовании шаблонов и передаче в качестве аргумента шаблона класса, который предоставляет все соответствующие значения (это обычно называют шаблоном «Политика»).

В заключение - для целей первоначального примера нет необходимости в таких «виртуальных статических» элементах. Если вы все еще думаете, что они нужны для кода, который вы пишете, попробуйте уточнить и добавить больше контекста.

Пример того, что я описал выше:

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

Я хотел бы остановиться на этом решении, и, возможно, дать решение проблемы де-инициализации:

С небольшим изменением, вам может реализовать описанный выше проект, не обязательно создавать новый экземпляр «дескриптора» для каждого экземпляра производного класса.

Вы можете создать одноэлементный объект, DescriptorMap, который будет содержать один экземпляр каждого дескриптора, и использовать его при построении производных объектов, как так:

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

Теперь мы можем сделать это:

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

В конце выполнения, когда C runtime выполняет uninitializations, он также вызывает деструктор статических объектов, включая наш autoptr, который удаляет наш экземпляр DescriptorsMap.

Итак, теперь у нас есть один экземпляр каждого дескриптора, который также удаляется в конце выполнения.

Обратите внимание, что если единственной целью производного класса является предоставление соответствующих «дескрипторных» данных (т. Е. В отличие от реализации виртуальных функций), то вам следует сделать неклассический базовый класс и просто создать экземпляр с соответствующим дескриптором каждый раз.

1

@ Hershi: проблема с этим подходом заключается в том, что каждый экземпляр каждого производного класса имеет копию данных, которая может быть дорогостоящей в некотором роде.

Возможно, вы могли бы попробовать что-то вроде этого (я сплевы без компиляционного примера, но идея должна быть ясной).


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

Что касается последующих вопрос об удалении статического объекта: единственное решение, которое приходит на ум, является использование смарт-указатель, что-то вроде Boost shared pointer.

1

Я согласен с предложением Hershi использовать шаблон в качестве «базового класса». Из того, что вы описываете, это больше похоже на использование шаблонов, а не на подклассирование.

Вы можете создать шаблон следующим образом (не пробовал компилировать это):

 

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

Это позволит вам использовать методы класса шаблона для изменения данных по мере необходимости в пределах пользовательских классов, которые используют данных и разделять различные аспекты класса шаблона.

Если вы намерены использовать наследование, то вам, возможно, придется прибегнуть к «радостям» с использованием недействительных * указателя в вашем BaseClass и дела с литьем и т.д.

Однако, основываясь на ваших объяснениях , похоже, вам нужны шаблоны, а не наследование.

0

Это звучит так, как будто вы пытаетесь избежать необходимости дублировать код на классы листа, так почему бы не просто получить промежуточный базовый класс от базового класса. этот промежуточный класс может хранить статические данные и иметь все ваши классы листа из промежуточного базового класса. Это предполагает, что требуется один статический кусок данных, который хранится над всеми производными классами, что, как кажется, из вашего примера.