Вопрос

Этот вопрос, связанный с инициализацией объектов в C ++

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

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

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

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

Есть ли способ сделать это или подражать статическому конструктору в C ++ в целом?

Это было полезно?

Решение

Вы описываете разные проблемы одновременно. По конкретному вопросу о наличии какого -то Статическая инициализация, Простой подход - создание поддельного класса, который будет выполнять регистрацию. Тогда каждый из разных классов может иметь static const X Участник, участник должен быть определен в блоке перевода, и определение запустит экземпляр экземпляра и регистрацию класса.

Это не решает жесткую проблему, которая является фиаско заказа по началу инициализации. Язык не дает никакой гарантии на порядок инициализации объектов в различных единицах перевода. То есть, если вы составляете три блока перевода с такими классами, нет никакой гарантии относительно относительного порядка выполнения поддельного члена. Это также применяется к библиотеке: нет никакой гарантии, что контейнер, в котором вы хотите зарегистрировать свои классы, был инициализирован, если такой контейнер является глобальным/статическим атрибутом участника.

Если у вас есть доступ к коду, вы можете изменить код контейнера на использование static local variables, и это будет шагом вперед, чтобы обеспечить порядок инициализации. Как набросок возможного решения:

// registry lib
class registry { // basically a singleton
public:
   static registry& instance() { // ensures initialization in the first call
      static registry inst;
       return inst;
   }
// rest of the code
private:
   registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
   template <typename T>
   register( std::string name ) {
       registry::instance().register( name, T (*factory)() ); // or whatever you need to register
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

Теперь в этом случае нет определенного порядка среди регистрации a а также b классы, но это не может быть проблемой. С другой стороны, с помощью локальная статическая переменная в registry::instance Функция Инициализация синглтона гарантированно будет выполнена до любой звонок к registry::register методы (как часть первого вызова к instance метод).

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

Это может или нет решением, в зависимости от того, создает ли другой код объекты типа или нет. В конкретном случае заводских функций (сначала пришел на ум), если ничто иное не разрешено создавать объекты типов a или же b... Тогда регистрация поддержки сетки на вызовах конструкторов также не будет решением.

Другие советы

Это против парадигмы ООП, но как насчет того, чтобы ваши статические участники образовали связанный список, управляемый 2 глобальными переменными? Вы могли бы сделать что -то подобное:

ClassRegistrator *head=NULL;
ClassRegistrator *tail=NULL;

struct ClassRegistrator {
    ... //data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (head==NULL) head=tail=this;
      else {
        tail->next=this;
        tail=this;
      }
      ... //do other stuff that you need for registration
    }
};


class MyClass { //the class you want to register
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); //call the constructor

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

Очевидно, что это не безопасно и т. Д., Но должно выполнить вашу работу.

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

Одна идея - пройти Регистрация Функтор для классов. Каждый потомок выполнит функцию для регистрации. Этот функтор может быть передан в конструкторе.

Пример:

struct Registration_Interface
{
  virtual void operator() (const std::string& component_name) = 0;
};

struct Base
{
};

struct Child1
  : public Base
{
  Child(Registration_Interface& registration_ftor)
  {
     //...
     registration_ftor("Child1");
  }
};

Видеть: http://www.parashift.com/c+-faq-lite/ctors.html#faq-10.14

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

  void AddToContainer(...) {
    // Will be initialized the first time this function is called.
    static Container* c = new Container();
    c->Add(...);
  }

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

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

class cExample
{
    public :
       cExample() ;

       // Static functions here

    private :
        static bool static_init ;

        // other static members here
}

cExample::static init = false ;

cExample::cExample()
{
    // Static initialisation on first use
    if( !static_init )
    {
        // initialise static members
    }

    // Instance initialisation here (if needed)
}

// Dummy instance to force initialisation before main() (if necessary)
static cExample force_init ;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top