Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП (С++) Лекции Бобин.doc
Скачиваний:
57
Добавлен:
08.02.2015
Размер:
625.66 Кб
Скачать

Глава 4. Статические компоненты классов

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

В Си для этого использовались глобальные переменные. Но данных подход небезопасен, поскольку существует возможность изменения такой переменной в других частях программы; безопасная альтернатива – статические поля классов.

Статическое поле– поле класса, которое относится ко всему классу в целом и не тиражируется в его объектах.

Каждое статическое поле существует в единственном экземпляре, и в отличие от обычных полей размещается не в стеке, а в сегменте данных (статическое поле – глобальная переменная, доступ к которой ограничен).

Статические компоненты находятся в контексте класса (т.е. не тиражируются).

Статические поля обладают особенностями:

  1. Допускают только внешнее определение;

  2. К статическому полю можно обратиться даже не имея объекта класса;

  3. На них не распространяется действие спецификатора доступа (public,protected,private).

п.4.1. Создание статических полей

Статическое поле должно быть объявлено в классе с ключевым словом static, и иметь внешнее определение без этого ключевого слова.

Пример:

class Point

{

static int counter;

double x, y;

public:

Point();

Point(const Point& p);

~Point();

}

//внешнее определение статического поля:

int Point::counter;

п.4.2. Обращение к статическому полю

К нему можно обратиться как к обычному полю класса:

  1. имя_объекта.статическое_поле;

  2. указатель_на_объект->статическое_поле;

Учитывая принадлежность статического поля к классу, мы можем обратиться к нему:

  1. имя_класса::статическое_поле;

Внутри методов класса к этому полю можно обратиться:

Point::Point()

{

x = 0.0;

y = 0.0;

++counter;

}

Point::Point(const Point &p)

{

x = px;

y = py;

++counter;

}

Point::~Point()

{

--counter;

}

п.4.3 Статические методы

По аналогии со статическими полями существуют статические метода (то есть к ним применимы те же свойства)

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

Пример:

class Point

{

static int counter;

double x, y;

public:

Point();

Point(const Point &);

~Point();

static void view();

};

int Point::counter = 0;

...

void Point::view()

{

cout << “В программе существует ” << counter << “точек\n”;

}

int main()

{

Point p1, p2;

{

Point p3 = p2;

Point::view(); // 3 (точки)

}

p1.view(); // 2 (точки)

return 0;

}

Глава 5. Друзья класса

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

п.5.1. Дружественные функции

Дружественная функция– внешняя по отношению к классу функция (возможно метод другого класса), которая имеет доступ к его закрытым и защищенным компонентам.

Чтобы внешняя функция стала дружественной классу, ее нужно объявить в теле класса с ключевым слово friend.

Замечание: Поскольку дружественная функция не является методом класса, на нее не действуют спецификаторы доступа.

class Point

{

...

private:

friend double RadiusVector(Point &p);

};

...

double RadiusVector(Point &p)

{

return sqrt(p.x*p.x + p.y*p.y);

}

____________________________________________

class Point;

class Editor

{

public:

void edit(Point &p);

};

class Point

{

...

friend void Editor::edit(Point &p);

};

...

void Editor::edit(Point &p)

{

p.x = 8;

p.y = p.x – 6;

}

п.5.2. Дружественные классы

Аналогично можно добавить в друзья целый класс. В этом случае все методы дружественного класса становятся дружественными функциями.

friend class Editor;

п.5.3. Вложенные классы

В С++ есть понятия вложенный и локальный классы.

  1. Вложенный класс (определен внутри определения другого класса)

class outer

{

...

class inner

{

...

};

...

};

  1. Локальный класс (определен внутри блока, например, функции)

void func()

{

class local

{

...

};

...

}

На вложенный класс распространяются ограничения доступа внешнего класса.

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

  1. Объявить внутренний класс в теле внешнего;

  2. Объявить внутренний класс в теле внешнего в качестве дружественного;

  3. Определить внутренний класс в теле внешнего;

class outer

{

int x, y;

public:

class inner;

friend class inner;

outer();

outer(const outer &);

class inner

{

int a, b;

public:

inner();

inner(const inner &);

};

};

outer::outer()

{

...

}

outer::outer(const outer &o)

{

...

}

outer::inner::inner()

{

...

}

outer::inner::inner(const outer::inner &i)

{

...

}

п.5.4. Важное предупреждение!

Концепция дружественности классов – это добровольный отказ программиста от средств автоматического контроля доступа. Эта технология крайне небезопасна и пользоваться ей следует только в крайнем случае.

Point::Point(double x, double y)

{

this->x = x;

this->y = y;

}

Глава 6. Константность в языке С++

п.6.1. Переменная-константа

const тип имя; // Имя пременной не является леводопустимым выражением.

п.6.2. Константность в указателях

(1) тип (2) * (3) имя;

Необходимо читать данную запись слева-направо.

  1. сonst тип * имя;

const int * x;

x– это указатель наconstint, то естьуказатель на константу.

x = y; - можно

*x = z; - нельзя

Можно изменять сам указатель, но нельзя изменять ячейку, на которую он настроен.

  1. тип const * имя;

int const * x;

x– это указатель наconstтипаint.

  1. тип * const имя;

int * const x;

x– это константный указатель на ячейку типаint

x = y; - нельзя

*x = z; - можно

Нельзя перенастраивать указатель.

  1. const тип * const имя;

x – константный указатель на константу

Имя массива является константным указателем на первый элемент.

Пример:

int x;

int *px;

const int *pcx;

px = &x;

pcx = &x;

x = 5; // можно

*pcx = 5; // нельзя

const int x = 5;

int *px;

const int *pcx;

px = &x; // нельзя (открывает возможность изменять ячейки,

// которые запрещено изменять

pcx = & x; // можно

Адрес неконстантного объекта можно присвоить указателю на константу.

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

п.6.3. Константы в ссылках

int x = 5;

const int & rx = x;

Константная ссылка и ссылка на константу – одно и то же.

x++; // можно

rx++; // нельзя

п.6.4. Передача константы как параметра функции

void func(const int x)

{

++x; // нельзя

}

вызов функции: (при вызове функции в нее передается копия объекта (в стеке)

int a = 5;

const int b = 6;

func(a);

func(b);

п.6.5. Возврат константы

Возврат константы для встроенных и пользовательских типов данных отличается.

Пример1:

int func1()

{

return 5;

}

const int func2()

{

return 5;

}

int main()

{

int a1, a2;

const int b1, b2;

a1 = func1(); // Все 4 записи

b1 = func1(); // допустимы

a2 = func2(); // При работе со встроенными типами данных

b2 = func2(); // не требует точного соответсвия по константности

return 0;

}

class Point

{

...

};

Point func1() // Существенная особенность:

{ // При возврате на пользовательский тип данных

return Point(); // возвращаемое значение и переменная-приемник

} // должны юыть совместимы по константности

const Point func2()

{

return Point();

}

int main()

{

Point a1, a2;

const Point b1, b2;

a1 = func1(); // +

b1 = func1(); // -

a2 = func2(); // -

b2 = func2(); // +

return 0;

}

п.6.6. Временные объекты

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

(1) Функционально временный объект не отличается от обычного, за исключением того, что он является константой.

Пример:

class Point

{

public:

void print();

};

void f(Print &x)

{

}

Point g()

{

return Point();

}

int main()

{

g().print(); // можно

f(g()); // нельзя (*)

return 0;

}

(*) Сначала вызывается функция g(), после этого вызывается функцияf(), принимающая на вход результат функцииg(). Функцияg() возвращает объект классаPoint, который представляет собой временный объект, подаваемый на вход функцииf(). В силу утверждения (1), объект, подаваемый на вход функцииf(), является константой.

g()->Point->const Point->f(Point &)

п.6.7. Константные поля классов

Обычное поле класса, объявленное с ключевым словом const, играет роль локальной константы, принадлежащей конкретному объекту данного класса. С такой константой обязательно связана конкретная ячейка памяти. Инициализировать можно только 1 раз при создании объекта (в конструкторе).

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

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

имя_конструктора(параметры):список_инициализации

{

тело_конструктора

}

имя_компонента(параметры_конструктора_компонента)

Это элемент списка инициализации.

class Int

{

const int minVal;

const int maxVal;

int Val;

public:

Int(int v = 0, int min = 0, max = 25);

Int(const Int &other);

...

};

Int::Int(int v, int min, int max): val(v), minVal(min), maxVal(max)

{

}

Int::Int(const Int &other):minVal(other.minVal), maxVal(other.maxVal)

{

val = other.val;

}

Если конструктор компонента отсутствует в списке инициализации конструктора объекта, то для данного компонента вызывается конструктор умолчаний. Конструкторы вызываются в порядке их упоминания.

п.6.9. Константы времени компиляции в классах

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

Статическое константное поле должно быть инициализировано непосредственно в теле класса. Оно может быть только целым.

class Stack

{

static const int size = 100;

int data[size];

int count;

public:

Stack();

void push(int value);

int pop();

};

Stack::Stack():count(0)

{

}

void Stack::push(int value)

{

if(count < size)

data[count++] = value;

}

int Stack::pop()

{

if(count > 0)

return data[--count];

else

return 0;

}

п.6.10. Константные методы класса

При создании в программе константного объекта класса компилятор обязан обеспечить неизменность полей объекта.

class Point

{

double x, y;

public:

...

void print();

};

int main()

{

...

const Point cp(0.1, 0.7);

cp.print(); // выдаст ошибку, т.к. для константного объекта

... // вызывается метод, который потенциально может

} // содержать программный код, изменяющий

// состояние объекта.

void Point::print()

{

cout << “x= “ << x << endl;

cout << “y= “ << y << endl;

}

Компилятор запрещает вызов обычных методов для константных объектов.

Константный метод класса – метод, который гарантирует неизменность объекта, для которого он вызван. Для константных объектов разрешено вызывать только константные методы.

Правильно:

void print() const;

...

void Point::print() const;

Ни конструкторы, ни деструкторы не могу быть константными методами.

Замечание 1: Перегрузка методов класса может осуществляться не только по списку параметров, но и по константности.

Замечание 2: Техническая реализация:

При вызове константного метода указатель thisявляется указателем на константу.

constимя класса * const this;

...

p.print();

Point::print(&p);

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

п.6.11. Ключевое слово mutable

Иногда возникает потребность в изменении состояния объекта константным методом (например, подсчитать количество вызовов данного метода для объекта). Если определить поле класса с ключевым словом mutable, то это поле можно изменять внутри константного метода.

class Point

{

mutable unsigned int counter;

...

};

...

void Point::print() const

{

...

counter++;

}