Могу ли я иметь статические данные в абстрактном классе? - PullRequest
4 голосов
/ 17 июня 2011

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

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

У меня вопрос: если все в порядке, и если да, то как я могу построить его, не вызывая метод «заливки вектора» из одного из производных классов?

Я думал сделать что-то вроде

class Resource {};

enumR {RES0, RES1};

class AbstractClass
{
    public:
        virtual void OnInit() = 0;
        void static fillVector(Resource* pResource, enumR Resourcename)
            {lResource[Resourcename]=pResource;};
    protected:
        static vector<Resource*> lResource;
};

vector<Resource*> AbstractClass::lResource;

int main()
{
    Resource res0, res1;
    AbstractClass::fillVector(&res0, RES0);
    AbstractClass::fillVector(&res1, RES1);

    return 0;
};

Затем, когда я создаю экземпляр объекта любого класса, производного от AbstractClass, у меня будет доступ к вектору lResource, чего я и хочу.

Будет ли это работать? Это ужасно? Это нормально?

Ответы [ 7 ]

0 голосов
/ 17 июня 2011

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

0 голосов
/ 16 марта 2015

В этих случаях я обычно добавляю средний шаблонный слой, поэтому у меня есть такая структура:

Определите ваш абстрактный интерфейс:

class A
{
public:
virtual ~A(){}

virtual void f() = 0 

virtual A* Clone() const = 0
}

Поместите ресурсы, обычно используемыедочерний элемент в шаблоне и определите функцию вспомогательного элемента с помощью CRTP, если необходимо, оставив необходимую интерфейсную функцию абстрактной

template<class Derived>
class A_T: public A
{
public:
virtual void f() = 0;

virtual A* Clone const
{
return new Derived(dyn_cast<Derived&>(*this))
}

void ManageRes();

private:
vector myResource;
}

Наконец, различные конкретные классы завершают реализацию и при необходимости делают что-то особенное с этими ресурсами

class Child: public A_T<Child>
{
public: 
void foo();
}
0 голосов
/ 17 июня 2011

У меня здесь мало очков.

  • Возможно, ваш вектор имеет размер 0. Это может привести к некоторому сбою. Вы должны будете выделить его перед использованием. Вы можете дать статическую или глобальную инициализацию.
  • Вы действительно хотите вектор? Вектор подходит для динамического выделения памяти. Перечисления являются статическими. Вы можете указать простой счет и выделить его в виде массива.
  • Вы действительно хотите статический член? Статический член обычно используется, когда вы делитесь им между объектами одного и того же класса. Можете ли вы удовлетворить требования с внешними объектами, которые являются локальными / глобальными в классе? Также вы можете сделать статическую функцию из класса?
0 голосов
/ 17 июня 2011

Вы можете попробовать повысить :: assign :: list_of, примерно так:

vector<Resource*> AbstractClass::lResource = list_of( &res0 )( &res1 );
0 голосов
/ 17 июня 2011

Вы можете добавить статическую функцию для инициализации статического вектора:

class AbstractClass
{
    private:
        // Add this
        static vector<Resource*> CreateResources();

    protected:
        static vector<Resource*> lResource;
};

vector<Resource*> AbstractClass::lResource = CreateResources();

vector<Resource*> AbstractClass::CreateResources()
{
    vector<Resource*> resources;

    resources[RES0] = new Resource();
    resources[RES1] = new Resource();

    return resources;
}
0 голосов
/ 17 июня 2011

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

0 голосов
/ 17 июня 2011

Это сработало бы, где work = compile & run.

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

Чтобы лучше объяснить, что я имею в виду, прочитайте следующую ветку So:

Унаследованы ли статические поля?

РЕШЕНИЕ:

Одно из решений состоит в том, чтобы ваш родительский класс имел шаблонный класс следующим образом:

template<T>
class Parent<T> {
    public:
        static std::vector<T> sharedResource_;
}

class ChildA : Parent<ChildA> {
}

class ChildB : Parent<ChildB> {
}

В приведенном выше коде вы получите общий ресурс для всех экземпляров ChildA и другогоодин разделен между экземплярами ChildB.

Это правильно?

Ну, я думаю, это не считается хорошим.Одно из связанных с этим обсуждений находится в комментариях к следующему вопросу SO, а также под моим ответом на вопрос:

Как сделать «статическую перегруженную константу» в C #?

...