Страница 31 из 74 116. Избегайте перегрузки функций и аргументов, используемых по умолчанию.
Это правило не применяется к конструкторам и функциям перегрузки операций. Перегрузка функций, подобно многим другим свойствам С++, была добавлена к этому языку по особым причинам. Не позволяйте себя увлечь этим. Функции, которые делают разные вещи, должны иметь и разные имена. Перегруженные функции обычно вызывают больше проблем, чем их решают. Во-первых, проблема двусмысленности: f( int, long ); f( long, int ); f( 10, 10 ); // ОШИБКА: Какую из функций я вызываю? Более коварно следующее: f( int ); f( void* ); f( 0 ); // ОШИБКА: Вызов двусмысленный? Проблемой здесь является С++, который считает, что 0 может быть как указателем, так и типом int. Если вы делаете так: const void *NULL = 0; const int ZERO = 0; то вы можете записать f(NULL) для выбора варианта с указателем и f(ZERO) для доступа к целочисленному варианту, но это ведет к большой путанице. В такой ситуации вам бы лучше просто использовать функции с двумя разными именами. Аргументы по умолчанию, создающие на самом деле перегруженные функции (по одной на каждую возможную комбинацию аргументов), также вызывают проблемы. Например, если вы написали: f( int x = 0 );и затем случайно вызвали f() без аргументов, компилятор успешно и без возражений вставит 0. Все, чего вы добились, - это устранили то, что в ином случае вызвало бы полезное сообщение об ошибке во время компиляции, и сдвинули ошибку на этап выполнения. Исключениями из сказанного выше являются перегруженные операции и конструкторы; многие классы имеют их по нескольку, и аргументы по умолчанию часто имеют смысл в конструкторах. Код, подобный следующему, вполне приемлем: class string { public: string( char *s = "" ); string( const string ?r ); string( const CString ?r ); // преобразование из класса MFC. // ... };Для пояснения: разные классы будут часто обрабатывать одно и то же сообщение, реализуя функции-обработчики с совпадающими именами. Например, большинство классов реализуют сообщение print(). Смысл того, что я пытаюсь здесь добиться, такой: плохая мысль - в одном классе иметь много обработчиков сообщений с одним и тем же именем. Вместо: class string { // ... public: print( FILE *fp ); print( iostream ?ios ); print( window ?win ); я бы рекомендовал: class string { // ... public: print_file ( FILE *fp ); print_stream ( iostream ?ios ); print_window ( window ?win ); Еще лучше, если бы у вас был класс устройства device, который бы мог представлять типы: файловый FILE, потоковый iostream и оконный window, в зависимости от того, как он инициализируется - тогда бы вы могли реализовать единственную функцию print(), принимающую в качестве аргумента device. Я должен сказать, что сам порой нарушаю это правило, но делаю это, зная, что, переступив черту, могу навлечь на себя беду. |