Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2 Мб
Скачать
☆

5.5.7 Предостережение

Когда в конструкторе производится указателю this, значе­ние this до этого присваивания неопределено. Таким образом, ссылка на член до этого присваивания неопределена и скорее всего приведет к катастрофе. Имеющийся компилятор не пытается убедиться в том, что присваивание указателю this происходит на всех траекториях выполнения:

mytype::mytype(int i)

(*

if (i) this = mytype_alloc();

// присваивание членам

*);

откомпилируется, и при i==0 никакой объект размещен не будет.

Конструктор может определить, был ли он вызван операцией new, или нет. Если он вызван new, то указатель this на входе имеет нулевое значение, в противном случае this указывает на пространство, уже выделенное для объекта (например, на стек). Поэтому можно просто написать конструктор, который выделяет память, если (и только если) он был вызван через new. Напри­мер:

mytype::mytype(int i)

(*

if (this == 0) this = mytype_alloc();

// присваивание членам

*);

Эквивалентного средства, которое позволяет деструктору решить вопрос, был ли его объект создан с помощью new, не имеется, как нет и средства, позволяющего ему узнать, вызвала ли его delete, или он вызван объектом, выходящим из области видимости. Если для пользователя это существенно, то он может сохранить где-то соответствующую информацию для деструктора. Другой способ - когда пользователь обеспечивает, что объекты этого класса размещаются только соответствующим образом. Если удается справиться с первой проблемой, то второй способ инте­реса не представляет.

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

5.5.8 Объекты Переменного Размера

Когда пользователь берет управление распределением и ос­вобождением памяти, он может конструировать объекты размеры, которых во время компиляции недетерминирован. В предыдущих примерах вмещающие (или контейнерные - перев.) классы vector, stack, intset и table реализовывались как структуры доступа фиксированного размера, содержащие указатели на реальную па­мять. Это подразумевает, что для создания таких объектов в свободной памяти необходимо две операции по выделению памяти, и что любое обращение к хранимой информации будет содержать дополнительную косвенную адресацию. Например:

class char_stack (*

int size;

char* top;

char* s;

public:

char_stack(int sz) (* top=s=new char[size=sz]; *)

~char_stack() (* delete s; *) // деструктор

void push(char c) (* *top++ = c; *)

char pop() (* return *--top; *)

*);

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

class char_stack (*

int size;

char* top;

char s[1];

public:

char_stack(int sz);

void push(char c) (* *top++ = c; *)

char pop() (* return *--top; *)

*);

char_stack::char_stack(int sz)

(*

if (this) error("стек не в свободной памяти");

if (sz < 1) error("размер стека < 1");

this = (char_stack*) new char[sizeof(char_stack)+sz-1]; size = sz;

top = s;

*)

Заметьте, что деструктор больше не нужен, поскольку па­мять, которую использует char_stack, может освободить delete без всякого содействия со стороны программиста.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]