Как можно принудительно вызвать функцию базового класса после конструктора производного класса? - PullRequest
3 голосов
/ 08 апреля 2010

Я ищу чистый язык C ++ для следующей ситуации:

class SomeLibraryClass {
  public:
    SomeLibraryClass() { /* start initialization */ }
    void addFoo() { /* we are a collection of foos */ }
    void funcToCallAfterAllAddFoos() { /* Making sure this is called is the issue */ }
};
class SomeUserClass : public SomeLibraryClass {
  public:
    SomeUserClass() {
      addFoo();
      addFoo();
      addFoo(); // SomeUserClass has three foos.
    }
};
class SomeUserDerrivedClass : public SomeUserClass {
  public:
    SomeUserDerrivedClass() {
      addFoo(); // This one has four foos.
    }
};

Итак, я действительно хочу, чтобы SomeLibraryClass принудительно вызвал вызов funcToCallAfterAllAddFoos в конце процесса построения. Пользователь не может поместить его в конец SomeUserClass :: SomeUserClass (), что могло бы испортить SomeUserDerrivedClass. Если он помещает его в конец SomeUserDerrivedClass, то он никогда не вызывается для SomeUserClass.

Чтобы уточнить, что мне нужно, представьте, что / * начать инициализацию * / получает блокировку, а funcToCallAfterAllAddFoos () снимает блокировку.

Компилятор знает, когда все инициализации объекта выполнены, но могу ли я получить эту информацию с помощью какого-нибудь приятного трюка?

Ответы [ 5 ]

9 голосов
/ 08 апреля 2010

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

class LibraryClass
{
public:
   template<typename D>
   static D *GetNewInstance()
   {
      // by assigning the new D to a LibraryClass pointer, you guarantee it derives from LibraryClass at compile time
      // that way, the user can't accidentally type "LibraryClass::GetNewInstance<int>()" and have it work
      LibraryClass *c = new D();
      c->funcToCallAfterAllAddFoos();
      return c;
   }

   ...
};
5 голосов
/ 08 апреля 2010

Я не уверен, что это возможно.Тем не менее, вы можете немного изменить дизайн: дать конструктору базового класса аргумент std::vector<Foo> const &foosToBeAdded и позволить производным классам передавать правильные foo s:

class SomeLibraryClass {
  public:
    SomeLibraryClass(std::vector<Foo> const &foosToBeAdded) {
      /* start initialization */
      std::for_each(foosToBeAdded.begin(), foosToBeAdded.end(),
                    std::bind1st(std::mem_fun(&SomeLibraryClass::addFoo), this));
      funcToCallAfterAllAddFoos();
    }
  private:
    void addFoo(Foo const &someFoo) { /* we are a collection of foos */ }
    void funcToCallAfterAllAddFoos() { /* this is now called at the right time */ }
};

class SomeUserClass : public SomeLibraryClass {
  public:
    SomeUserClass() :
      SomeLibraryClass(makeAFooVector())
    {
    }
  private:
    std::vector<Foo> makeAFooVector() { /* return a vector with three Foos */ }
};

Шаблон можно расширить, еслиКонструктор SomeUserClass также получает vector из Foo с.Затем он будет добавлять свои собственные Foo в список перед вызовом конструктора базового класса.

Вы также можете передавать итераторы вместо vector s.Оставлено как упражнение.

0 голосов
/ 08 апреля 2010

Зачем вообще нужен публичный метод addfoo? Вы говорите, что это все инициализация, поэтому позвольте коллекции перейти в конструктор.

Тогда вы можете вызвать невиртуальный functocall из конструктора

0 голосов
/ 08 апреля 2010

Поскольку C ++ не разрешает рефлексию, нет, вы не можете напрямую получить эту информацию. (Хотя, может быть, я не знаю, как предотвратить успешную компиляцию)

Однако я подвергаю сомнению дизайн здесь. Разве не имеет смысла снимать блокировку после завершения SomeLibraryClass? Если вы обеспокоены эффективностью многократного вызова AddFoo, ваша библиотека может предоставить члену, принимающему std::vector<Foo>, который должен будет получить и снять блокировку только один раз.

0 голосов
/ 08 апреля 2010

Попробуйте Идиома не виртуального интерфейса . Сделайте ваш публичный метод не виртуальным, но сделайте так, чтобы он вызывал частный виртуальный метод. Производные классы переопределяют этот частный виртуальный метод (да, производные классы могут переопределять частные виртуальные). Поместите блокировку в открытый не виртуальный метод вокруг вызова частного виртуального метода.

EDIT: После более внимательного изучения кода, я думаю, что вам может быть лучше иметь конструктор в базовом классе, который принимает контейнер объектов Foo и добавляет их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...