Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 36 37 38 [ 39 ] 40 41 42 ... 120


В этом примере разумный транслятор смог бы обнаружить, что функция X::writeme() пытается изменить постоянный объект. Однако, это непростая задача для транслятора. Из-за раздельной трансляции он в общем случае не может гарантировать постоянство объекта, если нет соответствующего описания со спецификацией const. Например, определения readme() и writeme() могли быть в другом файле:

class X { int m; public:

readme() const; writeme(int i);

В таком случае описание readme() со спецификацией const существенно.

Тип указателя this в постоянной функции-члене класса X есть const X *const. Это значит, что без явного приведения с помощью this нельзя изменить значение объекта:

class X { int m; public:

...

void implicit cheat() const { m++; } ошибка void explicit cheat() const { ((X*)this)->m++; } нормально

Отбросить спецификацию const можно потому, что понятие постоянства объекта имеет два значения. Первое, называемое физическим постоянством состоит в том, что объект хранится в защищенной от записи памяти. Второе, называемое логическим постоянством заключается в том, что объект выступает как постоянный (неизменяемый) по отношению к пользователям. Операция над логически постоянным объектом может изменить часть данных объекта, если при этом не нарушается его постоянство с точки зрения пользователя. Операциями, ненарушающими логическое постоянство объекта, могут быть буферизация значений, ведение статистики, изменение переменных-счетчиков в постоянных функциях-членах.

Логического постоянства можно достигнуть приведением, удаляющим спецификацию const:

class calculator1 { int cache val; int cache arg;

...

public:

int compute(int i) const; ...

int calculator1::compute(int i) const

if (i == cache arg) return cache val; нелучший способ

((calculator1*)this)->cache arg = i; ((calculator1*)this)->cache val = val; return val;

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

struct cache { int val; int arg;

class calculator2 {



public:

int compute(int i) const;

int calculator2::compute(int i) const

if (i == p->arg) return p->val; нелучший способ p->arg = i; p->val = val; return val;

Отметим, что const нужно указывать как в описании, так и в определении постоянной функции-члена. Физическое постоянство обеспечивается помещением объекта в защищенную по записи память, только если в классе нет конструктора ($$7.1.6).

5.2.4 Инициализация

Инициализация объектов класса с помощью таких функций как set date() - неэлегантное и чреватое ошибками решение. Поскольку явно не было указано, что объект требует инициализации, программист может либо забыть это сделать, либо сделать дважды, что может привести к столь же катастрофическим последствиям. Лучше дать программисту возможность описать функцию, явно предназначенную для инициализации объектов. Поскольку такая функция конструирует значение данного типа, она называется конструктором. Эту функцию легко распознать - она имеет то же имя, что и ее класс:

class date { ...

date(int, int, int);

Если в классе есть конструктор, все объекты этого класса конструктору требуются параметры, их надо указывать:

date today = date(23,6,1983); date xmas(25,12,0);

date my birthday;

будут проинициализированы. Если

краткая форма

неправильно, нужен инициализатор

Часто бывает удобно указать несколько способов инициализации объекта. Для этого нужно описать несколько конструкторов:

class date {

int month, day, year;

public:

date(int, int, int); date(int, int); date(int);

date();

date(const char*);

день, месяц, год

день , месяц и текущий год

день и текущие год и месяц

стандартное значение: текущая дата

дата в строковом представлении

Параметры конструкторов подчиняются тем же правилам о типах параметров, что и все остальные функции ($$4.6.6). Пока конструкторы достаточно различаются по типам своих параметров, транслятор способен правильно выбрать конструктор:

date today(4);

date july4( July 4, 1983 );

date guy( 5 Nov );

cache* p;



date now; инициализация стандартн1м значением

Размножение конструкторов в примере c date типично. При разработке класса всегда есть соблазн добавить еще одну возможность, - а вдруг она кому-нибудь пригодится. Чтобы определить действительно нужные возможности, надо поразмышлять, но зато в результате, как правило, получается более компактная и понятная программа . Сократить число сходных функций можно с помощью стандартного значения параметра. В примере с date для каждого параметра можно задать стандартное значение, что означает: взять значение из текущей даты .

class date {

int month, day, year; public: ...

date(int d =0, int m =0, y=0); ...

date::date(int d, int m, int y)

day = d ? d : today.day; month = m ? m : today.month; year = y ? y : today.year; проверка правильности даты ...

Когда используется стандартное значение параметра, оно должно отличаться от всех допустимых значений параметра. В случае месяца и дня очевидно, что при значении нуль - это так, но неочевидно, что нуль подходит для значения года. К счастью, в европейском календаре нет нулевого года, т.к. сразу после 1 г. до р.х. (year==-1) идет 1 г. р.х. (year==1). Однако для обычной программы это, возможно, слишком тонкий момент.

Объект класса без конструктора может инициализироваться присваиванием ему другого объекта этого же класса. Это незапрещено и в том случае, когда конструкторы описаны:

date d = today; инициализация присваиванием

На самом деле, имеется стандартный конструктор копирования, определенный как поэлементное копирование объектов одного класса. Если такой конструктор для класса X не нужен, можно переопределить его как конструктор копирования X::X(const X&). Подробнее поговорим об этом в $$7.6.

5.2.5 Удаление

Пользовательские типы чаще имеют, чем не имеют, конструкторы , которые проводят надлежащую инициализацию. Для многих типов требуется и обратная операция - деструктор, гарантирующая правильное удаление объектов этого типа. Деструктор класса X обозначается ~X ( дополнение конструктора ). В частности, для многих классов используется свободная память (см. $$3.2.6), выделяемая конструктором и освобождаемая деструктором. Вот, например, традиционное определение типа стек, из которого для краткости полностью выброшена обработка ошибок:

class char stack { int size; char* top; char* s; public:

char stack(int sz) { top=s=new char[size=sz]; } ~char stack() { delete[] s; } деструктор void push(char c) { *top++ = c; } void pop() { return *--top; }

Когда объект типа char stack выходит из текущей области видимости, вызывается деструктор:



1 ... 36 37 38 [ 39 ] 40 41 42 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика