Объектно-ориентированное программирование, страница 8

Теперь рассмотрим особенности функционирования статических функций — членов класса. Так как static-метод может быть вызван безотносительно к какому-либо объекту класса, то при его вызове не происходит неявной передачи указателя this. Вспомните, указатель this всегда неявно передается обычному (не static) методу. Следовательно, внутри статического метода невозможно прямое обращение к остальным (не статическим) членам этого же класса, так как неизвестно, какому представителю класса (объекту) должны принадлежать эти члены. Выходом из этого положения служит явная передача ссылки или адреса объекта, для которого вызывается статическая функция. Так мы поступили при вызове A::Set(a,7);. Только потому, что ссылка на объект a была передана статической функции в качестве параметра, она может работать с полем int i конкретного объекта. Традиционный вызов^

 arr[3].Set(a,8);

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

Описатель const

Рассмотрим особенности использования описателя const в С++. В дополнение к сказанному ранее относительно const-переменных и const-указателей следует отметить, что этот описатель используется в качестве средства повышения безопасности, а следовательно, и надежности в объектно-ориентированных программах. Он применим к методам класса, параметрам функций и объектам класса. Если какой-то метод класса снабжен описателем const, это означает, что он не должен изменять никаких данных класса. Применение const к параметру метода означает, что этот конкретный параметр не может быть изменен внутри данного метода. Попытка изменить его приводит к ошибке на этапе компиляции. Наконец, если какой-то объект класса определен с описателем const, то это означает, что он должен пользоваться const-методами. Случай пользования обычным методом порождает ошибку. Например,

class A

{

int j;

public:

A (int k=0) { j=k; }      // Конструктор с параметром

int GetJ (int k) const    // Метод не может изменять j

{

  return k>j ? j : k;      // Он этого и не делает

}

int GetJ (int k)            // Метод может изменять j

{

  return k<j ? j : (j=k); // и он это делает

}

void Change (A& a1, const A& a2)

{

  a1.j = a2.j;       // Параметр a1 можно изменять

  a2.j = 5;          // Ошибка; a2 нельзя изменять

}

void Out() { printf(" j =%d",j); }

};

void main()

{

A a, b(5);    // Обычные объекты

const A c;    // Const-объект

a.GetJ(1);    // Будет вызвана int GetJ(int)

a.Out();      // Выведет j=1

c.GetJ(2);    // Будет вызвана int GetJ(int) const

c.Out();      // Ошибка, так как Out() не const-метод

a.Out();      // Выведет j=1

a.Change(a, b);

a.Out();      // Выведет j=5

c.Change(a, b); // Ошибка, так как Change не const-метод

}

Переопределение операций

Термин переопределение операции является одним из переводов оригинального понятия operator overloading. Иногда встречаются переводы: совмещение или перегрузка операции. Суть этой уникальной возможности, предоставляемой языком С++, состоит в том, что программист может переопределить смысл любой из более 40 разрешенных операций для объектов данного класса. Сделав это однажды, мы как бы наделяем язык свойствами, которыми он раньше не обладал. Например, переопределив операции: +,-,*,/ в классе комплексных чисел, мы как бы наделяем язык С++ способностью оперировать с комплексными числами по соответствующим правилам. Конечно, этого же можно добиться и традиционным способом, например, создав четыре функции: cadd, csubtr, cmult, cdiv. Однако вызов этих функций будет выглядеть намного менее естественным, чем просто запись вида: z=z1+z2; или z=z1/z2; в предположении, что z, z1 и z2 были объявлены объектами класса комплексных чисел. Чтобы переопределить операцию необходимо особым образом определить ее либо как метод класса, либо как friend-функцию. Существует возможность переопределить в классе любую операцию, за исключением следующих шести: . .* :: ?: # ##. Функция, переопределяющая операцию, не может изменить количество аргументов операции. Например, унарная операция не может стать бинарной и наоборот. Невозможно также изменить существующий приоритет и правила ассоциации, действующие при нормальном использовании операции. Рассмотрим выражение x + y, где x, y являются объектами какого-либо класса. Предположим, что в этом классе одним из двух возможных способов переопределена операция +. Тогда выражение x + y может быть интерпретировано компилятором также двумя способами:

x.operator + (y);   // либо как

operator + (x,y);

Первая интерпретация означает, что объекту x посылается сообщение operator+ (y). То есть вызывается функция (член класса) с именем operator+(), при этом в качестве единственного параметра передается объект y. Вторая — означает вызов внешней функции с именем operator+(), которой передаются два параметра x и y. В соответствии с этим существует два способа переопределения бинарных операций: public-методом с одним аргументом и friend-функцией с двумя аргументами. Сходные рассуждения приводят к выводу, что унарную операцию можно переопределить либо public-методом класса без параметров, либо friend-функцией с одним параметром. Следует обратить внимание на следующие особенности:

¨  следует отдельно переопределять префиксную и постфиксную модификации унарных операций ++ и --, чтобы иметь возможность их различать;

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