This page was last modified 08:15, 18 July 2008.
FAQ по дескрипторам
From Forum Nokia Wiki
Разрешение на публикацию перевода этого FAQ любезно предоставлены Jo Stichbury. С оригинальной версией документа вы можете ознакомиться здесь.
Об авторе.
Мое имя - Jo Stichbury, я автор книги Symbian OS Explained, издательства Symbian Press. Об этой книге вы можете узнать больше на моем сайте.
Некоторые ответы этого FAQ схожи с выводами в главе о дескрипторах из моей книги, но они не скопированы оттуда. Вы заметите, что некоторые объяснения стали более подробными, а некоторые, совершенно отличны от тех, что вы читали в моей книге.
Многое в этом документе ранее было опубликовано в FAQ моего бывшего коллеги из Sony Ericsson - John Blaiklock. Я очень признательна ему за разрешение воспользоваться этой информацией. Я также хочу выразить признательность Jarmo Petajaaho, за его корректуру и ценные замечания.
Я благодарю Bill Bonney за его предложения и модификацию шаблона блога.
Добро пожаловать.
Кто-то из великих сказал, что наибольший процент всех вычислений составляют операции со строками. Если вы не сомневаетесь в этом, то вам просто необходимо взять в свои руки управление дескрипторами.
Большинство изучающих Symbian OS программистов, уже имели опыт программирования в других средах - они считают, что уже знают о строках все. Действительно, в C строки очень просты в использовании, хотя есть и позволяют допустить ошибку множеством способов. Классы String и StringBuf в Java обладают почти идеальным соотношением производительности и простоты использования. А различные String и CString подобные классы в библиотеках множества разнообразных сред программирования на C++ обычно работают удивительно быстро.
Но затем они сталкиваются с дескрипторами в Symbian OS. Если и есть лучший способ спустить с небес на землю C++ программиста в первую же неделю разработки для Symbian OS - то это дескрипторы. “Господи, да их тут тьма тьмущая” - вот что обычно можно услышать от читателя, познакомившегося с главой о дескрипторах из книги по программированию на Symbian OS. Они смотрят на разнообразные диаграммы соответствия типов дескрипторов их эквивалентам из C. Они запускают примеры и уверены, что уже во всем разобрались. А затем, они пытаются использовать дескрипторы на практике - и понимают, что все работает совсем не так, как они предполагали.
Дескрипторы, словно спроектированы для того, чтобы запутать вас. Когда вы уверены, что все правильно - код перестает компилироваться. Или компилируется без ошибок, но вызывает панику в приложении во время использовании. И даже если все компилируется и запускается, то напрочь отказывается работать так, как вы хотите. В этой ситуации очень хочется перейти к главе об использовании стандартных C библиотек и реализовать все с помощью C строк.
Не делайте этого. Боритесь с ними. Честно говоря, у вас нет другого выбора, если вы решили стать разработчиком для Symbian OS. Есть множество причин для использования дескрипторов и вытекающих из этого преимуществ - это станет очевидно по мере вашего знакомства с этим документом.
Все что вам нужно - это действительно простое объяснение дескрипторов на доходчивом языке, понятным любому программисту. Это объяснение должно быть подробным, т.к. без подробностей вы не сможете полноценно использовать дескрипторы. Вместе с обычными примерами работы с дескрипторами, оно также должно содержать примеры неправильного их использования.
Все начинающие Symbian OS программисты делают одни и те же ошибки при работе с дескрипторами, и зачастую не могут понять, в чем они заключаются. Эта статья начертит для вас кратчайшую прямую через длинный болезненный путь опыта, которым долго и мучительно шли ее авторы. Не следуйте их путем.
Новый подход к объяснению дескрипторов.
В каждой книге по программированию для Symbian OS есть глава о дескрипторах, обычно расположенная одной из первых. Они все очень похожи, и содержат иерархию классов, пару диаграмм со стрелочками, и, чаще всего, таблицу соответствия строк из C каждому типу дескрипторов. Кроме того, вы обнаружите несколько примеров использования дескрипторов, которые, конечно же, будут отлично работать.
Вы постараетесь не заснуть, и дочитаете эту неинтересную главу. Затем вы рассмотрите примеры, и окончательно убедитесь, что теперь вы можете использовать дескрипторы. Хотя в вашей голове могут затаиться вопросы вроде “Почему Ptr() не возвращает TPtr?”, вы продолжите читать книгу дальше. Затем вы начнете создавать какое-либо приложение, а оно неизбежно потребует использование дескрипторов, и тут - бац! Все не так. Вы не можете решить, какой тип дескрипторов использовать, а когда решите - эта дрянь не станет компилироваться, или не станет работать без ошибок или вообще не запуститься. Но не волнуйтесь, это случается с большинством новичков в программировании для Symbian OS, а, возможно, и со всеми новичками. Мы здесь для того, чтобы помочь вам разобраться со всем этим с помощью нашего нового подхода к объяснению дескрипторов.
Почему FAQ?
Эта статья содержит часто задаваемые вопросы. Длинные, сухие и невзрачные главы о дескрипторах тяжело изучать. Совсем другое дело - FAQ. Их гораздо интереснее читать, и они подают информацию в хорошо структурированном виде.
Хорошие примеры и плохие примеры
Книги о Symbian OS всегда содержат примеры, которые работают (или должны были бы, если бы не опечатки во время публикации). Обычно, с неправильным использованием дескрипторов мы знакомимся на досуге. В тот момент, когда мы разбираемся, почему оно не работает. Это не самый приятный досуг. Поэтому, в этой статье вы также найдете множество примеров неправильного использования дескрипторов. Мы публикуем наши собственные ошибки, для того чтобы вы их не повторяли.
Повторение
Мы будем повторять одно и то же разными способами. Повторять собственные слова. Мы делаем это специально. Без понимания некоторых вещей, невозможно успешное использование дескрипторов. Если повторение поможет вашему пониманию, то оно того стоит.
Я уже говорила, что буду повторяться?
Основы.
Что такое дескрипторы?
Дескрипторы – это строки в Symbian OS.
Так почему они называются дескрипторами, а не строками?
Их назвали дескрипторами (от англ. describe – описывать, также известны как описатели, прим. перев.), т.к. они сами себя описывают. Каждый объект дескриптор содержит не только строку данных, но и ее длину, а также ее “тип”, определяющий метод хранения дескриптора в памяти и его структуру.
Так они могут быть использованы для хранения текста?
Да, могут. Также они могут быть использованы для хранения бинарных данных.
Хранят ли они текстовые данные в ASCII или Unicode?
И так, и так. Есть два типа: 8-ми и 16-ти битные дескрипторы. 8-ми битный вариант - используется для хранения ASCII символов и бинарных данных. В этом случае, наименьшим элементом является 8-ми битный (1 байт) символ. 16-ти битный тип используется для хранения Unicode символов. В этом случае, наименьшим элементом является 16-ти битный (2 байта) символ.
Как я узнаю, какой из них я использую?
Каждый тип оканчивается на 8 или 16. Например, TDesC8 или TDesC16.
А что за тип дескриптора без 8 или 16 на конце? Например, TDesC?
Они известны как дескрипторы нейтрального размера (“neutral” width), т. к. размер их элементов определяется при компиляции и зависит от платформы. Давным-давно, в Symbian OS (тогда она была известна еще и как EPOC32) использовался 8-ми битный ASCII текст, и по-умолчанию размер элемента нейтрально дескриптора был 8-бит. Но после выхода Symbian OS версии 5U (также известной как ER5U), Symbian OS использует Unicode строки.
Поэтому, до тех пор, пока вы не работаете с SDK для платформ, вышедших до версии 5U - все нейтральные дескрипторы (TBufC, TPtr, HBufC и т.п.) будут 16-ти битными, и в действительности являются TBufC16, TPtr16, HBufC16 и т.д.
Как мне узнать размер символа для моей платформы?
Как было сказано выше – если вы не пишете программу для OS вышедшей до Symbian OS версии 5U (использовалась в телефоне Ericsson R380 в 2000 г.), то вы можете быть уверены, что по-умолчанию будут использованы 16-ти битные символы.
Так какой размер символа мне использовать?
Если вам нужны 8-ми битные данные, к примеру, в вызовах определенных методов API (таких как функции Read() или Write() класса RFile), для электронной почты, или для манипулирования ASCII данными, то вам следует использовать 8-ми битные дескрипторы. Они также пригодятся для хранения цепочки бинарных данных.
Если вам необходимо работать с 16-ти битным текстом, например, Java строками, и вы хотите явно на это указать, используйте 16-ти битные дескрипторы.
Но в общем случае, когда нет необходимости явно указывать размер символов, используйте нейтральные дескрипторы. Это сделает ваш код более удобным для чтения, и, возможно, обеспечит портируемость на будущие платформы, если размер символа по-умолчанию снова изменится.
Дескрипторы также являются NULL-завершенными, как и строки в C?
Нет.
Почему?
Потому что они описывают себя сами, дескрипторам известен размер хранящихся в них данных. Это означает, что для них нет необходимости быть NULL-завершенными, т.к. не нужно помечать конец данных. Это также позволяет хранить бинарные данные в тех же классах, что и текстовые данные. В C строках это невозможно, т.к. нет способа отличить завершающий NULL символ от содержащегося в данных NULL символа.
Их сложнее использовать?
Не совсем. Просто их часто применяют неправильно. А ведь для правильного использования вам потребуется совсем немного - знание нескольких правил и приемов. Они просто не были хорошо объяснены - до сих пор...
Вы можете ознакомиться с описанием дескрипторов в Symbian Developer Library (библиотека разработчика Symbian), которая входит в состав любого Software Development Kit (набор инструментальных средств разработки программного обеспечения), например, для телефонов на базе UIQ или S60. Это описание также доступно в сети – ссылка помещена в раздел "внешние ссылки на другие ресурсы о дескрипторах".
Описание дескрипторов [1].
Зачем использовать дескрипторы?
Могу я использовать обычные массивы из C для хранения текста и бинарных данных, и стандартную библиотеку C для работы с ними, как и прежде?
Да.
Должен ли я использовать стандартные массивы из C и стандартную библиотеку для строк из C при программировании для Symbian OS?
Нет.
Почему нет?
Стандартные массивы C небезопасны. Почему? Потому что они не имеют информации о своем размере. NULL-завершенные строки опасны и неэффективны. Более того, ничто не помешает вам продолжить запись данных за пределами памяти, отведенной C строке. Это может привести к разнообразным негативным последствиям, например - к повреждению данных.
Я пользуюсь C строками, но мне нужно воспользоваться API, требующим работы с дескрипторами. Как мне конвертировать мои данные?
Совершенно верно. Почти везде в Symbian OS C++ API вы встретитесь с использованием дескрипторов для передачи куда-либо текста и бинарных данных. Поэтому, если вы используете C строки, то у вас возникнут проблемы с вызовом функций API. Вы должны будете конвертировать данные при обращении к каждой требующей передачи дескриптора функции, а это неэффективно. Так может быть вам продолжить чтение, и, наконец, научиться правильно использовать дескрипторы?
В чем отличие дескрипторов от прочих типов?
Так как выделение памяти для дескриптора и ее освобождение ложится на плечи программиста (подробно описано в разделе "Управление памятью и дескрипторы"), они не похожи на стандартные строки из C++ и Java или CString из MFC.
Также, благодаря встроенной защите от переполнения буфера, они не нуждаются в определении размера хранящихся данных с помощью NULL-завершения, и тем самым не похожи на строки из C. В разделе "Зачем использовать дескрипторы?" вы увидите преимущества их использования по сравнению с привычными, но опасными C строками.
Управление памятью и дескрипторы.
Изменяют ли дескрипторы свой размер автоматически, когда им требуется больше памяти?
Нет. Размер выделенный дескриптору памяти не меняется, и указывается при создании. Они не могут динамически менять ее размер, однако вы можете повторно выделить в куче больший объем памяти для дескриптора типа HBufC. Но, как мы увидим в разделе "Как использовать дескрипторы-буферы, создаваемые в куче?", это имеет свои подводные камни.
Почему? Было бы удобнее делать это автоматически.
Это сделало бы жизнь программиста куда проще, но и превратило бы код в гораздо менее быстродействующий. Для обработки всех возникающих исключений, код пришлось бы усложнить. Задумайтесь над этим:
Большинство методов, изменяющих данные дескриптора, могут потребовать увеличения занимаемой им памяти. Если класс дескриптора будет делать это сам, большинство методов станут потенциально способными вызвать сброс (leave), или должны будут быть окружены снижающими быстродействие макросами TRAP. И это только одна из возникающих проблем. В большинстве случаев, вам будет вполне достаточно строк фиксированной длины.
В Symbian OS производительность является наивысшим приоритетом, поэтому программист должен взять на себя заботу о выделении памяти.
Что будет, если я выйду за пределы выделенной дескриптором памяти?
Вы получите панику как в debug, так и в release сборках. Паника будет из категории USER, информация о которой можно найти в документации [2]. Предполагается, что не произойдет никакого неразрешенного доступа к памяти, перемещения или повреждения данных. Однако, выполнение вашего кода будет аварийно завершено. В случае если код выполнялся в системном потоке, это может привести к перезагрузке телефона.
А это не слишком радикально?
Может быть, зато безопасно. Вы не перепишите данные в не предназначенной для этого области памяти, что исключает возникновение сложно объяснимых и обнаруживаемых ошибок, к которым может приводить использование C строк.
Выполняют ли дескрипторы сборку мусора?
Нет. По тем же причинам, по которым они не выполняют выделения памяти или изменения ее размера. Дескрипторы не выполняют сборку мусора из-за большого количества вызываемых в связи с этим сложностей и издержек.
Есть ли ограничения на размер дескриптора?
Нечто вроде этого.
Дескрипторы могут храниться в стеке и куче. На них накладываются естественные ограничения Symbian OS, как и на прочие переменные:
- Дескриптор в стеке должен быть небольшим: размер в 256 байтов (128 двухбайтовых символов) наиболее оптимален. В разделе "Большие дескрипторы в стеке" представлена информация о том, как избежать напрасных трат памяти в стеке из-за использования переопределений (typedef) типов дескрипторов и создаваемых в стеке классов, инкапсулирующих дескрипторы большого размера.
- Дескриптор в куче может быть намного больше, и его размер зависит от количества доступной в куче памяти. Но помните, что Symbian OS работает на устройствах с весьма ограниченным объемом памяти, так что выделение дескриптору большого объема памяти в любом случае неразумно.
Сама структура объекта дескриптора (будет описана позднее) ограничивает максимальный размер дескриптора 2^28 байтами (256 Мб). Т.к. каждый Unicode символ равен двум байтам, то максимальной длиной дескриптора можно считать 2^27 символов.
Большие дескрипторы в стеке.
Размер стека в Symbian OS очень ограничен (по умолчанию - 8 KB). Поэтому, вы должны по возможности избегать создания в стеке дескрипторов большого размера. Иногда Symbian OS подталкивает к нарушению этого правила, определяя некоторые классы и переопределяя типы дескрипторов, с помощью которых очень легко исчерпать всю память стека. Вам должны не забывать об этом, и использовать их только убедившись, что в стеке достаточно свободного места.
К примеру, TFileName переопределяет следующий тип дескриптора:
typedef TBuf<KMaxFileName> TFileName;
где
const TInt KMaxFileName=0x100; // = 256 в десятичной системе счисления
Конечно же, каждый символ в этом дескрипторе займет 2 байта, т. к. ваша Symbian OS, скорее всего, использует UNICODE, и при сборке программы размер символа по умолчанию будет равен двум байтам (как было рассказано в разделе основы). Поэтому, каждый объект TFileName созданный в стеке займет 512 байт (1/16-ю от стандартного размера стека), в не зависимости от того, сколько текста вы в него поместите!
Конечно, это очень привлекательно - т.к. вы сможете не волноваться о переполнении буфера. Но это влетит вам в копеечку.
Задумываясь о размере стека, вы всегда должны помнить об объеме памяти, поглощаемом следующими объектами:
- TFileName 512 байт
- TEntry 544 байт
- TFullName 512 байт
- TName 256 байт
Если же вам все же пришлось их использовать, то лучше всего создавать эти объекты в куче. Этого можно с легкостью добиться, объявив их членами создаваемого в куче класса (к примеру, C класса). После этого вам уже не нужно выделять для них память в куче самостоятельно – будучи членами C класса, они будут созданы там автоматически. Другой способ – выделение для них памяти в куче с помощью оператора new. Но при этом убедитесь, что ваш код не приведет к сбросу, а объект уничтожается, как только он больше не нужен.
Как создать небольшой дескриптор в стеке?
Если он должен быть изменяемым, воспользуйтесь TBuf. Если не изменяемым - используйте TBufC. Вам потребуется указать размер памяти выделяемой для вашего дескриптора в стеке.
Вот пример:
_LIT(KFred, "fred"); // Символьная строка, о них мы расскажем позднее TBufC<4> constantFred(KFred);
На практике, вы, возможно, не станете использовать неизменяемый дескриптор, т.к. вы можете сразу использовать символьную строку KFred с оператором () (об этом мы расскажем отдельно в разделе о символьных дескрипторах). Вместо создания дескриптора Fred, вы можете вызывать просто KFred().
Однако, этот подход необходим при использовании изменяемых дескрипторов. Например, для ведения лога:
TInt CMyActiveObject::RunError(TInt aError) { _LIT8(KError, "CMyActiveObject::RunError: %d"); TBuf8<35> errorBuf; errorBuf.Format(KError, aError); // Информацию о TDes::Format() вы можете почерпнуть // пройдя по ссылке 3 в разделе ссылок. iLogger.Log(errorBuf); // iLogger - объект, принадлежащий классу, // обеспечивающий вывод на экран или в файл. return (KErrNone); }
Что если я хочу хранить ASCII текст в дескрипторе?
В некоторых случаях, к примеру, при работе с MIME типами или бинарными данными, вам не избежать операций с 8-ми битным текстом. В этом случае, просто воспользуйтесь 8-ми битными дескрипторами:
_LIT8(KImageGif, "image/gif"); TBufC8<15> mimeType(KImageGif);
Как создать дескриптор в куче? Как использовать такой дескриптор?
Создайте дескриптор типа HBufC (или HBufC8, если вы работаете с 8-ми битным текстом).
HBufC* heapbasedFred=HBufC::NewL(4);
Но как записать данные в созданный в куче дескриптор? HBufC порожден от неизменяемого класса дескрипторов?
Очень просто. Сначала вызовите HBufC::Des() чтобы получить TPtr, а он - изменяемый. Вот пример:
TPtr fredPtr(heapbasedFred->Des()); _LIT(KFred, "fred"); fredPtr.Copy(KFred);
А как читать данные из HBufC?
Вы можете просто переобъявить указатель HBufC следующим образом:
TBuf<4> stackbasedFred(*heapBasedFred);
Но я видел код, в котором вызывается функция Des() класса HBufC. Почему?
Это типичная ошибка, вносящая свою лепту в снижение производительности вашего кода. Функция Des() возвращает изменяемый дескриптор типа TDes, порожденный от TDesC, поэтому вы можете использовать его в любой функции, принимающей аргумент типа TDesC. Но это не обязательно.
Вот пример этой распространенной ошибки:
TBuf<4> stackbasedFred(heapBasedFred->Des()); // Не обязательно, просто используйте TBuf<4> stackBasedFred(*heapBasedFred); // Более эффективный код
Как использовать дескрипторы в качестве параметров?
Базовые классы описаны в разделах о неизменяемых и изменяемых дескрипторах.
Базовые классы содержат реализацию основных операций для порожденных от них изменяемых и неизменяемых типов дескрипторов. Очевидно, что именно базовые классы дескрипторов должны использоваться в качестве аргументов функций, тем самым, позволяя передать в функцию дескриптор любого порожденного от них типа.
К примеру, вы можете вызывать следующую функцию, передав два аргумента любых типов, порожденных от TDesC м TDes соответственно. Например, HBufC и TBuf<8>, или TPtr и TBuf<3>:
void SomeFunction(const TDesC& aReadOnlyDescriptor, TDes& aReadWriteDescriptor);
Как сделать дескриптор-параметр доступным только для чтения?
Нужно объявить его как константу от неизменяемого дескриптора (const TDesC&). Например:
void SomeFunction(const TDesC& aReadOnlyDescriptor);
Эта функция все равно может вызываться с изменяемым дескриптором в качестве аргумента, таким как TPtr, потому что он порожден от TDesC (TPtr - это специальный тип TDes). В теле функции вы сможете выполнять над таким параметром только операции реализованные в TDesC. Содержимое параметра нельзя будет изменить, даже если вы передадите изменяемый, порожденный от TDes дескриптор.
Как сделать дескриптор-параметр доступным и для чтения и для записи?
Объявите его как изменяемый дескриптор (TDes&). Например:
void SomeFunction(TDes& aReadWriteDescriptor);
Вы не сможете вызвать эту функцию с неизменяемым дескриптором.
В теле функции над таким параметром вы можете выполнять как операции чтения данных, так и операции их записи. Помните, что если вы решите увеличить размер его содержимого, вам необходимо убедится, что дескриптору выделено достаточно памяти. В противном случае вы вызовете панику в приложении. (Некоторые функции выполняют проверку размера дескриптора, перед тем как произвести способную вызвать панику операцию, и в случае переполнения буфера вернут ошибку. Но многие такой проверки не выполняют, так что результат непредсказуем.)
Это одна из причин, по которой вы всегда должны тестировать ваш код с "граничными условиями" - т. е., передавать параметры в функции с максимальными и минимальными возможными значениями; а также, проверять, что ошибки возвращаемые функцией корректно обрабатываются.
Можно ли использовать другие типы дескрипторов в качестве параметров функций?
Можно, но не нужно. К примеру, вы можете объявить аргумент функции типа TBuf<10>&. Но что, если в нее потребуется передать TPtrC, или HBufC, или даже TBuf<11>? Это означает, что вашу функцию не смогут вызвать без дополнительных манипуляций для того, чтобы уместить дескриптор в тот тип, который в ней указан. TDes и TDesC - наиболее общие типы, и именно они должны использоваться в качестве типов аргументов функций.
Моя функция использует параметры типа TDes или TDesC, как вы и сказали, но передача дескриптора в функцию не работает. Что не так?
О, сколько головной боли доставляет эта ошибка! В забыли один маленький символ &. Ваши типы параметров должны передаваться по ссылке, а не по значению, т. е. const TDesC& или TDes&.
Базовые классы TDesC и TDes не содержат методов для хранения данных. Если вы передаете их по значению, а не по ссылке - то вы используете статическое связывание, а значит, полиморфизм уже не работает - и вы получите на входе объект базового класса, не содержащий данных. При этом, все прекрасно скомпилируется, но работать не будет.
Никогда не пытайтесь создавать или работать напрямую с объектами классов TDesC или TDes. Это высокоэффективные абстрактные классы. Необходимость в создании объекта базового класса появляется очень редко, если вообще может появиться. Объекты всегда создаются от порожденных ими классов (TBufC, TBuf, TPtrC, TPtr, HBufC или RBuf).
Как использовать созданный в куче дескриптор в качестве результата функции?
Я хочу создать новый дескриптор в моей функции, как вернуть его из функции?
Вы должны возвращать HBufC*, как показано ниже:
HBufC* CreateSomeDescriptorL() { _LIT(KBert, "bert"); HBufC* newBert = KBert().AllocL(); return (newBert); }
Вызвавший такую функцию должен знать, что берет на себя заботу об удалении ее результата. Очень часто, программисты забывают об этом, что приводит к утечкам памяти.
Схожая функция, оставляющая созданный дескриптор в стеке очистки, будет выглядеть следующим образом:
HBufC* CreateSomeDescriptorLC() { _LIT(KBert, "bert"); HBufC* newBert = KBert().AllocLC(); return (newBert); }
Когда я могу вернуть из функции TPtr или TPtrC? А когда нет?
TPtr или TPtrC - это дескрипторы, не владеющие строкой данных. Они просто ссылаются на данные, содержащиеся в другом дескрипторе. Поэтому, вы можете вернуть их в качестве дескриптора, ссылающегося на часть данных другого дескриптора. Но вы должны быть уверены, что владеющий данными дескриптор будет существовать достаточное время. Пример:
TPtrC LeftChar(const TDesC& aInput) { if (aInput.Length()>0) return aInput.Left(1); // Возвращает первый символ в начале строки else return KNullDesC; }
А вот следующей пример неверен, т. к. созданный в стеке fred будет уничтожен сразу после завершения выполнения функции GetFred():
TPtrC GetFred() { _LIT(KFred, "Fred"); TBufC<4> fred(KFred()); TPtrC fredPtr(fred); return (fredPtr); }
Как хранятся в памяти неизменяемые дескрипторы?
Во-первых, следует различать символьные дескрипторы и неизменяемые дескрипторы. Символьные дескрипторы являются константами, размер которых фиксируется в момент сборки приложения, что позволяет встраивать их в ROM память. Содержимое неизменяемого дескриптора также является константой, но его размер не может быть определен во время компиляции. Символьные дескрипторы в Symbian OS реализуются немного иначе, чем остальные типы, поэтому, мы обсудим их отдельно в символьных дескрипторах.
Все (кроме символьных) дескрипторы порождены от базового типа TDesC (переопределены от TDesC16 в e32std.h и определены в e32des16.h). Суффикс "C" в конце имени класса указывает на то, что этот дескриптор является неизменяемым. Класс TDesC содержит реализацию методов для определения размера хранящихся в нем данных и доступа к ним (чтение, сравнение, поиск), но не содержит методов для изменения этих данных.
Из этого следует, что все методы неизменяемых дескрипторов реализуются в этом общем базовом классе, а не перегружаются в каждом порожденном классе (TBufC, TPtrC и HBufC). Это сделано для более эффективного использования памяти - ведь иначе, пришлось бы использовать виртуальные функции, что в свою очередь заняло бы дополнительные 4 байта памяти в объекте порожденного класса для хранения указателя (vptr) на таблицу виртуальных функций. Этот излишек занимаемой памяти нежелателен. В частности, для дескрипторов небольших строк эти 4 байта составляют значительный процент от их общего размера.
Хранение всех методов дескрипторов в одном классе также позволяет серьезно уменьшить объем памяти, занимаемой кодом, реализующим полный набор функций для работы со строками. Повторное использование кода позволяет уменьшить размер ROM, и демонстрирует лучшие традиции программирования на C++, т. к. такой код легче тестировать и им легче управлять. Производные классы реализуют лишь специфичные методы для создания данных и копирующего присваивания.
Первые 4 байта любого объекта дескриптора содержат одно и то же: размер хранимых данных. Вообще-то, только 28 из 32 бит используется для этого - первые 4 бита содержат тип дескриптора. Использование лишь 4 бит для задания типа ограничивает максимальное число различных типов 2^4 (=16), но во всех ныне существующих версиях Symbian OS используются только шесть (TBufC, TBuf, TPtrC, TPtr, HBufC и RBuf). Не похоже, что в обозримом будущем их число резко возрастет. Остальные 28 бит хранят размер данных, а это значит, что максимальный размер хранимых данных ограничен 2^28 байтами = 256 Мб.
Размещение в памяти прочих данных о дескрипторе зависит от его типа. Доступ к данным дескриптора каждого типа происходит с помощью (не виртуального) метода Ptr() базового класса TDesC. Этот метод использует выражение switch чтобы определить тип дескриптора (ис
