Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


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



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

#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 ); // перегружает print()

}

namespace Disney {

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

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

extern void print( int );

}

Использование using-объявлений и using-директив помогает сделать члены пространства имен доступными в других областях видимости. Эти механизмы оказывают определенное влияние на объявления перегруженных функций. (Using-объявления и using-директивы рассматривались в разделе 8.6.)

Каким образом using-объявление сказывается на перегрузке функций? Напомним, что оно вводит псевдоним для члена пространства имен в ту область видимости, в которой это объявление встречается. Что делают такие объявления в следующей программе?

namespace libs_R_us {

int max( int, int );

int max( double, double );

 

extern void print( int );

extern void print( double );

}

 

// using-объявления

using libs_R_us:: max;

using libs_R_us:: print( double ); // ошибка

 

void func()

{

max( 87, 65 ); // вызывает libs_R_us:: max( int, int )

max( 35.5, 76.6 ); // вызывает libs_R_us:: max( double, double )

Первое using-объявление вводит обе функции libs_R_us:: max в глобальную область видимости. Теперь любую из функций max() можно вызвать внутри func(). По типам аргументов определяется, какую именно функцию вызывать. Второе using-объявление – это ошибка: в нем нельзя задавать список параметров. Функция libs_R_us:: print() объявляется только так:

using libs_R_us:: print;

Using-объявление всегда делает доступными все перегруженные функции с указанным именем. Такое ограничение гарантирует, что интерфейс пространства имен libs_R_us не будет нарушен. Ясно, что в случае вызова

print( 88 );

автор пространства имен ожидает, что будет вызвана функция libs_R_us:: print(int). Если разрешить пользователю избирательно включать в область видимости лишь одну из нескольких перегруженных функций, то поведение программы становится непредсказуемым.

Что происходит, если using-объявление вводит в область видимости функцию с уже существующим именем? Эти функции выглядят так, как будто они объявлены прямо в том месте, где встречается using-объявление. Поэтому введенные функции участвуют в процессе разрешения имен всех перегруженных функций, присутствующих в данной области видимости:

#include < string>

namespace libs_R_us {

extern void print( int );

extern void print( double );

}

 

extern void print( const string & );

 

// libs_R_us:: print( int ) и libs_R_us:: print( double )

// перегружают print( const string & )

using libs_R_us:: print;

 

void fooBar( int ival )

{

print( " Value: " ); // вызывает глобальную функцию

                  // print( const string & )

print( ival );   // вызывает libs_R_us:: print( int )

}

Using-объявление добавляет в глобальную область видимости два объявления: для print(int) и для print(double). Они являются псевдонимами в пространстве libs_R_us и включаются в множество перегруженных функций с именем print, где уже находится глобальная print(const string & ). При разрешении перегрузки print в fooBar рассматриваются все три функции.

Если using-объявление вводит некоторую функцию в область видимости, в которой уже имеется функция с таким же именем и таким же списком параметров, это считается ошибкой. С помощью using-объявления нельзя задать псевдоним для функции print(int) в пространстве имен libs_R_us, если в глобальной области видимости уже есть print(int). Например:

namespace libs_R_us {

void print( int );

void print( double );

}

 

void print( int );

 

using libs_R_us:: print; // ошибка: повторное объявление print(int)

 

void fooBar( int ival )

{

print( ival );    // какая print? :: print или libs_R_us:: print

}

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

#include < string>

namespace libs_R_us {

extern void print( int );

extern void print( double );

}

 

extern void print( const string & );

 

// using-директива

// print(int), print(double) и print(const string & ) - элементы

// одного и того же множества перегруженных функций

using namespace libs_R_us;

 

void fooBar( int ival )

{

print( " Value: " ); // вызывает глобальную функцию

                  // print( const string & )

print( ival );   // вызывает libs_R_us:: print( int )

}

Это верно и в том случае, когда есть несколько using-директив. Одноименные функции, являющиеся членами разных пространств, включаются в одно и то множество:

namespace IBM {

int print( int );

}

namespace Disney {

double print( double );

}

// using-директива

// формируется множество перегруженных функций из различных

// пространств имен

using namespace IBM;

using namespace Disney;

 

long double print(long double);

 

int main() {

print(1);    // вызывается IBM:: print(int)

print(3.1); // вызывается Disney:: print(double)

return 0;

}

Множество перегруженных функций с именем print в глобальной области видимости включает функции print(int), print(double) и print(long double). Все они рассматриваются в main() при разрешении перегрузки, хотя первоначально были определены в разных пространствах имен.

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

9.1.5. Директива extern " C" и перегруженные функции A

В разделе 7.7 мы видели, что директиву связывания extern " C" можно использовать в программе на C++ для того, чтобы указать, что некоторый объект находится в части, написанной на языке C. Как эта директива влияет на объявления перегруженных функций? Могут ли в одном и том же множестве находиться функции, написанные как на C++, так и на C?

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

// ошибка: для двух перегруженных функций указана директива extern " C"

extern " C" void print( const char* );

extern " C" void print( int );

Приведенный ниже пример перегруженной функции calc() иллюстрирует типичное применение директивы extern " C":

class SmallInt ( /*... */ );

class BigNum ( /*... */ );

 

// написанная на C функция может быть вызвана как из программы,

// написанной на C, так и из программы, написанной на C++.

// функции C++ обрабатывают параметры, являющиеся классами

extern " C" double calc( double );

extern SmallInt calc( const SmallInt& );

extern BigNum calc( const BigNum& );

Написанная на C функция calc() может быть вызвана как из C, так и из программы на C++. Остальные две функции принимают в качестве параметра класс и, следовательно, их допустимо использовать только в программе на C++. Порядок следования объявлений несуществен.

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

Smallint si = 8;

int main() {

calc( 34 ); // вызывается C-функция calc( double )

calc( si ); // вызывается функция C++ calc( const SmallInt & )

//...

return 0;

}


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 295; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.029 с.)
Главная | Случайная страница | Обратная связь