пятница, 3 сентября 2010 г.

Функция set_new_handler

Нашел интересный метод в С++.
Называется set_new_handler и призван помочь нам в случае если не получилось выделить память.
Описание можно глянуть тут и тут
Ниже пример использования:

#include "stdafx.h"
#include
using namespace std;

void no_memory()
{
printf( "Failed to allocate memory!\n");
throw bad_alloc();
}

int _tmain(int argc, _TCHAR* argv[])
{
set_new_handler(no_memory);
while(1)
{
try
{
int* p = new int[1024*1024*256];
printf("successed\n");
}
catch(bad_alloc b)
{
return 0;
}
}
//delete []p;
return 0;
}

Сразу же возникает закономерный вопрос: а как же его использовать в практическом коде?
А ответ прост - никак.

На проекте может быть два способа работы с памятью:
  • ленивый - все отдается на откуп стандартному new и надеемся, что памяти у пользователя всегда хватит.
  • практичный - команда берет на себя все управление памятью, каждый байт подсчитан и задокументирован.
Начнем со второго случая. Если команда взяла на себя управление памятью, то new переопределен и стало быть метод установленный вызовом set_new_handler никогда не сработает. Да и не нужен он, при нормально написанном менеджере памяти.

В первом же случае, если команда дошла до точки, когда закончилась память и приходиться использовать set_new_handler, то это значит, что пора задумываться о втором случае. Пора садиться считать байты и брать управление памятью на себя. set_new_handler - это всего лишь заплатка, "гвоздь", который поможет пережить текущий майлстоун, но сдавать проект с ним нельзя.

Доброй всем ночи.

четверг, 5 августа 2010 г.

Final class в C++ 2

Месяц назад написал статью Final class в C++, в которой пытался решить проблему с запретом наследования в С++. Проблема решилась, но при этом возникло два вопроса.

Для опытных программистов эти вопросы не очень сложные, но написать ответы считаю нужным. Обещал все-таки.

С первым все достаточно просто. И звучит он следующим образом: "Почему переопределенные операторы new/delete не имеют доступа к приватным конструктору и деструктору?".
Ответ: потому, что конструктор и деструктор вызываются не из операторов new/delete, их вызывает внешний код, который встраивается компилятором.

Второй вопрос повеселее: "Почему именно виртуальное наследование запрещает наследование от класса объявленного финальным?"
Ответ находится по ссылке С++ Faq. Если по-русски то суть в том, что если в цепочке родителей есть виртуальное наследование, то наследник должен иметь прямой доступ к конструктору данного класса: constructors for virtual base classes anywhere in your class's inheritance hierarchy are called by the "most derived" class's constructor.

Всем доброй ночи.

среда, 7 июля 2010 г.

Final class в С++

Последнее время приходится проводить много собеседований. С одной стороны занимает много времени и сил, а с другой дополнительный стимул расти самому.
Вот недавно всплыл такой вопрос: можно ли запретить наследование от класса в С++. В Java есть такое понятие как final класс, т.е. класс от которого нельзя наследоваться.

Предположим есть некий класс А:

class A
{
public:
A(){}
~A(){}
};

и класс B

class B : public A //все отлично собирается
{
};


Необходимо сделать так, чтобы при попытке наследование компилятор выдавал сообщение об ошибке.

Первое, что приходит в голову это сделать конструктор и деструктор класса А приватными. Действительно после этого я не смогу наследоваться, но возникает другая проблема - я не могу создавать экземпляры класса А.
Пытаемся развить мысль далее. Что если переопределить для класса А операторы new и delete. Так как это будут операторы класса, то они смогут получить доступ к приватным конструктору и деструктору. Получается вот такой класс:

class A
{
private:
A(){}
~A(){}


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

void operator delete(void* block)
{
::operator delete(block);
}
};

сlass B : public A //компилятор ругается
{
};


Но на самом деле это не работает. Переопределенные операторы new\delete все равно не получают доступа к приватным конструктору и деструктору класса А. Объявление операторов new\delete друзьями класса А тоже не помогает.

Второй мыслью (после часа попыток) было полезть во всемирную паутину и найти ответ там. Ответ я нашел, но не на все вопросы. Итак реализация final класса для С++ выглядит следующим образом:

class ClassLocker
{
friend class A;
private:
ClassLocker(){}
};

class A : public virtual ClassLocker
{
public:
A(){}
~A(){}
};

class B : public A //компилятор выдает сообщение об ошибке
{
};

Самое интересное, что если мы убрать виртуальное наследование класса А от класса ClassLocker, т.е. написать class A : public ClassLocker, то все отлично собирается. Слово virtual в данном случае является ключевым.

Итак в итоге есть два вопроса:
  • Почему переопределенные операторы new\delete не имеют доступа к приватном конструктору и деструктору?
  • Почему именно виртуальное наследование запрещает дальнейшее наследование?
Ответы на вопросы я дам в следующей статье.

Всем доброй ночи.