Повторный вызов конструктора c

Обновлено: 15.05.2024

есть ли способ сделать это в C++?

Я попытался вызвать имя класса и использовать ключевое слово "this", но оба не удалось.

C++11: Да!

C++11 и далее имеет эту же функцию (называется делегирование конструкторов).

C++03: Нет

к сожалению, нет способа сделать это в C++03, но есть два способа моделирования этого:

вы можете объединить два (или более) конструкторы по умолчанию параметры:

используйте метод init для совместного использования общего кода:

посмотреть запись FAQ на C++ для справки.

нет, вы не можете вызвать один конструктор из другого, в C++03 (так называемое делегирование) конструктор.

это изменилось в C++11 (он же C++0x), который добавил поддержку следующего синтаксиса:
(пример взят из Википедия)

Я считаю, что вы можете вызвать конструктор из конструктора. Он будет компилироваться и запускаться. Недавно я видел, как кто-то это сделал, и он работал как на Windows, так и на Linux.

Он просто не делает то, что вы хотите. Внутренний конструктор создаст временный локальный объект, который будет удален после возвращения внешнего конструктора. Они также должны быть разными конструкторами, или вы создадите рекурсивный вызов.

стоит отметить, что вы can вызов конструктора родительского класса в конструкторе, например:

но, нет, вы не можете вызвать другой конструктор того же класса.

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

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

Если вы хотите быть злым, вы можете использовать оператор "new" на месте:

Кажется, работает для меня.

редактировать

Как указывает @ElvedinHamzagic, если Foo содержит объект, который выделил память, этот объект не может быть освобожден. Это еще больше все усложняет.

более общий пример:

выглядит немного менее элегантно, наверняка. Решение @ JohnIdol намного лучше.

нет, в C++ вы не можете вызвать конструктор из конструктора. Что вы можете сделать, как указал Уоррен, так это:

  • перегрузите конструктор, используя разные подписи
  • используйте значения по умолчанию для аргументов, чтобы сделать "более простую" версию доступной

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

в Visual C++ вы также можете использовать эту нотацию внутри конструктора: this - >Classname:: Classname(параметры другого конструктора). См. пример ниже:

С. П.: честно говоря, я был удивлен, что это не было упомянуто ранее.

Если я правильно понимаю ваш вопрос, вы спрашиваете, можете ли вы вызвать несколько конструкторов на C++?

Если это то, что вы ищете, то нет - это невозможно.

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

вы даже можете иметь один конструктор с аргументами по умолчанию в конце.

но вы не можете иметь несколько конструкторы, а затем вызвать каждый из них отдельно.

еще один вариант, который еще не был показан, - разделить ваш класс на два, обернув легкий класс интерфейса вокруг исходного класса, чтобы достичь эффекта, который вы ищете:

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

Я бы предложил использовать private friend метод, который реализует логику приложения конструктора и вызывается различными конструкторами. Вот пример:

предположим, что у нас есть класс StreamArrayReader С некоторыми частными полями:

и мы хотим определить два конструктора:

где второй просто использует первый (и, конечно, мы не хотим дублировать реализацию первого). В идеале, хотелось бы сделать что-то вроде:

однако это не разрешено в C++. По этой причине мы можем определить метод private friend следующим образом, который реализует то, что должен делать первый конструктор:

теперь этот метод (потому что это друг) имеет доступ к закрытым полям o . Затем первым конструктором становится:

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

то есть вместо того, чтобы один конструктор вызывал другой, оба звонят частному другу!

этот подход может работать для некоторых классов (когда оператор присваивания ведет себя "хорошо"):

при вызове конструктора он на самом деле выделяет память из стека или кучи. Таким образом, вызов конструктора в другом конструкторе создает локальную копию. Таким образом, мы модифицируем другой объект, а не тот, на котором фокусируемся.

проще говоря, вы не можете до C++11.

делегирующий конструктор

Если имя самого класса отображается как class-or-identifier в список инициализатора, список должен состоять из одного члена только инициализатор; такой конструктор известен как делегирование конструктор и конструктор, выбранный единственным членом список инициализаторов конструктор цели

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

делегирование конструкторов не может быть рекурсивным.

обратите внимание, что делегирующий конструктор является предложением "все или ничего"; если конструктор делегирует другому конструктору, вызывающему конструктору не разрешается иметь никаких других члены в списке инициализации. Это имеет смысл, если вы думаете об инициализации членов const / reference один раз и только один раз.

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

вопрос, а что вообще нужно от вызова конструктора?
выполнение некоторых операторов из него ведь так?
может стоит их вынести в отдельный метод?
(а то ведь не очень как то получается)

Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Вообще мне это нужно для сброса private-членов класса в дефолт.
CLS() : x(0), y(0).
Вы правы, конечно можно сделать это отдельным методом, но "дизайн" класса будет не тот

2EUGY
ты не понимаешь смысла конструктора, если хочешь вызывать его напрямую. почитай страуструпа, например. видно, что ты сишник, переходящий на плюсы, но тут другие правила)

I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Просто любопытно, как рантайм C++ вызывает конструктор объекта класса.
Не думаю, что это против правил.
Где увидеть нечто такое:

Вам правильно советуют: неправильно это - вызывать конструктор ради сброса приватных членов. Напишите метод - reset, например.
Смысл конструктора и его вызова несколько иной, чем просто обнуление переменных.

Здесь будут созданы 2 разных объекта - один в стеке и один в куче.

По теме:
Конструктор почти нормальный член класса и его можно вызывать.

CLS cls; // вызывается конструктор (по сути структура кладется на стек и заполняются поля, если они есть)
.
// после его использования вызывается деструктор, который выталкивает его со стека, если в описании класса нет деструктора, он создается автоматически.
return 0;

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

вопрос, а что вообще нужно от вызова конструктора?
выполнение некоторых операторов из него ведь так?
может стоит их вынести в отдельный метод?
(а то ведь не очень как то получается)

Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Вообще мне это нужно для сброса private-членов класса в дефолт.
CLS() : x(0), y(0).
Вы правы, конечно можно сделать это отдельным методом, но "дизайн" класса будет не тот

2EUGY
ты не понимаешь смысла конструктора, если хочешь вызывать его напрямую. почитай страуструпа, например. видно, что ты сишник, переходящий на плюсы, но тут другие правила)

I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Просто любопытно, как рантайм C++ вызывает конструктор объекта класса.
Не думаю, что это против правил.
Где увидеть нечто такое:

Вам правильно советуют: неправильно это - вызывать конструктор ради сброса приватных членов. Напишите метод - reset, например.
Смысл конструктора и его вызова несколько иной, чем просто обнуление переменных.

Здесь будут созданы 2 разных объекта - один в стеке и один в куче.

По теме:
Конструктор почти нормальный член класса и его можно вызывать.

CLS cls; // вызывается конструктор (по сути структура кладется на стек и заполняются поля, если они есть)
.
// после его использования вызывается деструктор, который выталкивает его со стека, если в описании класса нет деструктора, он создается автоматически.
return 0;

- Тоже работает. Оператор присваивания для класса не перегружен. Притом что если явным образом еще раз вызвать конструктор от этого объекта, компилятор конечно ругается. Объясните пожалуйста, что здесь вообще тогда происходит? Еще раз вызывается конструктор и создается новый объект, а старый продолжает висеть в памяти и ссылка на него теряется?

Повторный вызов функции
Добрый вечер, У меня немного странный вопрос, в универе преподаватель его мне задал и я не смогла.


Функции, повторный вызов
Здравствуйте! Можете помочь? Например есть функция любая например: void showMsgи мне.

Повторный вызов деструктора
< bar b; b.~bar(); >В данном примере деструктор bar вызовется дважды. Как у уже удаленного.

В учебнике у меня была подобная программа, я ее переписывал \ совершенствовал, вот, может будет интересно:

Использую для того что бы сделать например конкатенацию строк, если сумма длин при конкатенации получится больше выделенной памяти, использовать realloc. А как это сделать с new?

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

По сути, realloc делает тоже самое.

Добавлено через 4 минуты
CatsCanFly, насчет ошибки.

здесь после этой строки str - это побайтовая копия str2. Указатель char* str; один и тот же.
Когда срабатывает деструктор для str и str2 по этому указателю удаление происходит два раза, на втором разе будет ошибка.

Решение: определить оператор = для строк.

Если ему не повезёт и он не сможет довыделить память из излишков или от границы кучи.

то создается временный объект внутри кода перегруженного оператора + и возвращается им. Только тут получается, что после присваивания str3 мы временный объект теряем, соответственно указатель на массив char который он в себе содержит, и возможность освободить от него память, то есть получаем утечку памяти. Я прав? Подскажите пожалуйста, как решить такую проблему. VLK в своем решении использовал тот же подход что и я, и как понимаю там проблема эта тоже должна быть.

у меня в обоих случая вызывался только один конструктор (насколько я понял проблема в том, что вызывалось несколько)

а теперь открываем g++ и компилим с -fno-elide-constructors.
или g++ своевольничает и нарушает стантадарт?

Добавлено через 49 секунд
цитату из стандарта об эквивалентности этих двух строк от Dmitriy_M так и не получили

Я не знаю что за g++, что даст "g++ и компилим с -fno-elide-constructors" ? напишет когда вызывается конструктор?

Недавно разбирался для себя с темой пропущенного копирования. Jupiter прав, текущий стандарт 12.8/32.

Тот факт, что msvc и gcc (на них я проверял) по умолчанию опускают вызов конструктора копирования, не означает, что они обязаны так делать.

то создается временный объект внутри кода перегруженного оператора + и возвращается им. Только тут получается, что после присваивания str3 мы временный объект теряем, соответственно указатель на массив char который он в себе содержит, и возможность освободить от него память, то есть получаем утечку памяти. Я прав?

Я проверил, у меня почему то не вызывается.

Тогда я немного не понял, можно пояснить?
Ни в str & operator= (const str & rhs) ни в str operator+ (const str & rhs) не увидел удаления объекта

Тогда я немного не понял, можно пояснить?
Ни в str & operator= (const str & rhs) ни в str operator+ (const str & rhs) не увидел удаления объекта

Ну все правильно, ты когда пишешь
int var = 100;
100 это же не где то там, как я понимаю это временный объект, после присвоения, а наверное после выполнения этой строчки и перехода на новую строчку, данный временны объект будет удален.
Я не знаю точно, я только подозреваю, и я надеюсь профессионалы подтвердят мои догадки.

Ну все правильно, ты когда пишешь
int var = 100;
100 это же не где то там, как я понимаю это временный объект, после присвоения, а наверное после выполнения этой строчки и перехода на новую строчку, данный временны объект будет удален.
Я не знаю точно, я только подозреваю, и я надеюсь профессионалы подтвердят мои догадки.

Это же простые типы а не объекты. Я понимаю это выражение так: в сегмент кода записывается константа 100, дальше ее значение копируется в переменную var, которая находится в стеке. В сегменте кода навсегда остается эта константа 100. Разве не так?

Добавлено через 8 часов 30 минут
И снова мой вопрос) Проблему с удалением временного объекта решил создав новый перегруженный конструктор и оператор присваивания. Вот мой класс сейчас:

Error 6 error C2677: binary '+' : no global operator found which takes type 'CString' (or there is no acceptable conversion)
Error 5 error C2784: 'std::_Revranit std::operator +(_Diff,const std::_Revranit &)' : could not deduce template argument for 'const std::_Revranit &' from 'CString'
Error 3 error C2784: 'std::_String_const_iterator std::operator +(_String_const_iterator::difference_t ype,std::_String_const_iterator)' : could not deduce template argument for 'std::_String_const_iterator' from 'CString'
Error 2 error C2784: 'std::_String_iterator std::operator +(_String_iterator::difference_type,st d::_String_iterator)' : could not deduce template argument for 'std::_String_iterator' from 'CString'
Error 4 error C2784: 'std::reverse_iterator std::operator +(_Diff,const std::reverse_iterator &)' : could not deduce template argument for 'const std::reverse_iterator &' from 'CString'

image

Но это уже не имело значения, потому что вызов был принят.

image


Приведенные ниже приемы не рекомендуется использовать в реальной жизни. Точнее даже рекомендуется не использовать. Это скорее тема для легкого светского разговора с коллегой. Или собеседующим.

Подготовка

Создаем цепочку наследования. Для простоты будем использовать конструкторы без параметров. В конструкторе будем выводить информацию о типе и идентификатор объекта, на котором он вызывается.


И получаем вывод:

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


и таким фокусом компилятор тоже не провести:

Удаление дублирующегося кода

Добавляем вспомогательный класс:


И заменяем во всех конструкторах


Однако теперь программа выводит:

Получение доступа к конструктору базового типа

Здесь на помощь приходит рефлексия. Добавляем в Extensions метод:


В типы B и C добавляем свойство:

Вызов конструктора базового типа в произвольном месте

Меняем содержимое конструкторов B и C на:


Теперь вывод выглядит так:

Изменение порядка вызова конструкторов базового типа

Внутри типа A создаем вспомогательный тип:


Так как здесь важна только семантика, конструктор типа целесообразно сделать закрытым. Создание экземпляров не имеет смысла. Тип предназначен исключительно для различения перегрузок конструкторов типа A и производных от него. По этой же причине тип следует разместить внутри A и сделать защищенным.

Добавляем в A, B и C соответствующие конструкторы:


Для типов B и C ко всем конструкторам добавляем вызов:

И вывод становится:

Осмысление результата

Добавив в Extensions метод:


и вызвав его во всех конструкторах, принимающих CtorHelper, мы получим вывод:

Читайте также: