Вызов конструктора базового класса после некоторых других инструкций в C++

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

MyClass::MyClass(/* args */) : Base(/* args */)
{
   // ...
}

но это вызовет конструктор в начале. Есть ли способ вызвать его где-нибудь еще в конструкторе? Что-то вроде этого:

MyClass::MyClass(/* args */)
{
   // ... instructions
   Base::Base(/* args */);
   // ... other_instructions
}

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

error: invalid use of 'class Base'.

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

Спасибо!

РЕДАКТИРОВАТЬ: я понимаю, что я забыл ключевой момент: базовый класс является частью каркаса, и поэтому было бы хорошо не менять его, если это возможно.

7 ответов

Решение

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

int DoStuffBeforeCtorAndForwardInt(int arg, Foo foo)
{
    DoStuff(arg, foo);
    return arg;
}

MyClass::MyClass(int arg, Foo foo)
    : Base(DoStuffBeforeCtorAndForwardInt(arg, foo))
{
   // ...
}

Если вы хотите инициализировать базовый класс по умолчанию, вы можете использовать copy-ctor, чтобы скопировать инициализированный экземпляр базового класса по умолчанию:

Base DoStuffBeforeCtorAndReturnDefaultBase(int arg, Foo foo)
{
    DoStuff(arg, foo);
    return Base();
}

MyClass::MyClass(int arg, Foo foo)
    : Base(DoStuffBeforeCtorAndReturnDefaultBase(arg, foo))
{
   // ...
}

Или если Base не должен быть первым базовым классом, вы можете получить MyClass из вспомогательного класса:

MyClass::MyClass(/* ... */)
    : DoStuffHelperClass(/* ... */),
    Base(/* ... */)
{
   // ...
}

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

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

(РЕДАКТИРУЙТЕ, за исключением решения DoStuffHelperClass, вы, конечно, можете иметь членов в DoStuffHelperClass, получать к ним доступ, а что нет)


Хотя я должен сказать, что я не могу вспомнить, чтобы когда-либо использовал / нуждался / хотел что-то подобное. Вполне вероятно, что есть другое (предпочтительное) решение для того, что вы пытаетесь сделать.

Используйте идиому base-from-member для запуска вашего кода перед ctor "реального" базового класса (который является Base):

struct Base {
  Base(string, int);
};

struct DerivedDetail {
  DerivedDetail() {
    value = compute_some_value();
    value += more();
    value += etc();
    other = even_more_code(value);
  }
  string value;
  int other;
};

struct Derived : private DerivedDetail, Base {
  Derived() : Base(value, other) {}
  // In particular, note you can still use this->value and just
  // ignore that it is from a base, yet this->value is still private
  // within Derived.
};

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

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

Пример:

MyClass::MyClass()
{
    // Implicit call to Base::Base()

    int result = computeSomething();
    Base::setResult(result);

    // ... 
}

Помимо уже написанных решений, вы также можете использовать статическую функцию конструктора и сделать конструктор MyClass частный.

class QtBase{
  // ...
};

class MyClass : public QtBase{
public:
  // copy ctor public
  MyClass(MyClass const& other);

  static MyClass Create(/*args*/){
    // do what needs to be done
    int idata;
    float fdata;
    // work with idata and fdata as if they were members of MyClass
    return MyClass(idata,fdata); // finally make them members
  }

  static MyClass* New(/*args*/){
    int idata;
    float fdata;
    // work with idata and fdata as if they were members of MyClass
    return new MyClass(idata,fdata); // finally make them members
  }

private:
  // ctor private
  MyClass(int a_idata, float a_fdata)
    : idata(a_idata)
    , fdata(a_fdata)
  {}

  int idata;
  float fdata;
};

Теперь вам придется создавать экземпляры MyClass либо как:

MyClass obj = MyClass::Create(/*args*/);

или же

MyClass* ptr = MyClass::New(/*args*/);

Нет, потому что это не будет безопасным типом.
считают, что у вас есть: класс A и переменная A.var,
теперь рассмотрим B наследуется от Aи использует var до A был инициализирован. Вы получите ошибку во время выполнения! язык хочет предотвратить это, поэтому конструктор суперкласса должен быть сначала инициализирован.

Нет, вы не можете сделать это таким образом, как другие описали в своих предыдущих ответах.

Твой единственный шанс - это композиция. MyClass использования Base Класс как поле члена:

class MyClass {
public:
   /** the methods... */

private:
   Base* _base;
};

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

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

Другие вопросы по тегам