Intereting Posts
Как очистить файл вывода stdlib на win32? Несколько консольных окон из одного консольного приложения Win32 Как правильно преобразовать unsigned char в CString и еще раз, обратное преобразование в результат CString в unsigned char? Почему это плохая идея использовать «новое»? Копирование из одного динамически выделенного массива в другой C ++ Перегрузка унарного оператора & Получение исключения с плавающей запятой: 8 Технически, как работают вариационные функции? Как работает printf? Подача линии и возврат каретки Нужен совет по реализации ограниченного времени судебного разбирательства C ++ Добавление 2 массивов вместе быстро memcpy / memmove члену объединения, устанавливает ли это ‘активный’ член? Проблема с прокруткой QGraphicsView Неявное преобразование с оператором Почему функции уведомления и ожидания std :: condition_variable нуждаются в заблокированном мьютексе

C / C ++ Более редкие ключевые слова – регистр, нестабильность, внешний, явный

Можете ли вы дать мне быстрый обзор того, для чего используются эти 4 ключевых слова и почему?

Я понимаю основы, которые Google расскажет вам о регистрации и нестабильности, но хотел бы узнать немного больше (просто практический обзор). Extern и явным образом путают меня немного, так как я никогда не нашел причины, чтобы использовать их самостоятельно, несмотря на то, что делал довольно низкоуровневый код встроенных систем. Опять же, я могу google, но я бы предпочел бы быстрое, практическое резюме от эксперта, так что это все в моих мыслях.

внешний

extern перегружен для нескольких целей. Для глобальных переменных это означает, что он объявляет переменную, а не определяет ее. Это полезно для размещения глобальных переменных в заголовках. Если вы поместите это в заголовок:

 int someInteger; 

Каждый .cpp-файл, который включает этот заголовок, будет пытаться иметь собственный someInteger . Это вызовет ошибку компоновщика. Объявляя это с помощью extern , все, что вы говорите, это то, что где-то в коде будет someInteger то someInteger :

 extern int someInteger; 

Теперь в файле .cpp вы можете определить int someInteger , так что будет только одна его копия.

Существует также extern "C" , который используется для указания того, что определенные функции используют правила C-linkage, а не C ++. Это полезно для взаимодействия с библиотеками и кодом, скомпилированным как C.

В C ++ 0x также будут объявлены extern template . Это противоположность явного создания шаблона. Когда вы это сделаете:

 template class std::vector; 

Вы предлагаете компилятору создать экземпляр этого шаблона прямо сейчас. Обычно создание экземпляра задерживается до первого использования шаблона. В C ++ 0x вы можете сказать:

 extern template class std::vector; 

Это говорит компилятору не создавать экземпляр этого шаблона в этом .cpp-файле, никогда . Таким образом, вы можете контролировать, где создаются шаблоны. Разумное использование этого может существенно улучшить время компиляции.

явный

Это используется для предотвращения автоматических преобразований типов. Если у вас есть class ClassName со следующим конструктором:

 ClassName(int someInteger); 

Это означает, что если у вас есть функция, которая принимает ClassName , пользователь может вызвать ее с помощью int , и преобразование будет выполнено автоматически.

 void SomeFunc(const ClassName &className); SomeFunc(3); 

Это законно, потому что ClassName имеет конструктор преобразования, который принимает целое число. Это то, как функции, которые принимают std::string также могут принимать char* ; std::string имеет конструктор, который принимает char* .

Однако большую часть времени вам не нужны неявные преобразования, подобные этому. Вы обычно хотите, чтобы преобразования были явными. Да, это иногда полезно, как и для std :: string, но вам нужен способ отключить его для недопустимых преобразований. Введите explicit :

 explicit ClassName(int someInteger); 

Это предотвратит неявные преобразования. Вы все еще можете использовать SomeFunc(ClassName(3)); но SomeFunc(3) больше не будет работать.

BTW: если explicit редок для вас, то вы не используете его почти достаточно. Вы должны использовать его всегда, если вы специально не хотите конверсии. Это не так часто.

летучий

Это предотвращает определенные полезные оптимизации. Обычно, если у вас есть переменная, C / C ++ предположит, что ее содержимое изменится только в том случае, если оно явно изменит их. Поэтому, если вы объявите int someInteger; как глобальная переменная, компиляторы C / C ++ могут кэшировать значение локально, а не постоянно получать доступ к значению каждый раз, когда вы его используете.

Иногда вы хотите остановить это. В таких случаях вы используете volatile ; это предотвращает эти оптимизации.

регистр

Это всего лишь намек. Он сообщает компилятору попытаться поместить данные переменной в регистр. Это по существу не нужно; компиляторы лучше, чем вы решаете, что должно и не должно быть регистром.

register используется как подсказка для компилятора, что переменная должна храниться в регистре, а не в стеке. Компиляторы часто игнорируют это и делают все, что захотят; переменные будут распределяться в регистры, если это вообще возможно.

volatile указывает, что память может измениться, если программа фактически ничего не делает. Это еще один намек на компилятор, что он должен избегать оптимизации доступа к этому местоположению. Например, если у вас есть две последовательные записи в одно и то же место без промежуточных чтений, компилятор может оптимизировать первый. Однако, если местоположение, на которое вы пишете, является аппаратным регистром, вам нужно, чтобы каждая запись проходила точно так же, как написано. Так volatile походит на высказывание «просто поверьте мне на этом».

extern указывает, что определение происходит за пределами текущего файла. Полезно для глобальных переменных, но обычно подразумевается в декларации. Как отмечает Блинди, это также полезно для указания функции, которая должна иметь C-связь. Функция с C-связью будет скомпилирована с использованием ее фактического имени в качестве ее символа в выходном исполняемом файле. Функции C ++ include в себя больше информации, например, типы их аргументов в своих символах. Вот почему перегрузка работает на C ++, но не на C.

explicit применяется к конструкторам C ++. Это означает, что конструктор не следует называть неявным. Например, скажем, у вас есть class Array с конструктором, который принимает целочисленную capacity . Вы не хотите, чтобы целочисленные значения были неявно преобразованы в объекты Array только потому, что существует целочисленный конструктор.

register в основном игнорируется в наши дни, но предполагается, что он должен намекнуть компилятору, что вы предпочли бы, чтобы переменная была в регистре, а не в стеке. Оптимизаторы делают такую ​​хорошую работу в эти дни, что сейчас это не имеет значения.

volatile сообщает компилятору не предполагать, что значение изменяется только от текущего streamа, поэтому он всегда будет считывать фактическое значение памяти, а не кэшировать его между чтением. Это полезно для многопоточных приложений, но вам все равно лучше использовать примитивы ОС (события, семафоры и т. Д.).

extern не определяет значение, он только объявляет его. Он в основном используется для экспорта и импорта функций из библиотек DLL или разделяемых библиотек ( .a ). Побочный эффект также позволяет отключить манипуляцию имени для C ++ с использованием extern "C" .

И explicit позволяет указать, что конструктор должен быть явным, в отличие от неявных конструкторов преобразования (где вы можете написать CMyClass val=10; если он имеет неявный конструктор, который принимает int ).

register используется для указания компилятору использовать регистр для хранения этого значения, современные компиляторы оптимизируют его использование во время циклов обработки и т. п.

volatile – это переменные, которые могут быть изменены внешним процессом или во время многопоточных приложений.

extern сообщает компоновщику, что переменная определена в другом файле.

Явное указание компилятору не допускать неявных преобразований типа.

1: Регистр используется, когда вы хотите принудительно сохранить значение в регистре, а не хранить его в ОЗУ. Например, вы можете сделать следующее:

 register int x; 

Это позволит компилятору узнать, что вы хотите, чтобы этот int был помещен в регистр CPU, что должно дать вам быстрый доступ к нему. Тем не менее, вашему компилятору разрешено игнорировать это ключевое слово в процессе оптимизации, и в большинстве случаев хорошие компиляторы будут помещать переменные в регистры, если они должны быть в любом случае. На мой взгляд, это ключевое слово избыточное.

2: Летучие подсказки компилятору о том, что переменная будет изменяться регулярно, например, если вы определите некоторый float как volatile:

 volatile float flt; 

Это говорит компилятору, что вы хотите выполнить оптимизацию, характерную для регулярного изменения переменных. В нем также указывается, что переменная может меняться без ввода активной программы. Опять же, ключевое слово redundant в настоящее время (за исключением многопоточного программирования).

3: Extern сообщает компилятору, что определение находится в другом файле, к которому была объявлена ​​переменная, например, у вас может быть общий файл заголовка, который вы включаете в большинство ваших файлов, здесь вы можете захотеть объявить некоторые глобальные указатели , поэтому вы должны сделать следующее:

 extern MyClass* g_pClassPointer; 

Затем вы переходите к объявлению в верхней части файла cpp, в котором реализован MyClass :

 MyClass* g_pClassPointer = nullptr; 

Ключевое слово extern также используется для объявления компилятору, что вы используете исходный код C или код ASM, например, вы можете сделать следующее:

 extern __asm { mov eax, 2 mov ebx, 3 add eax, ebx } 

Или, если вы просто хотите использовать исходный код C, вы можете использовать extern "C" и ваш компилятор это узнает.

4: Явный для использования, когда вы не хотите неявного преобразования внутри конструктора, для получения дополнительной информации см. Эту тему . Он в основном используется для целей отладки и для обеспечения выполнения более строгих правил при выполнении ООП.