Как отмечалось, можно выделить память для любого типа данных. Сюда включаются и объекты. Например, в следующей программе выделяет память для объекта класса point:
#include <iostream.h>
class point
{
int x, y, z;
public:
point(int a, int b, int c) { x=a; y=b; z=c; }
~point() { cout << "Destructing\n"; }
void show() { cout << x << “ “ << y << “ “ << z << “\n”; }
Когда эта программа запускается, видно, что конструктор класса point вызывается в момент достижения строки, где стоит оператор new, а деструктор вызывается по достижении оператора delete. Заметим также, что инициализирующие значения автоматически передаются конструктору оператором new.
Ниже приведен пример, в котором размещается массив объектов типа point:
#include <iostream.h>
class point
{
int x, y, z;
public:
point(int a, int b, int c) { x=a; y=b; z=c; }
point() { cout << "Constructing\n"; }
~point() { cout << "Destructing\n"; }
void show() { cout << x << “ “ << y << “ “ << z << “\n”; }
Обратим внимание, что к классу point добавлен второй конструктор. Поскольку динамический массив не может быть инициализирован, то требуется конструктор без параметров. Если такой конструктор не будет определен, то компилятор выдаст сообщение об ошибке.
Имеется возможность перегрузить операторы new и delete. Это следует делать в том случае, если необходимо использовать какой-то особый способ выделения памяти. Например, может понадобиться процедура выделения памяти, автоматически использующая дисковый файл в качестве виртуальной памяти в том случае, когда куча оказывается исчерпанной. Какой бы ни была причина, осуществить такую перегрузку очень просто.
Для перегрузки операторов new и delete может использоваться следующий формат:
void* operator new(size_t размер)
{
// выполнение выделения памяти
return указатель_на_память;
}
void operator delete(void *p)
{
// освобождение памяти, на которую указывает р
}
Параметр размер будет содержать число в байтах, которое необходимо выделить для размещения объекта. Это значение будет сформировано автоматически. Перегруженная функция new должна возвращать указатель на выделенную память. За исключением этих ограничений перегруженная функция new может выполнять все, что необходимо.
Функция delete получает указатель на область памяти, которую необходимо освободить. Она обязана освободить эту память. Для перегрузки операторов new и delete применительно к массивам надо использовать следующий формат:
void* operator new[](size_t размер)
{
// выполнение выделения памяти
return указатель_на_памятъ;
}
void operator delete[](void *p)
{
// освобождение памяти, на которую указывает p
}
Можно перегрузить операторы new и delete глобально или относительно класса. Для того чтобы перегрузить их по отношению к классу, просто надо сделать их функциями-членами этого класса.
Глава 7. Библиотека классов ввода/вывода С++
C++ поддерживает все функции ввода/вывода языка С и, кроме того, определяет свою собственную объектно-ориентированную систему ввода/вывода. В этой главе дается обзор библиотеки классов ввода/вывода языка C++. Также описывается, каким образом можно перегрузить операторы << и >> так, чтобы выводить собственные объекты. Система ввода/вывода C++ очень велика, и мы не можем охватить ее здесь всю целиком. Однако в этой главе будут рассмотрены наиболее важные и широко используемые функции и особенности этой системы ввода/вывода.
7.1 Почему C++ имеет свою собственную систему ввода/вывода
Система ввода/вывода языка С — одна из самых мощных и гибких среди всех языков программирования. Но несмотря на мощь функций ввода/вывода С, эта система не обеспечивает поддержки объектов, определенных пользователем. Это является одной из причин, почему в языке C++ определена своя система функций ввода/вывода. Например, если на языке С создать структуру:
struct my_struct
{
int count;
char s[80];
double balance;
};
то невозможно приспособить или расширить систему ввода/вывода языка С таким образом, чтобы она могла непосредственно осуществлять ввод/вывод переменных типа my_struct. Используя подход C++ к вводу/выводу, можно перегрузить операторы << и >> таким образом, чтобы они знали, как работать с созданными программистом классами.
Хотя системы ввода/вывода С и C++ содержат фактически одинаковые операторы, тот факт, что система C++ может работать с определенными пользователем типами, неизмеримо повышает гибкость ввода/вывода. Такой подход также помогает предотвратить возникновение ошибок. Например, в следующем вызове функции scanf():
char str[80];
int i;
scanf("%d%s", str, &i);
стоящие в списке аргументов строка и целое число поменялись местами, так что %d соответствует строке str, a %s соответствует целому числу i. Такой вызов не порождает ошибки в языке С, хотя и выдает неверный результат. Тем не менее, такой вызов функции scanf() является ошибочным. По сути, при вызове функции scanf() в С не осуществляется строгой проверки типов. В противоположность этому, в C++ операции ввода/вывода для всех встроенных типов определены относительно операторов << и >>, так что возможности возникновения подобной ошибки в нем нет. Вместо этого автоматически будет вызвана корректная операция, которая определяется типом операнда. Эти черты ввода/вывода C++ имеют место также и для определенных пользователем объектов.