Выбрать главу

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

/* Демонстрация использования модификатора mutable.

*/

#include <iostream>

using namespace std;

class Demo {

  mutable int i;

  int j;

 public:

  int geti() const {

   return i; // все в порядке

  }

  void seti(int x) const {

   i = x; // теперь все в порядке

  }

  /* Следующая функция не скомпилируется,

   void setj (int х) const {

    j = x; // Это по-прежнему неверно!

   }

  */

};

int main()

{

 Demo ob;

 ob.seti(1900);

 cout << ob.geti();

 return 0;

}

Здесь член i определен с использованием модификатора mutable, поэтому его можно изменить с помощью функции seti(). Однако переменная j не является mutable-членом, поэтому функции setj() не разрешено модифицировать его значение.

Использование explicit-конструкторов

Для создания "неконвертирующего" конструктора используйте спецификатор explicit.

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

#include <iostream>

using namespace std;

class myclass {

  int a;

 public:

  myclass(int x) { a = x; }

  int geta() { return a; }

};

int main()

{

 myclass ob(4);

 cout << ob.geta();

 return 0;

}

Здесь конструктор класса myclass принимает один параметр. Обратите внимание на то, как объявлен объект ob в функции main(). Значение 4, заданное в круглых скобках после имени ob, представляет собой аргумент, который передается параметру x конструктора myclass(), а параметр x в свою очередь используется для инициализации члена a. Именно таким способом мы инициализируем члены класса с начала этой книги. Однако существует и альтернативный вариант инициализации. Например, при выполнении следующей инструкции член a также получит значение 4.

myclass ob = 4; /* Этот формат инициализации автоматически преобразуется в формат myclass(4). */

Как отмечено в комментарии, этот формат инициализации автоматически преобразуется в вызов конструктора класса myclass, а число 4 используется в качестве аргумента. Другими словами, предыдущая инструкция обрабатывается компилятором так, как если бы она была записана:

myclass ob(4);

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

Если вам не нужно, чтобы такое неявное преобразование имело место, можно предотвратить его с помощью спецификатора explicit. Ключевое слово explicit применяется только к конструкторам. Конструктор, определенный с помощью спецификатора explicit, будет задействован только в том случае, если для инициализации членов класса используется обычный синтаксис конструктора. Никаких автоматических преобразований выполнено не будет. Например, объявляя конструктор класса myclass с использованием спецификатора explicit, мы тем самым отменяем поддержку автоматического преобразования типов. В этом варианте определения класса функция myclass() объявляется как explicit-конструктор.