Возникающие проблемы
Классы, использующие свободную память
Преобразования типа
Возможны два способа преобразования типа для вновь создаваемого класса: преобразование из уже существующего типа в новый и преобразование нового разрабатываемого типа в уже существующий.
Из существующего типа в новый – с помощью одноаргументного конструктора.
Пример для класса Rational:
Rational x = Rational(23); // явный вызов
Rational y = 23; // неявный вызов
Возможны любые использования:
Rational a(1,2), b(1), c;
c = a + b;
c = a + 1; // эквивалентно c = a + Rational(1);
c = 2 + a; // эквивалентно c = Rational(2) + a;
Из нового типа в существующий - с помощью перегрузки оператора преобразования типа
Обязательно функция-член класса.
Прототип: operator имя_типа ();
Реализация: имя_класса::operator имя_типа() { ... }
Использование: неявно при вычислении выражений или явно с помощью обычного оператора преобразования типа: имя_типа(выражение).
Пример для класса Rational:
class Rational{
public:
...
operator float() { return (float) num / den; }
...
};
Использование:
Rational x(3,2);
float f = x;
Еще пример:
while( cin >> n)
cout << n;
Возможные неприятности:
Если в классе Rational есть одноаргументный конструктор (преобразование int в Rational) и в нем будет перегружена операция преобразования типа для int (преобразование Rational в int), тогда конструкция:
Rational a(1,2);
... a + 1 ...
вызовет сообщение об ошибке: два преобразования типа, определенные пользователем; что выбрать: int + int или Rational + Rational?
Причины, по которым приходится создавать классы с динамическим управлением памятью. Пример такого класса: функция, заданная таблично. Состояние класса включает в себя информацию о размере таблицы (количество узлов в задании функции) и массивы для хранения значений аргумента (х) и соответствующих значений функции (y). Массивы определяются через соответствующие указатели, размер таблицы определяет требуемый объем памяти. Кто выделит память? - конструктор. Пример разработки соответствующего класса.
class Function{
private:
float * Ptrx, * yPtr;
int size;
public:
Function(); // пустой конструктор
Function(int sz); // инициализирующий конструктор
~Function();
. . .
};
Function::Function()
{
size = 0;
xPtr = yPtr = NULL;
}
Function::Function(int sz)
{
size = sz;
xPtr = new float[size];
yPtr = new float[size];
}
Function::~Function()
{
delete [] xPtr;
delete [] yPtr;
}
Если в прикладной программе в некотором внутреннем блоке создается переменная типа “Указатель на класс” и ей присваивается значение некоторой другой – глобальной переменной: для классов (структур) определена операция присваивания - побайтное копирование:
Function f1[5]; f1 5 f2 5
. . .
{
Function f2;
f2 = f1;
. . .
}
В момент выхода из блока переменная f2 разрушается – для нее работает деструктор класса. В результате будет разрушена и переменная f1!
Результат не изменится, если во внутреннем блоке вместо присваивания использовать инициализацию (в этом случае будет работать копирующий конструктор по умолчанию – тоже побайтное копирование):
{
Function f2 = f1;
. . .
}