Указание CString формальных параметров
Для большинства функций, которым требуется строковый аргумент, в прототипе функции в качестве указателя на символ () вместо лучше всего указать формальный параметр. Если формальный параметр указан в качестве указателя на символ, можно передать либо указатель на массив, либо литеральную строку , либо объект. Объект будет автоматически преобразован в . В любом месте, где можно использовать , можно также использовать объект.
Можно также указать формальный параметр в виде ссылки на константную строку (то есть ), если аргумент не будет изменен. Удалите модификатор, если строка будет изменена функцией. Если требуется использовать по умолчанию значение NULL, инициализируйте его с нулевой строкой [], как показано ниже:
Для большинства результатов функции вы можете просто возвратить объект по значению.
Члены
Открытые методы
name | Описание |
---|---|
Возвращает число элементов в этой карте. | |
Определяет текущее количество элементов в хэш-таблице. | |
Возвращает следующий элемент для итерации. | |
Возвращает число элементов в этой карте. | |
Возвращает расположение первого элемента. | |
Вычисляет хэш-значение указанного ключа. | |
Инициализирует хэш-таблицу. | |
Проверяет условие на пустую карту (нет элементов). | |
Ищет указатель void на основе ключа указателя void. Значение указателя, а не сущность, на которое он указывает, используется для сравнения ключей. | |
Возвращает ссылку на ключ, связанный с указанным значением ключа. | |
Удаляет все элементы из этой схемы. | |
Удаляет элемент, указанный ключом. | |
Вставляет элемент в карту; заменяет существующий элемент, если найден соответствующий ключ. |
AfxLoadLibraryEx
Используйте для отображения модуля DLL.
Параметры
Указывает на строку, завершающуюся нулем, которая содержит имя модуля (.DLL или файл .EXE). Указанное имя является именем файла модуля.
Если строка указывает путь, но файл не существует в указанном каталоге, функция завершается ошибкой.
Если путь не указан и расширение имени файла не указано, добавляется расширение по умолчанию .DLL. Однако строка filename может содержать символ завершающей точки (.). значение указывает, что имя модуля не имеет расширения. Если путь не указан, функция использует .
Этот параметр зарезервирован для использования в будущем. Оно должно быть .
Действие, предпринимаемое при загрузке модуля. Если флаги не указаны, поведение этой функции идентично функции. Возможные значения этого параметра описаны в документации.
Комментарии
Возвращает маркер, который можно использовать в для получения адреса функции DLL. также может использоваться для отображения других исполняемых модулей.
Каждый процесс поддерживает счетчик ссылок для каждого загруженного модуля библиотеки. Этот счетчик ссылок увеличивается каждый раз, когда вызывается и уменьшается каждый раз . Когда счетчик ссылок достигает нуля, модуль не сопоставляется с адресным пространством вызывающего процесса, и этот маркер становится недействительным.
Обязательно используйте и (вместо функций Win32 и ), если приложение использует несколько потоков и динамически загружает библиотеку DLL расширения MFC. Использование и гарантирует, что код запуска и завершения работы, выполняемый при загрузке и ВЫГРУЗКЕ библиотеки DLL расширения MFC, не повреждает глобальное состояние MFC.
Для использования в приложении необходимо динамически связываться с DLL-версией MFC. Файл заголовка для , включается, только если MFC связан с приложением в качестве библиотеки DLL. Это требование обусловлено проектированием, поскольку для использования или создания библиотек DLL расширения MFC необходимо создать ссылку на библиотеку DLL MFC.
CStringНепосредственное изменение содержимого
В большинстве ситуаций для изменения содержимого объекта или для преобразования в символьную строку в стиле C следует использовать функции-члены .
В некоторых ситуациях имеет смысл напрямую изменить содержимое , например, при работе с функциями операционной системы, требующими символьный буфер.
Методы и предоставляют доступ к внутреннему символьному буферу объекта и позволяют изменять его напрямую. Приведенные ниже действия показывают, как использовать такие функции в этих целях.
Использование и для доступа к внутреннему буферу символов объекта
-
Вызовите для объекта и укажите требуемую длину буфера.
-
Используйте указатель, возвращенный , для записи символов напрямую в объект .
-
Вызовите для объекта , чтобы обновить все внутренние сведения о состоянии , например длину строки. После непосредственного изменения содержимого объекта необходимо вызвать до вызова любых других функций-членов .
AfxBeginThread
Вызовите эту функцию, чтобы создать новый поток.
Параметры
Указывает на функцию управления для рабочего потока. Указатель не может иметь значение . Эта функция должна быть объявлена следующим образом:
Объект объекта, производного от .
Параметр, передаваемый функции управления.
Приоритет, заданный для потока. полный список и описание доступных приоритетов см . в разделе Windows SDK.
Задает размер стека в байтах для нового потока. Если значение равно 0, то размер стека по умолчанию совпадает с размером стека создания потока.
Задает дополнительный флаг, управляющий созданием потока. Этот флаг может содержать одно из двух значений:
-
Запустите поток с числом приостановки, равным одному. Используйте , если требуется инициализировать все данные элементов объекта, например или любые члены производного класса перед запуском потока. После завершения инициализации используйте для запуска потока. Поток не будет выполняться до вызова метода.
-
. Запустите поток сразу после создания.
Указывает на структуру, указывающую атрибуты безопасности для потока. Если задано значение, то используются те же атрибуты безопасности, что и при создании потока. дополнительные сведения об этой структуре см. в Windows SDK.
Комментарии
Первая форма создает рабочий поток. Вторая форма создает поток, который может выступать в качестве потока пользовательского интерфейса или рабочего потока.
создает новый объект, вызывает его функцию, чтобы начать исполнение потока, и возвращает указатель на поток. Проверки выполняются на протяжении всей процедуры, чтобы убедиться, что все объекты освобождены должным образом, в случае сбоя любой части создания. Чтобы завершить поток, вызовите из потока или вернитесь из управляющей функции рабочего потока.
Многопоточность должна быть включена приложением; в противном случае эта функция завершится ошибкой. Дополнительные сведения о включении многопоточности см. в разделе , (использование библиотеки времени выполнения).
Дополнительные сведения о см. в статьях и многопоточность: создание User-Interface потоков.
Явные преобразования (приведения)
С помощью операции приведения можно указать компилятору преобразовать значение одного типа в другой тип. В некоторых случаях компилятор вызовет ошибку, если эти два типа полностью не связаны, но в других случаях не вызывает ошибку, даже если операция не является строго типизированной
Используйте приведение с осторожностью, так как любое преобразование из одного типа в другой является потенциальным источником ошибок программы. Однако иногда требуется выполнить приведения, а не все приведения являются опасными
Одно эффективное использование приведения заключается в том, что в коде выполняется понижающие преобразования и известно, что преобразование не приводит к созданию неверных результатов в программе. Фактически, это говорит компилятору о том, что вы делаете, а также о том, что вы выполняете предупреждения. Другой способ заключается в приведении из класса указателя на класс, производный от указатель на базовый. Другой способ — приведение к переменной постоянной, чтобы передать ее в функцию, для которой требуется аргумент, не являющийся константой. Большинство этих операций приведения к некоторым рискам требует определенного риска.
В программировании в стиле C для всех типов приведений используется один и тот же оператор приведения в стиле C.
Оператор приведения в стиле C идентичен оператору call () и, следовательно, инконспикуаус в коде и легко пропускаться. Оба являются некорректными, так как они трудно распознать на взгляде или найти, и они достаточно разнороды для вызова любой комбинации , и . Понять, что такое приведение старого стиля, действительно может быть трудно и подвержено ошибкам. По всем этим причинам, если требуется приведение, рекомендуется использовать один из следующих операторов приведения в C++, который в некоторых случаях значительно более строго типизирован, и что явно упрощает намерение программирования:
-
, для приведений, которые проверяются только во время компиляции. Возвращает ошибку, если компилятор обнаруживает, что вы пытаетесь выполнить приведение типов, которые полностью несовместимы. Его также можно использовать для приведения между указателями на базовые и производные указатели, но компилятор не всегда может определить, будут ли такие преобразования небезопасны во время выполнения.
Для получения дополнительной информации см. .
-
, для безопасного выполнения приведения указателя на Base к типу, который проверяется средой. Объект является более безопасным , чем для типов, но проверка среды выполнения требует некоторых дополнительных издержек.
Для получения дополнительной информации см. .
-
, для приведения параметра -rvalue характеристики переменной или для преобразования непеременной в значение . Приведение незавершенного использования — rvalue характеристики с помощью этого оператора, как и при использовании приведения в стиле C, за исключением того, что у вас меньше вероятность того, что приведение выполняется случайно. Иногда необходимо выполнить приведение rvalue характеристики переменной, например, чтобы передать переменную в функцию, которая принимает не параметр. В приведенном ниже примере показано, как это сделать.
Для получения дополнительной информации см. .
-
, для приведения между несвязанными типами, такими как тип указателя и .
Примечание
Этот оператор приведения не используется так часто, как другие, и не гарантирует перенос в другие компиляторы.
В следующем примере показано отличие от .
Дополнительные сведения см. в разделе оператор.
AfxGetMainWnd
Если приложение является сервером OLE, вызовите эту функцию, чтобы получить указатель на активное главное окно приложения. Используйте этот результат вместо непосредственного обращения к члену объекта приложения.
Возвращаемое значение
Возвращает указатель на объект окна фрейма, содержащий активный документ на месте, если сервер содержит объект, который находится на месте в активном контейнере.
Если в контейнере отсутствует объект, активный в месте, или ваше приложение не является сервером OLE, эта функция возвращает объект приложения.
Если вызывается из основного потока приложения, он возвращает главное окно приложения согласно приведенным выше правилам. Если функция вызывается из вторичного потока в приложении, функция возвращает главное окно, связанное с потоком, который выполнил вызов.
Комментарии
Если приложение не является сервером OLE, то вызов этой функции эквивалентен непосредственному обращению к члену объекта приложения.
strtok
char* strtok( char* src, const char* seps );
Последовательно разбивает строку src на лексемы
(токены), считая разделителями все символы строки seps.
При каждом вызове возвращается указатель на очередную найденную лексему или NULL, если
достигнут конец строки src. Отметим, что данная функция
модифицирует исходную строку. Пример использования:
Обратите внимание, что указатель на исходную строку передается только при первом вызове
функции; при последующих вызовах для работы с этой же строкой необходимо в качестве ее
адреса передавать значение NULL. Естественно, что в реальных случаях обработка лексем
выполняется в цикле, завершающемся при достижении конца исходной строки:
Текстовый ввод/вывод
Заголовочный файл | #include <stdio.h> |
Поскольку нашей основной целью является создание Windows-приложений, взаимодействующих
с пользователем посредством GUI, мы рассмотрим только две функции стандартной библиотеки,
предназначенные для текстового ввода/вывода.
Кмапстрингтуб:: Жетнекстассок
Извлекает элемент Map по адресу рнекстпоситион, а затем обновляет рнекстпоситион для ссылки на следующий элемент в сопоставлении.
Параметры
рнекстпоситион
Задает ссылку на значение, возвращенное предыдущим или вызовом метода.
ркэй
Указывает возвращаемый ключ извлеченного элемента (строка).
rValue
Указывает возвращаемое значение извлеченного элемента ( указатель). Дополнительные сведения об этом параметре см. в разделе «Примечания».
Комментарии
Эта функция наиболее полезна для прохода по всем элементам на карте
Обратите внимание, что последовательность позиций не обязательно совпадает с последовательностью значений ключа
Если извлеченный элемент является последним в сопоставлении, то новое значение рнекстпоситион устанавливается в NULL.
Для параметра rvalue обязательно приведите тип объекта к типу CObject * , который требуется компилятору, как показано в следующем примере:
Это не относится к картам, основанным на шаблонах.
В следующей таблице показаны другие функции элементов, аналогичные .
Класс | Функция-член |
---|---|
CMapPtrToPtr | void жетнекстассок (расположение рнекстпоситион, void * ркэй, void * rvalue) const; |
CMapPtrToWord | void жетнекстассок (расположение рнекстпоситион, void * ркэй, слово rvalue) const; |
CMapStringToPtr | void жетнекстассок (расположение рнекстпоситион, CString ркэй, void * rvalue) const; |
CMapStringToString | void жетнекстассок (расположение рнекстпоситион, CString ркэй, CString rvalue) const; |
CMapWordToOb | void жетнекстассок (расположение рнекстпоситион, слово ркэй, CObject * rvalue) const; |
CMapWordToPtr | void жетнекстассок (расположение рнекстпоситион, слово ркэй, void * rvalue) const; |
Пример
Список классов, используемых во всех примерах коллекции, см. в разделе .
Результаты этой программы выглядят следующим образом:
Анализ аргументов командной строки C++
-
Аргументы разделяются пробелами (пробел или табуляция).
-
Первый аргумент () обрабатывается особым образом. Он представляет имя программы. Это должен быть допустимый путь, поэтому разрешены части, заключенные в двойные кавычки ( ). Эти знаки двойных кавычек не включаются в выходные данные . Части, заключенные в двойные кавычки, не позволяют интерпретировать пробел или знак табуляции char актер в качестве конца аргумента. Последующие правила в этом списке не применяются.
-
Строка, заключенная в двойные кавычки, интерпретируется как один аргумент, который может содержать пробелы в char актерс. Строку в кавычках можно встроить в аргумент. Курсор ( ) не распознается как escape- char актер или разделитель. Внутри заключенной в кавычки строки пара двойных кавычек интерпретируется как одна экранированная двойная кавычка. Если командная строка заканчивается до тех пор, пока не будет найдена закрывающая двойная кавычка, то все char прочитанные актерс будут выводиться в качестве последнего аргумента.
-
Символ двойной кавычки после обратной косой черты ( ) интерпретируется как литеральный символ двойной кавычки ( ).
-
Символы обратной косой черты считаются литералами, если сразу за ними не стоит двойная кавычка.
-
Если двойная кавычка стоит после четного числа символов обратной косой черты, в массив помещается по одному символу обратной косой черты ( ) для каждой пары символов обратной косой черты ( ), а сама двойная кавычка ( ) интерпретируется как разделитель строк.
-
Если двойная кавычка стоит после нечетного числа символов обратной косой черты, в массив помещается по одному символу обратной косой черты ( ) для каждой пары символов обратной косой черты ( ). Двойная кавычка интерпретируется как escape-последовательность путем main обратной косой черты, что приводит к тому, что литеральная двойная кавычка ( ) будет помещена в .
Результаты синтаксического анализа командных строк
В следующей таблице показаны примеры входных данных и ожидаемые выходные данные, иллюстрирующие применение правил из приведенного выше списка.
Входные данные командной строки | argv | argv | argv3-5 |
---|---|---|---|
Пример. преобразование из CString
Описание
В этом примере показано, как преобразовать из в в другие типы строк, перечисленные выше. основан на типе данных TCHAR, который, в свою очередь, зависит от того, определен ли символ. Если значение не определено, то оно определено как char и содержит строку многобайтовых символов; если определено, то оно определяется как и содержит строку расширенных символов.
всегда является версией многобайтовой строки . она является версией только расширенных символов. Не используйте и для определения того, как они должны компилироваться. и используются в этом примере для уточнения незначительных различий в выделении размера буфера и обработке вывода.
Использование автоматического приведения типов в C++
Как я уже упоминал в п. 2, несмотря на то, что CString в своей основе – скрытый указатель, в нём есть несколько методов, позволяющих работать с указателем «открыто». Они используются в ситуациях, когда требуется обычный «сишный» указатель на строку оканчивающуюся нулём. Для этого CString активно использует возможности C++ по автоматическому приведению типов. Три метода класса CString: конструктор, операторы присваивания и преобразования типа, позволяют добиться того, что типы CString и LPCTSTR становятся как бы взаимозаменяемыми. Вот эти методы:
class CString {
public:
// …
CString( LPCTSTR lpsz );
const CString& operator = ( LPCTSTR lpsz );
operator LPCTSTR() const;
// …
};
Благодаря последнему, например, мы можем смело подставить CString в качестве параметра функции, который на самом деле должен иметь тип const char*, C++ автоматически выполнит преобразование:
CString sTitle( «Заголовок»
);
// Вызываем функцию Win32 API, которая естественно ничего не знает о CString
::SetWindowText( hWindow, sTitle ); // здесь будет вызван operator LPCTSTR
Или наоборот, конструктор позволяет использовать обычную строчку там, где должен быть CString:
void SomeFunction( CString _someString );
// …
SomeFunction( «Некая строчка»
); // здесь будет вызван конструктор CString(LPCTSTR lpsz)
В обоих примерах используется тот факт, что если тип параметра, который вы пытаетесь передать функции, не соответствует типу формального аргумента, C++ пытается сделать преобразование за вас. Для этого создаётся временный объект, который передаётся в качестве параметра, и удаляется сразу же после возврата из функции.
Такое неявное преобразование типов в C++ – очень полезный инструмент. Он удобен хотя бы тем, что как в случае с CString, часто позволяет значительно упростить исходный текст программы, скрывая второстепенные рутинные детали. А значит другому программисту (или даже вам самим через некоторое время) будет гораздо легче разобраться в коде вашей программы. Но здесь же заключена и обратная сторона: часто за простой конструкцией в C++ оказывается скрыта большая работа. За примером далеко ходить не надо: конструктор CString(LPCTSTR lpsz) всегда выполняет копирование. И если забыть об этом, вы можете вдруг обнаружить, что ваша программа тратит слишком много времени в, казалось бы, простом месте. Но что действительно опасно, излишнее увлечение такими фокусами приведёт к тому, что ваша программа превратится в головоломку, разобраться в которой станет гораздо сложнее. Поэтому использовать такие «скрытые» преобразования стоит лишь там, где они выглядят естественно и логично.
Кстати, с преобразованием типов связана ещё одна маленькая хитрость CString! Как мы помним, блок данных, на который ссылается скрытый указатель CString, состоит их структуры CStringData и собственно строки символов. Подробно эту конструкцию мы обсудили в п. 4. Но одну маленькую деталь я специально приберёг до этого места. Может показаться естественным, что указатель, который хранит CString, должен указывать на начало этого блока памяти. Но в CString сделано хитрее, он указывает на первый символ строки! Добраться до CStringData нетрудно – ведь она находится прямо перед этим местом и имеет фиксированный размер, зато доступ к самой строке максимально упрощается. Оператор LPCTSTR при этом фактически ничего не делает, CString хранит уже готовый указатель. Таким образом, мы получаем пусть и небольшой, но выигрыш в производительности, который может стать очень даже заметным при интенсивной работе со строками. Подобные простые приёмы оптимизации почти не требуют усилий от программиста, и при этом нередко дают эффект не меньший, чем трудоёмкое кодирование на ассемблере. Пренебрегать ими было бы недальновидно.
Метод монопольного захвата буфера
А теперь продолжим разговор о приведении типов
Вы обратили внимание, что CString определяет оператор LPCTSTR, но не LPTSTR? Напомню, что LPCTSTR означает const char*, а LPTSTR – просто char*, без const. То есть методом скрытого преобразования мы можем получить только указатель на константную строку, но не указатель на строку, которую можно изменить
Всё дело в том, что один и тот же буфер может использоваться одновременно несколькими объектами CString. Поэтому, operator char* не может быть таким же тривиальным, как и const char*. Прежде чем менять строку, мы должны сначала позаботиться о выполнении принципа копирования при записи.
Именно для этого в CString предусмотрен метод GetBuffer (и его вариант GetBufferSetLength), специально предназначенный для того, чтобы обрабатывать хранящуюся в буфере CString строку напрямую. Если при вызове этого метода оказывается, что данный буфер используется также кем-то ещё, создаётся его копия. В этом случае GetBuffer возвращает указатель именно на копию. После того, как вы модифицировали строку, следует вызвать ReleaseBuffer, который также позволяет задать новую длину строки. Наверно, было бы логично, чтобы GetBuffer устанавливал также специальный флаг, указывающий, что данный буфер предназначен для прямой работы и не может быть совместно использован другими экземплярами CString, тогда ReleaseBuffer должен был бы сбрасывать это флаг. Но в текущей реализации подобный флаг отсутствует, поэтому вызов ReleaseBuffer не является обязательным. Впрочем, я рекомендую вам всегда использовать его. Как минимум, это позволяет чётко выделить участок кода, где производится модификация строки напрямую.
Применение методов GetBuffer/ReleaseBuffer на практике оказалось очень удобным – далеко не все действия со строками, которые вам могут понадобиться, можно легко свести к набору стандартных операций. Кроме того, большинство функций Win32 API устроено так, что если они должны возвращать строку, они копируют её в указанный вами буфер. Выделите его с помощью GetBuffer, и вы получите результат в виде удобного для дальнейшей работы CString!
Но вернёмся к приведению типов. Почему бы метод GetBuffer не оформить как operator LPTSTR, и таким образом спрятать его так же, как это сделано с LPCTSTR? Начнём с того, что метод GetBuffer удобен тем, что позволяет дополнительно указать минимально необходимый размер буфера. Но есть и более глубокая причина. Пожалуй, это как раз тот случай, когда надо сказать: всё хорошо, что хорошо в меру. Как уже отмечалось, негативная сторона «продвинутых» возможностей C++, заключается в том, что за вроде бы простым выражением может скрываться очень большая работа. В данном случае ситуация ещё хуже: появляются два внешне очень похожих оператора: но один из них фактически ничего не делает, а другой наоборот делает большую работу, связанную с соблюдением принципа копирования при записи. Использование обычного метода вместо переопределённого оператора позволяет оставить эту деятельность на виду, и не дать программисту перепутать эти операторы. Кроме того, пара GetBuffer/ReleaseBuffer позволит чётко ограничить тот кусок кода, где происходит работа со строкой напрямую.
Кстати, хочу обратить внимание на то, как важно использовать атрибут const. Приведённый пример показывает, насколько могут различаться реализации двух методов, отличающихся только этим атрибутом. К сожалению, как показывает практика, встречаются программисты, даже не новички, которые полностью его игнорируют
Особенно много неприятностей это доставляет, когда написанный таким программистом код или библиотеку приходится совмещать с полноценным кодом, где аккуратно используется const. Поэтому призываю: используйте const!!! Этот атрибут позволяет сделать контроль типов более точным и тонким, а логику вашей программы более строгой и изящной
К сожалению, как показывает практика, встречаются программисты, даже не новички, которые полностью его игнорируют. Особенно много неприятностей это доставляет, когда написанный таким программистом код или библиотеку приходится совмещать с полноценным кодом, где аккуратно используется const. Поэтому призываю: используйте const!!! Этот атрибут позволяет сделать контроль типов более точным и тонким, а логику вашей программы более строгой и изящной.