|
Программирование >> Перегруженные имена функций и идентификаторы
if( CRC OK( dataStream ) ) { определяем тип конкретного результата и строим соответствующий объект прямо на месте себя switch( AnalysisOf( dataStream ) ) { case PHLEGM: ::new( this ) Phlegm( dataStream ); break; case BLOOD: ::new( this ) Blood( dataStream ); break; case Теперь, чтобы не вынуждать вас носиться по всему материалу в целях построения целостной картины, объясним происходящее по шагам. Менеджер памяти создан, инициализирован. Пул памяти существует (хотя бы от обычного malloc, а хоть и с потолка - ваше дело). Есть некоторый поток байт (пусть он зовётся stream), в котором то, с чем мы и боремся. Объект создаётся следующим образом: BCAR *analysis = new BCAR( stream ); Обратите внимание - мы создаём объект класса BCAR. В первую очередь вызывается BCAR::new, который в действительности завуалированный MemoryManager.largest(). Мы имеем адрес в свободной памяти, где и создаётся объект BCAR и запускается его конструктор BCAR::BCAR( const unsigned char * ). В конструкторе по информации из заголовка (полученного из потока stream) выясняется точный тип анализа и через глобальный new (который не делает ничего) создаётся на месте объекта BCAR объект уточнённого типа. Начинает исполняться его конструктор, который в свою очередь вызывает конструктор BCAR::BCAR(). Надеемся, стало понятно почему BCAR::BCAR() определяется с пустым телом. Потом в конструкторе конкретного объекта вызывается MemoryManager.alloc( int ), благодаря чему менеджер памяти получает информацию о точном размере объекта и соответствующим образом правит свои структуры. Уничтожение объектов примитивно, ибо всей необходимой информацией MemoryManager располагает: void BCAR::operator delete( void *p ) { MemoryManager.free( (BCAR *)p, ((BCAR *)p)->size() ); Переносимость этой конструкции очень высока, хотя может понадобиться некоторая правка для весьма экзотических машин. Факты же таковы, что она используется в трёх очень крупных мировых центрах на четырёх аппаратных платформах и пяти операционках. Ho это ещё не всё. Как особо дотошные могли заметить - здесь присутствует виртуальность конструктора, но в любом случае объект конкретного класса всё равно имеет фиксированный размер. А вот объектов одного класса, но разного размера нет. До относительно недавнего времени нас это вполне устраивало, пока не появились некоторые требования, в результате которых нам пришлось сделать и это. Для этого у нас есть два (по меньшей мере) способа. Один - переносимый, но неэстетичный, а второй - непереносимый, но из common practice. Эта самая common practice состоит в помещении последним членом класса конструкции вида unsigned char storage[ 1 ] в расчёте на то, что это будет действительно последним байтом во внутреннем представлении объекта и туда можно записать не байт, а сколько надо. Стандарт этого вовсе не гарантирует, но практика распространения нашего детища показала, что для применяемых нами компиляторов оно именно так и есть. И оно работает. Чуть-чуть поправим наши объекты: class Blood: public BCAR { friend BCAR; private: int bodySize; int size() { return sizeof( Blood ) + bodySize; } int getSize( const char * ); sturct BloodAnalysisBody { тут его поля } *body; Blood( const unsigned char *data ): BCAR() { body = (BloodAnalysisBody *) bodyStorage; bodySize = getSize( data ); ::memcpy( bodyStorage, data + sizeof( header ), bodySize ); MemoryManager.alloc( size() ); unsigned char bodyStorage[ 1 ]; Бороться с данными далее придётся через body->, но сейчас мы не об этом Однако вспомним, что менеджер памяти у нас свой в доску , и мы можем обойтись действительно переносимой конструкцией. Тело анализа достаточно разместить сразу за самим объектом, статический размер которого нам всегда известен. Ещё чуть-чуть правим: class Blood: public BCAR { friend BCAR; private: int bodySize; int size() { return sizeof( Blood ) + bodySize; } int getSize( const unsigned char * ); struct BloodAnalysisBody { тут его поля } *body; Blood( const unsigned char *data ): BCAR() { body = (BloodAnalysisBody *) ((unsigned char *)this + sizeof( Blood )); bodySize = getSize( data ); ::memcpy( body, data + sizeof( header ), bodySize ); MemoryManager.alloc( size() ); Данные гарантированно ложатся сразу за объектом в памяти, которую нам дал MemoryManager (а он, напомним, даёт нам всегда максимум из того, что имеет), а затем alloc соответствующим образом всё подправит.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |