Можем ли мы избежать конструктора по умолчанию в этом случае?

0

Наблюдение. Конструктор ClassMain должен вызвать Init прежде чем он сможет создать переменную-член a. Поскольку ClassA не имеет конструктора по умолчанию, код не компилируется.

ClassA
{
public:
    // This class has no default constructor
    ClassA(...){}
};

class ClassMain
{
public:
    ClassMain(...) {
        Init(...);
        a = ClassA(...); // error: ClassA has no default constructor
                         // a has to been constructed after the Init is called!
    }
    ClassMain(...) {
            Init(...);
            call other functions
            a = ClassA(...);
    }

private:
    // initialize environment
    void Init(...) {}

private:
    ClassA a;
};

Вопрос> Простым решением является предоставление конструктора по умолчанию для ClassA. Однако я хотел бы знать, есть ли лучшее решение для решения проблемы выше?

  • 4
    ... есть, не зависит от двухфазной инициализации
  • 0
    Если вы не можете реорганизовать это с помощью предложений ниже, вы можете сделать a std::unique_ptr<ClassA> a; и затем динамически выделить его после Init() .
Показать ещё 5 комментариев
Теги:

5 ответов

2

Очевидным решением является вызов Init() из списка инициализаторов раннего члена или базового класса. Как только этот подобъект построен, его результаты могут быть переданы конструкторам других подобъектов. Например, при определении классов потоков я обычно частным образом наследую от виртуальной базы, содержащей буфер потока:

struct somebuf_base {
    somebuf sbuf;
    // ...
};
class somestream
    : private virtual somebuf_base
    , public std::ostream
{
public:
    somestream(someargs)
        : somebuf_base(someargs)
        , std::ostream(&this->sbuf) {
    }
    // ...
};

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

При использовании C++ с версией 2011 года вы также можете использовать конструкторы пересылки для обмена логикой между несколькими конструкторами.

  • 1
    Мы должны принять «класс ваз» в качестве сокращения для «виртуального базового класса» [опечатка шутка].
  • 0
    @aschepler: если это единственная опечатка, которую я ввел при наборе этого ответа на телефоне, это очень хорошо ...
2

Лучшее решение - не требовать функции Init вообще. Вы пытаетесь изобретать конструкторы и нарушать их дизайн в процессе.

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

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

Существует одно неприятное обходное решение, которое вы можете использовать: на самом деле Init является базовым конструктором...

  • 0
    Основная причина, по которой я использую Init заключается в том, что класс ClassMain имеет перегруженные конструкторы, каждый из которых вызывает одну и ту же процедуру Init . Не стоит позволять пользователю выполнять работу Init потому что содержимое Init должно быть скрыто от пользователя.
  • 0
    @ Lightness, а как насчет исключений? Я обнаружил, что мои сложные классы имеют функции 'Init', так как я не хочу, чтобы Ctors генерировала исключения или терпела неудачу (во всем, кроме выделения памяти). В результате все, что содержит эти классы, имеет функции Init и т. Д. ... Функции Init не являются частными, но больше похожи на file.open / file.close
Показать ещё 2 комментария
0

Легче взять указатель на ClassA; Таким образом, вы можете создавать его, когда захотите. (После init())
Если вы использовали указатель, не забудьте реализовать виртуальный деструктор и освободить выделенную память для ClassA * a

0

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

ClassMain::ClassMain(int main_param)
  : a( (Init(init_arg), class_a_arg1), class_a_arg2 )
{
}
0

В этом случае: Нет, мы не можем этого избежать.

Причина в том, что при вызове Init или любой другой функции-члена вам гарантируется язык, на котором находится объект, в котором вы находитесь. Поскольку a является членом ClassMain он должен быть сконструирован до ClassMain будет вызвана любая функция ClassMain.

Единственный шанс, что вы здесь, - это реорганизовать код.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню