Выполняет ли вызов конструктора С++ из другой функции-члена/конструктора список инициализаторов?

В объекте C++, когда вы вызываете конструктор из другого конструктора или функции-члена (после того, как объект уже создан), выполняется ли список инициализаторов вызываемого вами конструктора?


person user553702    schedule 04.05.2012    source источник


Ответы (4)


В C++11 вы можете делегировать работу конструктора другому конструктору в том же классе, например 1:

#include <iostream>
struct SomeType  {
    int number;

    SomeType(int new_number) : number(new_number) {}
    SomeType() : SomeType(42) {}
};

int main() {
  SomeType a;
  std::cout << a.number << std::endl;
}

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

SomeType() : SomeType(42), number(0) {}

является ошибкой.


Если бы вопрос звучал так: «Учитывая отношения наследования, вызывается ли по-прежнему список инициализаторов?» ответ да, т.е.

#include <iostream>
struct SomeBase {
  SomeBase(int) {}
};

struct SomeType : SomeBase {
    int number;

    SomeType(int new_number=0) : SomeBase(new_number), number(new_number) {}
};

int main() {
  SomeType a;
  std::cout << a.number << std::endl;
}

Это нормально и работает именно так, как вы надеетесь.


Незаконно напрямую вызывать конструктор из другого конструктора, а в С++ до С++ 11 также не разрешено делегирование конструктора, поэтому что-то вроде:

#include <iostream>

struct SomeType {
    int number;

    SomeType(int new_number) : number(new_number) {}
    SomeType() {
      SomeType::SomeType(0); // Error!
    }
};

int main() {
  SomeType a;
  std::cout << a.number << std::endl;
}

Является ошибкой и не может быть выражено напрямую.

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

#include <iostream>

struct SomeType {
    int number;

    SomeType(int new_number) : number(new_number) {}
    SomeType() {
      SomeType(0); 
      number = 42;
    }
};

int main() {
  SomeType a;
  std::cout << a.number << std::endl;
}

Здесь конструктор без параметров создает анонимный временный экземпляр SomeType. Это совершенно отдельный экземпляр от того, для которого изначально был вызван конструктор. Это совершенно законно, но, вероятно, не то, что вы хотели сделать. Если вы сделаете это, есть риск, что вы можете создать бесконечную рекурсию, если вы не будете осторожны, и я думаю, что это, вероятно, хороший индикатор проблемы дизайна, если вы в конечном итоге сделаете что-то подобное!

1 Взято из статьи C++11 в Википедии.

person Flexo    schedule 04.05.2012

Я предполагаю, что вы имеете в виду вызов конструктора базового класса из списка инициализации производного класса. Предполагая, что это так, сначала выполняется конструктор БАЗОВОГО класса (включая его список инициализаторов), а ЗАТЕМ вызывается список инициализаторов производного класса.

Если вам интересно, конструктор базового класса ВСЕГДА вызывается перед выполнением списка инициализаторов для производного класса, ДАЖЕ, если вызов конструктора базового класса появляется явно в середине или конце списка инициализаторов производного класса (как ну как бы она появляется в начале). Причина в том, что элементы в списке инициализаторов выполняются в том порядке, в котором они появляются в DECLARATION класса в заголовочном файле класса, а не в том порядке, в котором они появляются в списке инициализаторов в определении конструктора. Кроме того, конструктор базового класса никогда не объявляется в объявлении производного класса. C++ требует, чтобы конструктор базового класса ВСЕГДА вызывался перед конструктором производного класса — независимо от того, появляется ли вызов конструктора базового класса явно в списке инициализаторов производного класса или нет — и независимо от того, где он появляется в списке инициализаторов.

person Dan Nissenbaum    schedule 04.05.2012

Не вызывайте конструктор явно из другого метода или другого конструктора.

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

person John Dibling    schedule 04.05.2012

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

person cdmh    schedule 04.05.2012