Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 [ 60 ] 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

класса. Вот реализация предыдущего производного класса для самого худшего случая:

class derived : public base

string s; public:

derived( const derived &r );

const derived &operator=( const derived &r );

-----------------------------------------------------------

derived::derived( const derived &r ) : base(r), s(r.s)

-----------------------------------------------------------const derived &derived::operator=( const derived &r )

(* (base*)this)=r; s = r.s;

Список инициализации членов в конструкторе копии описан ранее. Следующий отрывок из функции operator=() нуждается в некотором пояснении:

(* (base*)this)=r;

Указатель this указывает на весь текущий объект; добавление оператора приведения преобразует его в указатель на компонент базового класса в текущем объекте - (base*) this. (* (base*) this) является самим объектом, а выражение (* (base*) this)=r передает этому объекту сообщение, вызывая функцию operator=() базового класса для перезаписи информации из правого операнда в текущий объект. Вы могли бы заменить этот код таким образом:

base:: operator=(r);

но я видел компиляторы, которые бракуют этот оператор, если в базовом классе не объявлена явно функция operator=(). Первая форма работает независимо от того, объявлена явно operator=() , или нет. (Если не объявлена, то у вас будет по умолчанию реализовано почленное копирование).



Стандартом языка для этого предусмотрено ключевое слово explicit. - Ред.

1 32. Конструкторы, не предназначенные для

преобразования типов, должны иметь два или более аргумента

Си++ использует конструкторы для преобразования типов. Например, конструктор char* в 9-ой строке листинга 7 на странице 155 также обрабатывает следующую операцию приведения:

char *pchar = "абвг"; (string) pchar;

Запомните, что приведение является операцией времени выполнения, которая создает временную переменную нужного типа и инициализирует ее из аргумента. Если приводится класс, то для инициализации используется конструктор. Следующий код работает прекрасно, потому что строковая константа char* беспрепятственно преобразуется в string для передачи в функцию f() :

f( const string &s ); ...

f( " белиберда");

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

class array

... public:

array( int initial size );

Вероятно вы все же не захотите, чтобы следующий код работал:

f( const array &a ); ...

f( isupper(*str) );

(Этот вызов передает f() пустой одноэлементный массив, если *str состоит из заглавных букв, или массив без элементов, если *str - из строчных букв).

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



class array

...

public:

enum bogus { set size to }; array( bogus, int initial size );

array ar( array::set size to, 128 );

Это по настоящему уродливо, но у нас нет выбора. Заметьте, что я не дал аргументу bogus имени, потому что он используется только для выбора функции.

133. Используйте счетчики экземпляров объектов для инициализации на уровне класса

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

class window

static int num windows; public:

window(); ~window();

int window::num windows = 0; window::window()

if( ++num windows == 1 ) только что создано первое окно initialize video system();

window::~window()

if( --num windows == 0 ) только что уничтожено

shut down video system(); последнее окно

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 [ 60 ] 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76