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

1 ... 139 140 141 [ 142 ] 143 144 145 ... 395


объявляют одну и ту же функцию

void f( int ); void f( const int );

Спецификатор const важен только внутри определения функции: он показывает, что в теле функции запрещено изменять значение параметра. Однако аргумент, передаваемый по значению, можно использовать в теле функции как обычную инициированную переменную: вне функции изменения не видны. (Способы передачи аргументов, в частности передача но значению, обсуждаются в разделе 7.3.) Добавление спецификатора const к параметру, передаваемому по значению, не влияет на его интерпретацию. Функции, объявленной как f(int) , может быть передано любое значение типа int, равно как и функции f(const int) . Поскольку они обе принимают одно и то же множество значений аргумента, то приведенные объявления не считаются перегруженными. f() можно определить как

void f( int i ) { }

или как

void f( const int i ) { }

Наличие двух этих определений в одной программе - ошибка, так как одна и та же функция определяется дважды.

Однако, если спецификатор const или volatile применяется к параметру указательного

объявляются разные функции

void f( int* ); void f( const int* );

и здесь объявляются разные функции

void f( int& );

или ссылочного типа, то при сравнении объявлений он учитывается.

void f( const int& );

9.1.3. Когда не надо перегружать имя функции

В каких случаях перегрузка имени не дает преимуществ? Например, тогда, когда присвоение функциям разных имен облегчает чтение программы. Вот несколько примеров. Следующие функции оперируют одним и тем же абстрактным типом даты. На

void setDate( Date&, int, int, int );

Date &convertDate( const string & );

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

void printDate( const Date& );

Эти функции работают с одним типом данных - классом Date, но выполняют семантически различные действия. В этом случае лексическая сложность, связанная с



#include <string> class Date { public:

set( int, int, int ); Date& convert( const string & ); void print();

...

при этом оставить разные имена, отражающие смысл операции:

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

Screen& moveHome() ; Screen& moveAbs( int, int );

Screen& moveRel( int, int, char *direction ); Screen& moveX( int );

move() :

Screen& moveY( int );

Впрочем, последние две функции перегрузить нельзя, так как у них одинаковые списки

функция, объединяющая moveX() и moveY()

параметров. Чтобы сделать сигнатуру уникальной, объединим их в одну функцию:

Screen& move( int, char xy );

Теперь у всех функций разные списки параметров, так что их можно перегрузить под именем move() . Однако этого делать не следует: разные имена несут информацию, без которой программу будет труднее понять. Так, выполняемые данными функциями операции перемещения курсора различны. Например, moveHome() осуществляет специальный вид перемещения в левый верхний угол экрана. Какой из двух приведенных

какой вызов понятнее?

myScreen.home(); считаем, что этот!

ниже вызовов более понятен пользователю и легче запоминается?

myScreen.move();

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

употреблением различных имен, проистекает из принятого программистом соглашения об обеспечении набора операций над типом данных и именования функций в соответствии с семантикой этих операций. Правда, механизм классов C++ делает такое соглашение излишним. Следовало бы сделать такие функции членами класса Date, но



#include <string>

void print( const string & );

void print( double ); перегружает print()

void fooBar( int ival )

отдельная область видимости: скрывает обе реализации print() extern void print( int );

ошибка: print( const string & ) не видна в этой области print( Value: );

print( ival ); правильно: print( int ) видна

примеру, локально объявленная функция не перегружает, а просто скрывает глобальную:

Поскольку каждый класс определяет собственную область видимости, функции, являющиеся членами двух разных классов, не перегружают друг друга. (Функции-члены класса описываются в главе 13. Разрешение перегрузки для функций-членов класса рассматривается в главе 15.)

Объявлять такие функции разрешается и внутри пространства имен. С каждым из них также связана отдельная область видимости, так что функции, объявленные в разных

#include <string> namespace IBM {

extern void print( const string & ); extern void print( double ); пер

extern void print( double ); перегружает print()

namespace Disney

от не

отдельная область видимости:

не перегружает функцию print() из пространства имен IBM extern void print( int );

пространствах, не перегружают друг друга. Например:

moveAbs(int, int); moveAbs(int, int, char*);

различаются наличием третьего параметра тина char*. Если их реализации похожи и для третьего аргумента можно найти разумное значение по умолчанию, то обе функции можно заменить одной. В данном случае на роль значения по умолчанию подойдет указатель со значением 0:

move( int, int, char* = 0 );

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

9.1.4. Перегрузка и область видимости A

Все перегруженные функции объявляются в одной и той же области видимости. К



1 ... 139 140 141 [ 142 ] 143 144 145 ... 395

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