Программирование >>  Инициализация объектов класса, структура 

1 ... 219 220 221 [ 222 ] 223 224 225 ... 395


int main()

Account acct;

...

то компилятор сначала проверяет, определен ли для класса Account конструктор по умолчанию. Возникает одна из следующих ситуаций:

1. Такой конструктор определен. Тогда он применяется к acct.

2. Конструктор определен, но не является открытым. В данном случае определение acct помечается компилятором как ошибка: у функции main() нет прав доступа.

3. Конструктор по умолчанию не определен, но есть один или несколько конструкторов, требующих задания аргументов. Определение acct помечается как ошибка: слишком мало аргументов у конструктора.

4. Нет ни конструктора но умолчанию, ни какого-либо другого. Определение считается корректным, acct не инициализируется, конструктор не вызывается.

Пункты 1 и 3 должна: быть уже достаточно понятны (если это не так, перечитайте данную главу) Посмотрим более внимательно на пункты 2 и 4.

Допустим, что все члены класса Account объявлена: открытыми и не объявлено никакого

class Account { public:

char * name;

unsigned int acct nmbr;

double balance;

конструктора:

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

статический класс хранения

вся ассоциированная с объектом память обнуляется

Account global scope acct; static Account file scope acct;

Account foo() {

static Account local static acct;

...

классов):



/ / локальн1е и распределенные из хипа объекты не инициализированы до момента явной инициализации присваивания

Account bar() {

Account local acct; Account *heap acct = new Account;

...

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

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

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

14.2.2. Ограничение прав на создание объекта

Доступность конструктора определяется тем, в какой секции класса он объявлен. Мы можем ограничить или явно запретить некоторые формы создания объектов, если поместим соответствующий конструктор в неоткрытую секцию. В примере ниже конструктор по умолчанию класса Account объявлен закрытым, а с двумя параметрами -

class Account {

friend class vector< Account >; public:

explicit Account( const char*, double = 0.0 );

...

private:

Account();

...

открытым:

3 Для тех, кто раньше программировал на C: приведенное выше определение класса Account на C выглядело бы так:

typedef struct {

aba г-

char * name;

unsigned int acct nmbr; double balance;

} Account;

Однако объекты, определенные локально или распределенные динамически, в начальный



14.2.3. Копирующий конструктор

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

Часто почленная инициализация не обеспечивает корректного поведения класса. Поэтому мы явно определяем копирующий конструктор. В нашем классе Account это необходимо, иначе два объекта будут иметь одинаковые номера счетов, что запрещено спецификацией класса.

Копирующий конструктор принимает в качестве формального параметра ссылку на объект класса (традиционно объявляемую со спецификатором const). Вот его

inline Account:: Account( const Account &rhs )

: balance( rhs. balance )

name = new char[ strlen(rhs. name) + 1 ]; strcpy( name, rhs. name );

копировать rhs. acct nmbr нельзя acct nmbr = get unie acct nmr() ;

реализация:

Когда мы пишем:

Account acct2( acct1 );

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

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

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

предотвращения копирования одного объекта в другой объект того же класса (эта проблема рассматривается в следующем подразделе);

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



1 ... 219 220 221 [ 222 ] 223 224 225 ... 395

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика