Что означает «const static» в C и C ++? - PullRequest
99 голосов
/ 07 октября 2008
const static int foo = 42;

Я видел это в некотором коде здесь, в StackOverflow, и я не мог понять, что он делает. Затем я увидел некоторые запутанные ответы на других форумах. Мое лучшее предположение, что он используется в C, чтобы скрыть константу foo от других модулей. Это правильно? Если это так, зачем кому-то использовать его в контексте C ++, где вы можете просто сделать это private?

Ответы [ 12 ]

187 голосов
/ 07 октября 2008

Многие люди дали основной ответ, но никто не указал, что в C ++ const по умолчанию установлено значение static на уровне namespace (а некоторые дали неверную информацию). См. Раздел 3.5.3 стандарта C ++ 98.

Сначала немного фона:

Единица перевода: Исходный файл после препроцессора (рекурсивно) включал все свои включаемые файлы.

Статическая связь: Символ доступен только в пределах единицы перевода.

Внешняя связь: Символ доступен в других единицах перевода.

На namespace уровне

Сюда входит глобальное пространство имен или глобальные переменные .

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

На функциональном уровне

static означает, что значение поддерживается между вызовами функций.
Семантика переменных функции static аналогична глобальным переменным в том, что они находятся в сегменте данных программы (а не в стеке или куче), подробнее см. в этом вопросе подробности о static времени жизни переменных.

На class уровне

static означает, что значение является общим для всех экземпляров класса, а const означает, что оно не изменяется.

96 голосов
/ 07 октября 2008

Используется в C и C ++.

Как вы уже догадались, часть static ограничивает область действия этой единицей компиляции . Это также обеспечивает статическую инициализацию. const просто говорит компилятору не позволять никому изменять его. Эта переменная либо помещается в данные, либо в сегмент bss в зависимости от архитектуры и может находиться в памяти, помеченной только для чтения.

Все это то, как C обрабатывает эти переменные (или как C ++ обрабатывает переменные пространства имен). В C ++ член, помеченный static, является общим для всех экземпляров данного класса. Является ли это частным или нет, не влияет на тот факт, что одна переменная является общей для нескольких экземпляров. Наличие const там предупредит вас, если какой-либо код попытается изменить это.

Если бы он был строго закрытым, то каждый экземпляр класса получал бы свою собственную версию (несмотря на оптимизатор).

41 голосов
/ 07 октября 2008

Эта строка кода может фактически появляться в нескольких различных контекстах, и хотя она ведет себя примерно одинаково, есть небольшие различия.

Область пространства имен

// foo.h
static const int i = 0;

'i' будет отображаться в каждой единице перевода, которая включает заголовок. Однако, если вы на самом деле не используете адрес объекта (например, '&i'), я вполне уверен, что компилятор будет воспринимать 'i' просто как безопасный тип 0. Если еще две единицы перевода принимают '&i', то адрес будет отличаться для каждой единицы перевода.

// foo.cc
static const int i = 0;

'i' имеет внутреннюю связь, поэтому на нее нельзя ссылаться извне этой единицы перевода. Однако, если вы не используете его адрес, он, скорее всего, будет считаться безопасным с точки зрения типов 0.

Стоит отметить, что следующая декларация:

const int i1 = 0;

равно точно так же, как static const int i = 0. Переменная в пространстве имен, объявленная с const и не объявленная явно с extern, является неявно статической. Если вы подумаете об этом, то комитет C ++ намеревался разрешить объявление переменных const в заголовочных файлах без необходимости использования ключевого слова static во избежание нарушения ODR.

Область применения класса

class A {
public:
  static const int i = 0;
};

В приведенном выше примере стандарт явно указывает, что «i» не нужно определять, если его адрес не требуется. Другими словами, если вы используете только 'i' как типобезопасный 0, компилятор не определит его. Одно из различий между версиями класса и пространства имен состоит в том, что адрес «i» (если он используется в двух или более единицах перевода) будет одинаковым для члена класса. Если используется адрес, у вас должно быть определение для него:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address
18 голосов
/ 07 октября 2008

Это небольшая оптимизация пространства.

Когда вы говорите

const int foo = 42;

Вы не определяете константу, а создаете переменную только для чтения. Компилятор достаточно умен, чтобы использовать 42 всякий раз, когда видит foo, но он также выделяет для него пространство в инициализированной области данных. Это сделано потому, что, как определено, foo имеет внешнюю связь. Другой модуль компиляции может сказать:

extern const int foo;

Чтобы получить доступ к его значению. Это не очень хорошая практика, так как этот модуль компиляции не имеет представления о значении foo. Он просто знает, что это const int, и должен перезагружать значение из памяти при каждом его использовании.

Теперь, объявив, что это статично:

static const int foo = 42;

Компилятор может выполнить свою обычную оптимизацию, но он также может сказать: «Эй, никто за пределами этого модуля компиляции не может видеть foo, и я знаю, что всегда 42, поэтому нет необходимости выделять для него место».

Следует также отметить, что в C ++ предпочтительным способом предотвращения выхода имен из текущего модуля компиляции является использование анонимного пространства имен:

namespace {
    const int foo = 42; // same as static definition above
}
6 голосов
/ 07 октября 2008

В нем отсутствует «int». Должно быть:

const static int foo = 42;

В C и C ++ объявляется целочисленная константа со значением области действия локального файла 42.

Почему 42? Если вы еще не знаете (и вам трудно в это поверить), это ссылка на Ответ жизни, вселенной и всему .

3 голосов
/ 08 октября 2008

Ко всем отличным ответам я хочу добавить небольшую деталь:

Если вы пишете плагины (например, библиотеки DLL или библиотеки .so для загрузки системой САПР), то static спасает жизнь, что позволяет избежать конфликтов имен, таких как:

  1. Система CAD загружает плагин A, который имеет «const int foo = 42;» в нем.
  2. Система загружает плагин B, который имеет "const int foo = 23;" в нем.
  3. В результате плагин B будет использовать значение 42 для foo, потому что загрузчик плагинов поймет, что уже существует «foo» с внешней связью.

Еще хуже: шаг 3 может вести себя по-разному в зависимости от оптимизации компилятора, механизма загрузки плагина и т. Д.

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

3 голосов
/ 07 октября 2008

В С ++,

static const int foo = 42;

является предпочтительным способом определения и использования констант. То есть используйте это, а не

#define foo 42

потому что это не подрывает систему безопасности типов.

2 голосов
/ 11 мая 2016

Согласно спецификации C99 / GNU99:

  • static

    • - спецификатор класса хранения

    • объекты области действия уровня файла по умолчанию имеют внешнюю связь

    • объекты области уровня файла со статическим спецификатором имеют внутреннюю связь
  • const

    • является квалификатором типа (является частью типа)

    • ключевое слово применено к непосредственному левому экземпляру - т.е.

      • MyObj const * myVar; - неквалифицированный указатель на постоянный квалифицированный тип объекта

      • MyObj * const myVar; - постоянный квалифицированный указатель на неквалифицированный тип объекта

    • Крайнее левое использование - применяется к типу объекта, не является переменной

      • const MyObj * myVar; - неквалифицированный указатель на квалифицированный тип объекта const

THUS:

static NSString * const myVar; - постоянный указатель на неизменяемую строку с внутренней связью.

Отсутствие ключевого слова static сделает имя переменной глобальным и может привести к конфликтам имен в приложении.

2 голосов
/ 07 октября 2008

Это глобальная константа, видимая / доступная только в модуле компиляции (файл .cpp). Кстати, использование статического для этой цели не рекомендуется. Лучше использовать анонимное пространство имен и перечисление:

namespace
{
  enum
  {
     foo = 42
  };
}
2 голосов
/ 07 октября 2008

Да, она скрывает переменную в модуле от других модулей. В C ++ я использую его, когда я не хочу / не нужно изменять файл .h, который вызовет ненужную перестройку других файлов. Кроме того, я сначала поставил статический:

static const int foo = 42;

Кроме того, в зависимости от его использования, компилятор даже не выделит для него память и просто "встроит" значение, в котором он используется. Без статики компилятор не может предположить, что он не используется в другом месте и не может быть встроенным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...