Анимация
JavaScript
|
Главная Библионтека 119. Не портьте область глобальных имен: проблемы Си++ Определение класса обеспечивает отличный способ вывода идентификатора из области глобальных имен, потому что эти идентификаторы должны быть доступны или через объект, или посредством явного имени класса. Функция x.f() отличается от y.f(), если x и y являются объектами разных классов. Аналогично, x::f() отличается от y::f() . Вы должны смотреть на имя класса и :: как эффективную часть имени функции, которая может быть опущена лишь тогда, когда что-нибудь еще (типа . или ->) служит для уточнения. Я часто использую перечислитель для ограничения видимости идентификатора константы областью видимости класса: class tree enum { max nodes = 128 }; public: enum traversal mechanism { inorder, preorder, postorder }; print( traversal mechanism how = inorder ); ... ... tree t; ... t.print( tree::postorder ); Константа tree::postorder, переданная в функцию print() , определенно не в глобальной области имен, потому что для доступа к ней требуется префикс tree::. При этом не возникает конфликта имен, так как если другой класс имеет член с именем postorder, то он вне класса будет именоваться other class::postorder. Более того, константа max nodes является закрытой, поэтому к ней можно получить доступ лишь посредством функций-членов и друзей класса tree, что обеспечивает дальнейшее ограничение видимости. Преимущество перечислителя над членом-константой класса состоит в том, что его значение может быть инициализировано прямо в объявлении класса. Член-константа должен инициализироваться в функции-конструкторе, который может быть в другом файле. Перечислитель может Утверждение автора не соответствует стандарту язхка. - Ред. быть также использован в качестве размера в объявлении массива и в качестве значения case в операторе switch; константа ни в одном из этих мест работать не будет . Константа-член имеет свое предназначение. Во-первых, вы можете помещать в нее значения с типом, отличным от int. Во-вторых, вы можете инициализировать ее во время выполнения. Рассмотрим следующее определение глобальной переменной в Си++: const int default size = get default size from ini file(); Ее значение считывается из файла во время загрузки программы, и оно не может быть изменено во время выполнения. Вышеупомянутое также применимо к константам-членам класса, которые могут быть инициализированы через аргумент конструктора, но не могут меняться функциями-членами. Так как объект типа const не может стоять слева от знака равенства, константы-члены должны инициализироваться посредством списка инициализации членов следующим образом: class fixed size window const size height; const size width; fixed size window( size the height, size the width ) : height( the height ) , width ( the width ) Вложенные классы также полезны. Вам часто будет нужно создать "вспомогательный" класс, о котором ваш пользователь даже не будет знать. Например, текст программы из Листинга 10 реализует класс int array - динамический двухмерный массив, размер которого может быть неизвестен до времени выполнения. Вы можете получить доступ к его элементам, используя стандартный для Си/Си++ синтаксис массива (a[row][col]). Класс int array делает это, используя вспомогательный класс, о котором пользователь int array ничего не знает. Я использовал вложенное определение для удаления определения этого вспомогательного класса из области видимости глобальных имен. Вот как это работает: Выражение a[row][col] оценивается как (a[row])[col] . a[row] вызывает int array::operator[]() , который возвращает объект int array::row, ссылающийся на целую 1#include <iostream.h> 3 class int array 5 class row 7 friend class int array; 8 int *first cell in row; 9 10 row( int *p ) : first cell in row(p) {} 11 public: 12 int &operator[] ( int index ); 13 }; 15 int nrows; 16 int ncols; 17 int *the array; 18 19 public: 20 virtual 21 ~int array( void ); 22 int array( int rows, int cols ); 24 row operator[] ( int index); 25 }; 26 ======================================================== 27 функции-члены класса int array 28 ======================================================== 2 9 int array::int array( int rows, int cols ) 30 : nrows ( rows ) 31 , ncols ( cols ) 32 , the array ( new int[rows * cols]) 33 {} 34 --------------------------------------------------------35 int array::~int array( void ) 36 { 37 delete [] the array; 38 } 39 --------------------------------------------------------40 inline int array::row int array:: operator[]( int index ) 41 { строку. [col] применяется к этому объекту int array::row, приводя к вызову int array::row::operator[](). Эта вторая версия operator[]() возвращает ссылку на индивидуальную ячейку. Заметьте, что конструктор класса int array::row является закрытым, потому что я не хочу, чтобы любой пользователь имел возможность создать строку row. Строка должна предоставить дружественный статус массиву int array с тем, чтобы int array мог ее создать. Листинг 10. Вспомогательные классы 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 |