Сколько конструкторов может содержать класс языка c

Обновлено: 29.04.2024

добрый день.
при создании приложения/игры
возник вопрос,

а сколько вообще должно быть конструкторов в классе,

и как это может влиять на производительность приложения?

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

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

заранее благодарен за ответ.

Ответы

стоит мне в классе

держать несколько конструкторов ( про запас)
или же нет (ограничится только теми которые явно использую)
как это принято в мире ООП (есть какие нибудь "правила хорошего тона/кода")?
заранее благодарен за ответ.

Основные принципы, которыми следует руководствоваться:

Сделайте вот так, да и не переживайте :)

Все ответы

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

С конструкторами экземпляров может быть другая проблема:

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

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

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

"а сколько вообщедолжно быть конструкторов в классе," - как минимум один. Если вы ничего не создаёте, используется конструктор по умолчанию (без параметров).

"и как это может влиять на производительность приложения?" - количество конструкторов никак не влияет на производителность, только на объём кода. Другое дело, что вы там напишите. При создании объекта используется только один конструктор (хотя он может вызвать менее обобщённый конструктор того же типа, т.е. получается цепочка вызовов).

"стоит ли мне создавать варианты(перегруженные конструкторы)
или же обойтись одним по умолчанию и одним с нужным набором элементов?" - если в этом есть необходимость, да. Иначе нет. Не используйте возможность, только потому, что она есть. Используйте - если нужно.

При создании экземпляра класса или структуры вызывается его конструктор. Конструкторы имеют имя, совпадающее с именем класса или структуры, и обычно инициализируют члены данных нового объекта.

В следующем примере класс с именем Taxi определяется с помощью простого конструктора. Затем оператор new создает экземпляр этого класса. Конструктор Taxi вызывается оператором new сразу после того, как новому объекту будет выделена память.

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

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

Дополнительные сведения см. в разделе Закрытые конструкторы.

Конструкторы для типов структур похожи на конструкторы классов, но structs не могут содержать явный конструктор без параметров, так как он предоставляется компилятором автоматически. Этот конструктор инициализирует каждое поле в struct со значением по умолчанию. При этом конструктор без параметров вызывается только в том случае, если экземпляр struct создается с помощью переменной new . Например, этот код использует конструктор без параметров, Int32чтобы гарантировать, что целое число инициализируется:

Однако следующий код вызывает ошибку компилятора, так как он не используется new , и потому что он пытается использовать объект, который не был инициализирован:

Кроме того, объекты на основе structs (включая все встроенные числовые типы) можно инициализировать или назначить, а затем использовать, как в следующем примере:

Поэтому вызов конструктора без параметров для типа значения не требуется.

Оба класса и structs могут определять конструкторы, принимающие параметры. Конструкторы, принимающие параметры, необходимо вызывать с помощью оператора new или base. Классы и structs могут определять также несколько конструкторов; для определения конструктора без параметров ни один их них не требуется. Пример:

Этот класс можно создать, воспользовавшись одним из следующих операторов:

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

В этом примере конструктор базового класса вызывается перед выполнением соответствующего ему блока. Ключевое слово base можно использовать как с параметрами, так и без них. Любые параметры для конструктора можно использовать как параметры для base или как часть выражения. Дополнительные сведения см. в разделе base.

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

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

Конструктор может вызывать другой конструктор в том же объекте с помощью ключевого слова this. Как и base , this можно использовать с параметрами или без, а все параметры в конструкторе доступны как параметры this или как часть выражения. Например, второй конструктор в предыдущем примере можно переписать, используя this :

Применение ключевого слова this в приведенном выше примере привело к вызову конструктора:

Конструкторы могут иметь пометку public, private, protected, internal, protected internal или private protected. Эти модификаторы доступа определяют, каким образом пользователи класса смогут создавать класс. Дополнительные сведения см. в статье Модификаторы доступа.

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

Чтобы настроить, как класс инициализирует его члены или вызывать функции при создании объекта класса, определите конструктор. Конструкторы имеют имена, совпадающие с именами классов, и не имеют возвращаемых значений. Вы можете определить столько перегруженных конструкторов, сколько необходимо для настройки инициализации различными способами. Как правило, конструкторы имеют открытые специальные возможности, чтобы код за пределами определения класса или иерархии наследования может создавать объекты класса. Но вы также можете объявить конструктор как protected или private .

Конструкторы могут при необходимости принимать список инициализаторов элементов. Это более эффективный способ инициализации членов класса, чем назначение значений в тексте конструктора. В следующем примере показан класс Box с тремя перегруженными конструкторами. Последние два используют списки инициализации элементов:

При объявлении экземпляра класса компилятор выбирает, какой конструктор будет вызываться на основе правил разрешения перегрузки:

  • Конструкторы могут быть объявлены как inline , , explicitfriend или constexpr .
  • Конструктор может инициализировать объект, объявленный как const , volatile или const volatile . Объект становится const после завершения конструктора.
  • Чтобы определить конструктор в файле реализации, присвойте ему полное имя, как и любая другая функция-член: Box::Box() .

Списки инициализаторов элементов

При необходимости конструктор может иметь список инициализаторов элементов, который инициализирует члены класса перед запуском тела конструктора. (Список инициализаторов элементов не совпадает со списком инициализаторов типа std::initializer_list .)

Предпочитать инициализаторы элементов перечисляют значения вместо назначения значений в тексте конструктора. Список инициализаторов элементов напрямую инициализирует элементы. В следующем примере показан список инициализаторов элементов, состоящий из всех identifier(argument) выражений после двоеточия:

Идентификатор должен ссылаться на член класса; он инициализирован со значением аргумента. Аргумент может быть одним из параметров конструктора, вызова функции или . std::initializer_list

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

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

Конструкторы по умолчанию

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

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

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

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

Конструктор по умолчанию, созданный компилятором, будет определен как удаленный, если какие-либо члены класса не являются конструктором по умолчанию. Например, все члены типа класса и их члены класса должны иметь конструктор по умолчанию и деструкторы, которые доступны. Все члены данных ссылочного типа и все const члены должны иметь инициализатор элементов по умолчанию.

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

Это утверждение является примером проблемы "Большинство vexing Parse". Можно интерпретировать myclass md(); как объявление функции или как вызов конструктора по умолчанию. Поскольку средства синтаксического анализа C++ предпочитают объявления по сравнению с другими вещами, выражение рассматривается как объявление функции. Дополнительные сведения см. в разделе "Большинство синтаксического анализа".

Если объявлены какие-либо конструкторы, отличные от по умолчанию, компилятор не предоставляет конструктор по умолчанию:

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

Однако для инициализации массива объектов Box можно использовать набор списков инициализаторов:

Дополнительные сведения см. в разделе "Инициализаторы".

Конструкторы копии

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

Конструктор копирования может иметь одну из следующих сигнатур:

При определении конструктора копирования необходимо также определить оператор присваивания копирования (=). Дополнительные сведения см. в разделе "Назначение " и " Копирование конструкторов" и операторов присваивания копирования.

Вы можете запретить копирование объекта, определив конструктор копирования как удаленный:

При попытке копирования объекта возникает ошибка C2280: попытка ссылаться на удаленную функцию.

Конструкторы перемещения

Конструктор перемещения — это специальная функция-член, которая перемещает владение данными существующего объекта в новую переменную без копирования исходных данных. Он принимает ссылку rvalue в качестве первого параметра, а все последующие параметры должны иметь значения по умолчанию. Конструкторы перемещения могут значительно повысить эффективность программы при передаче больших объектов.

Компилятор выбирает конструктор перемещения, когда объект инициализируется другим объектом того же типа, если другой объект будет уничтожен и больше не нуждается в его ресурсах. В следующем примере показано одно дело, когда конструктор перемещения выбирается с помощью разрешения перегрузки. В конструкторе, который вызывает get_Box() , возвращаемое значение является xvalue (значение eXpiring). Поэтому он не назначается какой-либо переменной и поэтому выходит за пределы области действия. Чтобы обеспечить мотивацию для этого примера, давайте предоставим Box большой вектор строк, представляющих его содержимое. Вместо копирования вектора и его строк конструктор перемещения "крадет" его из значения "box", чтобы вектор теперь принадлежит новому объекту. Вызов std::move необходим, так как оба vector класса string реализуют собственные конструкторы перемещения.

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

Неявно объявленный конструктор перемещения определяется как удаленный, если какие-либо элементы, являющиеся типами классов, не имеют деструктора или если компилятор не может определить, какой конструктор следует использовать для операции перемещения.

Дополнительные сведения о написании конструктора нетривиального перемещения см. в разделе "Конструкторы перемещения" и "Операторы присваивания перемещения" (C++).

Явно заданные по умолчанию и удаленные конструкторы

Конструкторы копирования по умолчанию , конструкторы по умолчанию, конструкторы перемещения, операторы присваивания копирования, операторы присваивания перемещения и деструкторы. Вы можете явно удалить все специальные функции-члены.

Конструкторы constexpr

Конструктор может быть объявлен как constexpr , если

  • он либо объявлен как стандартный, либо удовлетворяет всем условиям для функций constexpr в целом;
  • класс не имеет виртуальных базовых классов;
  • каждый из параметров является литеральным типом;
  • тело не является блоком try-block функции;
  • инициализированы все нестатические члены данных и подобъекты базового класса;
  • Значение , если класс является (a) объединением, имеющим члены варианта, или (б) имеет анонимные объединения, инициализируется только один из членов профсоюза;
  • каждый нестатический член данных типа класса, а все подобъекты базового класса имеют конструктор constexpr.

Конструкторы списков инициализаторов

Затем создайте объекты Box следующим образом:

Явные конструкторы

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

Можно инициализировать Box следующим образом:

Или передать целое значение функции, принимающей объект Box:

В некоторых случаях подобные преобразования могут быть полезны, однако чаще всего они могут привести к незаметным, но серьезным ошибкам в вашем коде. Как правило, необходимо использовать ключевое explicit слово в конструкторе (и определяемых пользователем операторах), чтобы предотвратить такое неявное преобразование типов:

Когда конструктор является явным, эта строка вызывает ошибку компилятора: ShippingOrder so(42, 10.8); . Дополнительные сведения см. в разделе о преобразованиях определяемых пользователем типов.

Порядок строительства

Конструктор выполняет свою работу в следующем порядке.

Вызывает конструкторы базовых классов и членов в порядке объявления.

Если класс является производным от виртуальных базовых классов, конструктор инициализирует указатели виртуальных базовых классов объекта.

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

Выполняет весь код в теле функции.

В следующем примере показан порядок, в котором конструкторы базовых классов и членов вызываются в конструкторе для производного класса. Сначала вызывается базовый конструктор. Затем члены базового класса инициализируются в том порядке, в котором они отображаются в объявлении класса. Наконец, вызывается производный конструктор.

Выходные данные будут выглядеть следующим образом.

Конструктор производного класса всегда вызывает конструктор базового класса, чтобы перед выполнением любых дополнительных операций иметь в своем распоряжении полностью созданные базовые классы. Конструкторы базового класса вызываются в порядке наследования, например, если ClassA является производным от , производным от ClassC ClassB которого является конструктор, ClassC сначала вызывается конструктор, а затем ClassB конструктор, а затем ClassA конструктор.

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

Если конструктор создает исключение, то удаление выполняется в порядке, обратном созданию.

Отменяется код в теле функции конструктора.

Объекты базовых классов и объекты-члены удаляются в порядке, обратном объявлению.

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

Производные конструкторы и расширенная инициализация агрегатов

Если конструктор базового класса не является открытым, но доступен для производного класса, нельзя использовать пустые фигурные скобки для инициализации объекта производного типа в /std:c++17 режиме, а затем в Visual Studio 2017 и более поздних версий.

В следующем примере показана соответствующая реакция на событие в C++14:

В C++17 Derived теперь считается агрегатным типом. Это означает, что инициализация Base через закрытый конструктор по умолчанию происходит непосредственно как часть расширенного правила агрегатной инициализации. Ранее частный Base конструктор был вызван через Derived конструктор, и он был успешно выполнен из-за friend объявления.

В следующем примере показано поведение C++17 в Visual Studio 2017 и более поздних версий в /std:c++17 режиме:

Конструкторы для классов с множественным наследованием

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

Должны выводиться следующие выходные данные:

Делегирующие конструкторы

Делегирующий конструктор вызывает другой конструктор в том же классе для выполнения некоторых действий по инициализации. Эта функция полезна, если у вас есть несколько конструкторов, которые все должны выполнять аналогичную работу. Основную логику можно написать в одном конструкторе и вызвать из других. В следующем тривиальном примере Box(int) делегирует свою работу Box(int,int,int):

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

Наследование конструкторов (C++11)

Производный класс может наследовать конструкторы от прямого базового класса с помощью using объявления, как показано в следующем примере:

Visual Studio 2017 и более поздних версий: оператор using в /std:c++17 режиме и более поздних версиях преобразует все конструкторы из базового класса, за исключением тех, которые имеют идентичную сигнатуру конструкторам в производном классе. Как правило, рекомендуется использовать наследуемые конструкторы, когда производный класс не объявляет новые члены данных или конструкторы.

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

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

Конструкторы и составные классы

Классы, содержащие члены типа класса, называются составными классами. При создании члена типа класса составного класса конструктор вызывается перед собственным конструктором класса. Если у содержащегося класса нет конструктора по умолчанию, необходимо использовать список инициализации в конструкторе составного класса. В предыдущем примере StorageBox при присвоении типу переменной-члена m_label нового класса Label необходимо вызвать конструктор базового класса и инициализировать переменную m_label в конструкторе StorageBox :

I'm preparing for an upcoming C++ exam and came across this question about classes and constructors:

How many constructors does the class Fraction have?"

I thought it's only one, but they suggested there are three:

Or in other words:
Is a function with default values an overloaded function?

There are actually three call variants that can be used, but theres only one constructor. That's either a trick question or asked unclearly. Should have been "How many constructor call candidates does the class Fraction have?" Bad question otherwise, sue them if you fail the exam because of that :-P.

It all boils down to whether they gave you those 3 constructor invocations as "proof" that there are 3 constructors, if that's the case they are likely inadequate. They likely have the answer 3 which counts implicitly generated constructors as well, but without the clarification or knowledge that's the case, so they threw that confusing nonsense at you to simulate adequacy, hoping they will get away with it and you won't come asking here on SO :)

Look at the generated machine code and you'll see those three calls all call the same routine - and the "default" arguments are present (pushed or put in registers, as appropriate) at the call site. So "they" are wrong.

6 Answers 6

There is only one constructor corresponding to the posted declaration, not three overloads.

are equivalent to:

One more way to convince yourself that there is only one constructor corresponding to the declaration is that you only need to define one constructor, not three.

The section of the C++11 standard on default arguments has this:

8.3.6 Default arguments

1 If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.

2 [ Example: the declaration

declares a function that can be called with zero, one, or two arguments of\ type int . It can be called in any of these ways:

The last two calls are equivalent to point(1,4) and point(3,4) , respectively. —end example ]

Now the main question.

How many constructors does the class Fraction have?

If the person that framed the question wants to include the move constructor and the copy constructor, which are implicitly generated by the compiler unless explicitly deleted, in the set of constructors, then the answer is three. In that case, the question is a trick question.

Compiler generated copy and move constructors count or not? Answer would still be 3 if so. Looks like a trick question for me.

@πάνταῥεῖ Are the default ones implicitly defined only if you default them explicitly or if you actually use them? In this case, there are potentially three constructor definitions, but it mostly depends on the rest of the code.

When I was in education and on a C++ course the answer would have been 3 to make sure we remembered about the default and copy constructors that get automatically generated. I was in education between early 2001 to 2006. If that question is from an older exam paper, it will be about copy and default constructors to make sure students remember they will be there even if not shown in code.

@TafT, the default constructor will not be generated by the compiler in this particular case since the declared constructor also works as the default constructor. In pre-C++11 days, the answer to that question would be 2. This must be a newer question.

Is a function with default values an overloaded function?

No. Overloads look like

and have each their own implementation (definition), while a function with default parameters has a single implementation.

I thought it's only one, but they suggested there are 3: .

"How many constructors does the class Fraction have?"

The definite answer for the given code snippet is 3 (in words three).

There's one specialized constructor (which serves three variants of calling), and the compiler generates a copy and move constructor automatically if you don't delete them, or provide a custom implementation:

So for such exam, if you will answer

The class has one constructor

That's wrong anyways. If you will answer

The class has three constructors (as you wrote that is the accepted answer)

you'll need to explain in depth, why you think so (as explained above).
In any oral exam I'd ask you to backup why exactly, so I'd do in an apprentice test.

The answer to your question is relative to these three follow-up questions:

  1. Before C++ 11, C++ 11, or C++14 and beyond?
  2. Do the implicitly defined constructors count?
  3. What are the members? The presence of a non-copyable member will delete the implicit copy constructor.

The explicit definition is only one constructor; the compiler will insert a three-argument call regardless of whether the call explicitly supplies 0, 1, or 2 arguments.

In pre-'11 there are no move constructors, in '11 there are two implicit constructor definitions, Fraction(const Fraction &) noexcept and Fraction(Fraction &&) noexcept , check the accessible cppreference, in '14 the rules of when there is an implicitly defined move constructor change.

The question you got is unfortunately innocently looking but quite technical; I hope your class does not insist on oversimplifying C++, it is the worst way to learn it.

You have only one declaration of a constructor.
On the other side:

When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded

Because of that, I'd not use the term overloaded here.

This kind of function definition defines a single function but 2 additional calling syntaxes. The subtle difference becomes apparent when taking function pointers or matching a template function argument to overloaded functions: in that case you only have a function with full argument list as the available overloaded type.

Now the tricky thing is that we are talking about a constructor here, and a constructor does not engage in the same kind of overloading resolution as an ordinary function and is, for all purposes, not accessible other than syntactically. In particular, this definition does count separately as a default constructor. It also counts separately as a converting constructor from int and can be used as ((Fraction)3).

So for all practical purposes, it creates three different syntactic entities in the constructor category. And as opposed to ordinary functions, there is no functional context where overload resolution would expose the difference between three actual function signatures and three merely syntactical calling conventions.

This is not a good question for a written test. This is really something for an oral exam since there are so many subtleties involved that the difference between a formally correct answer (if any) and a formally wrong answer is not likely to correlate well with actual knowledge and skill, and the reasoning behind any answer is more important than the answer itself.

Gray Pipe

Приносим извинения. Запрошенное содержимое было удалено. Вы будете автоматически перенаправлены через 1 секунду.

Лучший отвечающий

Вопрос

добрый день.
при создании приложения/игры
возник вопрос,

а сколько вообще должно быть конструкторов в классе,

и как это может влиять на производительность приложения?

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

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

заранее благодарен за ответ.

Ответы

стоит мне в классе

держать несколько конструкторов ( про запас)
или же нет (ограничится только теми которые явно использую)
как это принято в мире ООП (есть какие нибудь "правила хорошего тона/кода")?
заранее благодарен за ответ.

Основные принципы, которыми следует руководствоваться:

Сделайте вот так, да и не переживайте :)

Все ответы

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

С конструкторами экземпляров может быть другая проблема:

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

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

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

"а сколько вообщедолжно быть конструкторов в классе," - как минимум один. Если вы ничего не создаёте, используется конструктор по умолчанию (без параметров).

"и как это может влиять на производительность приложения?" - количество конструкторов никак не влияет на производителность, только на объём кода. Другое дело, что вы там напишите. При создании объекта используется только один конструктор (хотя он может вызвать менее обобщённый конструктор того же типа, т.е. получается цепочка вызовов).

"стоит ли мне создавать варианты(перегруженные конструкторы)
или же обойтись одним по умолчанию и одним с нужным набором элементов?" - если в этом есть необходимость, да. Иначе нет. Не используйте возможность, только потому, что она есть. Используйте - если нужно.

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