Раздел 9.4.2, Статические члены данных, стандартных состояний C ++:
Если static
элемент данных имеет const
целочисленный или const
тип перечисления, его объявление в определении класса может указывать const-initializer , который должен быть выражением целочисленной константы.
Следовательно, возможно, что значение статического члена данных будет включено «в класс» (под этим я предполагаю, что вы имеете в виду в объявлении класса). Однако тип члена статических данных должен быть const
целочисленным или const
типом перечисления. Причина, по которой значения статических членов-членов других типов не могут быть указаны в объявлении класса, заключается в том, что, скорее всего, требуется нетривиальная инициализация (то есть запуск конструктора).
Представьте, если бы следующее было законно:
// my_class.hpp
#include <string>
class my_class
{
public:
static std::string str = "static std::string";
//...
Каждый объектный файл, соответствующий файлам CPP, которые включают этот заголовок, будет иметь не только копию пространства хранения для my_class::str
(состоящего из sizeof(std::string)
байтов), но также и «секцию ctor», которая вызывает std::string
конструктор, принимающий C-строку. Каждая копия пространства хранения для my_class::str
будет идентифицироваться общей меткой, так что компоновщик может теоретически объединить все копии пространства хранения в одну. Однако компоновщик не сможет изолировать все копии кода конструктора в секциях ctor объектных файлов. Это все равно что просить компоновщика удалить весь код для инициализации str
при компиляции следующего:
std::map<std::string, std::string> map;
std::vector<int> vec;
std::string str = "test";
int c = 99;
my_class mc;
std::string str2 = "test2";
EDIT Поучительно посмотреть на вывод ассемблера g ++ для следующего кода:
// SO4547660.cpp
#include <string>
class my_class
{
public:
static std::string str;
};
std::string my_class::str = "static std::string";
Код сборки можно получить, выполнив:
g++ -S SO4547660.cpp
Просматривая файл SO4547660.s
, сгенерированный g ++, вы можете увидеть, что для такого небольшого исходного файла имеется много кода.
__ZN8my_class3strE
- это метка места для хранения my_class::str
. Существует также источник сборки функции __static_initialization_and_destruction_0(int, int)
с меткой __Z41__static_initialization_and_destruction_0ii
. Эта функция является особенной для g ++, но просто знайте, что g ++ будет вызывать ее до того, как будет выполнен любой неинициализаторский код. Обратите внимание, что реализация этой функции вызывает __ZNSsC1EPKcRKSaIcE
. Это искаженный символ для std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
.
Возвращаясь к приведенному выше гипотетическому примеру и используя эти детали, каждый объектный файл, соответствующий файлу CPP, который включает my_class.hpp
, будет иметь метку
__ZN8my_class3strE
для sizeof(std::string)
байт, а также код сборки для вызова __ZNSsC1EPKcRKSaIcE
в рамках реализации функции __static_initialization_and_destruction_0(int, int)
. Компоновщик может легко объединить все вхождения __ZN8my_class3strE
, но он не может изолировать код, вызывающий __ZNSsC1EPKcRKSaIcE
в реализации объектного файла __static_initialization_and_destruction_0(int, int)
.