25
Автоматическое удаление — второе большое преимущество стековых объектов, поэтому
программисты часто создают маленькие вспомогательные стековые классы, которые играют роль
«обертки» для динамических объектов. В следующем забавном примере динамический класс
Foo
«упаковывается» в стековый класс
PFoo
. Конструктор выделяет память для
Foo
; деструктор
освобождает ее. Если вы незнакомы с операторами преобразования, обратитесь к соответствующему
разделу этой главы. В двух словах, функция operator
Foo*()
позволяет использовать класс
PFoo
везде,
где должен использоваться
Foo*
— например, при вызове функции
g()
.
class PFoo {
private:
Foo*
f;
public:
PFoo() : f(new Foo) {}
~PFoo() { delete f; }
operator Foo*() { return f; }
}
void g(Foo*);
{
PFoo
p;
g(p); //
Вызывает функцию operator Foo*() для преобразования
//
Уничтожается p, а за ним – Foo
}
Обратите внимание, что этот класс не совсем безопасен, поскольку адрес, возвращаемый функцией
operator Foo*()
, становится недействительным после удаления вмещающего
PFoo
. Мы разберемся
с этим чуть позже.
Мы еще не раз встретимся с подобными фокусами. Вся соль заключается в том, что стековые объекты
могут пригодиться просто из-за того, что их не приходится удалять вручную. Вскоре я покажу вам, как
организовать автоматическое удаление динамических объектов, но эта методика очень сложна и вряд
ли пригодна для повседневного применения.
У стековых объектов есть еще одно преимущество — если ваш компилятор поддерживает ANSI-
совместимую обработку исключений (exception). Когда во время раскрутки стека происходит
исключение, деструкторы стековых объектов вызываются автоматически. Для динамических объектов
этого не случается, и ваша куча может превратиться в настоящий хаос. Рискуя повториться, я скажу,
что мы вернемся к этой теме позднее.
Области действия и функции
Одно из значительных преимуществ C++ над С — возможность ограничения области действия
символических имен. Впрочем, это палка о двух концах, поскольку правила определения области
действия иногда довольно запутанны. Кроме того, в C++ появилась перегрузка функций и — как ее
расширение — перегрузка операторов. Предполагается, что вы уже знакомы с азами, поэтому в своем
кратком обзоре я ограничусь лишь некоторыми нетривиальными особенностями функций и областей
действия.
Области действия
Область действия создается следующими конструкциями:
•
класс;
•
структура;
•
объединение;
•
блок;
•
глобальное пространство имен.
26
Символические имена, объявленные в области действия, относятся только к данной области. Они не
ограничиваются перечислениями и простыми переменными. Структуры, классы и функции также
могут определяться в конкретной области действия.
Классы
Класс в C++ — нечто большее, чем простая структура данных. Это аналог модуля из других языков
программирования, средство упорядочения символьных имен.
class Foo {
public:
static
int
y;
//
Глобальная переменная
static void GFn();
// Глобальная функция
int
x;
//
Переменная класса
Foo();
//
Конструктор
void
Fn(); //
Функция класса
typedef int (*IntFn)();
//
Тип
enum Status { kOpen = 0, kClosed }; // Другой тип
struct
Bar
{
//
Вложенная структура
int
a;
int
b;
static void BarFn();
}
private:
void
Hn();
};
В этом фрагменте приведены некоторые вариации на тему классов. Переменная
у
— глобальная
переменная, a
GFn()
— глобальная функция, хотя область действия их имен ограничивается классом
Foo
. Во всех функциях класса
Foo
к ним можно обращаться просто по имени, но за его пределами
необходимо использовать оператор области действия
::
:
Foo::Foo()
{
GFn();
//
Мы уже находимся в области действия Foo
}
void f()
{
Foo::GFn();
//
Необходимо задать область действия
}
Аналогично, определение типа
IntFn
, перечисление
Status
и даже вложенную структуру
Bar
также
можно использовать без указания области действия в функциях класса
Foo
, но в любом другом месте
эту область необходимо задать. Для вложенных типов с открытой видимостью синтаксис указания
области действия может принять несколько устрашающий вид, как видно из следующего примера для
структуры
Ваr
:
Foo::Bar b;
Foo::Bar::BarFn();
По этой причине вложенные структуры либо делаются тривиальными, либо доступ к ним
ограничивается.
Члены класса
х
,
Foo
и
Fn()
, имеют смысл лишь в контексте конкретного экземпляра (instance) этого
класса. Для обращения к ним используются операторы-селекторы членов класса,
.
и
->
. Широкие
массы (и, как я выяснил на собственном горьком опыте, даже разработчики компиляторов C++) почти
не знают о том, что с помощью селекторов можно вызывать статические функции класса и обращаться
Do'stlaringiz bilan baham: |