Сколько конструкторов имеет класс?
Я готовлюсь к предстоящему экзамену С++ и наткнулся на этот вопрос о классах и конструкторах:
Сколько конструкторов имеет класс Fraction? "
class Fraction {
//...
public:
Fraction(int numerator = 0, int denominator = 1);
//...
};
Я думал, что это только один, но они предположили, что есть три:
Fraction();
Fraction(n);
Fraction(n, d);
Или другими словами:
Является ли функция со значениями по умолчанию перегруженной функцией?
Ответы
Ответ 1
Существует только один конструктор, соответствующий объявленному объявлению, а не три перегрузки.
Вызов
Fraction();
Fraction(n);
эквивалентны:
Fraction(0, 1);
Fraction(n, 1);
Еще один способ убедить себя в том, что только один конструктор, соответствующий объявлению, состоит в том, что вам нужно определить только один конструктор, а не три.
В разделе стандарта С++ 11 по умолчанию используется следующее:
8.3.6 Аргументы по умолчанию
1 Если в объявлении параметра указано предложение initializer, это условие инициализатора используется в качестве аргумента по умолчанию. Аргументы по умолчанию будут использоваться в вызовах, где отсутствуют отсутствующие аргументы.
2 [Пример: объявление
void point(int = 3, int = 4);
объявляет функцию, которая может быть вызвана с нулевым, одним или двумя аргументами \type int
. Его можно вызвать любым из следующих способов:
point(1,2); point(1); point();
Последние два вызова эквивалентны point(1,4)
и point(3,4)
соответственно. -end пример]
Теперь главный вопрос.
Сколько конструкторов имеет класс Fraction?
Если человек, создавший вопрос, хочет включить конструктор перемещения и конструктор копирования, которые неявно сгенерированы компилятором, если явно не удалено, в наборе конструкторов, тогда ответ будет три, В этом случае вопрос - это трюк.
Ответ 2
Является ли функция со значениями по умолчанию перегруженной функцией?
Нет. Перегрузки выглядят как
Fraction();
Fraction(int numerator);
Fraction(int numerator, int denominator);
и имеют свою собственную реализацию (определение), а функция с параметрами по умолчанию имеет единственную реализацию.
Я думал, что это только один, но они предположили, что есть 3:...
"Сколько конструкторов имеет класс Fraction?"
Это трюк, призванный обмануть вас, показывая доступные варианты вызовов для одного объявления конструктора.
Ответ определенный для данного фрагмента кода 3 (в словах три).
Существует один специализированный конструктор (который обслуживает три варианта вызова), и компилятор автоматически создает конструктор копирования и перемещения, если вы не delete
их, или предоставляете собственную реализацию:
Fraction(int numerator = 0, int denominator = 1); // (1)
// Redundant, just for demonstration:
Fraction(const Fraction& rhs) = default; // (2)
Fraction(Fraction&& rhs) = default; // (3)
Итак, для такого экзамена, если вы ответите
Класс имеет один конструктор
Это неправильно. Если вы ответите
В классе есть три конструктора (как вы написали, это принятый ответ)
вам нужно будет объяснить подробно, почему вы так думаете (как объяснялось выше).
В любом устном экзамене я прошу вас сделать резервную копию, почему именно, поэтому я бы сделал это на экзамене ученика.
Ответ 3
Ответ на ваш вопрос относится к этим трем последующим вопросам:
- Перед С++ 11, С++ 11 или С++ 14 и далее?
- Учитываются ли неявно определенные конструкторы?
- Что представляют собой члены? Присутствие не скопируемого элемента удалит неявный конструктор копии.
Явное определение - это только один конструктор; компилятор будет вставлять вызов с тремя аргументами независимо от того, явно ли он содержит аргументы 0, 1 или 2.
В pre-'11 нет конструкторов перемещения, в '11 есть два неявных определения конструктора, Fraction(const Fraction &) noexcept
и Fraction(Fraction &&) noexcept
, проверьте доступные cppreference, в '14 правила того, когда изменяется неявно определенная конструкция конструктора перемещения.
Вопрос, который вы получили, к сожалению, невинно выглядит, но довольно техничен; Надеюсь, ваш класс не настаивает на упрощении С++, это самый худший способ узнать его.
Ответ 4
У вас есть только одно объявление конструктора.
С другой стороны:
Если для одного имени в одной и той же области указано два или более разных объявления, это имя считается перегруженным
Из-за этого я не использовал бы здесь перегруженный термин.
Ответ 5
Такое определение функции определяет одну функцию, но 2 дополнительных синтаксиса вызова. Тонкая разница становится очевидной при использовании указателей функций или сопоставления аргумента функции шаблона с перегруженными функциями: в этом случае у вас есть только функция с полным списком аргументов как доступный перегруженный тип.
Теперь сложно сказать, что мы говорим о конструкторе здесь, и конструктор не участвует в том же виде перегрузочного разрешения, что и обычная функция, и для всех целей не доступен, кроме синтаксически. В частности, это определение действительно учитывает отдельно как конструктор по умолчанию. Он также учитывается отдельно как конструктор преобразования из int и может использоваться как ((Фракция) 3).
Таким образом, для всех практических целей он создает три разных синтаксических объекта в категории конструкторов. И в отличие от обычных функций нет функционального контекста, в котором разрешение перегрузки могло бы выявить разницу между тремя действительными сигнатурами функций и тремя просто соглашениями синтаксического вызова.
Это не очень хороший вопрос для письменного теста. Это действительно что-то для устного экзамена, потому что есть так много тонкостей, что разница между формально правильным ответом (если таковая имеется) и формально неправильным ответом вряд ли хорошо коррелирует с фактическими знаниями и навыками, а также аргументами в отношении любого ответа важнее самого ответа.
Ответ 6
Потому что это зависит от аргументов, которые вы передаете:
Fraction() --> Fraction(0,1)
Fraction(n)---> Fraction(n,1)
Fraction(n,m)
Таким образом, он дает 3 конструктора. Здесь нет перегрузки.
Ответ 7
Вы можете создать объект Fraction тремя способами, используя один конструктор, объявленный в указанном выше классе. Конструктор имеет параметры по умолчанию. Если вы не передаете какой-либо аргумент, он принимает соответствующее значение для аргумента.
-
Fraction a;
// числитель будет 0, а знаменатель будет 1
-
Fraction a(10);
// числитель будет 10, а знаменатель будет 1
-
Fraction a(10, 20);
// числитель будет 10, а знаменатель будет 20