Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Однако сначала нам следовало бы инициализировать члены их значениями, заданными по умолчанию, и лишь потом присваивать им новые значения. Кроме того, в этом случае не исключена возможность того, что мы случайно используем член класса до его инициализации. Обозначение :y(yy), m(mm), d(dd) точнее отражает наши намерения. Разница между этими фрагментами точно такая же, как между двумя примерами, приведенными ниже. Рассмотрим первый из них.


int x; // сначала определяем переменную x

// ...

x = 2; // потом присваиваем ей значение


Второй пример выглядит так:


int x = 2; // определяем и немедленно инициализируем двойкой


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


int x(2);               // инициализируем двойкой

Date sunday(2009,8,29); // инициализируем объект Sunday

                        // триадой (2009,8,29)


Функцию-член класса можно также определить в определении класса.


// простой класс Date (детали реализации будут рассмотрены позднее)

class Date {

public:

  Date(int yy, int mm, int dd)

  :y(yy), m(mm), d(dd)

  {

    // ...

  }


void add_day(int n)

{

  // ...

}


int month { return m; }

  // ...

private:

  int y, m, d; // год, месяц, день

};


Во-первых, отметим, что теперь объявление класса стало больше и запутаннее. В данном примере код конструктора и функции add_day могут содержать десятки строк. Это в несколько раз увеличивает размер объявления класса и затрудняет поиск интерфейса среди деталей реализации. Итак, мы не рекомендуем определять большие функции в объявлении класса. Тем не менее посмотрите на определение функции month. Оно проще и короче, чем определение Date::month, размещенное за пределами объявления класса. Определения коротких и простых функций можно размещать в объявлении класса.

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

  Определение функции-члена в классе приводит к следующим последствиям.

• Функция становится подставляемой (inlined), т.е. компилятор попытается сгенерировать код подставляемой функции вместо ее вызова. Это может дать значительное преимущество часто вызываемым функциям, таким как month.

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


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

9.4.5. Ссылка на текущий объект

Рассмотрим простой пример использования класса Date.


class Date {

  // ...

  int month { return m; }

  // ...

private:

  int y, m, d; // год, месяц, день

};


void f(Date d1, Date d2)

{

  cout << d1.month << ' ' << d2.month << '\n';

}


Откуда функции Date::month известно, что при первом вызове следует вернуть значение переменной d1.m, а при втором — d2.m? Посмотрите на функцию Date::month еще раз; ее объявление не имеет аргумента! Как функция Date::month “узнает”, для какого объекта она вызывается? Функции-члены класса, такие как Date::month, имеют неявный аргумент, позволяющий идентифицировать объект, для которого они вызываются. Итак, при первом вызове переменная m правильно ссылается на d1.m, а при втором — на d2.m. Другие варианты использования неявного аргумента описаны в разделе 17.10.

9.4.6. Сообщения об ошибках

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже