От Википедия (включая статью) на C ++ 11:
Это [новая функция делегирующих конструкторов] поставляется с оговоркой: C ++ 03 считает, что объект должен быть построен, когда его конструктор завершает выполнение, но C ++ 11 рассматривает объект, созданный после завершения выполнения любого конструктора. Поскольку разрешено выполнение нескольких конструкторов, это будет означать, что каждый делегирующий конструктор будет выполняться для полностью сконструированного объекта своего собственного типа. Конструкторы производных классов будут выполняться после завершения делегирования в их базовых классах ».
Означает ли это, что цепочки делегирования создают уникальный временный объект для каждой ссылки в цепочке делегирования ctor? Такие издержки просто для того, чтобы избежать простого определения функции инициализации, не будут стоить дополнительных издержек.
Отказ от ответственности: я задал этот вопрос, потому что я студент, но ответы до сих пор все были неправильными и демонстрируют недостаток исследований и / или понимания исследований, на которые ссылаются. Я был несколько разочарован этим, и в результате мои правки и комментарии были поспешно и плохо составлены, в основном по смартфонам. Пожалуйста, извините это; Я надеюсь, что я минимизировал это в своем ответе ниже, и я понял, что должен быть осторожным, полным и ясным в своих комментариях.
Нет, они эквивалентны. Делегирующий конструктор ведет себя как обычная функция-член, действующая на объекте, созданном предыдущим конструктором.
Я не смог найти никакой информации, явно поддерживающей это в предложение по добавлению делегирующих конструкторов, но создание копий в общем случае невозможно. Некоторые классы могут не иметь конструкторов копирования.
В Разделе 4.3 — Изменения к §15 предлагаемое изменение к стандартным состояниям:
если не делегирующий конструктор для объекта завершил выполнение и делегирующий конструктор для этого объекта завершается с исключением, будет вызван деструктор объекта.
Это подразумевает, что делегирующий конструктор работает с полностью сконструированным объектом (в зависимости от того, как вы это определяете) и позволяет реализации иметь делегирующие ctors, работающие как функции-члены.
Конструкторы классов состоят из двух частей: списка инициализаторов и тела функции. При делегировании конструктора сначала выполняется список инициализатора и тело функции делегированного (целевого) конструктора. После этого выполняется тело функции делегирующего конструктора. В некоторых случаях вы можете считать объект полностью сконструированным, когда выполняется список инициализатора и тело функции какого-либо конструктора.
Вот почему вики говорит каждый делегирующий конструктор будет выполняться на полностью сконструированном объекте своего собственного типа. Фактически, семантика может быть более точно описана как:
…функция тела каждый делегирующий конструктор будет выполняться на полностью сконструированном объекте своего собственного типа.
Тем не менее, делегированный конструктор может только частично создать объект, и предназначен для вызова другими конструкторами только для использования отдельно. Такой конструктор обычно объявляется закрытым. Поэтому не всегда целесообразно считать объект полностью сконструированным после выполнения делегированного конструктора.
В любом случае, поскольку выполняется только один список инициализаторов, таких издержек, как вы упомянули, нет. Ниже приводятся цитаты из cppreference:
Если имя самого класса отображается как идентификатор класса или в
список инициализатора члена, тогда список должен состоять из этого одного члена
только инициализатор; такой конструктор известен как делегирование
конструктор, и конструктор, выбранный единственным членом
список инициализатора является целевым конструкторомВ этом случае целевой конструктор выбирается из-за перегрузки.
разрешение и выполняется сначала, затем элемент управления возвращается к
делегирующий конструктор и его тело выполняется.Делегирующие конструкторы не могут быть рекурсивными.
Цепные делегирующие конструкторы в C ++ 11 несут больше накладных расходов, чем стиль функции инициализации C ++ 03!
См. Проект стандарта C ++ 11 N3242, раздел 15.2. Исключение может возникнуть в блоке выполнения любой ссылки в цепочке делегирования, и C ++ 11 расширяет существующее поведение обработки исключений, чтобы учесть это.
[текст] и акцент мой.
У объекта любой продолжительности хранения, инициализация или уничтожение которого завершается исключением, будут деструкторы, выполненные для всех его полностью построенных подобъектов …, то есть для подобъектов, для которых главный конструктор (12.6.2) завершил выполнение, и деструктор еще не начал казнить. так же, если не делегирующий конструктор для объекта завершил выполнение и делегирующий конструктор для этого объекта завершается с исключением, будет вызван деструктор объекта [обработанный как подобъект, как указано выше].
Это описывает делегирование согласованности ctors с моделью стека объектов C ++, которая обязательно вводит накладные расходы.
Мне нужно было ознакомиться с такими вещами, как, как стек работает на аппаратном уровне, что такое указатель стека, что такое автоматические объекты и что такое раскрутка стека, чтобы действительно понять, как это работает. Технически эти термины / концепции являются деталями, определяемыми реализацией, поэтому N3242 не определяет ни одного из этих терминов; но он использует их.
Суть этого: объекты, объявленные в стеке, размещаются в памяти, а исполняемый файл обрабатывает адресацию и очистку за вас. Реализация стека была проста в C, но в C ++ у нас есть исключения, и они требуют расширения разматывания стека C. Раздел 5 статья Страуструпа* обсуждается необходимость расширенного разматывания стека и необходимые дополнительные издержки, представленные такой функцией:
Если у локального объекта есть деструктор, этот деструктор должен быть вызван как часть разматывания стека. [Расширение C ++ для разматывания стека для автоматических объектов требует] … техники реализации, которая (в дополнение к стандартным накладным расходам на установление обработчика) включает в себя только минимальные накладные расходы.
Именно эту технику реализации и накладные расходы вы добавляете в свой код для каждая ссылка в вашей цепочке делегирования. Каждая область имеет потенциал для исключения, и каждый конструктор имеет свою собственную область, поэтому каждый конструктор в цепочке добавляет накладные расходы (по сравнению с функцией init, которая вводит только одну дополнительную область).
Это правда, что накладные расходы минимальны, и я уверен, что разумные реализации оптимизируют простые случаи, чтобы устранить эти накладные расходы. Однако рассмотрим случай, когда у вас есть цепочка наследования 5 классов. Допустим, у каждого из этих классов есть 5 конструкторов, и внутри каждого класса эти конструкторы вызывают друг друга в цепочке, чтобы уменьшить избыточное кодирование. Если вы создаете экземпляр экземпляра самого производного класса, вы будете нести вышеописанные накладные расходы до 25 раз, в то время как версия C ++ 03 понесла бы эти накладные расходы до 10 раз. Если вы сделаете эти классы виртуальными и многократно наследуемыми, это увеличит накладные расходы, связанные с накоплением этих функций, а также тех функций, которые сами вводят дополнительные издержки. Мораль здесь в том, что когда ваш код масштабируется, вы почувствуете укус этой новой функции.
*Ссылка Stroustrup была написана давно, чтобы мотивировать обсуждение обработки исключений в C ++ и определяет потенциальные (не обязательно) возможности языка C ++. Я выбрал эту ссылку, а не какую-то конкретную реализацию, потому что она удобочитаема и «переносима». Основное использование этой статьи — раздел 5: в частности, обсуждение необходимости разматывания стека C ++ и необходимости его накладных расходов. Эти концепции узаконены в статье и действительны сегодня для C ++ 11.