- •Литература
- •Цель курса
- •1. Объектно-ориентированный подход
- •1.1. Основные понятия и определения
- •Пример:
- •Пример:
- •1.2. Процесс разработки по
- •2.1. Основные этапы разработки класса
- •2.2. Определение класса
- •Пример использования ооп (объектно-ориентированного проектирования)
- •2. Разработка класса в ооп
- •Рекомендации по поводу использования уровней видимости при определении класса
- •2.3. Методы класса
- •2.4. Конструкторы и деструктор
- •2.5. Использование класса
- •2.5. Реализация класса
- •Пример: реализация класса Rational
- •Реализация методов класса
- •2.6. Использование класса
- •Пример использования класса
- •Перегрузка
- •3.1. Перегрузка функций
- •3.2. Перегрузка операторов
- •3.3. Выбор перегруженной функции
- •Примеры
- •Пример с ошибками
- •3.5. Друзья класса
- •3.6. Друзья или члены
- •3.6. Преобразования типа
- •4. Классы, использующие свободную память
- •4.1. Возникающие проблемы
- •4.2. Решение проблемы
- •4.3. Когда используются присваивание и копирующий конструктор
- •4.4. Использование модификатора const
- •Простое наследование: правила определения производного класса
- •Пример производного класса
- •Указатели на классы: базовый и производный
- •Вызов методов по указателю на класс
- •Понятие статического и динамического связывания
- •Виртуальные функции
- •Пример использования динамического связывания: список
- •Виртуальные деструкторы
3.3. Выбор перегруженной функции
Если определены несколько функций с одинаковым именем и разными списками параметров (перегруженные функции) и в программе встречается вызов функции, компилятор должен выбрать одну из перегруженных функций. Существует определенный алгоритм выбора функций, в соответствии с которым выбирается функция, наилучшим образом соответствующая вызову. Если не будет установлено соответствие ни одной из перегруженных функций или будет установлено неоднозначное соответствие, на этапе компиляции генерируется сообщение об ошибке.
Правила сравнения:
Точные совпадения
Расширения
Стандартные преобразования
Преобразования, требующие временные переменные
Преобразования, определенные пользователем
Преобразования, требующие временные переменные: параметр объявлен как ссылка, а аргумент требует преобразования (например, преобразование из float в int&) или задан выражением, значение которого не может быть изменено.
Примеры
void print(int);
void print(const char *);
void print(double);
void print(long);
void print(char);
char c; int i; short s; float f;
print(c); // правило 1; вызывается print(char)
print(i); // правило 1; вызывается print(int)
print(s); // правило 2; вызывается print(int)
print(f); // правило 2; вызывается print(double)
print('a'); // правило 1; вызывается print(char)
print(49); // правило 1; вызывается print(int)
print("a"); // правило 1; вызывается print(const char *)
Пример с ошибками
void f(int, float);
void f(float, int);
Вызов, который приведет к генерации сообщения об ошибке (неоднозначный выбор):
f(1.5, 1.5);
3.5. Друзья класса
Назначение методов – создание интерфейса между внешним миром и закрытой (защищенной) частями класса. Еще один способ получить доступ к закрытой части класса – использование внешних функций, объявленных как друг класса. Функции-друзья, как и члены, являются интерфейсом класса.
Функция становится другом после ее объявления в классе с использованием спецификатора friend, например:
Определение |
Реализация |
а) глобальная функция class X{ ... friend void f(); public: void fx(); ... } |
void f() { ... } |
б) функция – член класса class Y{ ... friend void X::fx(); ... }; |
void X::fx() { ... } |
в) класс class Z{ ... friend class Y; ... }; |
|
Другом класса могут быть: глобальная функция (см. примеры, приведенные выше); функция-член другого класса; все функции-члены другого класса, т.е. сам класс (при этом функции-друзья одного класса не становятся автоматически друзьями другого класса). Почти любой метод может быть сделан другом. Исключения: конструкторы, деструктор, кое-что еще.
Расположение объявления friend в определении класса (в открытой, закрытой или защищенной частях класса) роли не играют – функция-друг класса является внешней по отношению к этому классу. По этой же причине функция-друг класса, не являясь методом класса, не имеет адресата и, следовательно, не имеет указателя this (все аргументы в функцию передаются через список параметров).
Различия между членами и друзьями класса:
Функция-член |
Функция-друг |
class Rational{ public: void print(); ... }; void Rational::print() { cout << num; if(den != 1) cout << '/' << den; } ... Rational x(1,5); x.print(); |
class Rational{ public: friend void print(Rational r); ... }; void print(Rational r) { cout << r.num; if(r.den != 1) cout << '/' << r.den; } ... Rational x(1,5); print(x); |
Перегруженные операторы – друзья класса:
Бинарный оператор |
Унарный оператор (префиксный и постфиксный) |
Объявление |
|
friend тип operator знак_оп(op1, op2) |
friend тип operator знак_оп(op1) friend тип operator знак_оп(op1, int) |
Реализация |
|
тип operator знак_оп(тип op1, тип op2) { ... } |
тип operator знак_оп(тип op1) { ... } тип operator знак_оп(тип op1, int) { ... } |
Использование |
|
op1 знак_оп op2 эквивалентно: operator знак_оп(op1, op2) |
знак_оп op1 op1 знак_оп |
Пример перегрузки оператора записи в поток для класса Rational:
class Rational {
friend ostream& operator <<(ostream&, Rational);
...
};
ostream& operator <<(ostream& os, Rational r)
{
os << r.num;
if(r.den == 1)
os << ‘/’ << r.den;
return os;
}
