Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 93 94 95 96 97 98 99 100 101 ... 297
Перейти на страницу:

Обычно внутренняя область видимости может переопределить имя из внешней области видимости, даже если это имя уже использовалось во внутренней области видимости. Но если член класса использует имя из внешней области видимости и это имя типа, то класс не сможет впоследствии переопределить это имя:

typedef double Money;

class Account {

public:

 Money balance() { return bal; } // используется имя Money из внешней

                                 // область видимости

private:

 typedef double Money; // ошибка: нельзя переопределить Money

 Money bal;

 // ...

};

Следует заметить, что хотя определение типа Money в классе Account использует тот же тип, что и определение во внешней области видимости, этот код все же ошибочен.

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

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

При поиске имен в областях видимости члены класса следуют обычным правилам

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

• Сначала поиск объявления имени осуществляется в функции-члене. Как обычно, рассматриваются объявления в теле функции, только предшествующие месту использования имени.

• Если в функции-члене объявление не найдено, поиск продолжается в классе. Просматриваются все члены класса.

• Если объявление имени в классе не найдено, поиск продолжится в области видимости перед определением функции-члена.

Обычно не стоит использовать имя другого члена класса как имя параметра в функции-члене. Но для демонстрации поиска имени нарушим это правило в функции dummy_fcn():

// обратите внимание: это сугубо демонстрационный код, отражающий

// плохую практику программирования. Обычно не стоит использовать

// одинаковое имя для параметра и функции-члена

int height; // определяет имя, впоследствии используемое в Screen

class Screen {

public:

 typedef std::string::size_type pos;

 void dummy_fcn(pos height) {

  cursor = width * height; // какое имя height имеется в виду?

 }

private:

 pos cursor = 0;

 pos height = 0, width = 0;

};

Когда компилятор обрабатывает выражение умножения в функции dummy_fcn(), он ищет имена сначала в пределах данной функции. Параметры функции находятся в области видимости функции. Таким образом, имя height, используемое в теле функции dummy_fcn(), принадлежит ее параметру.

В данном случае имя height параметра скрывает имя height переменной-члена класса. Если необходимо переопределить обычные правила поиска, то это можно сделать так:

// плохой подход: имена, локальные для функций-членов, не должны

// скрывать имена переменных-членов класса

void Screen::dummy_fcn(pos height) {

 cursor = width * this->height; // переменная-член height

 // альтернативный способ указания переменной-члена

 cursor = width * Screen::height; // переменная-член height

}

Несмотря на то что член класса скрыт, его все равно можно использовать. Достаточно указать его полное имя, включающее имя класса, либо явно применить указатель this.

Значительно проще обеспечить доступ к переменной-члену height, присвоив параметру другое имя:

// хороший подход: не используйте имена переменных-членов для

// параметров или других локальных переменных

void Screen::dummy_fcn(pos ht) {

 cursor = width * height; // переменная-член height

}

Теперь, когда компилятор будет искать имя height, в функции dummy_fcn() он его не найдет. Затем компилятор просмотрит класс Screen. Поскольку имя height используется в функции-члене dummy_fcn(), компилятор просмотрит все объявления членов класса. Несмотря на то что объявление имени height расположено после места его использования в функции dummy_fcn(), компилятор решает, что оно относится к переменной-члену height.

После поиска в области видимости класса продолжается поиск в окружающей области видимости

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

// плохой подход: не скрывайте необходимые имена, которые

// определены в окружающих областях видимости

void Screen::dummy_fcn(pos height) {

 cursor = width * ::height; // который height? Глобальный

}

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

Поиск имен распространяется по всему файлу, где они были применены

Когда член класса определен вне определения класса, третий этап поиска его имени происходит не только в объявлениях глобальной области видимости, которые расположены непосредственно перед определением класса Screen, но и распространяется на остальные объявления в глобальной области видимости. Рассмотрим пример.

int height; // определяет имя, впоследствии используемое в Screen

class Screen {

public:

 typedef std::string::size_type pos;

 void setHeight(pos);

 pos height = 0; // скрывает объявление height из внешней

                 // области видимости

};

Screen::pos verify(Screen::pos);

void Screen::setHeight(pos var) {

 // var: относится к параметру

 // height: относится к члену класса

 // verify: относится к глобальной функции

 height = verify(var);

}

Обратите внимание, что объявление глобальной функции verify() не видимо перед определением класса Screen. Но третий этап поиска имени включает область видимости, в которой присутствует определение члена класса. В данном примере объявление функции verify() расположено перед определением функции setHeight(), a потому может использоваться.

Упражнения раздела 7.4.1

Упражнение 7.34. Что произойдет, если поместить определение типа pos в последнюю строку класса Screen?

Упражнение 7.35. Объясните код, приведенный ниже. Укажите, какое из определений, Type или initVal, будет использовано для каждого из имен. Если здесь есть ошибки, найдите и исправьте их.

typedef string Type;

Type initVal();

class Exercise {

public:

 typedef double Type;

 Type setVal(Type);

 Type initVal();

private:

 int val;

};

Type Exercise::setVal(Type parm) {

 val = parm + initVal();

 return val;

}

7.5. Снова о конструкторах

Конструкторы — ключевая часть любого класса С++. Основы конструкторов рассматривались в разделе 7.1.4, а в этом разделе описаны некоторые из дополнительных возможностей конструкторов и подробности материала, приведенного ранее.

7.5.1. Список инициализации конструктора

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

string foo = "Hello World!"; // определить и инициализировать

string bar;           // инициализация по умолчанию пустой строкой

bar = "Hello World!"; // присвоение нового значения переменной bar

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

// допустимый, но не самый лучший способ создания конструктора

1 ... 93 94 95 96 97 98 99 100 101 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии