7.1. Конструктори¶

Усі визначені користувачем типи включають конструктор. Навіть якщо ви не пишете одного явно, компілятор напише його для вас. Для більшості класів і структур, які ми бачили до цього часу, ми дозволяємо компілятору генерувати конструктори за замовчуванням для нас.

конструктора замовчуванням

Конструктор за замовчуванням може бути перевантажений, як і будь-яка інша функція. Під час проектування типів ви можете створювати конструктори з одним, двома або більше аргументами.

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

Отже, хоча це добре:

Ми можемо ініціалізувати нашу точку лише за допомогою конструктора за замовчуванням.

Якщо ми хочемо ініціалізувати членів класу під час побудови, тоді нам потрібно додати власні конструктори.

Тепер ми можемо використовувати наш конструктор аргументів 2, але тепер наше старе виклик за замовчуванням порушено. Ви побачите і помилку в такий спосіб:

Це виправляється будь-яким:

Написання власної реалізації за замовчуванням

Запрошення компілятора написати його

Запрошення компілятора видалити його

У цьому випадку спроба використання конструктора за замовчуванням все ще є помилкою компіляції, але помилка є більш явною: ви не можете використовувати її, оскільки її було видалено.

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

Загалом, подбайте про видалення конструктора за замовчуванням. Видаляйте його лише тоді, коли впевнені, що об’єкти класу будуть ніколи потрібно побудувати за замовчуванням.

7.1.1. Синтаксис ініціалізації¶

Деякі програмісти, що звертаються до C ++ з інших мов OO, іноді відчувають, ніби їм доводиться ініціалізувати такі об'єкти:

Незважаючи на те, що весь семестр ви писали:

Що стосується визначених користувачем типів, іноді це здається «неповним», якщо ви не включаєте (). Зазвичай ці дужки створюють більше проблем, ніж вирішують. Це пов’язано з невід’ємною неоднозначністю мови С ++. Хоча нам здається очевидним твердження пункт p (); є викликом конструктора за замовчуванням, і результатами має бути нова змінна p, компілятор інтерпретує це по-різному.

Основне правило:

Це означає, що у наведеному вище коді компілятор замість цього шукає:

функція з іменем p

що не бере аргументів

і повертає об'єкт типу point

Оскільки в цьому випадку такої функції немає, вона повертає помилку. Деякі компілятори, як clang, спробують сказати вам:

C ++ вирішує цю неоднозначність у C ++ 11, використовуючи єдиний синтаксис ініціалізатора. Ви можете використовувати фігурні дужки: <> замість дужок для ініціалізації об’єктів. Фігурні дужки - це розширення синтаксису списку ініціалізаторів для контейнерів і може використовуватися навіть для побудованих за замовчуванням об'єктів.

Хоча вищезазначене працює кожного разу, бажано повністю опускати фігурні дужки, коли це не потрібно:

Синтаксис ініціалізатора також працює в конструкторах.

Нагадаємо, що для контейнерів існує різниця між вектором (5) та вектором. Яка різниця?

Перша версія створює вектор розміром 5 без ініціалізованих значень.

Друга версія створює вектор розміром 1 з одним значенням, рівним 5.

7.1.2. Перевантажені конструктори¶

Ті самі вказівки, що стосуються написання хороших функцій, стосуються написання хороших перевантажених конструкторів. Хороший клас побудований навколо хороших функцій. Як і у випадку зі звичайними функціями, уникайте переплутування списків параметрів. Розглянемо наступне:

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

Навіть якщо ми прочитаємо код і вивчимо замовлення, все одно ймовірно, що ми забудемо замовлення і в якийсь момент перенесемо місяць і день.

Замість того, щоб змиритися з надією, що ми пам’ятаємо, або мати проблеми з налагодженням під час виконання, просто визначення відповідних типів покращує чіткість та корисність:

Ця версія легше запам'ятовується програмістам, а всі помилки - це помилки компіляції, а не помилки виконання.

7.1.3. Телескопічні конструктори¶

Початковий клас дати страждав від загальної проблеми дизайну: занадто багато параметрів одного типу. Тісно пов'язана проблема полягає в тому, як забезпечити гнучкість при побудові нових об'єктів. Загальним рішенням є надання конструкторам різної кількості аргументів:

А як щодо можливості вказати місяць і день? Скільки різних конструкторів слід дозволити? Кількість перестановок стає неможливою навіть за відносно невеликої кількості параметрів.

Це називається телескопічним конструктором і, як правило, вважається антивізером. Тобто є кращі рішення цієї проблеми.

Найпростішим рішенням у C ++ є використання значень за замовчуванням для параметрів функції. Це найкраще працює, коли значення за замовчуванням мають різні типи, і немає необхідності дозволяти всі можливі комбінації параметрів.

Це рішення все ще обмежується тим фактом, що значення за замовчуванням все ще оцінюються зліва направо. Декларація дати за формою

не створить дату на 15 число поточного місяця та року. Крім того, рішення погано працює, коли всі (або більшість) параметрів однакові. Розглянемо цей приклад:

Правильний порядок калорій, жиру, вуглеводів чи жиру, калорій, вуглеводів чи щось інше? Навіть якщо ми даємо цим параметрам значущі назви, виконання вимог до виконання не існує. Легко помилитися, коли забагато параметрів однакові.

Коли стикається з багатьма необов’язковими параметрами, конструктор є ефективною альтернативою. Основні ідеї:

Використовуйте параметри конструктора, щоб прийняти обов’язкові параметри.

Використовуйте допоміжний клас (Builder) для ініціалізації за замовчуванням необов’язкових параметрів.

Функція Builder: build () створює об’єкт NutritionFacts із конструктора.

Будівельник робить клас, який допомагає другові.

Це використовується лише для того, щоб уникати створення додаткових функцій конструктора.

Конструктор перетворення використовується для копіювання стану конструктора в клас, що обгортає.

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

Хоча це не найідіоматичніше рішення на C ++, це те, що ми можемо створити та використовувати лише знання класів, які ми маємо на сьогодні. Пізніше ми переглянемо шаблон будівельника після того, як охопимо спадщину.

Більше для вивчення

Будівництво шаблон дизайну:

Ефективна Java, Джошуа Блох. Пункт 2: Розгляньте конструктор, коли стикаєтеся з багатьма параметрами конструктора