Чтение онлайн

ЖАНРЫ

Идиомы и стили С++

Makhmutov Albert

Шрифт:

 ~ampstack {

for (int iCounter = 0; iCounter ‹ iTop; iCounter ++)

if (*(array+iCounter)!= NULL) delete *(array+iCounter);

delete[] array;

 }

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

 // Направить указатель в стек

 void push (Type* _t) { array[iTop++]=_t; }

 // Вынуть указатель из стека

 Type* pop (void) {

if (iTop == 0) return NULL;

else return array[--iTop];

 }

 // Стек пуст?

 int isEmpty (void) { return iTop==0; }

 // Очистить стек

 void emptyAll (void) {

for (int iCounter = 0; iCounter ‹ iTop; iCounter ++)

if (*(array+iCounter)!= NULL) delete *(array+iCounter);

iTop = 0;

 }

};

// Шаблон класса с многоуровневой отменой

template ‹class Type›

class MLTrans {

 typedef ampstack‹Type› stack;

private:

 Type* that; // Текущее значение

 stack history; // контейнер предыдущих значений

public:

 // конструктор-деструктор

 MLTrans: that(new Type) {}

 ~MLTrans { delete that; }

 // Сохранение текущего значения, aналог SAVE TRANSACTION в SQL серверах

 void Push {

history.push(that);

that = new Type(*that);

 }

 // удаление промежуточных состояний

 void Commit { history.emptyAll; }

 // Откат на одну позицию; уничтожает текущее значение.

 void PopOne {

if (!history.isEmpty) {

delete that;

that = history.pop;

}

 }

 // Откат к началу транзакции.

 void Rollback {

Type* old = history.pop;

Type* older = NULL;

if (old!= NULL) {

while ((older = history.pop)!= NULL) {

delete old;

old = older;

}

delete that;

that = old;

}

 }

 // Переопределенный operator-›

 Type* operator-› { return that; }

}

// проверим работу

int main {

 int t;

 MLTrans‹CType› a;

 a-›set(5);

 t = a-›get;

 a.Push;

 a-›set(6);

 t = a-›get;

 a.Push;

 t = a-›get;

 a-›set(7);

 t = a-›get;

 a.Push;

 t = a-›get;

 a-›set(9);

 t = a-›get;

// a.Push;

 t = a-›get;

 a.PopOne;

 t = a-›get;

 a.Rollback;

 t = a-›get;

 return 0;

}

Шаг 24 - Как создавать ТОЛЬКО локальные переменные.

В Шаге 17 мы изыскали способ подавить создание локальных переменных. Решим обратную задачу - как подавить иные способы их создания. А какие иные? Любые другие способы предполагают вызов оператора operator new для выделения памяти и потом вызов конструктора. Значит, надо объявить operator new закрытым членом класса, да и все. Ничего в нем делать не надо, а сразу назад. Попробуем?

class CNoHeap {

public:

 int a;

private:

 void* operator new(size_t size) { return NULL; }

};

int main {

/*

CNoHeap* firstTestNoHeap = new CNoHeap; // Не откомпилируется

*/

 CNoHeap secondTestNoHeap; // А это пожалуйста.

 return 0;

}

Теперь, если определить макрос:

#define DECLARE_LOCAL \

 private: \

 void* operator new(size_t size) { return NULL; }

и потом вкладывать его во всякие разные объекты, отвечающие за захват и освобождение ресурсов, то получится весьма удобно; Вы ГАРАНТИРОВАННО освободите любые ресурсы, захваченные в конструкторе и освобождаемые в деструкторе, в том числе в исключении. В любом случае, всякое ограничение уменьшает энтропию.

Для Шага 17, где мы рисовали производящие и разрушающие функции, тоже можно нарисовать макрос… и назвать его DECLARE_DYNCREATE. То есть, я хочу сказать, что Вы можете аккуратно переписать нужное из него в свою версию, а в результате получите

class CSomeClass {

 DECLARE_NOLOCAL

public:

 bool Initialize (param list);

};

И это будет уже иметь определенный Вами набор функций, возможно, включая конструкторы и деструктор.

Шаг 25 - Как сделать виртуальной свободную функцию.

Чаще всего этот прием я видел в отношении оператора operator‹‹. Точнее, не чаще, а всегда. На нем и разберем. Пусть у нас есть иерархия классов, и мы хотим определить диагностическую функцию Dump. Она должна вываливать диагностику в заданное что-то (CDestination). У нас есть два варианта: или сделать функцию виртуальной в иерархии классов:

Поделиться с друзьями: